Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/hybrid #13

Merged
merged 14 commits into from
Aug 11, 2024
28 changes: 21 additions & 7 deletions src/cjs/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,18 @@ const checkUInt64 = (n) => {
throw new RangeError('value out of range');
}
};
function checkUInt53(n) {
if (n < 0 || n > Number.MAX_SAFE_INTEGER || n % 1 !== 0)
throw new RangeError('value out of range');
}
function checkUint53OrUint64(n) {
if (typeof n === 'number')
checkUInt53(n);
else
checkUInt64(n);
}
function encode(n, buffer, offset) {
checkUInt64(n);
checkUint53OrUint64(n);
if (offset === undefined)
offset = 0;
if (buffer === undefined) {
Expand All @@ -60,7 +70,7 @@ function encode(n, buffer, offset) {
}
else {
buffer.set([0xff], offset);
tools.writeUInt64(buffer, offset + 1, n, 'LE');
tools.writeUInt64(buffer, offset + 1, BigInt(n), 'LE');
bytes = 9;
}
return { buffer, bytes };
Expand All @@ -73,29 +83,33 @@ function decode(buffer, offset) {
throw new Error('buffer too small');
// 8 bit
if (first < 0xfd) {
return { value: BigInt(first), bytes: 1 };
return { numberValue: first, bigintValue: BigInt(first), bytes: 1 };
// 16 bit
}
else if (first === 0xfd) {
const val = tools.readUInt16(buffer, offset + 1, 'LE');
return {
value: BigInt(tools.readUInt16(buffer, offset + 1, 'LE')),
numberValue: val,
bigintValue: BigInt(val),
bytes: 3
};
// 32 bit
}
else if (first === 0xfe) {
const val = tools.readUInt32(buffer, offset + 1, 'LE');
return {
value: BigInt(tools.readUInt32(buffer, offset + 1, 'LE')),
numberValue: val,
bigintValue: BigInt(val),
bytes: 5
};
// 64 bit
}
else {
const number = tools.readUInt64(buffer, offset + 1, 'LE');
return { value: number, bytes: 9 };
return { numberValue: null, bigintValue: number, bytes: 9 };
}
}
function encodingLength(n) {
checkUInt64(n);
checkUint53OrUint64(n);
return n < 0xfd ? 1 : n <= 0xffff ? 3 : n <= 0xffffffff ? 5 : 9;
}
7 changes: 4 additions & 3 deletions src/cjs/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export declare function encode(n: bigint, buffer?: Uint8Array, offset?: number): {
export declare function encode(n: number | bigint, buffer?: Uint8Array, offset?: number): {
buffer: Uint8Array;
bytes: number;
};
export declare function decode(buffer: Uint8Array, offset?: number): {
value: bigint;
numberValue: number | null;
bigintValue: bigint;
bytes: number;
};
export declare function encodingLength(n: bigint): number;
export declare function encodingLength(n: number | bigint): number;
28 changes: 21 additions & 7 deletions src/esm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@ const checkUInt64 = (n) => {
throw new RangeError('value out of range');
}
};
function checkUInt53(n) {
if (n < 0 || n > Number.MAX_SAFE_INTEGER || n % 1 !== 0)
throw new RangeError('value out of range');
}
function checkUint53OrUint64(n) {
if (typeof n === 'number')
checkUInt53(n);
else
checkUInt64(n);
}
export function encode(n, buffer, offset) {
checkUInt64(n);
checkUint53OrUint64(n);
if (offset === undefined)
offset = 0;
if (buffer === undefined) {
Expand All @@ -33,7 +43,7 @@ export function encode(n, buffer, offset) {
}
else {
buffer.set([0xff], offset);
tools.writeUInt64(buffer, offset + 1, n, 'LE');
tools.writeUInt64(buffer, offset + 1, BigInt(n), 'LE');
bytes = 9;
}
return { buffer, bytes };
Expand All @@ -46,29 +56,33 @@ export function decode(buffer, offset) {
throw new Error('buffer too small');
// 8 bit
if (first < 0xfd) {
return { value: BigInt(first), bytes: 1 };
return { numberValue: first, bigintValue: BigInt(first), bytes: 1 };
// 16 bit
}
else if (first === 0xfd) {
const val = tools.readUInt16(buffer, offset + 1, 'LE');
return {
value: BigInt(tools.readUInt16(buffer, offset + 1, 'LE')),
numberValue: val,
bigintValue: BigInt(val),
bytes: 3
};
// 32 bit
}
else if (first === 0xfe) {
const val = tools.readUInt32(buffer, offset + 1, 'LE');
return {
value: BigInt(tools.readUInt32(buffer, offset + 1, 'LE')),
numberValue: val,
bigintValue: BigInt(val),
bytes: 5
};
// 64 bit
}
else {
const number = tools.readUInt64(buffer, offset + 1, 'LE');
return { value: number, bytes: 9 };
return { numberValue: null, bigintValue: number, bytes: 9 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if number is <= to Number.MAX_SAFE_INTEGER then return the numberValue.

}
}
export function encodingLength(n) {
checkUInt64(n);
checkUint53OrUint64(n);
return n < 0xfd ? 1 : n <= 0xffff ? 3 : n <= 0xffffffff ? 5 : 9;
}
4 changes: 4 additions & 0 deletions test/fixtures.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@
"dec": -1,
"msg": "^RangeError: value out of range$"
},
{
"dec": 0.1,
"msg": "^RangeError: value out of range$"
},
{
"dec": 900719925474099213131,
"msg": "^RangeError: value out of range$"
Expand Down
29 changes: 25 additions & 4 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,26 @@ valid.forEach(function (fixture, i) {

tape("valid decode #" + (i + 1), function (t) {
const res = decode(Buffer.from(fixture.hex, "hex"));
t.same(res.value, BigInt(fixture.dec));
if(fixture.dec <= 0xffffffff) {
t.same(res.numberValue, fixture.dec);
}else {
t.same(res.numberValue, null);
t.same(res.bigintValue, BigInt(fixture.dec));
}
t.same(res.bytes, fixture.hex.length / 2);
t.end();
});

tape("valid encodingLength #" + (i + 1), function (t) {
t.same(encodingLength(BigInt(fixture.dec)), fixture.hex.length / 2);
t.same(encodingLength(fixture.dec), fixture.hex.length / 2);
t.end();
});
});

invalid.forEach(function (fixture, i) {
tape("invalid encode #" + (i + 1), function (t) {
t.throws(function () {
encode(BigInt(fixture.dec));
encode(fixture.dec);
}, new RegExp(fixture.msg));
t.end();
});
Expand All @@ -42,6 +47,22 @@ invalid.forEach(function (fixture, i) {
});
});

tape("encode", function(t) {
t.test("should throw if number and > 53 bits", function (t) {
t.throws(function () {
encode(Number.MAX_SAFE_INTEGER + 2);
}, new RegExp(/value out of range/));
t.end();
})

t.test("should throw if bigint and > 64 bits", function (t) {
t.throws(function () {
encode(0xffffffffffffffffffn + 2n);
}, new RegExp(/value out of range/));
t.end();
})
})

tape("encode", function (t) {
t.test("write to buffer with offset", function (t) {
const buffer = Buffer.from([0x00, 0x00]);
Expand All @@ -58,7 +79,7 @@ tape("decode", function (t) {
t.test("read from buffer with offset", function (t) {
var buffer = Buffer.from([0x00, 0xfc]);
const res = decode(buffer, 1);
t.same(res.value, 0xfcn);
t.same(res.numberValue, 0xfc);
t.same(res.bytes, 1);
t.end();
});
Expand Down
33 changes: 23 additions & 10 deletions ts_src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,21 @@ const checkUInt64 = (n: bigint): void => {
}
}

function checkUInt53 (n: number): void {
if (n < 0 || n > Number.MAX_SAFE_INTEGER || n % 1 !== 0) throw new RangeError('value out of range')
}

function checkUint53OrUint64 (n: number | bigint): void {
if (typeof n === 'number') checkUInt53(n)
else checkUInt64(n)
}

export function encode (
n: bigint,
n: number | bigint,
buffer?: Uint8Array,
offset?: number
): { buffer: Uint8Array, bytes: number } {
checkUInt64(n)
checkUint53OrUint64(n)
if (offset === undefined) offset = 0

if (buffer === undefined) {
Expand Down Expand Up @@ -44,7 +53,7 @@ export function encode (
// 64 bit
} else {
buffer.set([0xff], offset)
tools.writeUInt64(buffer, offset + 1, n, 'LE')
tools.writeUInt64(buffer, offset + 1, BigInt(n), 'LE')

bytes = 9
}
Expand All @@ -55,39 +64,43 @@ export function encode (
export function decode (
buffer: Uint8Array,
offset?: number
): { value: bigint, bytes: number } {
): { numberValue: number | null, bigintValue: bigint, bytes: number } {
if (offset === undefined) offset = 0

const first = buffer.at(offset)
if (first === undefined) throw new Error('buffer too small')

// 8 bit
if (first < 0xfd) {
return { value: BigInt(first), bytes: 1 }
return { numberValue: first, bigintValue: BigInt(first), bytes: 1 }

// 16 bit
} else if (first === 0xfd) {
const val = tools.readUInt16(buffer, offset + 1, 'LE')
return {
value: BigInt(tools.readUInt16(buffer, offset + 1, 'LE')),
numberValue: val,
bigintValue: BigInt(val),
bytes: 3
}

// 32 bit
} else if (first === 0xfe) {
const val = tools.readUInt32(buffer, offset + 1, 'LE')
return {
value: BigInt(tools.readUInt32(buffer, offset + 1, 'LE')),
numberValue: val,
bigintValue: BigInt(val),
bytes: 5
}

// 64 bit
} else {
const number = tools.readUInt64(buffer, offset + 1, 'LE')

return { value: number, bytes: 9 }
return { numberValue: null, bigintValue: number, bytes: 9 }
}
}

export function encodingLength (n: bigint): number {
checkUInt64(n)
export function encodingLength (n: number | bigint): number {
checkUint53OrUint64(n)
return n < 0xfd ? 1 : n <= 0xffff ? 3 : n <= 0xffffffff ? 5 : 9
}
Loading