1 //
2 // Copyright (c) 2010-2025 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using System.Collections.Generic;
10 using System.Runtime.CompilerServices;
11 using System.Linq;
12 using Antmicro.Renode.Debugging;
13 
14 namespace Antmicro.Renode.Utilities
15 {
16     public static class BitHelper
17     {
GetMostSignificantSetBitIndex(ulong value)18         public static int GetMostSignificantSetBitIndex(ulong value)
19         {
20             var mask = 1UL << 63;
21             var result = 63;
22 
23             while(mask != 0)
24             {
25                 if((value & mask) != 0)
26                 {
27                     return result;
28                 }
29 
30                 mask >>= 1;
31                 result--;
32             }
33 
34             return -1;
35         }
36 
Bit(byte b)37         public static long Bit(byte b)
38         {
39             return (0x1 << b);
40         }
41 
Bits(int position, int width)42         public static ulong Bits(int position, int width)
43         {
44             // Force 0 with width = 64 and up because (1UL << 64) is 1
45             var pow2 = width < 64 ? (1UL << width) : 0;
46             var nbits = pow2 - 1;
47             // Same as above
48             return position < 64 ? (nbits << position) : 0;
49         }
50 
IsBitSet(uint reg, byte bit)51         public static bool IsBitSet(uint reg, byte bit)
52         {
53             return ((0x1 << bit) & reg) != 0;
54         }
55 
IsBitSet(ulong reg, byte bit)56         public static bool IsBitSet(ulong reg, byte bit)
57         {
58             return ((0x1UL << bit) & reg) != 0;
59         }
60 
SetBitsFrom(uint source, uint newValue, int position, int width)61         public static uint SetBitsFrom(uint source, uint newValue, int position, int width)
62         {
63             var mask = ((1u << width) - 1) << position;
64             var bitsToSet = newValue & mask;
65             return source | bitsToSet;
66         }
67 
SetBitsFrom(ulong source, ulong newValue, int position, int width)68         public static ulong SetBitsFrom(ulong source, ulong newValue, int position, int width)
69         {
70             var mask = ((1UL << width) - 1) << position;
71             var bitsToSet = newValue & mask;
72             return source | bitsToSet;
73         }
74 
ClearBits(ref uint reg, params byte[] bits)75         public static void ClearBits(ref uint reg, params byte[] bits)
76         {
77             uint mask = uint.MaxValue;
78             foreach(var bit in bits)
79             {
80                 mask -= 1u << bit;
81             }
82             reg &= mask;
83         }
84 
ClearBits(ref ulong reg, params byte[] bits)85         public static void ClearBits(ref ulong reg, params byte[] bits)
86         {
87             ulong mask = ulong.MaxValue;
88             foreach(var bit in bits)
89             {
90                 mask -= 1u << bit;
91             }
92             reg &= mask;
93         }
94 
ClearBits(ref uint reg, int position, int width)95         public static void ClearBits(ref uint reg, int position, int width)
96         {
97             uint mask = uint.MaxValue;
98             for(var i = 0; i < width; i++)
99             {
100                 mask -= 1u << (position + i);
101             }
102             reg &= mask;
103         }
104 
ClearBits(ref ulong reg, int position, int width)105         public static void ClearBits(ref ulong reg, int position, int width)
106         {
107             ulong mask = ulong.MaxValue;
108             for(var i = 0; i < width; i++)
109             {
110                 mask -= 1u << (position + i);
111             }
112             reg &= mask;
113         }
114 
SetBits(ref uint reg, int position, int width)115         public static void SetBits(ref uint reg, int position, int width)
116         {
117             var mask = 0x0u;
118             for(var i = 0; i < width; i++)
119             {
120                 mask += 1u << (position + i);
121             }
122             reg |= mask;
123         }
124 
SetBits(ref ulong reg, int position, int width)125         public static void SetBits(ref ulong reg, int position, int width)
126         {
127             var mask = 0x0u;
128             for(var i = 0; i < width; i++)
129             {
130                 mask += 1u << (position + i);
131             }
132             reg |= mask;
133         }
134 
ReplaceBits(ref uint destination, uint source, int width, int destinationPosition = 0, int sourcePosition = 0)135         public static void ReplaceBits(ref uint destination, uint source, int width, int destinationPosition = 0, int sourcePosition = 0)
136         {
137             if(width < 0 || width > 32)
138             {
139                 throw new ArgumentException("width not in [0,32]");
140             }
141             uint mask = (uint)((1ul << width) - 1);
142             source &= mask << sourcePosition;
143             destination &= ~(mask << destinationPosition);
144 
145             var positionDifference = sourcePosition - destinationPosition;
146             destination |= (positionDifference >= 0)
147                 ? (source >> positionDifference)
148                 : (source << -positionDifference);
149         }
150 
ReplaceBits(ref ulong destination, ulong source, int width, int destinationPosition = 0, int sourcePosition = 0)151         public static void ReplaceBits(ref ulong destination, ulong source, int width, int destinationPosition = 0, int sourcePosition = 0)
152         {
153             if(width < 0 || width > 64)
154             {
155                 throw new ArgumentException("width not in [0,64]");
156             }
157             ulong mask = (width == 64)
158                 ? ulong.MaxValue
159                 : (ulong)((1ul << width) - 1);
160 
161             source &= mask << sourcePosition;
162             destination &= ~(mask << destinationPosition);
163 
164             var positionDifference = sourcePosition - destinationPosition;
165             destination |= (positionDifference >= 0)
166                 ? (source >> positionDifference)
167                 : (source << -positionDifference);
168         }
169 
ReplaceBits(this byte destination, byte source, int width, int destinationPosition = 0, int sourcePosition = 0)170         public static byte ReplaceBits(this byte destination, byte source, int width, int destinationPosition = 0, int sourcePosition = 0)
171         {
172             if(width < 0 || width > 8)
173             {
174                 throw new ArgumentException("width not in [0,8]");
175             }
176             var mask = (byte)((1u << width) - 1);
177             source &= (byte)(mask << sourcePosition);
178             destination &= (byte)(~(mask << destinationPosition));
179 
180             var positionDifference = sourcePosition - destinationPosition;
181             return (byte)(destination | ((positionDifference >= 0)
182                 ? (byte)(source >> positionDifference)
183                 : (byte)(source << -positionDifference)));
184         }
185 
ReplaceBits(this uint destination, uint source, int width, int destinationPosition = 0, int sourcePosition = 0)186         public static uint ReplaceBits(this uint destination, uint source, int width, int destinationPosition = 0, int sourcePosition = 0)
187         {
188             if(width < 0 || width > 32)
189             {
190                 throw new ArgumentException("width not in [0,32]");
191             }
192             uint mask = (uint)((1ul << width) - 1);
193             source &= mask << sourcePosition;
194             destination &= ~(mask << destinationPosition);
195 
196             var positionDifference = sourcePosition - destinationPosition;
197             return destination | ((positionDifference >= 0)
198                 ? (source >> positionDifference)
199                 : (source << -positionDifference));
200         }
201 
ReplaceBits(this ulong destination, ulong source, int width, int destinationPosition = 0, int sourcePosition = 0)202         public static ulong ReplaceBits(this ulong destination, ulong source, int width, int destinationPosition = 0, int sourcePosition = 0)
203         {
204             if(width < 0 || width > 64)
205             {
206                 throw new ArgumentException("width not in [0,64]");
207             }
208             ulong mask = (width == 64 ? 0 : (1ul << width)) - 1;
209             source &= mask << sourcePosition;
210             destination &= ~(mask << destinationPosition);
211 
212             var positionDifference = sourcePosition - destinationPosition;
213             return destination | ((positionDifference >= 0)
214                 ? (source >> positionDifference)
215                 : (source << -positionDifference));
216         }
217 
AreAnyBitsSet(uint reg, int position, int width)218         public static bool AreAnyBitsSet(uint reg, int position, int width)
219         {
220             var mask = CalculateMask(width, position);
221             return (reg & mask) != 0;
222         }
223 
AreAnyBitsSet(ulong reg, int position, int width)224         public static bool AreAnyBitsSet(ulong reg, int position, int width)
225         {
226             var mask = CalculateQuadWordMask(width, position);
227             return (reg & mask) != 0;
228         }
229 
AreAllBitsSet(ulong reg, int position, int width)230         public static bool AreAllBitsSet(ulong reg, int position, int width)
231         {
232             var mask = CalculateQuadWordMask(width, position);
233             return (~reg & mask) == 0;
234         }
235 
UpdateWithShifted(ref uint reg, uint newValue, int position, int width)236         public static void UpdateWithShifted(ref uint reg, uint newValue, int position, int width)
237         {
238             UpdateWith(ref reg, newValue << position, position, width);
239         }
240 
UpdateWithShifted(ref ulong reg, ulong newValue, int position, int width)241         public static void UpdateWithShifted(ref ulong reg, ulong newValue, int position, int width)
242         {
243             UpdateWith(ref reg, newValue << position, position, width);
244         }
245 
UpdateWithMasked(ref uint reg, uint newValue, uint mask)246         public static void UpdateWithMasked(ref uint reg, uint newValue, uint mask)
247         {
248             reg = (reg & ~mask) | (newValue & mask);
249         }
250 
UpdateWithMasked(ref ulong reg, ulong newValue, ulong mask)251         public static void UpdateWithMasked(ref ulong reg, ulong newValue, ulong mask)
252         {
253             reg = (reg & ~mask) | (newValue & mask);
254         }
255 
UpdateWith(ref uint reg, uint newValue, int position, int width)256         public static void UpdateWith(ref uint reg, uint newValue, int position, int width)
257         {
258             var mask = CalculateMask(width, position);
259             reg = (reg & ~mask) | (newValue & mask);
260         }
261 
UpdateWith(ref ulong reg, ulong newValue, int position, int width)262         public static void UpdateWith(ref ulong reg, ulong newValue, int position, int width)
263         {
264             var mask = CalculateQuadWordMask(width, position);
265             reg = (reg & ~mask) | (newValue & mask);
266         }
267 
OrWith(ref uint reg, uint newValue, int position, int width)268         public static void OrWith(ref uint reg, uint newValue, int position, int width)
269         {
270             var mask = CalculateMask(width, position);
271             reg |= (newValue & mask);
272         }
273 
OrWith(ref ulong reg, ulong newValue, int position, int width)274         public static void OrWith(ref ulong reg, ulong newValue, int position, int width)
275         {
276             var mask = CalculateQuadWordMask(width, position);
277             reg |= (newValue & mask);
278         }
279 
AndWithNot(ref uint reg, uint newValue, int position, int width)280         public static void AndWithNot(ref uint reg, uint newValue, int position, int width)
281         {
282             var mask = CalculateMask(width, position);
283             reg &= ~(newValue & mask);
284         }
285 
AndWithNot(ref ulong reg, ulong newValue, int position, int width)286         public static void AndWithNot(ref ulong reg, ulong newValue, int position, int width)
287         {
288             var mask = CalculateQuadWordMask(width, position);
289             reg &= ~(newValue & mask);
290         }
291 
XorWith(ref uint reg, uint newValue, int position, int width)292         public static void XorWith(ref uint reg, uint newValue, int position, int width)
293         {
294             var mask = CalculateMask(width, position);
295             reg ^= (newValue & mask);
296         }
297 
XorWith(ref ulong reg, ulong newValue, int position, int width)298         public static void XorWith(ref ulong reg, ulong newValue, int position, int width)
299         {
300             var mask = CalculateQuadWordMask(width, position);
301             reg ^= (newValue & mask);
302         }
303 
CalculateParity(uint word)304         public static bool CalculateParity(uint word)
305         {
306             word ^= word >> 16;
307             word ^= word >> 8;
308             word ^= word >> 4;
309             word ^= word >> 2;
310             word ^= word >> 1;
311             return (word & 0x1) == 0x1;
312         }
313 
ClearBits(ref byte reg, params byte[] bits)314         public static void ClearBits(ref byte reg, params byte[] bits)
315         {
316             uint mask = 0xFFFFFFFFu;
317             foreach(var bit in bits)
318             {
319                 mask -= 1u << bit;
320             }
321             reg &= (byte)mask;
322         }
323 
ClearBitsIfSet(ref uint reg, uint testValue, params byte[] bits)324         public static void ClearBitsIfSet(ref uint reg, uint testValue, params byte[] bits)
325         {
326             uint mask = 0xFFFFFFFFu;
327             foreach(var bit in bits)
328             {
329                 if(IsBitSet(testValue, bit))
330                 {
331                     mask -= 1u << bit;
332                 }
333             }
334             reg &= mask;
335         }
336 
ClearBitsIfSet(ref byte reg, byte testValue, params byte[] bits)337         public static void ClearBitsIfSet(ref byte reg, byte testValue, params byte[] bits)
338         {
339             uint mask = 0xFFFFFFFFu;
340             foreach(var bit in bits)
341             {
342                 if(IsBitSet(testValue, bit))
343                 {
344                     mask -= 1u << bit;
345                 }
346             }
347             reg &= (byte)mask;
348         }
349 
SetBit(ref ulong reg, byte bit, bool value)350         public static void SetBit(ref ulong reg, byte bit, bool value)
351         {
352             if(value)
353             {
354                 reg |= (0x1ul << bit);
355             }
356             else
357             {
358                 reg &= (ulong.MaxValue - (0x1ul << bit));
359             }
360         }
361 
SetBit(ref uint reg, byte bit, bool value)362         public static void SetBit(ref uint reg, byte bit, bool value)
363         {
364             if(value)
365             {
366                 reg |= (0x1u << bit);
367             }
368             else
369             {
370                 reg &= (0xFFFFFFFFu - (0x1u << bit));
371             }
372         }
373 
SetBit(ref byte reg, byte bit, bool value)374         public static void SetBit(ref byte reg, byte bit, bool value)
375         {
376             if(value)
377             {
378                 reg |= (byte)(0x1 << bit);
379             }
380             else
381             {
382                 reg &= (byte)(0xFFFF - (0x1u << bit));
383             }
384         }
385 
GetSetBits(ulong reg)386         public static IList<int> GetSetBits(ulong reg)
387         {
388             var result = new List<int>();
389             var pos = 0;
390             while(reg > 0)
391             {
392                 if((reg & 1u) == 1)
393                 {
394                     result.Add(pos);
395                 }
396 
397                 reg >>= 1;
398                 pos++;
399             }
400 
401             return result;
402         }
403 
GetSetBitsPretty(ulong reg)404         public static string GetSetBitsPretty(ulong reg)
405         {
406             var setBits = new HashSet<int>(GetSetBits(reg));
407             if(setBits.Count == 0)
408             {
409                 return "(none)";
410             }
411             var beginnings = setBits.Where(x => !setBits.Contains(x - 1)).ToArray();
412             var endings = setBits.Where(x => !setBits.Contains(x + 1)).ToArray();
413             return beginnings.Select((x, i) => endings[i] == x ? x.ToString() : string.Format("{0}-{1}", x, endings[i])).Stringify(", ");
414         }
415 
ForeachBit(ulong reg, Action<byte, bool> action, byte? bitCount = null)416         public static void ForeachBit(ulong reg, Action<byte, bool> action, byte? bitCount = null)
417         {
418             // Iterate bitCount times or all bits in ulong
419             byte iterations = bitCount ?? sizeof(ulong) * 8;
420             for(byte i = 0; i < iterations; i++)
421             {
422                 action(i, IsBitSet(reg, i));
423             }
424         }
425 
ForeachActiveBit(ulong reg, Action<byte> action)426         public static void ForeachActiveBit(ulong reg, Action<byte> action)
427         {
428             byte pos = 0;
429             while(reg > 0)
430             {
431                 if((reg & 1u) == 1)
432                 {
433                     action(pos);
434                 }
435 
436                 reg >>= 1;
437                 pos++;
438             }
439 
440         }
441 
GetLeastSignificantOne(ulong val)442         public static ulong GetLeastSignificantOne(ulong val)
443         {
444             return (ulong)((long)val & -(long)val);
445         }
446 
GetLeastSignificantZero(ulong val)447         public static ulong GetLeastSignificantZero(ulong val)
448         {
449             return GetLeastSignificantOne(~val);
450         }
451 
GetBytesFromValue(byte[] bytes, int offset, ulong val, int typeSize, bool reverse = false)452         public static void GetBytesFromValue(byte[] bytes, int offset, ulong val, int typeSize, bool reverse = false)
453         {
454             if(offset + typeSize > bytes.Length)
455             {
456                 throw new ArgumentOutOfRangeException("The sum of offset and typeSize can't be greater that length of the bytes array.");
457             }
458 
459             int valOffset = 0;
460             for(int i = typeSize - 1; i >= 0; --i)
461             {
462                 int byteIndex = offset + (reverse ? typeSize - 1 - i : i);
463                 bytes[byteIndex] = (byte)((val >> valOffset) & 0xFF);
464                 valOffset += 8;
465             }
466         }
467 
GetBytesFromValue(ulong val, int typeSize, bool reverse = false)468         public static byte[] GetBytesFromValue(ulong val, int typeSize, bool reverse = false)
469         {
470             var result = new byte[typeSize];
471             GetBytesFromValue(result, 0, val, typeSize, reverse);
472             return result;
473         }
474 
GetBits(ulong reg)475         public static bool[] GetBits(ulong reg)
476         {
477             return GetBitsInner(reg, 64);
478         }
479 
GetBits(uint reg)480         public static bool[] GetBits(uint reg)
481         {
482             return GetBitsInner(reg, 32);
483         }
484 
GetBits(ushort reg)485         public static bool[] GetBits(ushort reg)
486         {
487             return GetBitsInner(reg, 16);
488         }
489 
GetBits(byte reg)490         public static bool[] GetBits(byte reg)
491         {
492             return GetBitsInner(reg, 8);
493         }
494 
GetNibbles(ulong reg)495         public static byte[] GetNibbles(ulong reg)
496         {
497             var nibbles = new byte[16];
498             var i = 0;
499             while(reg > 0)
500             {
501                 nibbles[i++] = (byte)(reg & 0xF);
502                 reg >>= 4;
503             }
504             return nibbles;
505         }
506 
GetValue(byte reg, int offset, int size)507         public static byte GetValue(byte reg, int offset, int size)
508         {
509             if(size < 0 || size > 8)
510             {
511                 throw new ArgumentException("size not in [0,8]");
512             }
513             return (byte)(((uint)reg >> offset) & ((0x1ul << size) - 1));
514         }
515 
GetValue(uint reg, int offset, int size)516         public static uint GetValue(uint reg, int offset, int size)
517         {
518             if(size < 0 || size > 32)
519             {
520                 throw new ArgumentException("size not in [0,32]");
521             }
522             return (uint)((reg >> offset) & ((0x1ul << size) - 1));
523         }
524 
GetValue(ulong reg, int offset, int size)525         public static ulong GetValue(ulong reg, int offset, int size)
526         {
527             if(size < 0 || size > 64)
528             {
529                 throw new ArgumentException("size not in [0,64]");
530             }
531             return (ulong)((reg >> offset) & ((size == 64 ? 0 : (0x1ul << size)) - 1));
532         }
533 
GetMaskedValue(uint reg, int maskOffset, int maskSize)534         public static uint GetMaskedValue(uint reg, int maskOffset, int maskSize)
535         {
536             return reg & CalculateMask(maskSize, maskOffset);
537         }
538 
GetMaskedValue(ulong reg, int maskOffset, int maskSize)539         public static ulong GetMaskedValue(ulong reg, int maskOffset, int maskSize)
540         {
541             return reg & CalculateQuadWordMask(maskSize, maskOffset);
542         }
543 
SetMaskedValue(ref uint reg, uint value, int maskOffset, int maskSize)544         public static void SetMaskedValue(ref uint reg, uint value, int maskOffset, int maskSize)
545         {
546             var mask = CalculateMask(maskSize, maskOffset);
547             value <<= maskOffset;
548             value &= mask;
549             reg &= ~mask;
550             reg |= value;
551         }
552 
SetMaskedValue(uint source, uint value, int maskOffset, int maskSize)553         public static uint SetMaskedValue(uint source, uint value, int maskOffset, int maskSize)
554         {
555             SetMaskedValue(ref source, value, maskOffset, maskSize);
556             return source;
557         }
558 
SetMaskedValue(ref ulong reg, ulong value, int maskOffset, int maskSize)559         public static void SetMaskedValue(ref ulong reg, ulong value, int maskOffset, int maskSize)
560         {
561             var mask = CalculateQuadWordMask(maskSize, maskOffset);
562             value <<= maskOffset;
563             value &= mask;
564             reg &= ~mask;
565             reg |= value;
566         }
567 
SetMaskedValue(ulong source, ulong value, int maskOffset, int maskSize)568         public static ulong SetMaskedValue(ulong source, ulong value, int maskOffset, int maskSize)
569         {
570             SetMaskedValue(ref source, value, maskOffset, maskSize);
571             return source;
572         }
573 
GetValueFromBitsArray(IEnumerable<bool> array)574         public static uint GetValueFromBitsArray(IEnumerable<bool> array)
575         {
576             var ret = 0u;
577             var i = 0;
578             foreach(var item in array)
579             {
580                 if(item)
581                 {
582                     ret |= 1u << i;
583                 }
584                 i++;
585             }
586             return ret;
587         }
588 
GetValueFromBitsArray(params bool[] array)589         public static uint GetValueFromBitsArray(params bool[] array)
590         {
591             return GetValueFromBitsArray((IEnumerable<bool>)array);
592         }
593 
ToUInt64(byte[] data, int index, int length, bool reverse)594         public static ulong ToUInt64(byte[] data, int index, int length, bool reverse)
595         {
596             ulong result = 0;
597             if(index + length > data.Length)
598             {
599                 throw new ArgumentException("index/length combination exceeds buffer length");
600             }
601             for(var i = 0; i < length; i++)
602             {
603                 result = (result << 8) | data[index + (reverse ? length - 1 - i : i)];
604             }
605             return result;
606         }
607 
ToUInt32(byte[] data, int index, int length, bool reverse)608         public static uint ToUInt32(byte[] data, int index, int length, bool reverse)
609         {
610             return (uint)ToUInt64(data, index, length, reverse);
611         }
612 
ToUInt16(byte[] data, int index, bool reverse)613         public static ushort ToUInt16(byte[] data, int index, bool reverse)
614         {
615             return (ushort)ToUInt32(data, index, 2, reverse);
616         }
617 
SignExtend(uint value, int size)618         public static uint SignExtend(uint value, int size)
619         {
620             if(value >= (1 << size - 1))
621             {
622                 return 0xFFFFFFFF << size | value;
623             }
624             return value;
625         }
626 
SignExtend(ulong value, int size)627         public static ulong SignExtend(ulong value, int size)
628         {
629             if(value >= (1ul << size - 1))
630             {
631                 return 0xFFFFFFFFFFFFFFFF << size | value;
632             }
633             return value;
634         }
635 
636         [MethodImpl(MethodImplOptions.AggressiveInlining)]
CalculateMask(int width, int position)637         public static uint CalculateMask(int width, int position)
638         {
639             const int MaxWidth = 32;
640             AssertMaskParameters(width, position, MaxWidth);
641             if(width == MaxWidth && position == 0)
642             {
643                 return uint.MaxValue;
644             }
645             return (1u << width) - 1 << position;
646         }
647 
648         [MethodImpl(MethodImplOptions.AggressiveInlining)]
CalculateQuadWordMask(int width, int position)649         public static ulong CalculateQuadWordMask(int width, int position)
650         {
651             const int MaxWidth = 64;
652             AssertMaskParameters(width, position, MaxWidth);
653             if(width == MaxWidth && position == 0)
654             {
655                 return ulong.MaxValue;
656             }
657             return (1ul << width) - 1 << position;
658         }
659 
660         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBitsByByte(uint i)661         public static uint ReverseBitsByByte(uint i)
662         {
663             i = ((i >> 1) & 0x55555555) | ((i & 0x55555555) << 1);
664             i = ((i >> 2) & 0x33333333) | ((i & 0x33333333) << 2);
665             return ((i >> 4) & 0x0F0F0F0F) | ((i & 0x0F0F0F0F) << 4);
666         }
667 
668         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBitsByWord(uint i)669         public static uint ReverseBitsByWord(uint i)
670         {
671             i = ReverseBitsByByte(i);
672             return ((i >> 8) & 0x00FF00FF) | ((i & 0x00FF00FF) << 8);
673         }
674 
675         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBits(byte b)676         public static byte ReverseBits(byte b)
677         {
678             return (byte)ReverseBitsByByte(b);
679         }
680 
681         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBits(ushort s)682         public static ushort ReverseBits(ushort s)
683         {
684             return (ushort)ReverseBitsByWord(s);
685         }
686 
687         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBits(uint i)688         public static uint ReverseBits(uint i)
689         {
690             i = ReverseBitsByWord(i);
691             return (i >> 16) | (i << 16);
692         }
693 
694         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBits(ulong v)695         public static ulong ReverseBits(ulong v)
696         {
697             return ((ulong)ReverseBits((uint)v) << 32) | ReverseBits((uint)(v >> 32));
698         }
699 
700         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBytes(ushort v)701         public static ushort ReverseBytes(ushort v)
702         {
703             return (ushort)((v << 8) | (v >> 8));
704         }
705 
706         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBytes(uint v)707         public static uint ReverseBytes(uint v)
708         {
709             return ((uint)ReverseBytes((ushort)v) << 16) | ReverseBytes((ushort)(v >> 16));
710         }
711 
712         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseBytes(ulong v)713         public static ulong ReverseBytes(ulong v)
714         {
715             return ((ulong)ReverseBytes((uint)v) << 32) | ReverseBytes((uint)(v >> 32));
716         }
717 
718         [MethodImpl(MethodImplOptions.AggressiveInlining)]
ReverseWords(uint v)719         public static uint ReverseWords(uint v)
720         {
721             return (v << 16) | (v >> 16);
722         }
723 
CalculateBytesCount(int bitsCount)724         public static int CalculateBytesCount(int bitsCount)
725         {
726             DebugHelper.Assert(bitsCount >= 0);
727             // Return count of bytes, including one which isn't full of bits.
728             return (bitsCount - 1) / BitsPerByte + 1;
729         }
730 
731         public const int BitsPerByte = 8;
732 
AssertMaskParameters(int width, int position, int maxWidth)733         private static void AssertMaskParameters(int width, int position, int maxWidth)
734         {
735             DebugHelper.Assert(width >= 0 && position >= 0, $"Width (0x{width:X}) and position (0x{position:X}) should be grater than 0.");
736             DebugHelper.Assert(checked(width + position) <= maxWidth, $"Sum of width (0x{width:X}) and position (0x{position:X}) should be lower than or equal to {maxWidth}.");
737         }
738 
GetBitsInner(ulong reg, int length)739         private static bool[] GetBitsInner(ulong reg, int length)
740         {
741             var result = new bool[length];
742             for(var i = 0; i < result.Length; ++i)
743             {
744                 result[i] = (reg & 1u) == 1;
745                 reg >>= 1;
746             }
747             return result;
748         }
749 
750         // TODO: enumerator + lazy calculation
751         public class VariableLengthValue
752         {
VariableLengthValue(int? size = null)753             public VariableLengthValue(int? size = null)
754             {
755                 fragments = new List<Fragment>();
756                 sizeLimit = size;
757             }
758 
DefineFragment(int offset, int length, ulong value, string name = null, int valueOffset = 0)759             public VariableLengthValue DefineFragment(int offset, int length, ulong value, string name = null, int valueOffset = 0)
760             {
761                 if(valueOffset + length > 64)
762                 {
763                     throw new ArgumentException("value offset/length combination exceeds value length");
764                 }
765 
766                 var f = new Fragment
767                 {
768                     Offset = offset,
769                     Length = length,
770                     RawValue = (value >> valueOffset) & ((1u << length) - 1),
771                     Name = name
772                 };
773 
774                 InnerDefine(f);
775                 return this;
776             }
777 
DefineFragment(int offset, int length, Func<ulong> valueProvider, string name = null)778             public VariableLengthValue DefineFragment(int offset, int length, Func<ulong> valueProvider, string name = null)
779             {
780                 if(length > 64)
781                 {
782                     throw new ArgumentException("Length exceeds");
783                 }
784 
785                 var f = new Fragment
786                 {
787                     Offset = offset,
788                     Length = length,
789                     ValueProvider = valueProvider,
790                     Name = name
791                 };
792 
793                 InnerDefine(f);
794                 return this;
795             }
796 
GetBits(int skip)797             public BitStream GetBits(int skip)
798             {
799                 // TODO: skip value works only for padding area - make it complete
800 
801                 // construct the final value
802                 var result = new BitStream();
803                 for(var i = 0; i < fragments.Count; i++)
804                 {
805                     // pad the space before the fragment
806                     while(result.Length + skip < fragments[i].Offset)
807                     {
808                         result.AppendBit(false);
809                     }
810 
811                     // append the fragment value
812                     var value = fragments[i].EffectiveValue;
813                     result.AppendMaskedValue((uint)value, 0, (uint)Math.Min(fragments[i].Length, 32));
814                     if(fragments[i].Length > 32)
815                     {
816                         result.AppendMaskedValue((uint)(value >> 32), 0, (uint)(fragments[i].Length - 32));
817                     }
818                 }
819                 if(sizeLimit.HasValue)
820                 {
821                     // add final padding
822                     while(result.Length + skip < sizeLimit.Value)
823                     {
824                         result.AppendBit(false);
825                     }
826                 }
827 
828                 return result;
829             }
830 
831             public BitStream Bits
832             {
833                 get
834                 {
835                     return GetBits(skip: 0);
836                 }
837             }
838 
InnerDefine(Fragment f)839             private void InnerDefine(Fragment f)
840             {
841                 if(sizeLimit.HasValue && f.Offset + f.Length > sizeLimit.Value)
842                 {
843                     throw new ArgumentException("Fragment exceeds size limit.");
844                 }
845                 if(!CanAcceptFragment(f, out var index))
846                 {
847                     throw new ArgumentException("Fragment overlaps with another one.");
848                 }
849                 fragments.Insert(index, f);
850             }
851 
CanAcceptFragment(Fragment f, out int index)852             private bool CanAcceptFragment(Fragment f, out int index)
853             {
854                 var result = fragments.BinarySearch(f, comparer);
855                 if(result >= 0)
856                 {
857                     index = -1;
858                     return false;
859                 }
860 
861                 index = ~result;
862                 return ((index == 0) || (fragments[index - 1].Offset + fragments[index - 1].Length <= f.Offset))
863                     && ((index == fragments.Count) || (f.Offset + f.Length <= fragments[index + 1].Offset));
864             }
865 
866             private readonly List<Fragment> fragments;
867             private readonly int? sizeLimit;
868 
869             private static FragmentComparer comparer = new FragmentComparer();
870 
871             private struct Fragment
872             {
873                 public int Offset;
874                 public int Length;
875                 public ulong RawValue;
876                 public Func<ulong> ValueProvider;
877                 public string Name;
878 
879                 public ulong EffectiveValue => ValueProvider != null ? ValueProvider() & ((1u << Length) - 1) : RawValue;
880             }
881 
882             private class FragmentComparer : IComparer<Fragment>
883             {
Compare(Fragment x, Fragment y)884                 public int Compare(Fragment x, Fragment y)
885                 {
886                     if(x.Offset < y.Offset)
887                     {
888                         return -1;
889                     }
890                     else if(x.Offset > y.Offset)
891                     {
892                         return 1;
893                     }
894                     return 0;
895                 }
896             }
897         }
898 
899         // TODO: optimize it - add padding automatically, limit number of copying
900         public class BitConcatenator
901         {
New(int? size = null)902             public static BitConcatenator New(int? size = null)
903             {
904                 return new BitConcatenator(size);
905             }
906 
907             private BitStream bs = new BitStream();
908 
909             public BitStream Bits => bs;
910 
StackAbove(uint value, int length, int position = 0)911             public BitConcatenator StackAbove(uint value, int length, int position = 0)
912             {
913                 if(maxHeight.HasValue && bs.Length + length > maxHeight.Value)
914                 {
915                     throw new ArgumentException("This operation would exceed maximal height");
916                 }
917 
918                 bs.AppendMaskedValue(value, (uint)position, (uint)length);
919                 return this;
920             }
921 
BitConcatenator(int? size)922             private BitConcatenator(int? size)
923             {
924                 maxHeight = size;
925             }
926 
927             private readonly int? maxHeight;
928         }
929     }
930 }
931