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