1 module barcode.util;
2 
3 import std.exception;
4 import std.typecons : Flag;
5 import std.bitmanip : bitfields;
6 
7 public import std.typecons : tuple;
8 public import std.bitmanip : BitArray;
9 
10 alias AppendCheckSum = Flag!"appendCheckSum";
11 
12 ///
13 struct Bits(T=ulong)
14     if (is(T==ubyte) || is(T==ushort) || is(T==uint) || is(T==ulong))
15 {
16          static if (is(T==ubyte))  enum COUNTBITS = 3;
17     else static if (is(T==ushort)) enum COUNTBITS = 4;
18     else static if (is(T==uint))   enum COUNTBITS = 5;
19     else static if (is(T==ulong))  enum COUNTBITS = 6;
20 
21     enum VALUEBITS = T.sizeof * 8 - COUNTBITS;
22 
23     mixin(bitfields!(ubyte, "count", COUNTBITS,
24                          T, "value", VALUEBITS));
25 
26     alias value this;
27 
28 
29 pure nothrow @nogc:
30 
31     ///
32     this(ulong cnt, ulong val)
33     {
34         count = cast(ubyte)cnt;
35         value = cast(T)val;
36     }
37 
38     ///
39     bool opIndex(size_t i) const
40     { return cast(bool)((value>>i)&1); }
41 
42     ///
43     bool opEquals(X)(auto ref const Bits!X v) const
44     { return v.value == value && v.count == count; }
45 
46     ///
47     bool opEquals(X)(X v) const
48     if (!is(X : Bits!U, U))
49     { return v == value; }
50 }
51 
52 unittest
53 {
54     static assert(bitsStr!"---".count == 3);
55     static assert(bitsStr!"--".count == 2);
56     static assert(bitsStr!"---" != bitsStr!"--");
57 }
58 
59 void addBits(ref BitArray ba, size_t value, int bits)
60 {
61     enforce(bits <= size_t.sizeof*8, "so many bits");
62     enforce(bits >= 0, "bits must be more that 0");
63     addBits(ba, Bits!ulong(bits, value));
64 }
65 
66 unittest
67 {
68     BitArray ba;
69     ba.addBits(0b11000111010, 11);
70     auto tst = BitArray([1,1,0,0,0,1,1,1,0,1,0]);
71     assert (ba == tst);
72 }
73 
74 void addBits(T)(ref BitArray ba, auto ref const(Bits!T) bits)
75 { foreach_reverse (i; 0 .. bits.count) ba ~= bits[i]; }
76 
77 unittest
78 {
79     BitArray ba;
80     ba.addBits(bitsStr!"##---###-#-");
81     auto tst = BitArray([1,1,0,0,0,1,1,1,0,1,0]);
82     assert (ba == tst);
83 }
84 
85 // for more readable bits writing
86 template bitsStr(string mask, char ONE='#')
87     if (mask.length <= 58)
88 {
89     static pure auto bitsImpl(T)() nothrow @nogc
90     {
91         ulong ret;
92         foreach (i; 0 .. mask.length)
93             ret |= cast(ulong)(mask[i] == ONE) << (mask.length - 1 - i);
94         return Bits!T(mask.length, ret);
95     }
96 
97          static if (mask.length < 6)  alias S = ubyte;
98     else static if (mask.length < 13) alias S = ushort;
99     else static if (mask.length < 28) alias S = uint;
100     else                              alias S = ulong;
101 
102     static if (mask.length >= 1) enum bitsStr = bitsImpl!S();
103     else static assert(0, "can't create 0 bits value");
104 }
105 
106 unittest
107 {
108     assert(1 == bitsStr!"#");
109     assert(0 == bitsStr!"-");
110     assert(0b1100011101011 == bitsStr!"##---###-#-##");
111     assert(bitsStr!"---".count == 3);
112 }
113 
114 auto getDict(alias F, T)(T[] arr)
115 {
116     alias frt = typeof(F(size_t(0), T.init));
117     alias KEY = typeof(frt.init[0]);
118     alias VAL = typeof(frt.init[1]);
119 
120     VAL[KEY] ret;
121     foreach (i, e; arr)
122     {
123         auto tmp = F(i, e);
124         ret[tmp[0]] = tmp[1];
125     }
126     return ret;
127 }
128 
129 unittest
130 {
131     static struct X { char ch; ushort mask; }
132     enum data = [ X('0', 0b001), X('1', 0b010), X('2', 0b100), ];
133 
134     {
135         enum ushort[char] t = getDict!((i,a) => tuple(a.ch, a.mask))(data);
136         static assert(t.keys.length == 3);
137         assert('0' in t);
138         assert(t['0'] == 0b001);
139         assert('1' in t);
140         assert(t['1'] == 0b010);
141         assert('2' in t);
142         assert(t['2'] == 0b100);
143     }
144 
145     {
146         enum ushort[char] t = getDict!((i,a) => tuple(a.ch, i))(data);
147         static assert(t.keys.length == 3);
148         assert('0' in t);
149         assert(t['0'] == 0);
150         assert('1' in t);
151         assert(t['1'] == 1);
152         assert('2' in t);
153         assert(t['2'] == 2);
154     }
155 
156     {
157         enum char[ubyte] t = getDict!((i,a) => tuple(cast(ubyte)i, a.ch))(data);
158         static assert(t.keys.length == 3);
159         assert(0 in t);
160         assert(t[0] == '0');
161         assert(1 in t);
162         assert(t[1] == '1');
163         assert(2 in t);
164         assert(t[2] == '2');
165     }
166 }