1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2010 Ulrik Mikaelsson. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         author:         Ulrik Mikaelsson
8 
9         standards:      rfc3548, rfc4648
10 
11 *******************************************************************************/
12 
13 /*******************************************************************************
14 
15     This module is used to decode and encode hex char[] arrays.
16 
17     Example:
18     ---
19     char[] blah = "Hello there, my name is Jeff.";
20 
21     scope encodebuf = new char[allocateEncodeSize(cast(ubyte[])blah)];
22     char[] encoded = encode(cast(ubyte[])blah, encodebuf);
23 
24     scope decodebuf = new ubyte[encoded.length];
25     if (cast(char[])decode(encoded, decodebuf) == "Hello there, my name is Jeff.")
26         Stdout("yay").newline;
27     ---
28 
29     Since v1.0
30 
31 *******************************************************************************/
32 
33 module tango.util.encode.Base16;
34 
35 /*******************************************************************************
36 
37     calculates and returns the size needed to encode the length of the
38     array passed.
39 
40     Params:
41     data = An array that will be encoded
42 
43 *******************************************************************************/
44 
45 
46 size_t allocateEncodeSize(const(ubyte[]) data)
47 {
48     return allocateEncodeSize(data.length);
49 }
50 
51 /*******************************************************************************
52 
53     calculates and returns the size needed to encode the length passed.
54 
55     Params:
56     length = Number of bytes to be encoded
57 
58 *******************************************************************************/
59 
60 size_t allocateEncodeSize(size_t length)
61 {
62     return length*2;
63 }
64 
65 
66 /*******************************************************************************
67 
68     encodes data and returns as an ASCII hex string.
69 
70     Params:
71     data = what is to be encoded
72     buff = buffer large enough to hold encoded data
73 
74     Example:
75     ---
76     char[512] encodebuf;
77     char[] myEncodedString = encode(cast(ubyte[])"Hello, how are you today?", encodebuf);
78     Stdout(myEncodedString).newline; // 48656C6C6F2C20686F772061726520796F7520746F6461793F
79     ---
80 
81 
82 *******************************************************************************/
83 
84 char[] encode(const(ubyte[]) data, char[] buff)
85 in
86 {
87     assert(data);
88     assert(buff.length >= allocateEncodeSize(data));
89 }
90 body
91 {
92     size_t i;
93     foreach (ubyte j; data) {
94         buff[i++] = _encodeTable[j >> 4];
95         buff[i++] = _encodeTable[j & 0b0000_1111];
96     }
97 
98     return buff[0..i];
99 }
100 
101 /*******************************************************************************
102 
103     encodes data and returns as an ASCII hex string.
104 
105     Params:
106     data = what is to be encoded
107 
108     Example:
109     ---
110     char[] myEncodedString = encode(cast(ubyte[])"Hello, how are you today?");
111     Stdout(myEncodedString).newline; // 48656C6C6F2C20686F772061726520796F7520746F6461793F
112     ---
113 
114 
115 *******************************************************************************/
116 
117 
118 char[] encode(const(ubyte[]) data)
119 in
120 {
121     assert(data);
122 }
123 body
124 {
125     auto rtn = new char[allocateEncodeSize(data)];
126     return encode(data, rtn);
127 }
128 
129 /*******************************************************************************
130 
131     decodes an ASCII hex string and returns it as ubyte[] data. Pre-allocates
132     the size of the array.
133 
134     This decoder will ignore non-hex characters. So:
135     SGVsbG8sIGhvd
136     yBhcmUgeW91IH
137     RvZGF5Pw==
138 
139     Is valid.
140 
141     Params:
142     data = what is to be decoded
143 
144     Example:
145     ---
146     char[] myDecodedString = cast(char[])decode("48656C6C6F2C20686F772061726520796F7520746F6461793F");
147     Stdout(myDecodeString).newline; // Hello, how are you today?
148     ---
149 
150 *******************************************************************************/
151 
152 ubyte[] decode(const(char[]) data)
153 in
154 {
155     assert(data);
156 }
157 body
158 {
159     auto rtn = new ubyte[data.length+1/2];
160     return decode(data, rtn);
161 }
162 
163 /*******************************************************************************
164 
165     decodes an ASCII hex string and returns it as ubyte[] data.
166 
167     This decoder will ignore non-hex characters. So:
168     SGVsbG8sIGhvd
169     yBhcmUgeW91IH
170     RvZGF5Pw==
171 
172     Is valid.
173 
174     Params:
175     data = what is to be decoded
176     buff = a big enough array to hold the decoded data
177 
178     Example:
179     ---
180     ubyte[512] decodebuf;
181     char[] myDecodedString = cast(char[])decode("48656C6C6F2C20686F772061726520796F7520746F6461793F", decodebuf);
182     Stdout(myDecodeString).newline; // Hello, how are you today?
183     ---
184 
185 *******************************************************************************/
186 
187 ubyte[] decode(const(char[]) data, ubyte[] buff)
188 in
189 {
190     assert(data);
191 }
192 body
193 {
194     bool even=true;
195     size_t i;
196     foreach (c; data) {
197         auto val = _decodeTable[c];
198         if (val & 0b1000_0000)
199             continue;
200         if (even) {
201             buff[i] = cast(ubyte) (val << 4); // Store val in high for bits
202         } else {
203             buff[i] |= val;     // OR-in low 4 bits,
204             i += 1;             // and move on to next
205         }
206         even = !even; // Switch mode for next iteration
207     }
208     assert(even, "Non-even amount of hex characters in input.");
209     return buff[0..i];
210 }
211 
212 debug (UnitTest)
213 {
214     unittest
215     {
216         immutable immutable(char)[][] testRaw = [
217             "",
218             "A",
219             "AB",
220             "BAC",
221             "BACD",
222             "Hello, how are you today?",
223             "AbCdEfGhIjKlMnOpQrStUvXyZ",
224         ];
225         immutable immutable(char)[][] testEnc = [
226             "",
227             "41",
228             "4142",
229             "424143",
230             "42414344",
231             "48656C6C6F2C20686F772061726520796F7520746F6461793F",
232             "4162436445664768496A4B6C4D6E4F7051725374557658795A",
233         ];
234 
235         for (size_t i; i < testRaw.length; i++) {
236             auto resultChars = encode(cast(ubyte[])testRaw[i]);
237             assert(resultChars == testEnc[i],
238                     testRaw[i]~": ("~resultChars~") != ("~testEnc[i]~")");
239 
240             auto resultBytes = decode(testEnc[i]);
241             assert(resultBytes == cast(ubyte[])testRaw[i],
242                     testEnc[i]~": ("~cast(char[])resultBytes~") != ("~testRaw[i]~")");
243         }
244     }
245 }
246 
247 
248 
249 private:
250 
251 /*
252     Static immutable tables used for fast lookups to
253     encode and decode data.
254 */
255 immutable ubyte hex_PAD = '=';
256 immutable char[] _encodeTable = "0123456789ABCDEF";
257 
258 immutable ubyte[] _decodeTable = [
259     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
260     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
261     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
262     0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
263     0xFF,0x0A,0x0B,0x0C, 0x0D,0x0E,0x0F,0x1F, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
264     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
265     0xFF,0x0A,0x0B,0x0C, 0x0D,0x0E,0x0F,0x1F, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
266     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
267     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
268     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
269     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
270     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
271     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
272     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
273     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
274     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
275 ];