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