@iov/encoding
Advanced tools
Comparing version 0.6.0 to 0.7.0
@@ -11,2 +11,3 @@ "use strict"; | ||
const base64js = __importStar(require("base64-js")); | ||
const bech32 = __importStar(require("bech32")); | ||
const readonly_date_1 = require("readonly-date"); | ||
@@ -25,2 +26,3 @@ class Encoding { | ||
} | ||
// tslint:disable-next-line:readonly-array | ||
const listOfInts = []; | ||
@@ -48,2 +50,6 @@ for (let i = 0; i < hexstring.length; i += 2) { | ||
const charCode = x.charCodeAt(0); | ||
// 0x00–0x1F control characters | ||
// 0x20–0x7E printable characters | ||
// 0x7F delete character | ||
// 0x80–0xFF out of 7 bit ascii range | ||
if (charCode < 0x20 || charCode > 0x7e) { | ||
@@ -58,2 +64,6 @@ throw new Error("Cannot encode character that is out of printable ASCII range: " + charCode); | ||
const fromNums = (listOfNumbers) => listOfNumbers.map((x) => { | ||
// 0x00–0x1F control characters | ||
// 0x20–0x7E printable characters | ||
// 0x7F delete character | ||
// 0x80–0xFF out of 7 bit ascii range | ||
if (x < 0x20 || x > 0x7e) { | ||
@@ -67,11 +77,18 @@ throw new Error("Cannot decode character that is out of printable ASCII range: " + x); | ||
static toUtf8(str) { | ||
// Browser and future nodejs (https://github.com/nodejs/node/issues/20365) | ||
if (typeof TextEncoder !== "undefined") { | ||
return new TextEncoder().encode(str); | ||
} | ||
// Use Buffer hack instead of nodejs util.TextEncoder to ensure | ||
// webpack does not bundle the util module for browsers. | ||
return new Uint8Array(Buffer.from(str, "utf8")); | ||
} | ||
static fromUtf8(data) { | ||
// Browser and future nodejs (https://github.com/nodejs/node/issues/20365) | ||
if (typeof TextDecoder !== "undefined") { | ||
return new TextDecoder("utf-8", { fatal: true }).decode(data); | ||
} | ||
// Use Buffer hack instead of nodejs util.TextDecoder to ensure | ||
// webpack does not bundle the util module for browsers. | ||
// Buffer.toString has no fatal option | ||
if (!Encoding.isValidUtf8(data)) { | ||
@@ -94,2 +111,3 @@ throw new Error("Invalid UTF8 data"); | ||
const second = +matches[6]; | ||
// fractional seconds match either undefined or a string like ".1", ".123456789" | ||
const milliSeconds = matches[7] ? Math.floor(+matches[7] * 1000) : 0; | ||
@@ -99,2 +117,3 @@ let tzOffsetSign; | ||
let tzOffsetMinutes; | ||
// if timezone is undefined, it must be Z or nothing (otherwise the group would have captured). | ||
if (matches[8] === "Z") { | ||
@@ -110,3 +129,3 @@ tzOffsetSign = 1; | ||
} | ||
const tzOffset = tzOffsetSign * (tzOffsetHours * 60 + tzOffsetMinutes) * 60; | ||
const tzOffset = tzOffsetSign * (tzOffsetHours * 60 + tzOffsetMinutes) * 60; // seconds | ||
return new readonly_date_1.ReadonlyDate(readonly_date_1.ReadonlyDate.UTC(year, month - 1, day, hour, minute, second, milliSeconds) - tzOffset * 1000); | ||
@@ -134,2 +153,17 @@ } | ||
exports.Encoding = Encoding; | ||
class Bech32 { | ||
static encode(prefix, data) { | ||
const dataToWords = bech32.toWords(Buffer.from(data)); | ||
const encodedData = bech32.encode(prefix, dataToWords); | ||
return encodedData; | ||
} | ||
static decode(address) { | ||
const decodedAddress = bech32.decode(address); | ||
return { | ||
prefix: decodedAddress.prefix, | ||
data: new Uint8Array(bech32.fromWords(decodedAddress.words)), | ||
}; | ||
} | ||
} | ||
exports.Bech32 = Bech32; | ||
//# sourceMappingURL=encoding.js.map |
@@ -15,2 +15,3 @@ "use strict"; | ||
it("decodes from hex", () => { | ||
// simple | ||
expect(encoding_1.Encoding.fromHex("")).toEqual(new Uint8Array([])); | ||
@@ -23,4 +24,6 @@ expect(encoding_1.Encoding.fromHex("00")).toEqual(new Uint8Array([0x00])); | ||
expect(encoding_1.Encoding.fromHex("0123456789abcdef")).toEqual(new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])); | ||
// capital letters | ||
expect(encoding_1.Encoding.fromHex("AA")).toEqual(new Uint8Array([0xaa])); | ||
expect(encoding_1.Encoding.fromHex("aAbBcCdDeEfF")).toEqual(new Uint8Array([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])); | ||
// error | ||
expect(() => encoding_1.Encoding.fromHex("a")).toThrow(); | ||
@@ -60,11 +63,15 @@ expect(() => encoding_1.Encoding.fromHex("aaa")).toThrow(); | ||
expect(encoding_1.Encoding.fromBase64("YWJj")).toEqual(new Uint8Array([0x61, 0x62, 0x63])); | ||
// invalid length | ||
expect(() => encoding_1.Encoding.fromBase64("a")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("aa")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("aaa")).toThrow(); | ||
// proper length including invalid character | ||
expect(() => encoding_1.Encoding.fromBase64("aaa!")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("aaa*")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("aaaä")).toThrow(); | ||
// proper length plus invalid character | ||
expect(() => encoding_1.Encoding.fromBase64("aaaa!")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("aaaa*")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("aaaaä")).toThrow(); | ||
// extra spaces | ||
expect(() => encoding_1.Encoding.fromBase64("aaaa ")).toThrow(); | ||
@@ -76,5 +83,9 @@ expect(() => encoding_1.Encoding.fromBase64(" aaaa")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("aa\naa")).toThrow(); | ||
// position of = | ||
expect(() => encoding_1.Encoding.fromBase64("=aaa")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromBase64("==aa")).toThrow(); | ||
// concatenated base64 strings should not be supported | ||
// see https://github.com/beatgammit/base64-js/issues/42 | ||
expect(() => encoding_1.Encoding.fromBase64("AAA=AAA=")).toThrow(); | ||
// wrong number of = | ||
expect(() => encoding_1.Encoding.fromBase64("a===")).toThrow(); | ||
@@ -108,2 +119,8 @@ }); | ||
}); | ||
it("encodes null character", () => { | ||
expect(encoding_1.Encoding.toUtf8("\u0000")).toEqual(new Uint8Array([0x00])); | ||
}); | ||
it("decodes null byte", () => { | ||
expect(encoding_1.Encoding.fromUtf8(new Uint8Array([0x00]))).toEqual("\u0000"); | ||
}); | ||
it("encodes Basic Multilingual Plane strings", () => { | ||
@@ -124,10 +141,15 @@ expect(encoding_1.Encoding.toUtf8("ö")).toEqual(new Uint8Array([0xc3, 0xb6])); | ||
it("encodes Supplementary Multilingual Plane strings", () => { | ||
// U+1F0A1 | ||
expect(encoding_1.Encoding.toUtf8("🂡")).toEqual(new Uint8Array([0xf0, 0x9f, 0x82, 0xa1])); | ||
// U+1034A | ||
expect(encoding_1.Encoding.toUtf8("𐍊")).toEqual(new Uint8Array([0xf0, 0x90, 0x8d, 0x8a])); | ||
}); | ||
it("decodes Supplementary Multilingual Plane strings", () => { | ||
// U+1F0A1 | ||
expect(encoding_1.Encoding.fromUtf8(new Uint8Array([0xf0, 0x9f, 0x82, 0xa1]))).toEqual("🂡"); | ||
// U+1034A | ||
expect(encoding_1.Encoding.fromUtf8(new Uint8Array([0xf0, 0x90, 0x8d, 0x8a]))).toEqual("𐍊"); | ||
}); | ||
it("throws on invalid utf8 bytes", () => { | ||
// Broken UTF8 example from https://github.com/nodejs/node/issues/16894 | ||
expect(() => encoding_1.Encoding.fromUtf8(new Uint8Array([0xf0, 0x80, 0x80]))).toThrow(); | ||
@@ -138,5 +160,7 @@ }); | ||
it("parses dates with different time zones", () => { | ||
// time zone +/- 0 | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+00:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-00:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13))); | ||
// time zone positive (full hours) | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+01:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 1, 12, 13))); | ||
@@ -146,2 +170,3 @@ expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+02:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 2, 12, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+11:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 11, 12, 13))); | ||
// time zone negative (full hours) | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-01:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 1, 12, 13))); | ||
@@ -151,11 +176,15 @@ expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-02:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 2, 12, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-11:00")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 11, 12, 13))); | ||
// time zone positive (minutes only) | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+00:01")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 - 1, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+00:30")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 - 30, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+00:45")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 - 45, 13))); | ||
// time zone negative (minutes only) | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-00:01")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 + 1, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-00:30")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 + 30, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-00:45")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12 + 45, 13))); | ||
// time zone positive (hours and minutes) | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+01:01")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 1, 12 - 1, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+04:30")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 4, 12 - 30, 13))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+10:20")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 - 10, 12 - 20, 13))); | ||
// time zone negative (hours and minutes) | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-01:01")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 1, 12 + 1, 13))); | ||
@@ -171,5 +200,7 @@ expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13-04:30")).toEqual(new Date(Date.UTC(2002, 9, 2, 11 + 4, 12 + 30, 13))); | ||
it("parses dates with low precision fractional seconds", () => { | ||
// 1 digit | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.0Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.1Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 100))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.9Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 900))); | ||
// 2 digit | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.00Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
@@ -180,17 +211,24 @@ expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.12Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 120))); | ||
it("parses dates with high precision fractional seconds", () => { | ||
// everything after the 3rd digit is truncated | ||
// 4 digits | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.0000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.1234Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.9999Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999))); | ||
// 5 digits | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.00000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.12345Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.99999Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999))); | ||
// 6 digits | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.000000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.123456Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.999999Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999))); | ||
// 7 digits | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.0000000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.1234567Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.9999999Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999))); | ||
// 8 digits | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.00000000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.12345678Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123))); | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.99999999Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 999))); | ||
// 9 digits | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.000000000Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 0))); | ||
@@ -201,12 +239,19 @@ expect(encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13.123456789Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13, 123))); | ||
it("accepts space separators", () => { | ||
// https://tools.ietf.org/html/rfc3339#section-5.6 | ||
// Applications using this syntax may choose, for the sake of readability, | ||
// to specify a full-date and full-time separated by (say) a space character. | ||
expect(encoding_1.Encoding.fromRfc3339("2002-10-02 11:12:13Z")).toEqual(new Date(Date.UTC(2002, 9, 2, 11, 12, 13))); | ||
}); | ||
it("throws for invalid format", () => { | ||
// extra whitespace | ||
expect(() => encoding_1.Encoding.fromRfc3339(" 2002-10-02T11:12:13Z")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13Z ")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13 Z")).toThrow(); | ||
// wrong date separators | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002:10-02T11:12:13Z")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10:02T11:12:13Z")).toThrow(); | ||
// wrong time separators | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02T11-12:13Z")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02T11:12-13Z")).toThrow(); | ||
// wrong separator | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02TT11:12:13Z")).toThrow(); | ||
@@ -219,2 +264,3 @@ expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02 T11:12:13Z")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02.11:12:13Z")).toThrow(); | ||
// wrong time zone | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13")).toThrow(); | ||
@@ -224,2 +270,3 @@ expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13z")).toThrow(); | ||
expect(() => encoding_1.Encoding.fromRfc3339("2002-10-02T11:12:13+0000")).toThrow(); | ||
// wrong fractional seconds | ||
expect(() => encoding_1.Encoding.fromRfc3339("2018-07-30T19:21:12345Z")).toThrow(); | ||
@@ -234,2 +281,13 @@ expect(() => encoding_1.Encoding.fromRfc3339("2018-07-30T19:21:12.Z")).toThrow(); | ||
}); | ||
describe("Bech32", () => { | ||
// test data generate using https://github.com/nym-zone/bech32 | ||
// bech32 -e -h eth 9d4e856e572e442f0a4b2763e72d08a0e99d8ded | ||
const ethAddressRaw = encoding_1.Encoding.fromHex("9d4e856e572e442f0a4b2763e72d08a0e99d8ded"); | ||
it("encodes", () => { | ||
expect(encoding_1.Bech32.encode("eth", ethAddressRaw)).toEqual("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw"); | ||
}); | ||
it("decodes", () => { | ||
expect(encoding_1.Bech32.decode("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw")).toEqual({ prefix: "eth", data: ethAddressRaw }); | ||
}); | ||
}); | ||
//# sourceMappingURL=encoding.spec.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/* tslint:disable:no-bitwise */ | ||
const BN = require("bn.js"); | ||
const uint64MaxValue = new BN("18446744073709551615", 10, "be"); | ||
class Uint32 { | ||
@@ -8,2 +11,3 @@ static fromBigEndianBytes(bytes) { | ||
} | ||
// tslint:disable-next-line:prefer-for-of | ||
for (let i = 0; i < bytes.length; ++i) { | ||
@@ -14,2 +18,4 @@ if (bytes[i] > 255 || bytes[i] < 0 || Number.isNaN(bytes[i])) { | ||
} | ||
// Use mulitiplication instead of shifting since bitwise operators are defined | ||
// on SIGNED int32 in JavaScript and we don't want to risk surprises | ||
return new Uint32(bytes[0] * Math.pow(2, 24) + bytes[1] * Math.pow(2, 16) + bytes[2] * Math.pow(2, 8) + bytes[3]); | ||
@@ -19,6 +25,6 @@ } | ||
if (Number.isNaN(input)) { | ||
throw new Error("input is not a number"); | ||
throw new Error("Input is not a number"); | ||
} | ||
if (input < 0 || input > 4294967295) { | ||
throw new Error("input not in uint32 range: " + input.toString()); | ||
throw new Error("Input not in uint32 range: " + input.toString()); | ||
} | ||
@@ -28,2 +34,4 @@ this.data = input; | ||
toBytesBigEndian() { | ||
// Use division instead of shifting since bitwise operators are defined | ||
// on SIGNED int32 in JavaScript and we don't want to risk surprises | ||
return [ | ||
@@ -50,13 +58,13 @@ Math.floor(this.data / Math.pow(2, 24)) & 0xff, | ||
if (Number.isNaN(input)) { | ||
throw new Error("input is not a number"); | ||
throw new Error("Input is not a number"); | ||
} | ||
if (input < Number.MIN_SAFE_INTEGER || input > Number.MAX_SAFE_INTEGER) { | ||
throw new Error("input not in int53 range: " + input.toString()); | ||
throw new Error("Input not in int53 range: " + input.toString()); | ||
} | ||
this.data = input; | ||
} | ||
asNumber() { | ||
toNumber() { | ||
return this.data; | ||
} | ||
asString() { | ||
toString() { | ||
return this.data.toString(); | ||
@@ -66,2 +74,63 @@ } | ||
exports.Int53 = Int53; | ||
class Uint64 { | ||
static fromBytesBigEndian(bytes) { | ||
if (bytes.length !== 8) { | ||
throw new Error("Invalid input length. Expected 8 bytes."); | ||
} | ||
// tslint:disable-next-line:prefer-for-of | ||
for (let i = 0; i < bytes.length; ++i) { | ||
if (bytes[i] > 255 || bytes[i] < 0 || Number.isNaN(bytes[i])) { | ||
throw new Error("Invalid value in byte. Found: " + bytes[i]); | ||
} | ||
} | ||
// tslint:disable-next-line:readonly-array | ||
const asArray = []; | ||
// tslint:disable-next-line:prefer-for-of | ||
for (let i = 0; i < bytes.length; ++i) { | ||
asArray.push(bytes[i]); | ||
} | ||
return new Uint64(new BN([...asArray])); | ||
} | ||
static fromString(str) { | ||
if (!str.match(/^[0-9]+$/)) { | ||
throw new Error("Invalid string format"); | ||
} | ||
return new Uint64(new BN(str, 10, "be")); | ||
} | ||
static fromNumber(input) { | ||
if (Number.isNaN(input)) { | ||
throw new Error("Input is not a number"); | ||
} | ||
let bigint; | ||
try { | ||
bigint = new BN(input); | ||
} | ||
catch (_a) { | ||
throw new Error("Input is not a safe integer"); | ||
} | ||
return new Uint64(bigint); | ||
} | ||
constructor(data) { | ||
if (data.isNeg()) { | ||
throw new Error("Input is negative"); | ||
} | ||
if (data.gt(uint64MaxValue)) { | ||
throw new Error("Input exceeds uint64 range"); | ||
} | ||
this.data = data; | ||
} | ||
toBytesBigEndian() { | ||
return this.data.toArray("be", 8); | ||
} | ||
toBytesLittleEndian() { | ||
return this.data.toArray("le", 8); | ||
} | ||
toString() { | ||
return this.data.toString(10); | ||
} | ||
toNumber() { | ||
return this.data.toNumber(); | ||
} | ||
} | ||
exports.Uint64 = Uint64; | ||
//# sourceMappingURL=integers.js.map |
@@ -16,2 +16,3 @@ "use strict"; | ||
it("throws for values out of range", () => { | ||
// tslint:disable:no-unused-expression | ||
expect(() => new integers_1.Uint32(-1)).toThrowError(/not in uint32 range/); | ||
@@ -23,5 +24,8 @@ expect(() => new integers_1.Uint32(4294967296)).toThrowError(/not in uint32 range/); | ||
expect(() => new integers_1.Uint32(Number.POSITIVE_INFINITY)).toThrowError(/not in uint32 range/); | ||
// tslint:enable:no-unused-expression | ||
}); | ||
it("throws for invald numbers", () => { | ||
// tslint:disable:no-unused-expression | ||
expect(() => new integers_1.Uint32(NaN)).toThrowError(/not a number/); | ||
// tslint:enable:no-unused-expression | ||
}); | ||
@@ -96,2 +100,3 @@ it("can convert back to number", () => { | ||
it("throws for values out of range", () => { | ||
// tslint:disable:no-unused-expression | ||
expect(() => new integers_1.Int53(Number.MIN_SAFE_INTEGER - 1)).toThrowError(/not in int53 range/); | ||
@@ -101,44 +106,177 @@ expect(() => new integers_1.Int53(Number.MAX_SAFE_INTEGER + 1)).toThrowError(/not in int53 range/); | ||
expect(() => new integers_1.Int53(Number.POSITIVE_INFINITY)).toThrowError(/not in int53 range/); | ||
// tslint:enable:no-unused-expression | ||
}); | ||
it("throws for invald numbers", () => { | ||
// tslint:disable:no-unused-expression | ||
expect(() => new integers_1.Int53(NaN)).toThrowError(/not a number/); | ||
// tslint:enable:no-unused-expression | ||
}); | ||
it("can convert to number", () => { | ||
expect(new integers_1.Int53(0).asNumber()).toEqual(0); | ||
expect(new integers_1.Int53(1).asNumber()).toEqual(1); | ||
expect(new integers_1.Int53(42).asNumber()).toEqual(42); | ||
expect(new integers_1.Int53(1000000000).asNumber()).toEqual(1000000000); | ||
expect(new integers_1.Int53(2147483647).asNumber()).toEqual(2147483647); | ||
expect(new integers_1.Int53(2147483648).asNumber()).toEqual(2147483648); | ||
expect(new integers_1.Int53(4294967295).asNumber()).toEqual(4294967295); | ||
expect(new integers_1.Int53(9007199254740991).asNumber()).toEqual(9007199254740991); | ||
expect(new integers_1.Int53(-1).asNumber()).toEqual(-1); | ||
expect(new integers_1.Int53(-9007199254740991).asNumber()).toEqual(-9007199254740991); | ||
expect(new integers_1.Int53(0).toNumber()).toEqual(0); | ||
expect(new integers_1.Int53(1).toNumber()).toEqual(1); | ||
expect(new integers_1.Int53(42).toNumber()).toEqual(42); | ||
expect(new integers_1.Int53(1000000000).toNumber()).toEqual(1000000000); | ||
expect(new integers_1.Int53(2147483647).toNumber()).toEqual(2147483647); | ||
expect(new integers_1.Int53(2147483648).toNumber()).toEqual(2147483648); | ||
expect(new integers_1.Int53(4294967295).toNumber()).toEqual(4294967295); | ||
expect(new integers_1.Int53(9007199254740991).toNumber()).toEqual(9007199254740991); | ||
expect(new integers_1.Int53(-1).toNumber()).toEqual(-1); | ||
expect(new integers_1.Int53(-9007199254740991).toNumber()).toEqual(-9007199254740991); | ||
}); | ||
it("can convert to string", () => { | ||
expect(new integers_1.Int53(0).asString()).toEqual("0"); | ||
expect(new integers_1.Int53(1).asString()).toEqual("1"); | ||
expect(new integers_1.Int53(42).asString()).toEqual("42"); | ||
expect(new integers_1.Int53(1000000000).asString()).toEqual("1000000000"); | ||
expect(new integers_1.Int53(2147483647).asString()).toEqual("2147483647"); | ||
expect(new integers_1.Int53(2147483648).asString()).toEqual("2147483648"); | ||
expect(new integers_1.Int53(4294967295).asString()).toEqual("4294967295"); | ||
expect(new integers_1.Int53(9007199254740991).asString()).toEqual("9007199254740991"); | ||
expect(new integers_1.Int53(-1).asString()).toEqual("-1"); | ||
expect(new integers_1.Int53(-9007199254740991).asString()).toEqual("-9007199254740991"); | ||
expect(new integers_1.Int53(0).toString()).toEqual("0"); | ||
expect(new integers_1.Int53(1).toString()).toEqual("1"); | ||
expect(new integers_1.Int53(42).toString()).toEqual("42"); | ||
expect(new integers_1.Int53(1000000000).toString()).toEqual("1000000000"); | ||
expect(new integers_1.Int53(2147483647).toString()).toEqual("2147483647"); | ||
expect(new integers_1.Int53(2147483648).toString()).toEqual("2147483648"); | ||
expect(new integers_1.Int53(4294967295).toString()).toEqual("4294967295"); | ||
expect(new integers_1.Int53(9007199254740991).toString()).toEqual("9007199254740991"); | ||
expect(new integers_1.Int53(-1).toString()).toEqual("-1"); | ||
expect(new integers_1.Int53(-9007199254740991).toString()).toEqual("-9007199254740991"); | ||
}); | ||
it("can be constructed from string", () => { | ||
expect(integers_1.Int53.fromString("0").asString()).toEqual("0"); | ||
expect(integers_1.Int53.fromString("1").asString()).toEqual("1"); | ||
expect(integers_1.Int53.fromString("9007199254740991").asString()).toEqual("9007199254740991"); | ||
expect(integers_1.Int53.fromString("-1").asString()).toEqual("-1"); | ||
expect(integers_1.Int53.fromString("-9007199254740991").asString()).toEqual("-9007199254740991"); | ||
expect(integers_1.Int53.fromString("0").toString()).toEqual("0"); | ||
expect(integers_1.Int53.fromString("1").toString()).toEqual("1"); | ||
expect(integers_1.Int53.fromString("9007199254740991").toString()).toEqual("9007199254740991"); | ||
expect(integers_1.Int53.fromString("-1").toString()).toEqual("-1"); | ||
expect(integers_1.Int53.fromString("-9007199254740991").toString()).toEqual("-9007199254740991"); | ||
}); | ||
it("throws for invalid string format", () => { | ||
// tslint:disable:no-unused-expression | ||
expect(() => integers_1.Int53.fromString(" 0")).toThrowError(/invalid string format/i); | ||
expect(() => integers_1.Int53.fromString("+0")).toThrowError(/invalid string format/i); | ||
expect(() => integers_1.Int53.fromString("1e6")).toThrowError(/invalid string format/i); | ||
expect(() => integers_1.Int53.fromString("9007199254740992")).toThrowError(/input not in int53 range/i); | ||
expect(() => integers_1.Int53.fromString("-9007199254740992")).toThrowError(/input not in int53 range/i); | ||
// tslint:enable:no-unused-expression | ||
}); | ||
}); | ||
describe("Uint64", () => { | ||
it("can be constructed from bytes", () => { | ||
integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
integers_1.Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
}); | ||
it("can be constructed from Uint8Array", () => { | ||
integers_1.Uint64.fromBytesBigEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); | ||
}); | ||
it("throws for wrong number of bytes", () => { | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([])).toThrowError(/invalid input length/i); | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0x00])).toThrowError(/invalid input length/i); | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError(/invalid input length/i); | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError(/invalid input length/i); | ||
}); | ||
it("throws for wrong byte value", () => { | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, 256])).toThrowError(/invalid value in byte/i); | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, -1])).toThrowError(/invalid value in byte/i); | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError(/invalid value in byte/i); | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError(/invalid value in byte/i); | ||
expect(() => integers_1.Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NaN])).toThrowError(/invalid value in byte/i); | ||
}); | ||
it("can be constructed from string", () => { | ||
{ | ||
const a = integers_1.Uint64.fromString("0"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromString("1"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromString("01"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromString("9999999999999999999"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromString("18446744073709551615"); | ||
expect(a).toBeTruthy(); | ||
} | ||
}); | ||
it("throws for invalid string values", () => { | ||
expect(() => integers_1.Uint64.fromString(" 1")).toThrowError(/invalid string format/i); | ||
expect(() => integers_1.Uint64.fromString("-1")).toThrowError(/invalid string format/i); | ||
expect(() => integers_1.Uint64.fromString("+1")).toThrowError(/invalid string format/i); | ||
expect(() => integers_1.Uint64.fromString("1e6")).toThrowError(/invalid string format/i); | ||
}); | ||
it("throws for string values exceeding uint64", () => { | ||
expect(() => integers_1.Uint64.fromString("18446744073709551616")).toThrowError(/input exceeds uint64 range/i); | ||
expect(() => integers_1.Uint64.fromString("99999999999999999999")).toThrowError(/input exceeds uint64 range/i); | ||
}); | ||
it("can be constructed from number", () => { | ||
const a = integers_1.Uint64.fromNumber(0); | ||
expect(a.toNumber()).toEqual(0); | ||
const b = integers_1.Uint64.fromNumber(1); | ||
expect(b.toNumber()).toEqual(1); | ||
const c = integers_1.Uint64.fromNumber(Number.MAX_SAFE_INTEGER); | ||
expect(c.toNumber()).toEqual(Number.MAX_SAFE_INTEGER); | ||
}); | ||
it("throws when constructed from wrong numbers", () => { | ||
// not a number | ||
expect(() => integers_1.Uint64.fromNumber(Number.NaN)).toThrowError(/input is not a number/i); | ||
// not an integer | ||
expect(() => integers_1.Uint64.fromNumber(Number.NEGATIVE_INFINITY)).toThrowError(/input is not a safe integer/i); | ||
expect(() => integers_1.Uint64.fromNumber(Number.POSITIVE_INFINITY)).toThrowError(/input is not a safe integer/i); | ||
expect(() => integers_1.Uint64.fromNumber(Number.MAX_SAFE_INTEGER + 1)).toThrowError(/input is not a safe integer/i); | ||
// negative integer | ||
expect(() => integers_1.Uint64.fromNumber(-1)).toThrowError(/input is negative/i); | ||
expect(() => integers_1.Uint64.fromNumber(Number.MIN_SAFE_INTEGER)).toThrowError(/input is negative/i); | ||
}); | ||
it("can export bytes (big endian)", () => { | ||
expect(integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesBigEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian()).toEqual([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesBigEndian()).toEqual([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesBigEndian()).toEqual([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
}); | ||
it("can export bytes (little endian)", () => { | ||
expect(integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesLittleEndian()).toEqual([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesLittleEndian()).toEqual([0x0d, 0x4e, 0x20, 0xa9, 0x5f, 0xbc, 0x22, 0xab]); | ||
expect(integers_1.Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesLittleEndian()).toEqual([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
}); | ||
it("can export strings", () => { | ||
{ | ||
const a = integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(a.toString()).toEqual("0"); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(a.toString()).toEqual("1"); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromBytesBigEndian([0x8a, 0xc7, 0x23, 0x04, 0x89, 0xe7, 0xff, 0xff]); | ||
expect(a.toString()).toEqual("9999999999999999999"); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
expect(a.toString()).toEqual("18446744073709551615"); | ||
} | ||
}); | ||
it("can export numbers", () => { | ||
{ | ||
const a = integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(a.toNumber()).toEqual(0); | ||
} | ||
{ | ||
const a = integers_1.Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(a.toNumber()).toEqual(1); | ||
} | ||
{ | ||
// value too large for 53 bit integer | ||
const a = integers_1.Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); | ||
} | ||
{ | ||
// Number.MAX_SAFE_INTEGER + 1 | ||
const a = integers_1.Uint64.fromBytesBigEndian([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); | ||
} | ||
}); | ||
}); | ||
}); | ||
//# sourceMappingURL=integers.spec.js.map |
{ | ||
"name": "@iov/encoding", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "Encoding helpers for IOV projects", | ||
@@ -33,8 +33,11 @@ "author": "IOV SAS <admin@iov.one>", | ||
"base64-js": "^1.3.0", | ||
"bech32": "^1.1.3", | ||
"bn.js": "^4.11.8", | ||
"readonly-date": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
"@types/base64-js": "^1.2.5" | ||
"@types/base64-js": "^1.2.5", | ||
"@types/bech32": "^1.1.1" | ||
}, | ||
"gitHead": "a5259d28a4e22e5ebcf6ae30e4af7c2922647120" | ||
"gitHead": "559a368ef6e16cc8f3fe20b7d8df4a5ac1d995fc" | ||
} |
@@ -10,2 +10,11 @@ # @iov/encoding | ||
## Convert between bech32 and hex addresses | ||
``` | ||
>> Bech32.encode("tiov", Encoding.fromHex("1234ABCD0000AA0000FFFF0000AA00001234ABCD")) | ||
'tiov1zg62hngqqz4qqq8lluqqp2sqqqfrf27dzrrmea' | ||
>> Encoding.toHex(Bech32.decode("tiov1zg62hngqqz4qqq8lluqqp2sqqqfrf27dzrrmea").data) | ||
'1234abcd0000aa0000ffff0000aa00001234abcd' | ||
``` | ||
## API Documentation | ||
@@ -12,0 +21,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { Encoding } from "./encoding"; | ||
import { Bech32, Encoding } from "./encoding"; | ||
@@ -133,2 +133,10 @@ describe("Encoding", () => { | ||
it("encodes null character", () => { | ||
expect(Encoding.toUtf8("\u0000")).toEqual(new Uint8Array([0x00])); | ||
}); | ||
it("decodes null byte", () => { | ||
expect(Encoding.fromUtf8(new Uint8Array([0x00]))).toEqual("\u0000"); | ||
}); | ||
it("encodes Basic Multilingual Plane strings", () => { | ||
@@ -309,1 +317,15 @@ expect(Encoding.toUtf8("ö")).toEqual(new Uint8Array([0xc3, 0xb6])); | ||
}); | ||
describe("Bech32", () => { | ||
// test data generate using https://github.com/nym-zone/bech32 | ||
// bech32 -e -h eth 9d4e856e572e442f0a4b2763e72d08a0e99d8ded | ||
const ethAddressRaw = Encoding.fromHex("9d4e856e572e442f0a4b2763e72d08a0e99d8ded"); | ||
it("encodes", () => { | ||
expect(Bech32.encode("eth", ethAddressRaw)).toEqual("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw"); | ||
}); | ||
it("decodes", () => { | ||
expect(Bech32.decode("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw")).toEqual({ prefix: "eth", data: ethAddressRaw }); | ||
}); | ||
}); |
import * as base64js from "base64-js"; | ||
import * as bech32 from "bech32"; | ||
import { ReadonlyDate } from "readonly-date"; | ||
@@ -163,1 +164,17 @@ | ||
} | ||
export class Bech32 { | ||
public static encode(prefix: string, data: Uint8Array): string { | ||
const dataToWords = bech32.toWords(Buffer.from(data)); | ||
const encodedData = bech32.encode(prefix, dataToWords); | ||
return encodedData; | ||
} | ||
public static decode(address: string): { readonly prefix: string; readonly data: Uint8Array } { | ||
const decodedAddress = bech32.decode(address); | ||
return { | ||
prefix: decodedAddress.prefix, | ||
data: new Uint8Array(bech32.fromWords(decodedAddress.words)), | ||
}; | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
import { Int53, Uint32 } from "./integers"; | ||
import { Int53, Uint32, Uint64 } from "./integers"; | ||
@@ -131,36 +131,36 @@ describe("Integers", () => { | ||
it("can convert to number", () => { | ||
expect(new Int53(0).asNumber()).toEqual(0); | ||
expect(new Int53(1).asNumber()).toEqual(1); | ||
expect(new Int53(42).asNumber()).toEqual(42); | ||
expect(new Int53(1000000000).asNumber()).toEqual(1000000000); | ||
expect(new Int53(2147483647).asNumber()).toEqual(2147483647); | ||
expect(new Int53(2147483648).asNumber()).toEqual(2147483648); | ||
expect(new Int53(4294967295).asNumber()).toEqual(4294967295); | ||
expect(new Int53(9007199254740991).asNumber()).toEqual(9007199254740991); | ||
expect(new Int53(0).toNumber()).toEqual(0); | ||
expect(new Int53(1).toNumber()).toEqual(1); | ||
expect(new Int53(42).toNumber()).toEqual(42); | ||
expect(new Int53(1000000000).toNumber()).toEqual(1000000000); | ||
expect(new Int53(2147483647).toNumber()).toEqual(2147483647); | ||
expect(new Int53(2147483648).toNumber()).toEqual(2147483648); | ||
expect(new Int53(4294967295).toNumber()).toEqual(4294967295); | ||
expect(new Int53(9007199254740991).toNumber()).toEqual(9007199254740991); | ||
expect(new Int53(-1).asNumber()).toEqual(-1); | ||
expect(new Int53(-9007199254740991).asNumber()).toEqual(-9007199254740991); | ||
expect(new Int53(-1).toNumber()).toEqual(-1); | ||
expect(new Int53(-9007199254740991).toNumber()).toEqual(-9007199254740991); | ||
}); | ||
it("can convert to string", () => { | ||
expect(new Int53(0).asString()).toEqual("0"); | ||
expect(new Int53(1).asString()).toEqual("1"); | ||
expect(new Int53(42).asString()).toEqual("42"); | ||
expect(new Int53(1000000000).asString()).toEqual("1000000000"); | ||
expect(new Int53(2147483647).asString()).toEqual("2147483647"); | ||
expect(new Int53(2147483648).asString()).toEqual("2147483648"); | ||
expect(new Int53(4294967295).asString()).toEqual("4294967295"); | ||
expect(new Int53(9007199254740991).asString()).toEqual("9007199254740991"); | ||
expect(new Int53(0).toString()).toEqual("0"); | ||
expect(new Int53(1).toString()).toEqual("1"); | ||
expect(new Int53(42).toString()).toEqual("42"); | ||
expect(new Int53(1000000000).toString()).toEqual("1000000000"); | ||
expect(new Int53(2147483647).toString()).toEqual("2147483647"); | ||
expect(new Int53(2147483648).toString()).toEqual("2147483648"); | ||
expect(new Int53(4294967295).toString()).toEqual("4294967295"); | ||
expect(new Int53(9007199254740991).toString()).toEqual("9007199254740991"); | ||
expect(new Int53(-1).asString()).toEqual("-1"); | ||
expect(new Int53(-9007199254740991).asString()).toEqual("-9007199254740991"); | ||
expect(new Int53(-1).toString()).toEqual("-1"); | ||
expect(new Int53(-9007199254740991).toString()).toEqual("-9007199254740991"); | ||
}); | ||
it("can be constructed from string", () => { | ||
expect(Int53.fromString("0").asString()).toEqual("0"); | ||
expect(Int53.fromString("1").asString()).toEqual("1"); | ||
expect(Int53.fromString("9007199254740991").asString()).toEqual("9007199254740991"); | ||
expect(Int53.fromString("0").toString()).toEqual("0"); | ||
expect(Int53.fromString("1").toString()).toEqual("1"); | ||
expect(Int53.fromString("9007199254740991").toString()).toEqual("9007199254740991"); | ||
expect(Int53.fromString("-1").asString()).toEqual("-1"); | ||
expect(Int53.fromString("-9007199254740991").asString()).toEqual("-9007199254740991"); | ||
expect(Int53.fromString("-1").toString()).toEqual("-1"); | ||
expect(Int53.fromString("-9007199254740991").toString()).toEqual("-9007199254740991"); | ||
}); | ||
@@ -175,5 +175,149 @@ | ||
expect(() => Int53.fromString("9007199254740992")).toThrowError(/input not in int53 range/i); | ||
expect(() => Int53.fromString("-9007199254740992")).toThrowError(/input not in int53 range/i); | ||
// tslint:enable:no-unused-expression | ||
}); | ||
}); | ||
describe("Uint64", () => { | ||
it("can be constructed from bytes", () => { | ||
Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
}); | ||
it("can be constructed from Uint8Array", () => { | ||
Uint64.fromBytesBigEndian(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); | ||
}); | ||
it("throws for wrong number of bytes", () => { | ||
expect(() => Uint64.fromBytesBigEndian([])).toThrowError(/invalid input length/i); | ||
expect(() => Uint64.fromBytesBigEndian([0x00])).toThrowError(/invalid input length/i); | ||
expect(() => Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError(/invalid input length/i); | ||
expect(() => Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])).toThrowError(/invalid input length/i); | ||
}); | ||
it("throws for wrong byte value", () => { | ||
expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, 256])).toThrowError(/invalid value in byte/i); | ||
expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, -1])).toThrowError(/invalid value in byte/i); | ||
expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NEGATIVE_INFINITY])).toThrowError(/invalid value in byte/i); | ||
expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.POSITIVE_INFINITY])).toThrowError(/invalid value in byte/i); | ||
expect(() => Uint64.fromBytesBigEndian([0, 0, 0, 0, 0, 0, 0, Number.NaN])).toThrowError(/invalid value in byte/i); | ||
}); | ||
it("can be constructed from string", () => { | ||
{ | ||
const a = Uint64.fromString("0"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = Uint64.fromString("1"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = Uint64.fromString("01"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = Uint64.fromString("9999999999999999999"); | ||
expect(a).toBeTruthy(); | ||
} | ||
{ | ||
const a = Uint64.fromString("18446744073709551615"); | ||
expect(a).toBeTruthy(); | ||
} | ||
}); | ||
it("throws for invalid string values", () => { | ||
expect(() => Uint64.fromString(" 1")).toThrowError(/invalid string format/i); | ||
expect(() => Uint64.fromString("-1")).toThrowError(/invalid string format/i); | ||
expect(() => Uint64.fromString("+1")).toThrowError(/invalid string format/i); | ||
expect(() => Uint64.fromString("1e6")).toThrowError(/invalid string format/i); | ||
}); | ||
it("throws for string values exceeding uint64", () => { | ||
expect(() => Uint64.fromString("18446744073709551616")).toThrowError(/input exceeds uint64 range/i); | ||
expect(() => Uint64.fromString("99999999999999999999")).toThrowError(/input exceeds uint64 range/i); | ||
}); | ||
it("can be constructed from number", () => { | ||
const a = Uint64.fromNumber(0); | ||
expect(a.toNumber()).toEqual(0); | ||
const b = Uint64.fromNumber(1); | ||
expect(b.toNumber()).toEqual(1); | ||
const c = Uint64.fromNumber(Number.MAX_SAFE_INTEGER); | ||
expect(c.toNumber()).toEqual(Number.MAX_SAFE_INTEGER); | ||
}); | ||
it("throws when constructed from wrong numbers", () => { | ||
// not a number | ||
expect(() => Uint64.fromNumber(Number.NaN)).toThrowError(/input is not a number/i); | ||
// not an integer | ||
expect(() => Uint64.fromNumber(Number.NEGATIVE_INFINITY)).toThrowError(/input is not a safe integer/i); | ||
expect(() => Uint64.fromNumber(Number.POSITIVE_INFINITY)).toThrowError(/input is not a safe integer/i); | ||
expect(() => Uint64.fromNumber(Number.MAX_SAFE_INTEGER + 1)).toThrowError(/input is not a safe integer/i); | ||
// negative integer | ||
expect(() => Uint64.fromNumber(-1)).toThrowError(/input is negative/i); | ||
expect(() => Uint64.fromNumber(Number.MIN_SAFE_INTEGER)).toThrowError(/input is negative/i); | ||
}); | ||
it("can export bytes (big endian)", () => { | ||
expect(Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesBigEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesBigEndian()).toEqual([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesBigEndian()).toEqual([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]); | ||
expect(Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesBigEndian()).toEqual([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
}); | ||
it("can export bytes (little endian)", () => { | ||
expect(Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]).toBytesLittleEndian()).toEqual([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(Uint64.fromBytesBigEndian([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).toBytesLittleEndian()).toEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(Uint64.fromBytesBigEndian([0xab, 0x22, 0xbc, 0x5f, 0xa9, 0x20, 0x4e, 0x0d]).toBytesLittleEndian()).toEqual([0x0d, 0x4e, 0x20, 0xa9, 0x5f, 0xbc, 0x22, 0xab]); | ||
expect(Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).toBytesLittleEndian()).toEqual([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
}); | ||
it("can export strings", () => { | ||
{ | ||
const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(a.toString()).toEqual("0"); | ||
} | ||
{ | ||
const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(a.toString()).toEqual("1"); | ||
} | ||
{ | ||
const a = Uint64.fromBytesBigEndian([0x8a, 0xc7, 0x23, 0x04, 0x89, 0xe7, 0xff, 0xff]); | ||
expect(a.toString()).toEqual("9999999999999999999"); | ||
} | ||
{ | ||
const a = Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
expect(a.toString()).toEqual("18446744073709551615"); | ||
} | ||
}); | ||
it("can export numbers", () => { | ||
{ | ||
const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(a.toNumber()).toEqual(0); | ||
} | ||
{ | ||
const a = Uint64.fromBytesBigEndian([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); | ||
expect(a.toNumber()).toEqual(1); | ||
} | ||
{ | ||
// value too large for 53 bit integer | ||
const a = Uint64.fromBytesBigEndian([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]); | ||
expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); | ||
} | ||
{ | ||
// Number.MAX_SAFE_INTEGER + 1 | ||
const a = Uint64.fromBytesBigEndian([0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); | ||
expect(() => a.toNumber()).toThrowError(/number can only safely store up to 53 bits/i); | ||
} | ||
}); | ||
}); | ||
}); |
/* tslint:disable:no-bitwise */ | ||
import BN = require("bn.js"); | ||
const uint64MaxValue = new BN("18446744073709551615", 10, "be"); | ||
export class Uint32 { | ||
@@ -25,7 +28,7 @@ public static fromBigEndianBytes(bytes: ArrayLike<number>): Uint32 { | ||
if (Number.isNaN(input)) { | ||
throw new Error("input is not a number"); | ||
throw new Error("Input is not a number"); | ||
} | ||
if (input < 0 || input > 4294967295) { | ||
throw new Error("input not in uint32 range: " + input.toString()); | ||
throw new Error("Input not in uint32 range: " + input.toString()); | ||
} | ||
@@ -65,7 +68,7 @@ | ||
if (Number.isNaN(input)) { | ||
throw new Error("input is not a number"); | ||
throw new Error("Input is not a number"); | ||
} | ||
if (input < Number.MIN_SAFE_INTEGER || input > Number.MAX_SAFE_INTEGER) { | ||
throw new Error("input not in int53 range: " + input.toString()); | ||
throw new Error("Input not in int53 range: " + input.toString()); | ||
} | ||
@@ -76,9 +79,82 @@ | ||
public asNumber(): number { | ||
public toNumber(): number { | ||
return this.data; | ||
} | ||
public asString(): string { | ||
public toString(): string { | ||
return this.data.toString(); | ||
} | ||
} | ||
export class Uint64 { | ||
public static fromBytesBigEndian(bytes: ArrayLike<number>): Uint64 { | ||
if (bytes.length !== 8) { | ||
throw new Error("Invalid input length. Expected 8 bytes."); | ||
} | ||
// tslint:disable-next-line:prefer-for-of | ||
for (let i = 0; i < bytes.length; ++i) { | ||
if (bytes[i] > 255 || bytes[i] < 0 || Number.isNaN(bytes[i])) { | ||
throw new Error("Invalid value in byte. Found: " + bytes[i]); | ||
} | ||
} | ||
// tslint:disable-next-line:readonly-array | ||
const asArray: number[] = []; | ||
// tslint:disable-next-line:prefer-for-of | ||
for (let i = 0; i < bytes.length; ++i) { | ||
asArray.push(bytes[i]); | ||
} | ||
return new Uint64(new BN([...asArray])); | ||
} | ||
public static fromString(str: string): Uint64 { | ||
if (!str.match(/^[0-9]+$/)) { | ||
throw new Error("Invalid string format"); | ||
} | ||
return new Uint64(new BN(str, 10, "be")); | ||
} | ||
public static fromNumber(input: number): Uint64 { | ||
if (Number.isNaN(input)) { | ||
throw new Error("Input is not a number"); | ||
} | ||
let bigint: BN; | ||
try { | ||
bigint = new BN(input); | ||
} catch { | ||
throw new Error("Input is not a safe integer"); | ||
} | ||
return new Uint64(bigint); | ||
} | ||
private readonly data: BN; | ||
private constructor(data: BN) { | ||
if (data.isNeg()) { | ||
throw new Error("Input is negative"); | ||
} | ||
if (data.gt(uint64MaxValue)) { | ||
throw new Error("Input exceeds uint64 range"); | ||
} | ||
this.data = data; | ||
} | ||
public toBytesBigEndian(): ReadonlyArray<number> { | ||
return this.data.toArray("be", 8); | ||
} | ||
public toBytesLittleEndian(): ReadonlyArray<number> { | ||
return this.data.toArray("le", 8); | ||
} | ||
public toString(): string { | ||
return this.data.toString(10); | ||
} | ||
public toNumber(): number { | ||
return this.data.toNumber(); | ||
} | ||
} |
@@ -6,7 +6,3 @@ { | ||
"outDir": "build", | ||
"rootDir": "src", | ||
"typeRoots": [ | ||
"../../node_modules/@types", | ||
"custom_types" | ||
], | ||
"rootDir": "src" | ||
}, | ||
@@ -13,0 +9,0 @@ "include": [ |
@@ -15,1 +15,8 @@ import { ReadonlyDate } from "readonly-date"; | ||
} | ||
export declare class Bech32 { | ||
static encode(prefix: string, data: Uint8Array): string; | ||
static decode(address: string): { | ||
readonly prefix: string; | ||
readonly data: Uint8Array; | ||
}; | ||
} |
@@ -12,4 +12,15 @@ export declare class Uint32 { | ||
constructor(input: number); | ||
asNumber(): number; | ||
asString(): string; | ||
toNumber(): number; | ||
toString(): string; | ||
} | ||
export declare class Uint64 { | ||
static fromBytesBigEndian(bytes: ArrayLike<number>): Uint64; | ||
static fromString(str: string): Uint64; | ||
static fromNumber(input: number): Uint64; | ||
private readonly data; | ||
private constructor(); | ||
toBytesBigEndian(): ReadonlyArray<number>; | ||
toBytesLittleEndian(): ReadonlyArray<number>; | ||
toString(): string; | ||
toNumber(): number; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
171550
1800
27
4
2
28
+ Addedbech32@^1.1.3
+ Addedbn.js@^4.11.8
+ Addedbech32@1.1.4(transitive)
+ Addedbn.js@4.12.0(transitive)