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 ];