Skip to content

Commit 4a7f812

Browse files
committed
Import code from private repository
1 parent 926a2d2 commit 4a7f812

File tree

3 files changed

+228
-0
lines changed

3 files changed

+228
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
docs.json
2020
__dummy.html
2121
docs/
22+
*-test-library
2223

2324
# Code coverage
2425
*.lst

dub.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "bitblob",
3+
"authors": [
4+
"Mathias 'Geod24' Lang"
5+
],
6+
"description": "A small library to represent hash types as value types",
7+
"copyright": "Copyright © 2018, Mathias 'Geod24' Lang",
8+
"license": "MIT"
9+
}

source/geod24/bitblob.d

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*******************************************************************************
2+
3+
Variadic-sized value type to represent a hash
4+
5+
A `BitBlob` is a value type representing a hash.
6+
The argument is the size in bits, e.g. for sha256 it is 256.
7+
It can be initialized from the hexadecimal string representation
8+
or an `ubyte[]`, making it easy to interact with `std.digest`
9+
10+
Author: Mathias 'Geod24' Lang
11+
License: MIT (See LICENSE.txt)
12+
Copyright: Copyright (c) 2017-2018 Mathias Lang. All rights reserved.
13+
14+
*******************************************************************************/
15+
16+
module geod24.bitblob;
17+
18+
static import std.ascii;
19+
import std.algorithm;
20+
import std.range;
21+
import std.format;
22+
import std.utf;
23+
24+
///
25+
@nogc @safe pure nothrow unittest
26+
{
27+
import std.digest.sha;
28+
alias Hash = BitBlob!256;
29+
Hash k1 = sha256Of("Hello World");
30+
}
31+
32+
/*******************************************************************************
33+
34+
A value type representing a hash
35+
36+
Params:
37+
Bits = The size of the hash, in bits. Must be a multiple of 8.
38+
39+
*******************************************************************************/
40+
public struct BitBlob (size_t Bits)
41+
{
42+
/// Used by std.format
43+
/// Cannot be `nothrow @nogc` since sformat is not, but does not allocate
44+
public void toString (scope void delegate(const(char)[]) @safe sink) const @safe
45+
{
46+
sink("0x");
47+
char[2] data;
48+
// retro because the data is stored in little endian
49+
this.data[].retro.each!(
50+
(bin)
51+
{
52+
sformat(data, "%0.2x", bin);
53+
sink(data);
54+
});
55+
}
56+
57+
/// Used for serialization
58+
public string toString () const @safe
59+
{
60+
size_t idx;
61+
char[Width * 2 + 2] buffer = void;
62+
scope sink = (const(char)[] v) {
63+
buffer[idx .. idx + v.length] = v;
64+
idx += v.length;
65+
};
66+
this.toString(sink);
67+
return buffer.idup;
68+
}
69+
70+
pure nothrow @nogc @safe:
71+
72+
/***************************************************************************
73+
74+
Create a BitBlob from binary data, e.g. serialized data
75+
76+
Params:
77+
bin = Binary data to store in this `BitBlob`.
78+
isLE = `true` if the data is little endian, `false` otherwise.
79+
Internally the data will be stored in little endian.
80+
81+
Throws:
82+
If `bin.length != typeof(this).Width`
83+
84+
***************************************************************************/
85+
86+
public this (scope const ubyte[] bin, bool isLE = true)
87+
{
88+
assert(bin.length == Width);
89+
this.data[] = bin[];
90+
if (!isLE)
91+
this.data[].reverse;
92+
}
93+
94+
/***************************************************************************
95+
96+
Create a BitBlob from an hexadecimal string representation
97+
98+
Params:
99+
hexstr = String representation of the binary data in base 16.
100+
The hexadecimal prefix (0x) is optional.
101+
Can be upper or lower case.
102+
103+
Throws:
104+
If `hexstr_without_prefix.length != (typeof(this).Width * 2)`.
105+
106+
***************************************************************************/
107+
108+
public this (scope const(char)[] hexstr)
109+
{
110+
assert(hexstr.length == (Width * 2)
111+
|| hexstr.length == (Width * 2) + "0x".length);
112+
113+
auto range = hexstr.byChar.map!(std.ascii.toLower!(char));
114+
range.skipOver("0x".byChar);
115+
// Each doesn't work
116+
foreach (size_t idx, chunk; range.map!(fromHex).chunks(2).retro.enumerate)
117+
this.data[idx] = cast(ubyte)((chunk[0] << 4) + chunk[1]);
118+
}
119+
120+
/// Used for deserialization
121+
static auto fromString (const(char)[] str)
122+
{
123+
return BitBlob!(Bits)(str);
124+
}
125+
126+
static assert (
127+
Bits % 8 == 0,
128+
"Argument to BitBlob must be a multiple of 8");
129+
130+
/// The width of this aggregate, in octets
131+
public static immutable Width = Bits / 8;
132+
133+
/// Store the internal data
134+
private ubyte[Width] data;
135+
136+
/// Returns: If this BitBlob has any value
137+
public bool isNull () const
138+
{
139+
return this.data[].all!((v) => v == 0);
140+
}
141+
142+
/// Used for sha256Of
143+
public const(ubyte)[] opIndex () const
144+
{
145+
return this.data;
146+
}
147+
148+
/// Public because of a visibility bug
149+
public static ubyte fromHex (char c)
150+
{
151+
if (c >= '0' && c <= '9')
152+
return cast(ubyte)(c - '0');
153+
if (c >= 'a' && c <= 'f')
154+
return cast(ubyte)(10 + c - 'a');
155+
assert(0, "Unexpected char in string passed to BitBlob");
156+
}
157+
158+
public int opCmp (ref const typeof(this) s) const
159+
{
160+
// Reverse because little endian
161+
foreach_reverse (idx, b; this.data)
162+
if (b != s.data[idx])
163+
return b - s.data[idx];
164+
return 0;
165+
}
166+
}
167+
168+
pure @safe nothrow @nogc unittest
169+
{
170+
alias Hash = BitBlob!256;
171+
172+
Hash gen1 = GenesisBlockHashStr;
173+
Hash gen2 = GenesisBlockHash;
174+
assert(gen1.data == GenesisBlockHash);
175+
assert(gen1 == gen2);
176+
177+
Hash gm1 = GMerkle_str;
178+
Hash gm2 = GMerkle_bin;
179+
assert(gm1.data == GMerkle_bin);
180+
assert(gm1 == gm2);
181+
182+
Hash empty;
183+
assert(empty.isNull);
184+
assert(!gen1.isNull);
185+
186+
// Test opCmp
187+
assert(empty < gen1);
188+
assert(gm1 > gen2);
189+
}
190+
191+
/// Test toString
192+
unittest
193+
{
194+
import std.format;
195+
alias Hash = BitBlob!256;
196+
Hash gen1 = GenesisBlockHashStr;
197+
assert(format("%s", gen1) == GenesisBlockHashStr);
198+
}
199+
200+
version (unittest)
201+
{
202+
private:
203+
/// Bitcoin's genesis block hash
204+
static immutable GenesisBlockHashStr =
205+
"0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
206+
static immutable ubyte[32] GenesisBlockHash = [
207+
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 0xc1, 0xa6, 0xa2, 0x46,
208+
0xae, 0x63, 0xf7, 0x4f, 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
209+
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00 ];
210+
211+
/// Bitcoin's genesis block Merkle root hash
212+
static immutable GMerkle_str =
213+
"0X4A5E1E4BAAB89F3A32518A88C31BC87F618F76673E2CC77AB2127B7AFDEDA33B";
214+
static immutable ubyte[] GMerkle_bin = [
215+
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, 0x7a, 0xc7, 0x2c, 0x3e,
216+
0x67, 0x76, 0x8f, 0x61, 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
217+
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a ];
218+
}

0 commit comments

Comments
 (0)