1 
2 using System;
3 using System.Collections.Generic;
4 using System.Linq;
5 
6 namespace Antmicro.Renode.Utilities
7 {
8     public class BitStream
9     {
10         public static BitStream Empty = new BitStream();
11 
BitStream(IEnumerable<byte> b = null)12         public BitStream(IEnumerable<byte> b = null)
13         {
14             segments = (b == null)
15                 ? new List<byte>()
16                 : new List<byte>(b);
17 
18             Length = (uint)segments.Count * 8;
19         }
20 
Clear()21         public void Clear()
22         {
23             Length = 0;
24             segments.Clear();
25         }
26 
Append(byte[] bytes)27         public BitStream Append(byte[] bytes)
28         {
29             foreach(var b in bytes)
30             {
31                 Append(b);
32             }
33 
34             return this;
35         }
36 
Append(byte b)37         public BitStream Append(byte b)
38         {
39             EnsureIsAligned();
40             segments.Add(b);
41             Length += 8;
42             return this;
43         }
44 
Append(short s)45         public BitStream Append(short s)
46         {
47             EnsureIsAligned();
48             segments.Add((byte)s);
49             segments.Add((byte)(s >> 8));
50             Length += 16;
51             return this;
52         }
53 
Append(ushort s)54         public BitStream Append(ushort s)
55         {
56             return Append((short)s);
57         }
58 
EnsureIsAligned()59         private void EnsureIsAligned()
60         {
61             var offset = (int)(Length % BitsPerSegment);
62             if(offset != 0)
63             {
64                 throw new ArgumentException("Appending in an unaligned state is not supported yet.");
65             }
66         }
67 
AppendBit(bool state)68         public BitStream AppendBit(bool state)
69         {
70             var offset = (int)(Length % BitsPerSegment);
71             if(offset == 0)
72             {
73                 segments.Add(0);
74             }
75 
76             if(state)
77             {
78                 var segmentId = (int)(Length / BitsPerSegment);
79                 segments[segmentId] |= (byte)(1 << offset);
80             }
81 
82             Length++;
83 
84             return this;
85         }
86 
87         // TODO: this is not the most efficient way of doing things
AppendMaskedValue(uint value, uint offset = 0, uint length = 32, bool msbFirst = true)88         public void AppendMaskedValue(uint value, uint offset = 0, uint length = 32, bool msbFirst = true)
89         {
90             // TODO: add checks
91             var bits = BitHelper.GetBits(value).Skip((int)offset).Take((int)length);
92             if(!msbFirst)
93             {
94                 bits = bits.Reverse();
95             }
96             foreach(var bit in bits)
97             {
98                 AppendBit(bit);
99             }
100         }
101 
AsByte(uint offset = 0, int length = 8)102         public byte AsByte(uint offset = 0, int length = 8)
103         {
104             if(length > 8)
105             {
106                 length = 8;
107             }
108 
109             if(offset >= Length || length <= 0)
110             {
111                 return 0;
112             }
113 
114             byte result;
115             var firstSegment = (int)(offset / BitsPerSegment);
116             var segmentOffset = (int)(offset % BitsPerSegment);
117             if(segmentOffset == 0)
118             {
119                 // aligned access
120                 result = segments[firstSegment];
121             }
122             else
123             {
124                 var s1 = segments[firstSegment];
125                 var s2 = ((firstSegment + 1) < segments.Count) ? segments[firstSegment + 1] : (byte)0;
126                 result = (byte)((s1 << segmentOffset)
127                             | (s2 >> (BitsPerSegment - segmentOffset)));
128             }
129 
130             return (length == 8)
131                 ? result
132                 : (byte)(result & BitHelper.CalculateMask((int)length, 0));
133         }
134 
AsByteArray()135         public byte[] AsByteArray()
136         {
137             return segments.ToArray();
138         }
139 
AsByteArray(uint length)140         public byte[] AsByteArray(uint length)
141         {
142             return (length % 8 == 0)
143                 ? segments.Take((int)(length / 8)).ToArray()
144                 : AsByteArray(0, length);
145         }
146 
AsByteArray(uint offset, uint length)147         public byte[] AsByteArray(uint offset, uint length)
148         {
149             var result = new byte[length];
150             for(var i = 0u; i < length; i++)
151             {
152                 result[i] = AsByte(offset + 8 * i);
153             }
154             return result;
155         }
156 
AsUInt32(uint offset = 0, int length = 32)157         public uint AsUInt32(uint offset = 0, int length = 32)
158         {
159             if(length > 32)
160             {
161                 length = 32;
162             }
163 
164             return (AsByte(offset, length)
165                     | (uint)AsByte(offset + 8, length - 8) << 8
166                     | (uint)AsByte(offset + 16, length - 16) << 16
167                     | (uint)AsByte(offset + 24, length - 24) << 24);
168         }
169 
AsUInt64(uint offset = 0, int length = 64)170         public ulong AsUInt64(uint offset = 0, int length = 64)
171         {
172             if(length > 64)
173             {
174                 length = 64;
175             }
176 
177             return AsUInt32(offset, length)
178                 | (ulong)AsUInt32(offset + 32, length - 32) << 32;
179         }
180 
ToString()181         public override string ToString()
182         {
183             return Misc.PrettyPrintCollectionHex(segments);
184         }
185 
186         public uint Length { get; private set; }
187 
188         private readonly List<byte> segments;
189 
190         private const int BitsPerSegment = 8;
191     }
192 }