1 module tools.bitinfogen;
2 
3 struct BitRange
4 {
5     ubyte start;
6     ubyte count;
7 }
8 
9 enum BitInfoFlags
10 {
11     none,
12     hasPrefixZero = 1 << 0,
13     hasSuffixZero = 1 << 1
14 }
15 
16 struct BitInfo
17 {
18     ubyte unsetCount;
19     ubyte largestBitRangeCount;
20     BitRange[8] bitRangeForSize; // BitRange.init stands for "no range". Bigger bit ranges will stand in for smaller ones if there's no dedicated smaller one.
21     BitRange bitRangeForPrefix; // Only set for hasPrefixZero
22     BitRange bitRangeForSuffix; // Only set for hasSuffixZero
23     BitInfoFlags flags;
24 }
25 
26 void main()
27 {
28     import std;
29 
30     BitInfo[256] bitInfo;
31     findBitInfo(bitInfo);
32     ensureInProjectRoot();
33     const masks = findBitMasks(bitInfo);
34     const code  = formatBitInfoIntoCode(bitInfo, masks);
35     std.file.write("source/libd/datastructures/_bitinfo.d", code);
36 }
37 
38 void ensureInProjectRoot()
39 {
40     import std : exists, chdir;
41     while(!exists("dub.sdl"))
42         chdir("..");
43 }
44 
45 string formatBitInfoIntoCode(BitInfo[256] bitInfo, const ubyte[] masks)
46 {
47     import std : Appender, assumeUnique, format;
48 
49     Appender!(char[]) output;
50 
51     output.put(`// This file is generated by tools/bitinfogen.d
52 module libd.datastructures._bitinfo;
53 
54 package:
55 
56 struct BitRange
57 {
58     ubyte start;
59     ubyte count;
60 }
61 
62 enum BitInfoFlags
63 {
64     none,
65     hasPrefixZero = 1 << 0,
66     hasSuffixZero = 1 << 1
67 }
68 
69 struct BitInfo
70 {
71     ubyte unsetCount;
72     ubyte largestBitRangeCount;
73     BitRange bitRangeForPrefix; // Only set for hasPrefixZero
74     BitRange bitRangeForSuffix; // Only set for hasSuffixZero
75     BitInfoFlags flags;
76     BitRange[8] bitRangeForSize; // BitRange.init stands for "no range". Bigger bit ranges will stand in for smaller ones if there's no dedicated smaller one.
77 }
78 
79 immutable BitInfo[256] BIT_INFO = 
80 [  
81 `);
82     foreach(info; bitInfo)
83     {
84         output.put(format!"    BitInfo(%s, %s, BitRange(%s, %s), BitRange(%s, %s), cast(BitInfoFlags)%s, ["(
85             info.unsetCount, info.largestBitRangeCount,
86             info.bitRangeForPrefix.start, info.bitRangeForPrefix.count,
87             info.bitRangeForSuffix.start, info.bitRangeForSuffix.count,
88             cast(int)info.flags
89         ));
90         foreach(range; info.bitRangeForSize)
91         {
92             output.put(format!"BitRange(%s, %s), "(
93                 range.start, range.count
94             ));
95         }
96         output.put("]),\n");
97     }
98     output.put("];");
99 
100     output.put(`
101 immutable ubyte[%s] BIT_MASKS =
102 [
103     `.format(masks.length));
104     foreach(mask; masks)
105         output.put(format!"0x%X, "(mask));
106     output.put("\n];");
107 
108     return output.data.assumeUnique;
109 }
110 
111 ubyte[] findBitMasks(ref BitInfo[256] bitInfo)
112 {
113     import std : map;
114 
115     ubyte[] data;
116     foreach(start; 0..8)
117     {
118         foreach(count; 1..9)
119         {
120             const index   = (start * 10) + count;
121             const maskBit = 1 << start;
122             ubyte mask    = cast(ubyte)maskBit;
123             foreach(_; 0..count-1)
124             {
125                 mask <<= 1;
126                 mask |= cast(ubyte)maskBit;
127             }
128 
129             if(data.length <= index)
130                 data.length = index+1;
131             data[index] = mask;
132         }
133     }
134     return data;
135 }
136 
137 void findBitInfo(ref BitInfo[256] bitInfo)
138 {
139     foreach(i; 0..ubyte.max+1)
140     {
141         const b = cast(ubyte)i;
142         bitInfo[i].unsetCount = findUnsetCount(b);
143 
144         ubyte bitIndex;
145         while(bitIndex < 8)
146         {
147             auto range = nextBitRange(b, bitIndex);
148             if(range.count == 0)
149                 continue;
150 
151             if(range.count > bitInfo[i].largestBitRangeCount)
152                 bitInfo[i].largestBitRangeCount = range.count;
153             if(range.start == 0)
154             {
155                 bitInfo[i].flags |= BitInfoFlags.hasPrefixZero;
156                 bitInfo[i].bitRangeForPrefix = range;
157             }
158             if(range.start + range.count == 8)
159             {
160                 bitInfo[i].flags |= BitInfoFlags.hasSuffixZero;
161                 bitInfo[i].bitRangeForSuffix = range;
162             }
163 
164             foreach(ref r; bitInfo[i].bitRangeForSize[0..range.count])
165             {
166                 if(r.count == 0 || range.count < r.count)
167                     r = range;
168             }
169         }
170     }
171 }
172 
173 ubyte findUnsetCount(ubyte b)
174 {
175     ubyte count;    
176     foreach(i; 0..8)
177         count += (b & (1 << i)) == 0;
178     return count;
179 }
180 
181 BitRange nextBitRange(ubyte b, ref ubyte bitIndex)
182 {
183     BitRange range;
184     range.start = bitIndex;
185 
186     while(bitIndex < 8)
187     {
188         const isUnset = (b & (1 << bitIndex++)) == 0;
189         if(!isUnset)
190         {
191             if(range.count == 0)
192             {
193                 range.start = bitIndex;
194                 continue;
195             }
196             break;
197         }
198         range.count++;
199     }
200 
201     return range;
202 }