1 
2 /**
3  * Convert binary data to hex and ASCII.
4  *
5  * Copyright: Copyright Digital Mars 2015-2015
6  * License:   $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Authors:   $(WEB digitalmars.com, Walter Bright)
8  * Source:    $(SARGONSRC src/sargon/_dumphex.d)
9  */
10 
11 module sargon.dumphex;
12 
13 private import std.range;
14 
15 
16 /**********************************
17     Transform ubytes into hex and ASCII.
18 
19     Params:
20         r = input range of ubytes
21         startoffset = offset of start of bytes
22 
23     Returns:
24         range of chars with r's contents in hex and ASCII
25  */
26 
27 auto dumpHex(Range)(Range r, ulong startoffset = 0)
28     if (isInputRange!Range && is(ElementType!Range == ubyte))
29 {
30     import core.stdc.ctype;
31 
32     struct Result
33     {
34         enum PerLine = 16;
35         enum OffsetDigits = 16;
36 
37         this(Range r, ulong startoffset)
38         {
39             this.r = r;
40             this.offset = startoffset;
41         }
42 
43         @property bool empty()
44         {
45             return nleft == 0 && r.empty;
46         }
47 
48         @property char front()
49         {
50             immutable char[16] hexDigits = "0123456789abcdef";
51 
52             if (!nleft)
53             {
54                 line[0 .. $ - 1] = ' ';
55                 line[OffsetDigits] = ':';
56                 line[$ - 1] = '\n';
57 
58                 size_t ndigits;
59                 auto offs = offset;
60                 while (ndigits < 4 || offs)
61                 {
62                     line[OffsetDigits - ndigits - 1] = hexDigits[offs & 0x0F];
63                     offs >>= 4;
64                     line[OffsetDigits - ndigits - 2] = hexDigits[offs & 0x0F];
65                     offs >>= 4;
66                     ndigits += 2;
67                 }
68 
69                 size_t i;
70                 while (!r.empty)
71                 {
72                     ubyte c = r.front;
73                     r.popFront();
74 
75                     line[OffsetDigits + 2 + i * 3]     = (c >> 4) ? hexDigits[c >> 4] : ' ';
76                     line[OffsetDigits + 2 + i * 3 + 1] = hexDigits[c & 0x0F];
77                     line[OffsetDigits + 2 + PerLine * 3 + 3 + i] = core.stdc.ctype.isprint(c) ? c : '.';
78                     if (++i == PerLine)
79                         break;
80                 }
81                 nleft = line.length - OffsetDigits + ndigits;
82                 offset += PerLine;
83             }
84             return line[line.length - nleft];
85         }
86 
87         void popFront()
88         {
89             if (!nleft)
90                 front();
91             --nleft;
92         }
93 
94         static if (isForwardRange!Range)
95         {
96             @property typeof(this) save()
97             {
98                 auto ret = this;
99                 ret.r = r.save;
100                 return ret;
101             }
102         }
103 
104       private:
105         Range r;
106         ulong offset;
107         char[OffsetDigits + 2 + PerLine * 3 + 3 + PerLine + 1] line;
108         size_t nleft;
109     }
110 
111     return Result(r, startoffset);
112 }
113 
114 unittest
115 {
116     import std.conv;
117     import std.array;
118 
119   {
120     ubyte[] data = cast(ubyte[])hexString!"65 74 29 3b 0d 0a 7d 0d 0a";
121     auto s = data.dumpHex(0x0ad0).array;
122     assert(s == "0ad0: 65 74 29 3b  d  a 7d  d  a                         et);..}..       \n");
123   }
124   {
125     ubyte[] data = cast(ubyte[])"65 74 29 3b 0d 0a 7d 0d 0a";
126     auto s = data.dumpHex(0x0ad578).array;
127     assert(s == "0ad578: 36 35 20 37 34 20 32 39 20 33 62 20 30 64 20 30    65 74 29 3b 0d 0
128 0ad588: 61 20 37 64 20 30 64 20 30 61                      a 7d 0d 0a      \n");
129   }
130   {
131     ubyte[] data = cast(ubyte[])hexString!"65 74 29 3b 0d 0a 7d 0d 0a";
132     auto r = data.dumpHex(0x0ad0);
133     auto s = r.save;
134     r.popFront();
135     assert(s.front == '0');
136     assert(r.front == 'a');
137   }
138 }