1 module tagion.dart.DARTRim;
2 
3 import std.format;
4 import tagion.basic.Debug;
5 import tagion.basic.Types;
6 import tagion.basic.basic : EnumText;
7 import tagion.hibon.HiBONRecord;
8 
9 @safe:
10 enum RIMS_IN_SECTOR = 2;
11 /** 
12      * Sector range 
13      */
14 static struct SectorRange {
15     private {
16         @exclude ushort _sector;
17         @label("from") ushort _from_sector;
18         @label("to") ushort _to_sector;
19     }
20     /**
21         * The start start sector
22         * Returns: start angle
23         */
24     @property ushort from_sector() const {
25         return _from_sector;
26     }
27 
28     /**
29          * The end sector
30          * Returns: end angle 
31          */
32     @property ushort to_sector() const {
33         return _to_sector;
34     }
35 
36     @exclude protected bool flag;
37     mixin HiBONRecord!(q{
38                 this(const ushort from_sector, const ushort to_sector) pure nothrow @nogc {
39                     _from_sector = from_sector;
40                     _to_sector = to_sector;
41                     _sector = from_sector;
42                 }
43             });
44 
45     /**
46          * Checks if the range is a full angle dart (0x0000 to 0xFFFF)
47          * Returns: true if it a full-range=full-angle
48          */
49     bool isFullRange() const pure nothrow {
50         return _from_sector == _to_sector;
51     }
52 
53     /** 
54          * Checks if the sector is within the sector-range
55          * Params:
56          *   sector = sector number
57          * Returns: true if sector is within the range
58          */
59     bool inRange(const ushort sector) const pure nothrow {
60         return sectorInRange(sector, _from_sector, _to_sector);
61     }
62 
63     /** 
64          * Checks if the sector of a rim is within the sector-range
65          * Params:
66          *   rims = a rim path 
67          * Returns: 
68          */
69     bool inRange(const Rims rims) const pure nothrow {
70         if (rims.path.length == 1) {
71             return sectorInRange(rims.path[0] << 8, _from_sector & 0xFF00, _to_sector);
72         }
73         return (rims.path.length == 0) || sectorInRange(rims.sector, _from_sector, _to_sector);
74     }
75 
76     /**
77          * Checks if sector is within range 
78          * Params:
79          *   sector = sector number
80          *   from_sector = sector start angle
81          *   to_sector = sector end angle
82          * Returns: true if the sector is within the angle-span 
83          */
84     static bool sectorInRange(
85             const ushort sector,
86             const ushort from_sector,
87             const ushort to_sector) pure nothrow {
88         if (to_sector == from_sector) {
89             return true;
90         }
91         else {
92             immutable ushort sector_origin = (sector - from_sector) & ushort.max;
93             immutable ushort to_origin = (to_sector - from_sector) & ushort.max;
94             return (sector_origin < to_origin);
95         }
96     }
97 
98     /**
99          * Check if current sector has reached the end
100          * Returns: true of the sector reach the end of the angle-span
101          */
102     bool empty() const pure nothrow {
103         return !inRange(_sector) || flag;
104     }
105 
106     /** 
107          * Progress one sector
108          */
109     void popFront() {
110         if (!empty) {
111             _sector++;
112             if (_sector == _from_sector) {
113                 flag = true;
114             }
115         }
116     }
117 
118     /**
119          * Gets the current sector
120          * Returns: current sector
121          */
122     ushort front() const pure nothrow {
123         return _sector;
124     }
125 
126     /** 
127          * Gives an representation of the angle span
128          * Returns: text of angle span
129          */
130     string toString() const pure {
131         return format("(%04X, %04X)", _from_sector, _to_sector);
132     }
133 
134     ///
135     unittest {
136         enum full_dart_sectors_count = ushort.max + 1;
137         { //SectorRange: full sector iterator
138             auto sector_range = SectorRange(0, 0);
139             auto iteration = 0;
140             foreach (sector; sector_range) {
141                 iteration++;
142 
143                 if (iteration > full_dart_sectors_count)
144                     assert(0, "Range overflow");
145             }
146             assert(iteration == full_dart_sectors_count);
147         }
148         { //SectorRange: full sector iterator
149             auto sector_range = SectorRange(5, 5);
150             auto iteration = 0;
151             foreach (sector; sector_range) {
152                 iteration++;
153 
154                 if (iteration > full_dart_sectors_count)
155                     assert(0, "Range overflow");
156             }
157             assert(iteration == full_dart_sectors_count);
158         }
159         { //SectorRange:
160             auto sector_range = SectorRange(1, 10);
161             auto iteration = 0;
162             foreach (sector; sector_range) {
163                 iteration++;
164 
165                 if (iteration > 9)
166                     assert(0, "Range overflow");
167             }
168             assert(iteration == 9);
169         }
170     }
171 }
172 
173 /**
174      * Rim selecter
175      */
176 @recordType("Rims")
177 struct Rims {
178     Buffer path;
179     @label("keys") @optional @(filter.Initialized) Buffer key_leaves;
180     protected enum root_rim_path = [];
181     static immutable root = Rims(root_rim_path);
182     /**
183          * Returns: sector of the selected path
184          */
185     ushort sector() const pure nothrow {
186         if (path.length == 0) {
187             return ushort.init;
188         }
189         return .sector(path);
190     }
191 
192     mixin HiBONRecord!(
193             q{
194                 this(Buffer r, Buffer key_leaves=null) {
195                     this.path=r;
196                     this.key_leaves=key_leaves;
197                 }
198 
199                 this(const ushort sector)
200                 out {
201                     assert(path.length is ushort.sizeof);
202                 }
203                 do  {
204                     path=[sector >> 8*ubyte.sizeof, sector & ubyte.max];
205                     key_leaves=null;
206                 }
207 
208                 this(I)(const Rims rim, const I key) if (isIntegral!I) 
209                 in (key >= 0 && key <= ubyte.max) 
210                 do {
211 
212                     path = rim.path ~ cast(ubyte) key;
213                     this.key_leaves= rim.key_leaves.idup; 
214                 }
215             });
216 
217     /**
218          * rim-path as hex value
219          * Returns: hex string
220          */
221     string toString() const pure nothrow {
222         import std.exception : assumeWontThrow;
223 
224         if (path.length == 0) {
225             return "XXXX";
226         }
227         return assumeWontThrow(format!"%(%02x%)"(path));
228     }
229 }
230 
231 /++
232  + Sector is the little ending value the first two bytes of an fingerprint
233  + Returns:
234  +     Sector number of a fingerpint
235  +/
236 @safe
237 ushort sector(F)(const(F) fingerprint) pure nothrow @nogc if (isBufferType!F)
238 in (fingerprint.length >= ubyte.sizeof)
239 do {
240     ushort result = ushort(fingerprint[0]) << 8;
241     if (fingerprint.length > ubyte.sizeof) {
242         result |= fingerprint[1];
243 
244     }
245     return result;
246 }
247 
248 @safe
249 unittest {
250     import std.stdio;
251     import tagion.crypto.Types : Fingerprint;
252 
253     ubyte[] buf1 = [0xA7];
254     assert(sector(buf1) == 0xA700);
255     assert(sector(cast(Fingerprint)[0xA7, 0x15]) == 0xA715);
256     Buffer buf2 = [0xA7, 0x15, 0xE3];
257     assert(sector(buf2) == 0xA715);
258 
259 }