BitStack.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. //------------------------------------------------------------------------------
  2. // <copyright file="BitStack.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. //------------------------------------------------------------------------------
  7. namespace System.Xml {
  8. using System;
  9. using System.Diagnostics;
  10. /// <summary>
  11. /// Manages a stack of bits. Exposes push, pop, and peek operations.
  12. /// </summary>
  13. internal class BitStack {
  14. private uint[] bitStack;
  15. private int stackPos;
  16. private uint curr;
  17. /// <summary>
  18. /// Initialize stack.
  19. /// </summary>
  20. public BitStack() {
  21. // Set sentinel bit in 1st position. As bits are shifted onto this.curr, this sentinel
  22. // bit shifts to the left. When it's about to overflow, this.curr will be pushed
  23. // onto an unsigned int stack and the sentinel bit will be reset to 0x1.
  24. this.curr = 0x1;
  25. }
  26. /// <summary>
  27. /// Push a 0 or 1 bit onto the stack.
  28. /// </summary>
  29. public void PushBit(bool bit) {
  30. if ((this.curr & 0x80000000) != 0) {
  31. // If sentinel bit has reached the last position, push this.curr
  32. PushCurr();
  33. }
  34. // Shift new bit onto this.curr (which must have at least one open position)
  35. this.curr = (this.curr << 1) | (bit ? 1u : 0u);
  36. }
  37. /// <summary>
  38. /// Pop the top bit from the stack and return it.
  39. /// </summary>
  40. public bool PopBit() {
  41. bool bit;
  42. Debug.Assert(this.curr != 0x1, "Stack empty");
  43. // Shift rightmost bit from this.curr
  44. bit = (this.curr & 0x1) != 0;
  45. this.curr >>= 1;
  46. if (this.curr == 0x1) {
  47. // If sentinel bit has reached the rightmost position, pop this.curr
  48. PopCurr();
  49. }
  50. return bit;
  51. }
  52. /// <summary>
  53. /// Return the top bit on the stack without pushing or popping.
  54. /// </summary>
  55. public bool PeekBit() {
  56. Debug.Assert(this.curr != 0x1, "Stack empty");
  57. return (this.curr & 0x1) != 0;
  58. }
  59. #if !SILVERLIGHT // This property is not used in Silverlight
  60. /// <summary>
  61. /// Return true if there are currently no bits on the stack.
  62. /// </summary>
  63. public bool IsEmpty {
  64. get { return this.curr == 0x1; }
  65. }
  66. #endif
  67. /// <summary>
  68. /// this.curr has enough space for 31 bits (minus 1 for sentinel bit). Once this space is
  69. /// exhausted, a uint stack is created to handle the overflow.
  70. /// </summary>
  71. private void PushCurr() {
  72. int len;
  73. if (this.bitStack == null) {
  74. this.bitStack = new uint[16];
  75. }
  76. // Push current unsigned int (which has been filled) onto a stack
  77. // and initialize this.curr to be used for future pushes.
  78. this.bitStack[this.stackPos++] = this.curr;
  79. this.curr = 0x1;
  80. // Resize stack if necessary
  81. len = this.bitStack.Length;
  82. if (this.stackPos >= len) {
  83. uint[] bitStackNew = new uint[2 * len];
  84. Array.Copy(this.bitStack, bitStackNew, len);
  85. this.bitStack = bitStackNew;
  86. }
  87. }
  88. /// <summary>
  89. /// If all bits have been popped from this.curr, then pop the previous uint value from the stack in
  90. /// order to provide another 31 bits.
  91. /// </summary>
  92. private void PopCurr() {
  93. if (this.stackPos > 0)
  94. this.curr = this.bitStack[--this.stackPos];
  95. }
  96. }
  97. }