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 }