-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Update package-lock.json - Fix test assertion
- Loading branch information
1 parent
a83cacc
commit d504205
Showing
4 changed files
with
297 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
const { expect } = require('chai'); | ||
const { VarInt, Utils } = require('./varint'); | ||
|
||
describe('VarInt Tests', function() { | ||
|
||
it('testBytes', function() { | ||
let a = new VarInt(10); | ||
expect(a.getSizeInBytes()).to.equal(1); | ||
expect(a.encode().length).to.equal(1); | ||
expect(new VarInt(a.encode(), 0).value).to.equal(10n); | ||
}); | ||
|
||
it('testShorts', function() { | ||
let a = new VarInt(64000); | ||
expect(a.getSizeInBytes()).to.equal(3); | ||
expect(a.encode().length).to.equal(3); | ||
expect(new VarInt(a.encode(), 0).value).to.equal(64000n); | ||
}); | ||
|
||
it('testShortFFFF', function() { | ||
let a = new VarInt(0xFFFF); | ||
expect(a.getSizeInBytes()).to.equal(3); | ||
expect(a.encode().length).to.equal(3); | ||
expect(new VarInt(a.encode(), 0).value).to.equal(0xFFFFn); | ||
}); | ||
|
||
it('testSizeOfNegativeInt', function() { | ||
expect(VarInt.sizeOf(-1n)).to.equal(new VarInt(-1n).encode().length); | ||
}); | ||
|
||
it('testMaxInt', function() { | ||
let varInt = new VarInt(BigInt(Number.MAX_SAFE_INTEGER)); | ||
console.log(Buffer.from(varInt.encode()).toString('hex')); | ||
}); | ||
|
||
it('testDeserializeListOfValuesInHex', function() { | ||
let expectedValues = [14435729n, 255n, 187n, 13337n]; | ||
let values = Buffer.from('FE9145DC00FDFF00BBFD1934', 'hex'); | ||
let offset = 0; | ||
let idx = 0; | ||
while (values.length > offset) { | ||
let varIntValue = new VarInt(values, offset); | ||
offset += varIntValue.getSizeInBytes(); | ||
expect(varIntValue.value).to.equal(expectedValues[idx]); | ||
idx++; | ||
console.log(new Intl.NumberFormat().format(varIntValue.value)); | ||
} | ||
}); | ||
|
||
it('testSerializeListOfValuesInHex', function() { | ||
// FF0040075AF0750700 | ||
const maxBitcoin = 2100000000000000n; | ||
let values = [maxBitcoin, 14435729n, 255n, 187n, 13337n]; | ||
let stream = []; | ||
values.forEach(value => { | ||
let varIntValue = new VarInt(value); | ||
console.log(Buffer.from(varIntValue.encode()).toString('hex')); | ||
stream = stream.concat(Array.from(varIntValue.encode())); | ||
}); | ||
|
||
let expectedResult = Buffer.from('FF0040075AF0750700FE9145DC00FDFF00BBFD1934', 'hex'); | ||
expect(Buffer.from(stream)).to.deep.equal(expectedResult); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
class VarInt { | ||
constructor(valueOrBuf, offset = 0) { | ||
if (typeof valueOrBuf === 'number' || typeof valueOrBuf === 'bigint') { | ||
this.value = BigInt.asUintN(64, BigInt(valueOrBuf)); // Ensure value is treated as unsigned 64-bit integer | ||
this.originallyEncodedSize = this.getSizeInBytes(); | ||
} else if (valueOrBuf instanceof Uint8Array) { | ||
const buf = valueOrBuf; | ||
const first = buf[offset] & 0xFF; | ||
if (first < 253) { | ||
this.value = BigInt(first); | ||
this.originallyEncodedSize = 1; | ||
} else if (first === 253) { | ||
this.value = BigInt((buf[offset + 1] & 0xFF) | ((buf[offset + 2] & 0xFF) << 8)); | ||
this.originallyEncodedSize = 3; | ||
} else if (first === 254) { | ||
this.value = Utils.readUint32(buf, offset + 1); | ||
this.originallyEncodedSize = 5; | ||
} else { | ||
this.value = Utils.readInt64(buf, offset + 1); | ||
this.originallyEncodedSize = 9; | ||
} | ||
} else { | ||
throw new Error('Invalid constructor argument'); | ||
} | ||
} | ||
|
||
getOriginalSizeInBytes() { | ||
return this.originallyEncodedSize; | ||
} | ||
|
||
getSizeInBytes() { | ||
return VarInt.sizeOf(this.value); | ||
} | ||
|
||
static sizeOf(value) { | ||
if (value < 0) return 9; // 1 marker + 8 data bytes | ||
if (value < 253) return 1; // 1 data byte | ||
if (value <= 0xFFFFn) return 3; // 1 marker + 2 data bytes | ||
if (value <= 0xFFFFFFFFn) return 5; // 1 marker + 4 data bytes | ||
return 9; // 1 marker + 8 data bytes | ||
} | ||
|
||
encode() { | ||
let bytes; | ||
switch (VarInt.sizeOf(this.value)) { | ||
case 1: | ||
return new Uint8Array([Number(this.value)]); | ||
case 3: | ||
return new Uint8Array([253, Number(this.value) & 0xFF, (Number(this.value) >> 8) & 0xFF]); | ||
case 5: | ||
bytes = new Uint8Array(5); | ||
bytes[0] = 254; | ||
Utils.uint32ToByteArrayLE(this.value, bytes, 1); | ||
return bytes; | ||
default: | ||
bytes = new Uint8Array(9); | ||
bytes[0] = 255; | ||
Utils.uint64ToByteArrayLE(this.value, bytes, 1); | ||
return bytes; | ||
} | ||
} | ||
} | ||
|
||
class Utils { | ||
static readUint32(buf, offset) { | ||
return BigInt((buf[offset] & 0xFF) | | ||
((buf[offset + 1] & 0xFF) << 8) | | ||
((buf[offset + 2] & 0xFF) << 16) | | ||
((buf[offset + 3] & 0xFF) << 24) >>> 0); | ||
} | ||
|
||
static readInt64(buf, offset) { | ||
let low = Utils.readUint32(buf, offset); | ||
let high = Utils.readUint32(buf, offset + 4); | ||
return (high << 32n) + low; | ||
} | ||
|
||
static uint32ToByteArrayLE(value, buf, offset) { | ||
value = BigInt(value); | ||
buf[offset] = Number(value & 0xFFn); | ||
buf[offset + 1] = Number((value >> 8n) & 0xFFn); | ||
buf[offset + 2] = Number((value >> 16n) & 0xFFn); | ||
buf[offset + 3] = Number((value >> 24n) & 0xFFn); | ||
} | ||
|
||
static uint64ToByteArrayLE(value, buf, offset) { | ||
value = BigInt(value); | ||
Utils.uint32ToByteArrayLE(value & 0xFFFFFFFFn, buf, offset); | ||
Utils.uint32ToByteArrayLE(value >> 32n, buf, offset + 4); | ||
} | ||
} | ||
|
||
module.exports = { VarInt, Utils }; |
Oops, something went wrong.