some updates
[iv.d.git] / base58.d
blob459a8234d369d3c8c92e11c83733b30dbb4b9c32
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // BASE58 codec; because i can!
18 module iv.base58 /*is aliced*/;
19 private:
20 import iv.alice;
23 // all alphanumeric characters except for "0", "I", "O", and "l"
24 static immutable string base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
25 static immutable ubyte[256] b58idx = () {
26 ubyte[256] res = 255;
27 foreach (immutable idx, immutable char ch; base58Alphabet) res[ch] = cast(ubyte)idx;
28 return res;
29 }();
32 public struct Base58Decoder {
33 private:
34 ubyte[2048] b256 = 0; // int size = cast(int)(buf.length)*733/1000+1; // log(58) / log(256), rounded up
35 uint zeroes = 0; // bit 31 set: done zero counting; 0xffff_ffffU is error
37 public:
38 void clear () nothrow @trusted @nogc { pragma(inline, true); this = this.init; }
40 @property bool error () const nothrow @trusted @nogc { pragma(inline, true); return (zeroes == 0xffff_ffffU); }
42 bool put (const(char)[] buf...) nothrow @trusted @nogc {
43 if (error) return false;
44 foreach (immutable char ch; buf) {
45 // counting zeroes?
46 if (zeroes < 0x8000_0000U) {
47 if (ch == '1') {
48 if (++zeroes == 0x0fff_ffffU) { zeroes = 0xffff_ffffU; return false; }
49 continue;
51 zeroes |= 0x8000_0000U;
53 // ok, this should be a valid char
54 int carry = b58idx.ptr[cast(ubyte)ch];
55 if (carry > 57) { zeroes = 0xffff_ffffU; return false; }
56 foreach_reverse (ref ubyte vb; b256[]) {
57 carry += 58*vb;
58 vb = carry&0xff;
59 carry /= 256;
60 if (carry == 0 && vb == 0) break;
62 if (carry != 0) { zeroes = 0xffff_ffffU; return false; }
64 return true;
67 uint length () const nothrow @trusted @nogc {
68 if (!error) {
69 uint bpos = 0;
70 while (bpos < b256.length && b256.ptr[bpos] == 0) ++bpos;
71 return cast(uint)(b256.length)-bpos+(zeroes&0x7fff_ffffU);
72 } else {
73 return 0;
77 @property uint getZeroes () const nothrow @trusted @nogc { pragma(inline, true); return (zeroes == 0xffff_ffffU ? 0 : zeroes&0x7fff_ffffU); }
79 const(ubyte)[] getBuf () const nothrow @trusted @nogc {
80 if (error) return null;
81 uint bpos = 0;
82 while (bpos < b256.length && b256.ptr[bpos] == 0) ++bpos;
83 uint xlen = cast(uint)(b256.length)-bpos;
84 return b256[0..xlen];
87 // return slice of the resuling buffer or `null` if there is no room in `dest`
88 ubyte[] get (ubyte[] dest) const nothrow @trusted @nogc {
89 if (error) return null;
90 uint bpos = 0;
91 while (bpos < b256.length && b256.ptr[bpos] == 0) ++bpos;
92 uint xlen = cast(uint)(b256.length)-bpos+(zeroes&0x7fff_ffffU);
93 if (dest.length < xlen) return null;
94 auto res = dest[0..xlen];
95 res[0..(zeroes&0x7fff_ffffU)] = 0;
96 res[(zeroes&0x7fff_ffffU)..$] = b256[bpos..$];
97 return res;
100 // allocate resuling buffer
101 ubyte[] get () const nothrow @trusted {
102 if (error) return null;
103 auto res = new ubyte[](length);
104 auto rx = get(res);
105 if (rx is null) { delete res; return null; }
106 return res;
111 public struct Base58Encoder {
112 private:
113 ubyte[2048] b58 = 0; // int size = cast(int)(buf.length)*138/100+1; // log(256) / log(58), rounded up
114 uint zeroes = 0; // bit 31 set: done zero counting; 0xffff_ffffU is error
116 public:
117 void clear () nothrow @trusted @nogc { pragma(inline, true); this = this.init; }
119 @property bool error () const nothrow @trusted @nogc { pragma(inline, true); return (zeroes == 0xffff_ffffU); }
121 bool put (const(ubyte)[] buf...) nothrow @trusted @nogc {
122 if (error) return false;
123 foreach (immutable ubyte b; buf) {
124 // counting zeroes?
125 if (zeroes < 0x8000_0000U) {
126 if (b == 0) {
127 if (++zeroes == 0x0fff_ffffU) { zeroes = 0xffff_ffffU; return false; }
128 continue;
130 zeroes |= 0x8000_0000U;
132 // ok, this should be a valid char
133 int carry = b;
134 foreach_reverse (immutable idx, ref ubyte vb; b58[]) {
135 carry += 256*vb;
136 vb = cast(ubyte)(carry%58);
137 carry /= 58;
138 if (carry == 0 && vb == 0) break;
140 if (carry != 0) { zeroes = 0xffff_ffffU; return false; }
142 return true;
145 uint length () const nothrow @trusted @nogc {
146 if (!error) {
147 uint bpos = 0;
148 while (bpos < b58.length && b58.ptr[bpos] == 0) ++bpos;
149 return cast(uint)(b58.length)-bpos+(zeroes&0x7fff_ffffU);
150 } else {
151 return 0;
155 // allocate resuling buffer
156 char[] get (char[] dest) const nothrow @trusted @nogc {
157 if (error) return null;
158 uint bpos = 0;
159 while (bpos < b58.length && b58.ptr[bpos] == 0) ++bpos;
160 uint xlen = cast(uint)(b58.length)-bpos+(zeroes&0x7fff_ffffU);
161 if (dest.length < xlen) return null;
162 auto res = dest[0..xlen];
163 res[0..(zeroes&0x7fff_ffffU)] = '1';
164 foreach (ref char rc; res) {
165 assert(bpos < b58.length);
166 assert(b58.ptr[bpos] < 58);
167 rc = base58Alphabet.ptr[b58.ptr[bpos++]];
169 return res;
172 // allocate resuling buffer
173 char[] get () const nothrow @trusted {
174 if (error) return null;
175 auto res = new char[](length);
176 auto rx = get(res);
177 if (rx is null) { delete res; return null; }
178 return res;
183 public ubyte[] base58Decode (const(void)[] vbuf) {
184 Base58Decoder dc;
185 foreach (immutable char ch; cast(const(char)[])vbuf) if (!dc.put(ch)) return null;
186 return dc.get();
190 public char[] base58Encode (const(void)[] vbuf) {
191 Base58Encoder ec;
192 foreach (immutable ubyte ch; cast(const(ubyte)[])vbuf) if (!ec.put(ch)) return null;
193 return ec.get();
197 public char[] base58EncodeCheck() (ubyte prefixbyte, const(void)[] data) {
198 import std.digest.sha : SHA256, sha256Of;
199 SHA256 hasher;
200 hasher.start();
201 hasher.put(prefixbyte);
202 hasher.put(cast(const(ubyte)[])data);
203 auto hash1 = hasher.finish();
204 scope(exit) hash1[] = 0;
205 auto hash2 = sha256Of(hash1[]);
206 scope(exit) hash2[] = 0;
207 Base58Encoder ec;
208 if (!ec.put(prefixbyte)) return null;
209 foreach (immutable ubyte b; cast(const(ubyte)[])data) if (!ec.put(b)) return null;
210 foreach (immutable ubyte b; hash2[0..4]) if (!ec.put(b)) return null;
211 return ec.get();
215 public ubyte[] base58DecodeCheck() (const(void)[] data) {
216 import std.digest.sha : SHA256, sha256Of;
217 Base58Decoder dc;
218 foreach (immutable char ch; cast(const(char)[])data) if (!dc.put(ch)) return null;
219 if (dc.length < 5) return null;
220 auto res = dc.get();
221 SHA256 hasher;
222 hasher.start();
223 hasher.put(res[0..$-4]);
224 auto hash1 = hasher.finish();
225 scope(exit) hash1[] = 0;
226 auto hash2 = sha256Of(hash1[]);
227 scope(exit) hash2[] = 0;
228 if (hash2[0..4] != res[$-4..$]) { res[] = 0; delete res; }
229 res[$-4..$] = 0;
230 return res[0..$-4];