iv.nanovega: removed unnecessary bounds checking
[iv.d.git] / base58.d
blob1bedb249f8ce50e4f59e389a0504f20bdc4cfec5
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, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 // BASE58 codec; because i can!
19 module iv.base58 /*is aliced*/;
20 private:
21 import iv.alice;
24 // all alphanumeric characters except for "0", "I", "O", and "l"
25 static immutable string base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
26 static immutable ubyte[256] b58idx = () {
27 ubyte[256] res = 255;
28 foreach (immutable idx, immutable char ch; base58Alphabet) res[ch] = cast(ubyte)idx;
29 return res;
30 }();
33 public struct Base58Decoder {
34 private:
35 ubyte[2048] b256 = 0; // int size = cast(int)(buf.length)*733/1000+1; // log(58) / log(256), rounded up
36 uint zeroes = 0; // bit 31 set: done zero counting; 0xffff_ffffU is error
38 public:
39 void clear () nothrow @trusted @nogc { pragma(inline, true); this = this.init; }
41 @property bool error () const nothrow @trusted @nogc { pragma(inline, true); return (zeroes == 0xffff_ffffU); }
43 bool put (const(char)[] buf...) nothrow @trusted @nogc {
44 if (error) return false;
45 foreach (immutable char ch; buf) {
46 // counting zeroes?
47 if (zeroes < 0x8000_0000U) {
48 if (ch == '1') {
49 if (++zeroes == 0x0fff_ffffU) { zeroes = 0xffff_ffffU; return false; }
50 continue;
52 zeroes |= 0x8000_0000U;
54 // ok, this should be a valid char
55 int carry = b58idx.ptr[cast(ubyte)ch];
56 if (carry > 57) { zeroes = 0xffff_ffffU; return false; }
57 foreach_reverse (ref ubyte vb; b256[]) {
58 carry += 58*vb;
59 vb = carry&0xff;
60 carry /= 256;
61 if (carry == 0 && vb == 0) break;
63 if (carry != 0) { zeroes = 0xffff_ffffU; return false; }
65 return true;
68 uint length () const nothrow @trusted @nogc {
69 if (!error) {
70 uint bpos = 0;
71 while (bpos < b256.length && b256.ptr[bpos] == 0) ++bpos;
72 return cast(uint)(b256.length)-bpos+(zeroes&0x7fff_ffffU);
73 } else {
74 return 0;
78 @property uint getZeroes () const nothrow @trusted @nogc { pragma(inline, true); return (zeroes == 0xffff_ffffU ? 0 : zeroes&0x7fff_ffffU); }
80 const(ubyte)[] getBuf () const nothrow @trusted @nogc {
81 if (error) return null;
82 uint bpos = 0;
83 while (bpos < b256.length && b256.ptr[bpos] == 0) ++bpos;
84 uint xlen = cast(uint)(b256.length)-bpos;
85 return b256[0..xlen];
88 // return slice of the resuling buffer or `null` if there is no room in `dest`
89 ubyte[] get (ubyte[] dest) const nothrow @trusted @nogc {
90 if (error) return null;
91 uint bpos = 0;
92 while (bpos < b256.length && b256.ptr[bpos] == 0) ++bpos;
93 uint xlen = cast(uint)(b256.length)-bpos+(zeroes&0x7fff_ffffU);
94 if (dest.length < xlen) return null;
95 auto res = dest[0..xlen];
96 res[0..(zeroes&0x7fff_ffffU)] = 0;
97 res[(zeroes&0x7fff_ffffU)..$] = b256[bpos..$];
98 return res;
101 // allocate resuling buffer
102 ubyte[] get () const nothrow @trusted {
103 if (error) return null;
104 auto res = new ubyte[](length);
105 auto rx = get(res);
106 if (rx is null) { delete res; return null; }
107 return res;
112 public struct Base58Encoder {
113 private:
114 ubyte[2048] b58 = 0; // int size = cast(int)(buf.length)*138/100+1; // log(256) / log(58), rounded up
115 uint zeroes = 0; // bit 31 set: done zero counting; 0xffff_ffffU is error
117 public:
118 void clear () nothrow @trusted @nogc { pragma(inline, true); this = this.init; }
120 @property bool error () const nothrow @trusted @nogc { pragma(inline, true); return (zeroes == 0xffff_ffffU); }
122 bool put (const(ubyte)[] buf...) nothrow @trusted @nogc {
123 if (error) return false;
124 foreach (immutable ubyte b; buf) {
125 // counting zeroes?
126 if (zeroes < 0x8000_0000U) {
127 if (b == 0) {
128 if (++zeroes == 0x0fff_ffffU) { zeroes = 0xffff_ffffU; return false; }
129 continue;
131 zeroes |= 0x8000_0000U;
133 // ok, this should be a valid char
134 int carry = b;
135 foreach_reverse (immutable idx, ref ubyte vb; b58[]) {
136 carry += 256*vb;
137 vb = cast(ubyte)(carry%58);
138 carry /= 58;
139 if (carry == 0 && vb == 0) break;
141 if (carry != 0) { zeroes = 0xffff_ffffU; return false; }
143 return true;
146 uint length () const nothrow @trusted @nogc {
147 if (!error) {
148 uint bpos = 0;
149 while (bpos < b58.length && b58.ptr[bpos] == 0) ++bpos;
150 return cast(uint)(b58.length)-bpos+(zeroes&0x7fff_ffffU);
151 } else {
152 return 0;
156 // allocate resuling buffer
157 char[] get (char[] dest) const nothrow @trusted @nogc {
158 if (error) return null;
159 uint bpos = 0;
160 while (bpos < b58.length && b58.ptr[bpos] == 0) ++bpos;
161 uint xlen = cast(uint)(b58.length)-bpos+(zeroes&0x7fff_ffffU);
162 if (dest.length < xlen) return null;
163 auto res = dest[0..xlen];
164 res[0..(zeroes&0x7fff_ffffU)] = '1';
165 foreach (ref char rc; res) {
166 assert(bpos < b58.length);
167 assert(b58.ptr[bpos] < 58);
168 rc = base58Alphabet.ptr[b58.ptr[bpos++]];
170 return res;
173 // allocate resuling buffer
174 char[] get () const nothrow @trusted {
175 if (error) return null;
176 auto res = new char[](length);
177 auto rx = get(res);
178 if (rx is null) { delete res; return null; }
179 return res;
184 public ubyte[] base58Decode (const(void)[] vbuf) {
185 Base58Decoder dc;
186 foreach (immutable char ch; cast(const(char)[])vbuf) if (!dc.put(ch)) return null;
187 return dc.get();
191 public char[] base58Encode (const(void)[] vbuf) {
192 Base58Encoder ec;
193 foreach (immutable ubyte ch; cast(const(ubyte)[])vbuf) if (!ec.put(ch)) return null;
194 return ec.get();
198 public char[] base58EncodeCheck() (ubyte prefixbyte, const(void)[] data) {
199 import std.digest.sha : SHA256, sha256Of;
200 SHA256 hasher;
201 hasher.start();
202 hasher.put(prefixbyte);
203 hasher.put(cast(const(ubyte)[])data);
204 auto hash1 = hasher.finish();
205 scope(exit) hash1[] = 0;
206 auto hash2 = sha256Of(hash1[]);
207 scope(exit) hash2[] = 0;
208 Base58Encoder ec;
209 if (!ec.put(prefixbyte)) return null;
210 foreach (immutable ubyte b; cast(const(ubyte)[])data) if (!ec.put(b)) return null;
211 foreach (immutable ubyte b; hash2[0..4]) if (!ec.put(b)) return null;
212 return ec.get();
216 public ubyte[] base58DecodeCheck() (const(void)[] data) {
217 import std.digest.sha : SHA256, sha256Of;
218 Base58Decoder dc;
219 foreach (immutable char ch; cast(const(char)[])data) if (!dc.put(ch)) return null;
220 if (dc.length < 5) return null;
221 auto res = dc.get();
222 SHA256 hasher;
223 hasher.start();
224 hasher.put(res[0..$-4]);
225 auto hash1 = hasher.finish();
226 scope(exit) hash1[] = 0;
227 auto hash2 = sha256Of(hash1[]);
228 scope(exit) hash2[] = 0;
229 if (hash2[0..4] != res[$-4..$]) { res[] = 0; delete res; }
230 res[$-4..$] = 0;
231 return res[0..$-4];