Add examples to snippets
This commit is contained in:
parent
db48e175d0
commit
e1bec6b996
418
snippets/b58.ts
Normal file
418
snippets/b58.ts
Normal file
@ -0,0 +1,418 @@
|
||||
/**
|
||||
* Base58 encoding/decoding
|
||||
*/
|
||||
|
||||
export {
|
||||
encode,
|
||||
decode
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// ENCODING
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Encode a Uint8Array into base58
|
||||
*/
|
||||
function
|
||||
encode
|
||||
(binary : Uint8Array)
|
||||
: string
|
||||
{
|
||||
let num_leading_zeros : number = nlz(binary);
|
||||
let rest : Uint8Array = binary.slice(num_leading_zeros);
|
||||
let ones : string = encode_zeros(num_leading_zeros);
|
||||
let rest_b58 : string = encode_rest(rest);
|
||||
let result : string = ones + rest_b58;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* count the number of leading zeros in a uint8array
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
nlz
|
||||
(bytes: Uint8Array)
|
||||
: number
|
||||
{
|
||||
let n = 0;
|
||||
for (let this_byte of bytes)
|
||||
{
|
||||
if (0 === this_byte) { n++; }
|
||||
else { break; }
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generate a bunch of '1's for however many leading zeros there are
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_zeros
|
||||
(how_many : number)
|
||||
: string
|
||||
{
|
||||
let ones : string = '';
|
||||
for (let i = 1;
|
||||
i <= how_many;
|
||||
i++)
|
||||
{
|
||||
ones += '1';
|
||||
}
|
||||
|
||||
return ones;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode a Uint8Array that has no leading zeros
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_rest
|
||||
(bytes : Uint8Array)
|
||||
: string
|
||||
{
|
||||
let bytes_bignum : bigint = bytes_to_bigint(bytes);
|
||||
let result : string = bignum_to_base58(bytes_bignum);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a bytestring to a bignum
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
bytes_to_bigint
|
||||
(bytes: Uint8Array)
|
||||
: bigint
|
||||
{
|
||||
let acc_bigint : bigint = 0n;
|
||||
for(let this_byte of bytes)
|
||||
{
|
||||
acc_bigint <<= 8n;
|
||||
acc_bigint += BigInt(this_byte);
|
||||
}
|
||||
return acc_bigint;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a BigInt to Base58
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
bignum_to_base58
|
||||
(q: bigint)
|
||||
: string
|
||||
{
|
||||
let s = '';
|
||||
while (q !== 0n)
|
||||
{
|
||||
let this_n : bigint = q % 58n;
|
||||
q /= 58n;
|
||||
|
||||
let this_b58_char : string = bigint_to_char(this_n);
|
||||
s = this_b58_char + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// DECODING
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Decode a Base58 string into a Uint8Array
|
||||
*/
|
||||
function
|
||||
decode
|
||||
(base58: string)
|
||||
: Uint8Array
|
||||
{
|
||||
let num_leading_ones : number = nlo(base58);
|
||||
let rest : string = base58.slice(num_leading_ones);
|
||||
let zeros : Array<number> = decode_ones(num_leading_ones);
|
||||
let rest_arr : Array<number> = decode_rest(rest);
|
||||
let pre_result : Array<number> = zeros.concat(rest_arr);
|
||||
return new Uint8Array(pre_result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* count the number of leading 1 characters in a uint8array
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
nlo
|
||||
(base58: string)
|
||||
: number
|
||||
{
|
||||
let n = 0;
|
||||
for (let this_char of base58)
|
||||
{
|
||||
if ('1' === this_char) { n++; }
|
||||
else { break; }
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generate a bunch of '0's for however many leading ones there are
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode_ones
|
||||
(how_many : number)
|
||||
: Array<number>
|
||||
{
|
||||
let zeros : Array<number> = [];
|
||||
for (let i = 1;
|
||||
i <= how_many;
|
||||
i++)
|
||||
{
|
||||
zeros.push(0);
|
||||
}
|
||||
|
||||
return zeros;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode a string that has no leading 1s
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode_rest
|
||||
(base58: string)
|
||||
: Array<number>
|
||||
{
|
||||
let result_bignum : bigint = base58_to_bigint(base58);
|
||||
let result : Array<number> = bigint_to_base256(result_bignum);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a base58 string to a bignum
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
base58_to_bigint
|
||||
(base58: string)
|
||||
: bigint
|
||||
{
|
||||
let acc_bigint : bigint = 0n;
|
||||
for(let this_char of base58)
|
||||
{
|
||||
acc_bigint *= 58n;
|
||||
acc_bigint += char_to_bigint(this_char);
|
||||
}
|
||||
return acc_bigint;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* convert a bignum into a byte array
|
||||
*
|
||||
* @end
|
||||
*/
|
||||
function
|
||||
bigint_to_base256
|
||||
(q: bigint)
|
||||
: Array<number>
|
||||
{
|
||||
let arr_reverse = [];
|
||||
while(q !== 0n)
|
||||
{
|
||||
let r: number = Number(q % 256n);
|
||||
q /= 256n;
|
||||
arr_reverse.push(r);
|
||||
}
|
||||
arr_reverse.reverse();
|
||||
return arr_reverse;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// TRANSLATION TABLES
|
||||
//=============================================================================
|
||||
|
||||
|
||||
/**
|
||||
* Base58 integer -> character conversion table
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
bigint_to_char
|
||||
(n: bigint)
|
||||
: string
|
||||
{
|
||||
switch(n) {
|
||||
case 0n: return '1';
|
||||
case 1n: return '2';
|
||||
case 2n: return '3';
|
||||
case 3n: return '4';
|
||||
case 4n: return '5';
|
||||
case 5n: return '6';
|
||||
case 6n: return '7';
|
||||
case 7n: return '8';
|
||||
case 8n: return '9';
|
||||
case 9n: return 'A';
|
||||
case 10n: return 'B';
|
||||
case 11n: return 'C';
|
||||
case 12n: return 'D';
|
||||
case 13n: return 'E';
|
||||
case 14n: return 'F';
|
||||
case 15n: return 'G';
|
||||
case 16n: return 'H';
|
||||
case 17n: return 'J';
|
||||
case 18n: return 'K';
|
||||
case 19n: return 'L';
|
||||
case 20n: return 'M';
|
||||
case 21n: return 'N';
|
||||
case 22n: return 'P';
|
||||
case 23n: return 'Q';
|
||||
case 24n: return 'R';
|
||||
case 25n: return 'S';
|
||||
case 26n: return 'T';
|
||||
case 27n: return 'U';
|
||||
case 28n: return 'V';
|
||||
case 29n: return 'W';
|
||||
case 30n: return 'X';
|
||||
case 31n: return 'Y';
|
||||
case 32n: return 'Z';
|
||||
case 33n: return 'a';
|
||||
case 34n: return 'b';
|
||||
case 35n: return 'c';
|
||||
case 36n: return 'd';
|
||||
case 37n: return 'e';
|
||||
case 38n: return 'f';
|
||||
case 39n: return 'g';
|
||||
case 40n: return 'h';
|
||||
case 41n: return 'i';
|
||||
case 42n: return 'j';
|
||||
case 43n: return 'k';
|
||||
case 44n: return 'm';
|
||||
case 45n: return 'n';
|
||||
case 46n: return 'o';
|
||||
case 47n: return 'p';
|
||||
case 48n: return 'q';
|
||||
case 49n: return 'r';
|
||||
case 50n: return 's';
|
||||
case 51n: return 't';
|
||||
case 52n: return 'u';
|
||||
case 53n: return 'v';
|
||||
case 54n: return 'w';
|
||||
case 55n: return 'x';
|
||||
case 56n: return 'y';
|
||||
case 57n: return 'z';
|
||||
default:
|
||||
throw new Error('invalid base58 bigint: ' + n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Base58 character -> integer conversion table
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
char_to_bigint
|
||||
(s: string)
|
||||
: bigint
|
||||
{
|
||||
switch(s) {
|
||||
case '1': return 0n;
|
||||
case '2': return 1n;
|
||||
case '3': return 2n;
|
||||
case '4': return 3n;
|
||||
case '5': return 4n;
|
||||
case '6': return 5n;
|
||||
case '7': return 6n;
|
||||
case '8': return 7n;
|
||||
case '9': return 8n;
|
||||
case 'A': return 9n;
|
||||
case 'B': return 10n;
|
||||
case 'C': return 11n;
|
||||
case 'D': return 12n;
|
||||
case 'E': return 13n;
|
||||
case 'F': return 14n;
|
||||
case 'G': return 15n;
|
||||
case 'H': return 16n;
|
||||
case 'J': return 17n;
|
||||
case 'K': return 18n;
|
||||
case 'L': return 19n;
|
||||
case 'M': return 20n;
|
||||
case 'N': return 21n;
|
||||
case 'P': return 22n;
|
||||
case 'Q': return 23n;
|
||||
case 'R': return 24n;
|
||||
case 'S': return 25n;
|
||||
case 'T': return 26n;
|
||||
case 'U': return 27n;
|
||||
case 'V': return 28n;
|
||||
case 'W': return 29n;
|
||||
case 'X': return 30n;
|
||||
case 'Y': return 31n;
|
||||
case 'Z': return 32n;
|
||||
case 'a': return 33n;
|
||||
case 'b': return 34n;
|
||||
case 'c': return 35n;
|
||||
case 'd': return 36n;
|
||||
case 'e': return 37n;
|
||||
case 'f': return 38n;
|
||||
case 'g': return 39n;
|
||||
case 'h': return 40n;
|
||||
case 'i': return 41n;
|
||||
case 'j': return 42n;
|
||||
case 'k': return 43n;
|
||||
case 'm': return 44n;
|
||||
case 'n': return 45n;
|
||||
case 'o': return 46n;
|
||||
case 'p': return 47n;
|
||||
case 'q': return 48n;
|
||||
case 'r': return 49n;
|
||||
case 's': return 50n;
|
||||
case 't': return 51n;
|
||||
case 'u': return 52n;
|
||||
case 'v': return 53n;
|
||||
case 'w': return 54n;
|
||||
case 'x': return 55n;
|
||||
case 'y': return 56n;
|
||||
case 'z': return 57n;
|
||||
default:
|
||||
throw new Error('invalid base58 char: ' + s);
|
||||
}
|
||||
}
|
||||
655
snippets/b64.ts
Normal file
655
snippets/b64.ts
Normal file
@ -0,0 +1,655 @@
|
||||
/**
|
||||
* Base64 Utility Functions in TypeScript
|
||||
*/
|
||||
|
||||
export {
|
||||
encode,
|
||||
decode
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode an array of bytes as a Uint8Array in base64 notation.
|
||||
*/
|
||||
function
|
||||
encode
|
||||
(bytes: Uint8Array)
|
||||
: string
|
||||
{
|
||||
// slice the array
|
||||
// length of head is a multiple of 3
|
||||
// treat the tail as a special case
|
||||
let {head, tail, tail_len} = slice3k(bytes);
|
||||
let head_str : string = encode_head(head);
|
||||
let tail_str : string = encode_tail(tail, tail_len);
|
||||
return head_str + tail_str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
type slice3k
|
||||
= {head : Uint8Array,
|
||||
tail : Uint8Array,
|
||||
tail_len : number};
|
||||
|
||||
/**
|
||||
* Take a Uint8Array, take the first 3k (k >= 0) bytes, put them in head, and
|
||||
* the remaining 0,1, or 2 bytes, put them in tail
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
slice3k
|
||||
(bytes: Uint8Array)
|
||||
: slice3k
|
||||
{
|
||||
let len : number = bytes.length;
|
||||
// too lazy to look up how to do integer division in js so this will do
|
||||
let tail_len : number = len % 3;
|
||||
let head_len : number = len - tail_len;
|
||||
// for slice:
|
||||
// first argument is the 0-index of the start
|
||||
// second - first is the length of the slice
|
||||
let head : Uint8Array = bytes.slice(0, head_len);
|
||||
// empty second argument means go to the end
|
||||
let tail : Uint8Array = bytes.slice(head_len);
|
||||
return {head : head,
|
||||
tail : tail,
|
||||
tail_len : tail_len};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode a Uint8Array whose length is known to be a multiple of 3
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_head
|
||||
(head_bytes: Uint8Array)
|
||||
: string
|
||||
{
|
||||
// can assume length of bytes is a multiple of 3
|
||||
// start index at 0
|
||||
// increment by 3
|
||||
let head_bytes_len : number = head_bytes.length;
|
||||
let max_idx0 : number = head_bytes_len - 1;
|
||||
|
||||
let head_str_acc : string = '';
|
||||
for(let this_3slice_start_idx0 = 0;
|
||||
this_3slice_start_idx0 <= max_idx0;
|
||||
this_3slice_start_idx0 += 3)
|
||||
{
|
||||
let this_3slice_bytes : Uint8Array = head_bytes.slice(this_3slice_start_idx0, this_3slice_start_idx0 + 3);
|
||||
let this_3slice_str : string = encode3(this_3slice_bytes);
|
||||
head_str_acc += this_3slice_str;
|
||||
}
|
||||
|
||||
return head_str_acc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode a 3 bytes into base64 notation
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode3
|
||||
(bytes: Uint8Array)
|
||||
: string
|
||||
{
|
||||
let b0 : number = bytes[0];
|
||||
let b1 : number = bytes[1];
|
||||
let b2 : number = bytes[2];
|
||||
|
||||
// ABCDEFGH 12345678 abcdefgh
|
||||
// b0 b1 b2
|
||||
// ABCDEF GH1234 5678ab cdefgh
|
||||
// n0 n1 n2 n3
|
||||
let n0 : number = b0 >> 2;
|
||||
// b0 = ABCDEFGH
|
||||
// 4 = _____1__
|
||||
// b0 % 4 = ______GH
|
||||
// (b0 % 4) << 4 = __GH____
|
||||
// b1 = 12345678
|
||||
// b1 >> 4 = ____1234
|
||||
// n1 = __GH1234
|
||||
let n1 : number = ((b0 % 4) << 4) + (b1 >> 4);
|
||||
// b1 = 12345678
|
||||
// 16 = ___1____
|
||||
// b1 % 16 = ____5678
|
||||
// (b1 % 16) << 2 = __5678__
|
||||
// b2 = abcdefgh
|
||||
// b2 >> 6 = ______ab
|
||||
// n2 = __5678ab
|
||||
let n2 : number = ((b1 % 16) << 2) + (b2 >> 6);
|
||||
// b2 = abcdefgh
|
||||
// 64 = _1______
|
||||
// n3 = __cdefgh
|
||||
let n3 : number = b2 % 64;
|
||||
|
||||
// convert to chars
|
||||
let s0 : string = int2char(n0);
|
||||
let s1 : string = int2char(n1);
|
||||
let s2 : string = int2char(n2);
|
||||
let s3 : string = int2char(n3);
|
||||
|
||||
// retrvn
|
||||
return s0 + s1 + s2 + s3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode the final 0, 1, or 2 bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_tail
|
||||
(tail_bytes : Uint8Array,
|
||||
tail_len : number)
|
||||
: string
|
||||
{
|
||||
switch(tail_len) {
|
||||
case 0: return '';
|
||||
case 1: return encode1(tail_bytes);
|
||||
case 2: return encode2(tail_bytes);
|
||||
default:
|
||||
throw new Error('encode_tail with tail_len = ' + tail_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode a single byte
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode1
|
||||
(bytes: Uint8Array)
|
||||
: string
|
||||
{
|
||||
let b0 : number = bytes[0];
|
||||
// n0 = __ABCDEF
|
||||
// b0 = ABCDEFGH
|
||||
// b0 >> 2 = __ABCDEF
|
||||
let n0 : number = b0 >> 2;
|
||||
// n1 = __GH____
|
||||
// b0 = ABCDEFGH
|
||||
// 4 = _____1__
|
||||
// b0 % 4 = ______GH
|
||||
// (b0 % 4) << 4 = __GH____
|
||||
let n1 : number = (b0 % 4) << 4;
|
||||
|
||||
return int2char(n0) + int2char(n1) + '==';
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode two bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode2
|
||||
(bytes: Uint8Array)
|
||||
: string
|
||||
{
|
||||
let b0 : number = bytes[0];
|
||||
let b1 : number = bytes[1];
|
||||
|
||||
// ABCDEFGH 12345678
|
||||
// b0 b1
|
||||
// ABCDEF GH1234 5678__
|
||||
// n0 n1 n2
|
||||
let n0 : number = b0 >> 2;
|
||||
// b0 = ABCDEFGH
|
||||
// 4 = _____1__
|
||||
// b0 % 4 = ______GH
|
||||
// (b0 % 4) << 4 = __GH____
|
||||
// b1 = 12345678
|
||||
// b1 >> 4 = ____1234
|
||||
// n1 = __GH1234
|
||||
let n1 : number = ((b0 % 4) << 4) + (b1 >> 4);
|
||||
// b1 = 12345678
|
||||
// 16 = ___1____
|
||||
// b1 % 16 = ____5678
|
||||
// (b1 % 16) << 2 = __5678__
|
||||
// n2 = __5678__
|
||||
let n2 : number = (b1 % 16) << 2;
|
||||
|
||||
// convert to chars
|
||||
let s0 : string = int2char(n0);
|
||||
let s1 : string = int2char(n1);
|
||||
let s2 : string = int2char(n2);
|
||||
|
||||
// retrvn
|
||||
return s0 + s1 + s2 + '=';
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode a base64-encoded string
|
||||
*/
|
||||
function
|
||||
decode
|
||||
(base64_str : string)
|
||||
: Uint8Array
|
||||
{
|
||||
// length of the string is guaranteed to be a multiple of 4
|
||||
// if the string is empty, return the empty array
|
||||
let len = base64_str.length;
|
||||
// this branching contains the implicit assertion that the length is a
|
||||
// multiple of 4. If this is not true, the bottom branch is triggered.
|
||||
// general case goes first because speeeeeed
|
||||
if ( (4 < len)
|
||||
&& (0 === (len % 4)))
|
||||
{
|
||||
// split the head and tail
|
||||
let tail_start_idx0 : number = len - 4;
|
||||
let head_s : string = base64_str.slice(0, tail_start_idx0);
|
||||
let tail_s : string = base64_str.slice(tail_start_idx0);
|
||||
// Using arrays because Uint8Arrays don't have a concat operation
|
||||
let head_arr : Array<number> = decode_head(head_s);
|
||||
let tail_arr : Array<number> = decode_tail(tail_s);
|
||||
// silly to put these in variables but this is exactly the type of
|
||||
// situation where JS type insanity shows up
|
||||
//
|
||||
// see: i forgot
|
||||
// > [1,2,3] + [4,5,6]
|
||||
// '1,2,34,5,6'
|
||||
//
|
||||
// Originally, I used + like some sort of moron who codes in a sane
|
||||
// language
|
||||
//
|
||||
// seriously what is this language
|
||||
//
|
||||
// this is some clown behavior
|
||||
let total_arr : Array<number> = head_arr.concat(tail_arr);
|
||||
return new Uint8Array(total_arr);
|
||||
}
|
||||
// special case if the length is exactly 4
|
||||
else if (4 === len)
|
||||
{
|
||||
// it's just a tail
|
||||
return new Uint8Array(decode_tail(base64_str));
|
||||
}
|
||||
// empty string
|
||||
else if (0 === len)
|
||||
{
|
||||
return new Uint8Array([]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Error('base64 decode: invalid string length: ' + len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode a string known to not have any padding
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode_head
|
||||
(s: string)
|
||||
: Array<number>
|
||||
{
|
||||
// go 4 characters at a time
|
||||
let max_i0 : number = s.length - 1;
|
||||
let decoded_acc : Array<number> = [];
|
||||
for(let i0 = 0;
|
||||
i0 <= max_i0;
|
||||
i0 += 4)
|
||||
{
|
||||
let this_slice_s : string = s.slice(i0, i0 + 4);
|
||||
let this_slice_arr : Array<number> = decode3(this_slice_s);
|
||||
// update accumulator
|
||||
decoded_acc = decoded_acc.concat(this_slice_arr);
|
||||
}
|
||||
return decoded_acc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode 4 characters that correspond to either 3 bytes, 2, bytes, or 1 byte
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode_tail
|
||||
(s: string)
|
||||
: Array<number>
|
||||
{
|
||||
// all that matters right now is the last 2 chars
|
||||
// s0, s1, s2, s3
|
||||
// 0 based indexing is so annoying
|
||||
let s2 = s[2];
|
||||
let s3 = s[3];
|
||||
|
||||
// braaaaaaaaaaaaaaaaaench
|
||||
// two equals signs means 1 byte
|
||||
if (('=' === s3) && ('=' === s2)) {
|
||||
return decode1(s);
|
||||
}
|
||||
// one equals sign means 2 bytes
|
||||
else if (('=' === s3)) {
|
||||
return decode2(s);
|
||||
}
|
||||
// 0 equals signs means 3 bytes
|
||||
else {
|
||||
return decode3(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode a 4-character long base64 string corresponding to 3 bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode3
|
||||
(s: string)
|
||||
: Array<number>
|
||||
{
|
||||
// pull out strings
|
||||
let s0 : string = s[0];
|
||||
let s1 : string = s[1];
|
||||
let s2 : string = s[2];
|
||||
let s3 : string = s[3];
|
||||
|
||||
// convert to numbers
|
||||
let n0 : number = char2int(s0);
|
||||
let n1 : number = char2int(s1);
|
||||
let n2 : number = char2int(s2);
|
||||
let n3 : number = char2int(s3);
|
||||
|
||||
// abcdef gh1234 5678ab cdefgh
|
||||
// n0 n1 n2 n3
|
||||
// abcdefgh 12345678 abcdefgh
|
||||
// b0 b1 b2
|
||||
|
||||
// n0 = __abcdef
|
||||
// n1 = __gh1234
|
||||
// n0 << 2 = abcdef__
|
||||
// n1 >> 4 = ______gh
|
||||
// b0 = abcdefgh
|
||||
let b0 : number = (n0 << 2) + (n1 >> 4);
|
||||
// n1 = __gh1234
|
||||
// 16 = ___1____
|
||||
// n1 % 16 = ____1234
|
||||
// (n1 % 16) << 4 = 1234____
|
||||
// n2 = __5678ab
|
||||
// n2 >> 2 = ____5678
|
||||
// b1 = 12345678
|
||||
let b1 : number = ((n1 % 16) << 4) + (n2 >> 2);
|
||||
// n2 = __5678ab
|
||||
// 4 = _____1__
|
||||
// n2 % 4 = ______ab
|
||||
// (n2 % 4) << 6 = ab______
|
||||
// n3 = __cdefgh
|
||||
let b2 : number = ((n2 % 4) << 6) + n3;
|
||||
|
||||
return [b0, b1, b2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode a 4-character long base64 string corresponding to 2 bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode2
|
||||
(s: string)
|
||||
: Array<number>
|
||||
{
|
||||
// xyz=
|
||||
// pull out strings
|
||||
let s0 : string = s[0];
|
||||
let s1 : string = s[1];
|
||||
let s2 : string = s[2];
|
||||
|
||||
// convert to numbers
|
||||
let n0 : number = char2int(s0);
|
||||
let n1 : number = char2int(s1);
|
||||
let n2 : number = char2int(s2);
|
||||
|
||||
// abcdef gh1234 5678__
|
||||
// n0 n1 n2
|
||||
// abcdefgh 12345678
|
||||
// b0 b1
|
||||
|
||||
// n0 = __abcdef
|
||||
// n1 = __gh1234
|
||||
// n0 << 2 = abcdef__
|
||||
// n1 >> 4 = ______gh
|
||||
// b0 = abcdefgh
|
||||
let b0 : number = (n0 << 2) + (n1 >> 4);
|
||||
// n1 = __gh1234
|
||||
// 16 = ___1____
|
||||
// n1 % 16 = ____1234
|
||||
// (n1 % 16) << 4 = 1234____
|
||||
// n2 = __5678__
|
||||
// n2 >> 2 = ____5678
|
||||
// b1 = 12345678
|
||||
let b1 : number = ((n1 % 16) << 4) + (n2 >> 2);
|
||||
|
||||
return [b0, b1];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode a 4-character long base64 string corresponding to 2 bytes
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode1
|
||||
(s: string)
|
||||
: Array<number>
|
||||
{
|
||||
// xy==
|
||||
// pull out strings
|
||||
let s0 : string = s[0];
|
||||
let s1 : string = s[1];
|
||||
|
||||
// convert to numbers
|
||||
let n0 : number = char2int(s0);
|
||||
let n1 : number = char2int(s1);
|
||||
|
||||
// abcdef gh____
|
||||
// n0 n1
|
||||
// abcdefgh
|
||||
// b0
|
||||
|
||||
// n0 = __abcdef
|
||||
// n1 = __gh____
|
||||
// n0 << 2 = abcdef__
|
||||
// n1 >> 4 = ______gh
|
||||
// b0 = abcdefgh
|
||||
let b0 : number = (n0 << 2) + (n1 >> 4);
|
||||
|
||||
return [b0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: these tables would *probably* be faster if they were made into objects
|
||||
|
||||
/**
|
||||
* Conversion table for base64 encode
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
int2char
|
||||
(n: number)
|
||||
: string
|
||||
{
|
||||
switch(n) {
|
||||
case 0: return 'A';
|
||||
case 1: return 'B';
|
||||
case 2: return 'C';
|
||||
case 3: return 'D';
|
||||
case 4: return 'E';
|
||||
case 5: return 'F';
|
||||
case 6: return 'G';
|
||||
case 7: return 'H';
|
||||
case 8: return 'I';
|
||||
case 9: return 'J';
|
||||
case 10: return 'K';
|
||||
case 11: return 'L';
|
||||
case 12: return 'M';
|
||||
case 13: return 'N';
|
||||
case 14: return 'O';
|
||||
case 15: return 'P';
|
||||
case 16: return 'Q';
|
||||
case 17: return 'R';
|
||||
case 18: return 'S';
|
||||
case 19: return 'T';
|
||||
case 20: return 'U';
|
||||
case 21: return 'V';
|
||||
case 22: return 'W';
|
||||
case 23: return 'X';
|
||||
case 24: return 'Y';
|
||||
case 25: return 'Z';
|
||||
case 26: return 'a';
|
||||
case 27: return 'b';
|
||||
case 28: return 'c';
|
||||
case 29: return 'd';
|
||||
case 30: return 'e';
|
||||
case 31: return 'f';
|
||||
case 32: return 'g';
|
||||
case 33: return 'h';
|
||||
case 34: return 'i';
|
||||
case 35: return 'j';
|
||||
case 36: return 'k';
|
||||
case 37: return 'l';
|
||||
case 38: return 'm';
|
||||
case 39: return 'n';
|
||||
case 40: return 'o';
|
||||
case 41: return 'p';
|
||||
case 42: return 'q';
|
||||
case 43: return 'r';
|
||||
case 44: return 's';
|
||||
case 45: return 't';
|
||||
case 46: return 'u';
|
||||
case 47: return 'v';
|
||||
case 48: return 'w';
|
||||
case 49: return 'x';
|
||||
case 50: return 'y';
|
||||
case 51: return 'z';
|
||||
case 52: return '0';
|
||||
case 53: return '1';
|
||||
case 54: return '2';
|
||||
case 55: return '3';
|
||||
case 56: return '4';
|
||||
case 57: return '5';
|
||||
case 58: return '6';
|
||||
case 59: return '7';
|
||||
case 60: return '8';
|
||||
case 61: return '9';
|
||||
case 62: return '+';
|
||||
case 63: return '/';
|
||||
default: throw new Error("invalid base64 encode byte: " + n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Conversion table for base64 decode
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
char2int
|
||||
(s: string)
|
||||
: number
|
||||
{
|
||||
switch(s) {
|
||||
case 'A': return 0;
|
||||
case 'B': return 1;
|
||||
case 'C': return 2;
|
||||
case 'D': return 3;
|
||||
case 'E': return 4;
|
||||
case 'F': return 5;
|
||||
case 'G': return 6;
|
||||
case 'H': return 7;
|
||||
case 'I': return 8;
|
||||
case 'J': return 9;
|
||||
case 'K': return 10;
|
||||
case 'L': return 11;
|
||||
case 'M': return 12;
|
||||
case 'N': return 13;
|
||||
case 'O': return 14;
|
||||
case 'P': return 15;
|
||||
case 'Q': return 16;
|
||||
case 'R': return 17;
|
||||
case 'S': return 18;
|
||||
case 'T': return 19;
|
||||
case 'U': return 20;
|
||||
case 'V': return 21;
|
||||
case 'W': return 22;
|
||||
case 'X': return 23;
|
||||
case 'Y': return 24;
|
||||
case 'Z': return 25;
|
||||
case 'a': return 26;
|
||||
case 'b': return 27;
|
||||
case 'c': return 28;
|
||||
case 'd': return 29;
|
||||
case 'e': return 30;
|
||||
case 'f': return 31;
|
||||
case 'g': return 32;
|
||||
case 'h': return 33;
|
||||
case 'i': return 34;
|
||||
case 'j': return 35;
|
||||
case 'k': return 36;
|
||||
case 'l': return 37;
|
||||
case 'm': return 38;
|
||||
case 'n': return 39;
|
||||
case 'o': return 40;
|
||||
case 'p': return 41;
|
||||
case 'q': return 42;
|
||||
case 'r': return 43;
|
||||
case 's': return 44;
|
||||
case 't': return 45;
|
||||
case 'u': return 46;
|
||||
case 'v': return 47;
|
||||
case 'w': return 48;
|
||||
case 'x': return 49;
|
||||
case 'y': return 50;
|
||||
case 'z': return 51;
|
||||
case '0': return 52;
|
||||
case '1': return 53;
|
||||
case '2': return 54;
|
||||
case '3': return 55;
|
||||
case '4': return 56;
|
||||
case '5': return 57;
|
||||
case '6': return 58;
|
||||
case '7': return 59;
|
||||
case '8': return 60;
|
||||
case '9': return 61;
|
||||
case '+': return 62;
|
||||
case '/': return 63;
|
||||
default: throw new Error("invalid base64 character: " + s);
|
||||
}
|
||||
}
|
||||
521
snippets/rlp.ts
Normal file
521
snippets/rlp.ts
Normal file
@ -0,0 +1,521 @@
|
||||
/**
|
||||
* Ethereum Recursive-Length Prefix Encoding implementation
|
||||
*
|
||||
* Two reference implementations:
|
||||
*
|
||||
* 1. Ethereum Python implementation
|
||||
* 2. My Erlang implementation (agrees with Ethereum Python in randomized
|
||||
* tests)
|
||||
*
|
||||
* This work can be found in ../test/testgen/
|
||||
*
|
||||
* FIXME: need to have Safe assertions for "i am decoding a list", "i am
|
||||
* decoding a binary", "I am decoding something with no remainder", etc
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
export {
|
||||
decoded_data,
|
||||
decode_result,
|
||||
decode,
|
||||
encode
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// DECODING
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
|
||||
|
||||
/**
|
||||
* Data that RLP can encode. Also the result of decoding.
|
||||
*/
|
||||
type decoded_data
|
||||
= Uint8Array
|
||||
| Array<decoded_data>;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decoding can have a remainder
|
||||
*/
|
||||
type decode_result
|
||||
= {decoded_data : decoded_data,
|
||||
remainder : Uint8Array};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode an RLP-encoded bytestring into a `decode_result` (decoded data +
|
||||
* whatever wasn't consumed)
|
||||
*/
|
||||
function
|
||||
decode
|
||||
(bytes: Uint8Array)
|
||||
: decode_result
|
||||
{
|
||||
// check the first byte
|
||||
let first_byte: number = bytes[0];
|
||||
let rest : Uint8Array = bytes.slice(1);
|
||||
// if the first byte is between 0 and 127, that is the data
|
||||
if
|
||||
(first_byte <= 127) {
|
||||
return dr(new Uint8Array([first_byte]), rest);
|
||||
}
|
||||
// if the first byte is between 128 and 183 = 128 + 55, it is a bytestring
|
||||
// and the length is Byte - 128
|
||||
else if
|
||||
(first_byte <= 183) {
|
||||
let payload_byte_length : number = first_byte - 128;
|
||||
let payload : Uint8Array = rest.slice(0, payload_byte_length);
|
||||
let rest2 : Uint8Array = rest.slice(payload_byte_length);
|
||||
return dr(payload, rest2);
|
||||
}
|
||||
// if the first byte is between 184 = 183 + 1 and 191 = 183 + 8, it is a
|
||||
// bytestring. the byte length of bytestring is FirstByte - 183. Then pull
|
||||
// out the actual data
|
||||
else if
|
||||
(first_byte <= 191) {
|
||||
let byte_length_of_byte_length : number = first_byte - 183;
|
||||
let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length);
|
||||
let byte_length : number = bytes_to_number(bytes_of_byte_length);
|
||||
let bytes : Uint8Array = rest.slice(byte_length_of_byte_length,
|
||||
byte_length + byte_length_of_byte_length);
|
||||
let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length);
|
||||
return dr(bytes, rest2);
|
||||
}
|
||||
// If the first byte is between 192 and 247 = 192 + 55, it is a list. The
|
||||
// byte length of the list-payload is FirstByte - 192. Then the list
|
||||
// payload, which needs to be decoded on its own.
|
||||
else if
|
||||
(first_byte <= 247) {
|
||||
let byte_length_of_list : number = first_byte - 192;
|
||||
let list_payload : Uint8Array = rest.slice(0, byte_length_of_list);
|
||||
let list : Array<decoded_data> = decode_list(list_payload);
|
||||
let rest2 : Uint8Array = rest.slice(byte_length_of_list);
|
||||
return dr(list, rest2);
|
||||
}
|
||||
// If the first byte is between 248 = 247 + 1 and 255 = 247 + 8, it is a
|
||||
// list. The byte length of the byte length of the list-payload is
|
||||
// FirstByte - 247. Then the byte length of the list. Then the list
|
||||
// payload, which needs to be decoded on its own.
|
||||
else {
|
||||
let byte_length_of_byte_length : number = first_byte - 247;
|
||||
let bytes_of_byte_length : Uint8Array = rest.slice(0, byte_length_of_byte_length);
|
||||
let byte_length : number = bytes_to_number(bytes_of_byte_length);
|
||||
let list_bytes : Uint8Array = rest.slice(byte_length_of_byte_length,
|
||||
byte_length + byte_length_of_byte_length);
|
||||
let list : Array<decoded_data> = decode_list(list_bytes);
|
||||
let rest2 : Uint8Array = rest.slice(byte_length + byte_length_of_byte_length);
|
||||
return dr(list, rest2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode a list payload (non-prefixed) into an `Array<decoded_data>`.
|
||||
*
|
||||
* Repeatedly decodes an element off the list until remainder is empty.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
decode_list
|
||||
(bytes: Uint8Array)
|
||||
: Array<decoded_data>
|
||||
{
|
||||
let arr : Array<decoded_data> = [];
|
||||
while (bytes.length > 0) {
|
||||
// grab an item off the bytes
|
||||
let {decoded_data, remainder} = decode(bytes);
|
||||
// push it
|
||||
arr.push(decoded_data);
|
||||
// update bytes
|
||||
bytes = remainder;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert bytestring to number
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
bytes_to_number
|
||||
(bytes: Uint8Array)
|
||||
: number
|
||||
{
|
||||
let n : number = 0;
|
||||
for (let b of bytes)
|
||||
{
|
||||
n <<= 8;
|
||||
n += b;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Make a `decode_result` containing the two arguments
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
dr
|
||||
(x : decoded_data,
|
||||
y : Uint8Array)
|
||||
: decode_result
|
||||
{
|
||||
return {decoded_data : x,
|
||||
remainder : y};
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// ENCODING
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* Encode some decoded data
|
||||
*/
|
||||
function
|
||||
encode
|
||||
(data: decoded_data)
|
||||
: Uint8Array
|
||||
{
|
||||
// is it an array or data
|
||||
if
|
||||
(is_binary(data)) {
|
||||
return encode_binary(data as Uint8Array);
|
||||
}
|
||||
else if
|
||||
(is_list(data)) {
|
||||
return encode_list(data as Array<decoded_data>);
|
||||
}
|
||||
else {
|
||||
throw new Error('encode told to encode something that is not an array or a binary: ' + data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode a binary into RLP
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_binary
|
||||
(bytes: Uint8Array)
|
||||
: Uint8Array
|
||||
{
|
||||
let len: number = bytes.length;
|
||||
// single byte case when the byte is between 0..127
|
||||
// result is the bytestring containing the byte itself
|
||||
if
|
||||
((len === 1) &&
|
||||
(bytes[0] <= 127)){
|
||||
return bytes;
|
||||
}
|
||||
// if the bytestring is 0..55 bytes long, the first byte in the result is
|
||||
// 128 + Length, the rest of the result bytestring is the input string
|
||||
else if
|
||||
(len <= 55) {
|
||||
// construct the result
|
||||
// <<128 + Len, Bytes/binary>>
|
||||
let result : Uint8Array = new Uint8Array(len + 1);
|
||||
// first byte is 128 + length
|
||||
result[0] = 128 + len;
|
||||
// copy input bytes into result
|
||||
for (let input_idx0 = 0;
|
||||
input_idx0 < len;
|
||||
input_idx0++)
|
||||
{
|
||||
let result_idx0 : number = input_idx0 + 1;
|
||||
result[result_idx0] = bytes[input_idx0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// if the bytestring is more than 55 bytes long, the first byte is 183 +
|
||||
// ByteLengthOfByteLength, followed by the byte length, followed by the
|
||||
// bytes
|
||||
else {
|
||||
let len_bytes : Uint8Array = encode_unsigned(len);
|
||||
let len_bytes_length : number = len_bytes.length;
|
||||
// total array is
|
||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
||||
// 1 byte len_bytes_length bytes len bytes
|
||||
let result : Uint8Array = new Uint8Array(1 + len_bytes_length + len);
|
||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
||||
// 1 byte len_bytes_length bytes len bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
result[0] = 183 + len_bytes_length;
|
||||
// copy len_bytes into result
|
||||
for (let len_bytes_idx0 = 0;
|
||||
len_bytes_idx0 < len_bytes_length;
|
||||
len_bytes_idx0++)
|
||||
{
|
||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
||||
// 1 byte len_bytes_length bytes len bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
let result_idx0: number = len_bytes_idx0 + 1;
|
||||
result[result_idx0] = len_bytes[len_bytes_idx0];
|
||||
}
|
||||
// copy original byte array into result
|
||||
for (let input_idx0 = 0;
|
||||
input_idx0 < len;
|
||||
input_idx0++)
|
||||
{
|
||||
// <<183 + len_bytes_length, len_bytes, Bytes>>
|
||||
// 1 byte len_bytes_length bytes len bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
let result_idx0: number = input_idx0 + (1 + len_bytes_length);
|
||||
result[result_idx0] = bytes[input_idx0];
|
||||
}
|
||||
// finally, return the result
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode a list
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_list
|
||||
(list: Array<decoded_data>)
|
||||
: Uint8Array
|
||||
{
|
||||
// first encode every element in the list, then branch
|
||||
let payloads : Array<Uint8Array> = list.map(encode);
|
||||
let payload : Uint8Array = uint8arr_concat(payloads);
|
||||
let payload_size : number = payload.length;
|
||||
// if the payload size is in 0..55, then the first byte is 192 + payload
|
||||
// size, followed by the payload
|
||||
if
|
||||
(payload_size <= 55) {
|
||||
// result: <<(192 + Payload_Size), Payload/binary >>;
|
||||
// 1 byte payload_size bytes
|
||||
let result : Uint8Array = new Uint8Array(1 + payload_size);
|
||||
// result: <<(192 + Payload_Size), Payload/binary >>;
|
||||
// 1 byte payload_size bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
result[0] = 192 + payload_size;
|
||||
// copy the rest of the payload into result
|
||||
for (let payload_idx0 = 0;
|
||||
payload_idx0 < payload_size;
|
||||
payload_idx0++)
|
||||
{
|
||||
// result: <<(192 + Payload_Size), Payload/binary >>;
|
||||
// 1 byte payload_size bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
let result_idx0: number = payload_idx0 + 1;
|
||||
result[result_idx0] = payload[payload_idx0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// if the payload size is greater than 55, the first byte is 247 +
|
||||
// size_of_payload_size, followed by the payload size, followed by the
|
||||
// payload
|
||||
else {
|
||||
// compute the payload size size
|
||||
let payload_size_bytes : Uint8Array = encode_unsigned(payload_size);
|
||||
let payload_size_size : number = payload_size_bytes.length;
|
||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
||||
// 1 byte payload_size_size bytes payload_size bytes
|
||||
let result: Uint8Array = new Uint8Array(1 + payload_size_size + payload_size);
|
||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
||||
// 1 byte payload_size_size bytes payload_size bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
// first byte is 247 + payload_size_size
|
||||
result[0] = 247 + payload_size_size;
|
||||
// copy the payload_size_bytes into result
|
||||
for (let psb_idx0 = 0;
|
||||
psb_idx0 < payload_size_size;
|
||||
psb_idx0++)
|
||||
{
|
||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
||||
// 1 byte payload_size_size bytes payload_size bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
// offset is 1 byte for this
|
||||
let result_idx0 : number = psb_idx0 + 1;
|
||||
result[result_idx0] = payload_size_bytes[psb_idx0];
|
||||
}
|
||||
// copy the payload into result
|
||||
for (let pb_idx0 = 0;
|
||||
pb_idx0 < payload_size;
|
||||
pb_idx0++)
|
||||
{
|
||||
// result = <<(247 + payload_size_size), payload_size_bytes/binary, payload/binary >>
|
||||
// 1 byte payload_size_size bytes payload_size bytes
|
||||
//
|
||||
// ^ YOU ARE HERE
|
||||
// offset is 1 + payload_size_size bytes for this
|
||||
let result_idx0 : number = pb_idx0 + (1 + payload_size_size);
|
||||
result[result_idx0] = payload[pb_idx0];
|
||||
}
|
||||
// finally, return result
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encode a number as a bytestring
|
||||
*
|
||||
* Not for general use, this assumes the input number is greater than 55
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
encode_unsigned
|
||||
(n: number)
|
||||
: Uint8Array
|
||||
{
|
||||
// can assume that n initially is greater than 55
|
||||
// have to encode it in reverse order
|
||||
let arr : Array<number> = [];
|
||||
// repeated division by 256 with remainder
|
||||
//
|
||||
// example: suppose bign was 1234 and we were dividing by 10
|
||||
//
|
||||
// iteration 0:
|
||||
// bign = 1234
|
||||
// arr = []
|
||||
// ---
|
||||
// bign / 10 = 123
|
||||
// bign % 10 = 4
|
||||
// ---
|
||||
// bign = 123
|
||||
// arr = [4]
|
||||
//
|
||||
// iteration 1:
|
||||
// bign = 123
|
||||
// arr = [4]
|
||||
// ---
|
||||
// bign / 10 = 12
|
||||
// bign % 10 = 3
|
||||
// ---
|
||||
// bign = 12
|
||||
// arr = [4, 3]
|
||||
//
|
||||
// iteration 2:
|
||||
// bign = 12
|
||||
// arr = [4, 3]
|
||||
// ---
|
||||
// bign / 10 = 1
|
||||
// bign % 10 = 2
|
||||
// ---
|
||||
// bign = 1
|
||||
// arr = [4, 3, 2]
|
||||
//
|
||||
// iteration 3:
|
||||
// bign = 1
|
||||
// arr = [4, 3, 2]
|
||||
// ---
|
||||
// bign / 10 = 0
|
||||
// bign % 10 = 1
|
||||
// ---
|
||||
// bign = 0
|
||||
// arr = [4, 3, 2, 1]
|
||||
//
|
||||
// iteration 4:
|
||||
// bign = 0
|
||||
// arr = [4, 3, 2, 1]
|
||||
// ---
|
||||
// DONE
|
||||
while (n > 0) {
|
||||
arr.push(n % 256);
|
||||
n >>= 8;
|
||||
}
|
||||
// reverse and make into Uint8Array
|
||||
arr.reverse();
|
||||
return new Uint8Array(arr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* concatenate an array of `Uint8Array`s into a single Uint8Array
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
uint8arr_concat
|
||||
(arrs: Array<Uint8Array>)
|
||||
: Uint8Array
|
||||
{
|
||||
// total length
|
||||
let total_len = arrs.reduce(// fold
|
||||
function (acc_len: number, this_uint8array: Uint8Array): number {
|
||||
return acc_len + this_uint8array.length;
|
||||
},
|
||||
// initial accumulator
|
||||
0);
|
||||
// start up result
|
||||
let result : Uint8Array = new Uint8Array(total_len);
|
||||
let result_idx0 : number = 0;
|
||||
// concatenate
|
||||
for (let this_uint8arr of arrs) {
|
||||
// bytewise copy of this uint8array
|
||||
for (let this_uint8arr_idx0 = 0;
|
||||
this_uint8arr_idx0 < this_uint8arr.length;
|
||||
this_uint8arr_idx0++)
|
||||
{
|
||||
// copy byte and increment result idx0
|
||||
result[result_idx0] = this_uint8arr[this_uint8arr_idx0];
|
||||
result_idx0++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if input is `instanceof Uint8Array`
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
is_binary
|
||||
(x: any)
|
||||
: boolean
|
||||
{
|
||||
return (x instanceof Uint8Array);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* returns true if input is `instanceof Array`
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function
|
||||
is_list
|
||||
(x: any)
|
||||
: boolean
|
||||
{
|
||||
return (x instanceof Array);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user