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 }