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