1 module libd.datastructures.bitkeeper;
2 
3 import libd.datastructures._bitinfo;
4 import libd.util.errorhandling;
5 import libd.util.maths : alignTo;
6 
7 struct BitKeeperSlice
8 {
9     package size_t startByte;
10     package ubyte  startBit;
11     package size_t bitCount;
12 
13     invariant(startBit >= 0 && startBit < 8);
14     invariant(startByte + startBit + bitCount == 0 || bitCount > 0);
15 
16     @property @safe @nogc
17     size_t bitIndex() nothrow pure const
18     {
19         return (this.startByte * 8) + this.startBit;
20     }
21 }
22 
23 struct BitKeeper
24 {
25     private ubyte[] _bytes;
26     private size_t  _maxBitsToUse;
27     private size_t  _bitsInUse;
28 
29     @disable this(this){}
30 
31     @safe @nogc nothrow:
32 
33     this(ubyte[] bytes, size_t maxBitsToUse)
34     {
35         this._bytes = bytes;
36         this._maxBitsToUse = maxBitsToUse;
37         assert(this._maxBitsToUse/8 <= bytes.length, "Not enough bytes were provided for the given maxBitsToUse value.");
38     }
39 
40     SimpleResult!BitKeeperSlice alloc(size_t bitCount)
41     {
42         assert(this._bytes !is null, "This BitKeeper hasn't been initialised.");
43         if(bitCount == 0)
44             return typeof(return)(raise("BitCount cannot be 0."));
45         else if(bitCount >= this._maxBitsToUse - this._bitsInUse)
46             return typeof(return)(raise("Not enough total bits available."));
47 
48         for(size_t i = 0; i < this._bytes.length; i++)
49         {
50             const byte_ = this._bytes[i];
51             if(byte_ == 0xFF)
52                 continue;
53 
54             const info = BIT_INFO[byte_];
55             if(info.largestBitRangeCount >= bitCount)
56             {
57                 const range = BitRange(info.bitRangeForSize[bitCount-1].start, cast(ubyte)bitCount);
58                 const mask  = rangeToMask(range);
59                 this._bytes[i] |= mask;
60                 return typeof(return)(BitKeeperSlice(
61                     i, range.start, bitCount
62                 ));
63             }
64             else if(info.flags & BitInfoFlags.hasSuffixZero)
65             {
66                 const startByte = i;
67                 const startRange = info.bitRangeForSuffix;
68                 auto remainingBits = bitCount - startRange.count;
69                 assert(remainingBits < bitCount);
70 
71                 i++;
72                 while(true)
73                 {
74                     if(i >= this._bytes.length)
75                         return typeof(return)(raise("Cannot find a long enough, continous range of bits."));
76                     
77                     const nextByte = this._bytes[i];
78                     const nextByteInfo = BIT_INFO[nextByte];
79                     
80                     if(nextByte == 0)
81                     {
82                         if(remainingBits <= 8)
83                         {
84                             const range = BitRange(0, cast(ubyte)remainingBits);
85                             const mask  = rangeToMask(range);
86                             this._bytes[i] |= mask;
87                             // Fallthrough to end
88                         }
89                         else
90                         {
91                             i++;
92                             remainingBits -= 8;
93                             continue;
94                         }
95                     }
96                     else if
97                     (
98                         !(nextByteInfo.flags & BitInfoFlags.hasPrefixZero)
99                      ||  nextByteInfo.bitRangeForPrefix.count < remainingBits
100                      ||  nextByte == 0xFF
101                     )
102                     {
103                         i--; // Negate the for loop's next i++
104                         break; // Failed match, try again with further bytes.
105                     }
106                     else
107                         this._bytes[i] |= rangeToMask(BitRange(0, cast(ubyte)remainingBits));
108 
109                     const inbetweenBytes = i - startByte;
110                     if(inbetweenBytes >= 2)
111                     {
112                         foreach(byteI; 1..inbetweenBytes)
113                             this._bytes[startByte+byteI] = 0xFF;
114                     }
115                     this._bytes[startByte] |= rangeToMask(startRange);
116                     this._bitsInUse += bitCount;
117                     return typeof(return)(BitKeeperSlice(
118                         startByte, startRange.start, bitCount 
119                     ));
120                 }
121             }
122         }
123 
124         return typeof(return)(raise("Cannot find a long enough, continous range of bits."));
125     }
126 
127     void free(BitKeeperSlice slice)
128     {
129         const startByteBitsFromStartToEnd = 8 - slice.startBit;
130         if(startByteBitsFromStartToEnd > slice.bitCount) // Slice is only in a single byte.
131         {
132             this._bytes[slice.startByte] &= ~cast(int)rangeToMask(BitRange(slice.startBit, cast(ubyte)slice.bitCount));
133             return;
134         } // Slice is multi-byte.
135 
136         this._bytes[slice.startByte] &= ~cast(int)rangeToMask(BitRange(slice.startBit, cast(ubyte)startByteBitsFromStartToEnd));
137 
138         // Clear the inbetween full bytes.
139         auto byteI = slice.startByte+1;
140         auto remainingBits = slice.bitCount - startByteBitsFromStartToEnd;
141         while(remainingBits >= 8)
142         {
143             this._bytes[byteI++] = 0;
144             remainingBits -= 8;
145         }
146 
147         // Clear the end byte.
148         this._bytes[byteI] &= ~cast(int)rangeToMask(BitRange(0, cast(ubyte)remainingBits));
149         this._bitsInUse -= slice.bitCount;
150     }
151 
152     size_t capacityInBits() pure const
153     {
154         return this._maxBitsToUse;
155     }
156 
157     size_t lengthInBits() pure const
158     {
159         return this._bitsInUse;
160     }
161 }
162 ///
163 @("BitKeeper")
164 unittest
165 {
166     ubyte[3] buffer;
167     auto bits = BitKeeper(buffer[0..$], 3*8);
168 
169     // NOTE: User code can't manually create BitKeeperSlices like the unittest can.
170 
171     // Single-byte
172     assert(bits.alloc(1).assumeValid == BitKeeperSlice(0, 0, 1));
173     assert(buffer[0] == 1);
174     bits.free(BitKeeperSlice(0, 0, 1));
175     assert(buffer[0] == 0);
176     assert(bits.alloc(1).assumeValid == BitKeeperSlice(0, 0, 1));
177     assert(bits.alloc(3).assumeValid == BitKeeperSlice(0, 1, 3));
178     assert(buffer[0] == 0b0000_1111);
179     bits.free(BitKeeperSlice(0, 1, 2));
180     assert(buffer[0] == 0b0000_1001);
181     assert(bits.alloc(1).assumeValid == BitKeeperSlice(0, 1, 1));
182     assert(bits.alloc(2).assumeValid == BitKeeperSlice(0, 4, 2));
183     assert(buffer[0] == 0b0011_1011);
184     buffer[0] = 0;
185 
186     // Two bytes
187     assert(bits.alloc(7).assumeValid == BitKeeperSlice(0, 0, 7));
188     assert(bits.alloc(3).assumeValid == BitKeeperSlice(0, 7, 3));
189     assert(buffer[0] == 0xFF);
190     assert(buffer[1] == 0b0000_0011);
191     bits.free(BitKeeperSlice(0, 7, 2));
192     assert(buffer[0] == 0b0111_1111);
193     assert(buffer[1] == 0b0000_0010);
194     assert(bits.alloc(3).assumeValid == BitKeeperSlice(1, 2, 3));
195     assert(buffer[0] == 0b0111_1111);
196     assert(buffer[1] == 0b0001_1110);
197     buffer[0..2] = 0;
198 
199     // Multi-byte
200     assert(bits.alloc(18).assumeValid == BitKeeperSlice(0, 0, 18));
201     assert(buffer[0] == 0xFF);
202     assert(buffer[1] == 0xFF);
203     assert(buffer[2] == 0b0000_0011);
204     bits.free(BitKeeperSlice(0, 0, 18));
205     assert(buffer[0] == 0);
206     assert(buffer[1] == 0);
207     assert(buffer[2] == 0);
208 }
209 
210 private:
211 
212 @safe @nogc
213 ubyte rangeToMask(const BitRange range) nothrow pure
214 {
215     return BIT_MASKS[(range.start * 10) + range.count];
216 }