1 module dcrypt.encoders.hex; 2 3 import std.conv: text; 4 5 import dcrypt.encoders.encoder; 6 7 unittest { 8 ubyte[] raw = cast(ubyte[]) x"c371d9573a8f3b347fa0cb80629f079ead15e9fa69cf045d762990a8ac64cc9aaec30989d677b0cee9e5362a25f9528b515ad9cde4abd09acb4abc3daa07e396"; 9 string hex = "c371d9573a8f3b347fa0cb80629f079ead15e9fa69cf045d762990a8ac64cc9aaec30989d677b0cee9e5362a25f9528b515ad9cde4abd09acb4abc3daa07e396"; 10 assert(hexEncode(raw) == hex, "failed to encode hex"); 11 assert(hexDecode(hex) == raw, "failed to decode hex"); 12 } 13 14 unittest { 15 string raw = x"c371d9573a8f3b347fa0cb80629f079ead15e9fa69cf045d762990a8ac64cc9aaec30989d677b0cee9e5362a25f9528b515ad9cde4abd09acb4abc3daa07e396"; 16 string hex = "c371d9573a8f3b347fa0cb80629f079ead15e9fa69cf045d762990a8ac64cc9aaec30989d677b0cee9e5362a25f9528b515ad9cde4abd09acb4abc3daa07e396"; 17 assert(toHexStr(raw) == hex, "failed to encode hex"); 18 assert(hexDecode(hex) == raw, "failed to decode hex"); 19 } 20 21 /// Convert a string to a hex string. 22 @safe 23 public string toHexStr(string data, bool upperCase = false) nothrow pure { 24 return toHexStr(cast(const ubyte[]) data, upperCase); 25 } 26 27 /// Convert a byte array to a hex string. 28 @safe 29 public string toHexStr(in ubyte[] data, bool upperCase = false) nothrow pure { 30 ubyte[] encoded = hexEncode(data); 31 string output = cast(string) encoded.idup; 32 return output; 33 } 34 35 /// Convert a hex string back to bytes. 36 /// 37 /// Throws: InvalidEncodingException if non-hex chars appear or input has uneven length 38 @safe 39 public ubyte[] hexDecode(string hexStr) pure { 40 return hexDecode(cast(const ubyte[]) hexStr); 41 } 42 43 @safe 44 public ubyte[] hexEncode(in ubyte[] data, bool upperCase = false) pure nothrow { 45 46 ubyte[] output = new ubyte[data.length*2]; 47 48 immutable ubyte[] table = upperCase ? HEXITS : hexits; 49 50 foreach (i, b; data) 51 { 52 output[2*i] = table[b >> 4]; 53 output[2*i+1] = table[b & 0xF]; 54 } 55 56 return output; 57 } 58 59 /** 60 * decodes hexData 61 * 62 * Params: hexData = hex encoded input 63 * 64 * Throws: InvalidEncodingException if non-hex chars appear or input has uneven length 65 */ 66 @safe 67 public ubyte[] hexDecode(in ubyte[] hexData) pure { 68 if(hexData.length % 2 != 0) { 69 throw new InvalidEncodingException("hex string needs to have a even length"); 70 } 71 ubyte[] output = new ubyte[hexData.length/2]; 72 73 ubyte[] oBuf = output; 74 const(ubyte)[] iBuf = hexData; 75 76 while(iBuf.length >= 2){ 77 78 ubyte b1 = decodingTable[iBuf[0]]; 79 ubyte b2 = decodingTable[iBuf[1]]; 80 81 if((b1 | b2) == 0xFF) { 82 throw new InvalidEncodingException(text("not a hex character: ", 83 cast(char)iBuf[0], cast(char)iBuf[1])); 84 } 85 86 oBuf[0] = cast(ubyte) (b1<<4 | b2); // assemble nibbles to byte 87 iBuf = iBuf[2..$]; 88 oBuf = oBuf[1..$]; 89 } 90 91 return output; 92 } 93 94 95 // encoding and decoding tables 96 private { 97 immutable ubyte[16] hexits = cast(immutable(ubyte[16])) "0123456789abcdef"; 98 immutable ubyte[16] HEXITS = cast(immutable(ubyte[16])) "0123456789ABCDEF"; 99 immutable ubyte[128] decodingTable = createDecodingTable(); 100 } 101 102 103 private ubyte[128] createDecodingTable() { 104 105 ubyte[128] decodingTable; 106 107 // set up the decoding table 108 109 decodingTable[] = 0xFF; // 0xFF means undefined value 110 111 foreach(i,b; hexits) { 112 decodingTable[b] = cast(ubyte) i; 113 } 114 115 foreach(i,b; HEXITS) { 116 decodingTable[b] = cast(ubyte) i; 117 } 118 119 return decodingTable; 120 }