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

refactor: add additional parsers and tests #1539

Open
wants to merge 6 commits into
base: arthur/parser-combinator
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
153 changes: 153 additions & 0 deletions src/parser/index.ts
Expand Up @@ -447,6 +447,117 @@ export class Int32BE extends Parser<number> {
}
}

export class Int64BE extends Parser<number> {
index: number;
lo: number;
hi: number;
first: number;

constructor() {
super();

this.index = 0;
this.lo = 0;
this.hi = 0;
this.first = 0;
}

parse(buffer: Buffer, offset: number): Result<number> {
const dataLength = 8;
if (this.index === 0) {
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}

// Fast path, buffer has all data available
if (offset + dataLength <= buffer.length) {
const low = buffer.readInt32BE(offset);
const high = buffer.readUInt32BE(offset + 4);
const bufferValue = Math.pow(2, 32) * low + ((buffer[offset] & 0x80) === 0x80 ? 1 : -1) * high;
return { done: true, value: bufferValue, offset: offset + dataLength };
}
this.first = buffer[offset];
this.lo += buffer[offset++] << 24;
this.index += 1;
}

while (this.index < dataLength / 2) {
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}
this.lo += buffer[offset++] * 2 ** (8 * (3 - this.index));
this.index += 1;
}

while (this.index < dataLength) {
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}
this.hi += buffer[offset++] * 2 ** (8 * (7 - this.index));
this.index += 1;
}

return { done: true, value: Math.pow(2, 32) * this.lo + ((this.first & 0x80) === 0x80 ? 1 : -1) * this.hi, offset: offset };
}
}

export class Int64LE extends Parser<number> {
index: number;
lo: number;
hi: number;

constructor() {
super();

this.index = 0;
this.lo = 0;
this.hi = 0;
}

parse(buffer: Buffer, offset: number): Result<number> {
const dataLength = 8;
if (this.index === 0) {
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}

// Fast path, buffer has all data available
if (offset + dataLength <= buffer.length) {
const low = buffer.readUInt32LE(offset);
const high = buffer.readInt32LE(offset + 4);
const bufferValue = Math.pow(2, 32) * high + ((buffer[offset + dataLength - 1] & 0x80) === 0x80 ? 1 : -1) * low;
return { done: true, value: bufferValue, offset: offset + dataLength };
}

this.lo += buffer[offset++];
this.index += 1;
}

while (this.index < dataLength / 2) {
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}

this.lo += buffer[offset++] * 2 ** (8 * this.index);
this.index += 1;
}

while (this.index < dataLength) {
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}
if (this.index < dataLength - 1) {
this.hi += buffer[offset++] * 2 ** (8 * (this.index - 4));
} else {
// Last byte
this.hi += buffer[offset++] << 24;
}
this.index += 1;
}
return { done: true, value: Math.pow(2, 32) * this.hi + ((buffer[offset - 1] & 0x80) === 0x80 ? 1 : -1) * this.lo, offset: offset };
}
}

export class UInt32LE extends Parser<number> {
index: number;
result: 0;
Expand Down Expand Up @@ -526,6 +637,48 @@ export class UInt32BE extends Parser<number> {
}
}

export class UInt40LE extends Parser<number> {
index: number;
result: 0;

constructor() {
super();

this.index = 0;
this.result = 0;
}
parse(buffer: Buffer, offset: number): Result<number> {
const dataLength = 5;
if (this.index === 0) {
// reach the end of the incoming buffer
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}

// Fast path, incoming buffer has all data available
if (offset + dataLength <= buffer.length) {
// read low bytes and high bytes consectivly
const low = buffer.readUInt32LE(offset);
const high = buffer.readUInt8(offset + 4);
return { done: true, value: Math.pow(2, 32) * high + low, offset: offset + dataLength };
mShan0 marked this conversation as resolved.
Show resolved Hide resolved
}

this.result += buffer[offset++];
this.index += 1;
}

while (this.index < dataLength) {
if (offset === buffer.length) {
return { done: false, value: undefined, offset: offset };
}

this.result += buffer[offset++] * 2 ** (8 * this.index);
this.index += 1;
}

return { done: true, value: this.result, offset: offset };
}
}

export class BigInt64LE extends Parser<bigint> {
index: number;
Expand Down
210 changes: 210 additions & 0 deletions test/unit/parser/parser-test.ts
Expand Up @@ -8,8 +8,11 @@ import {
UInt16LE,
Int32LE,
Int32BE,
Int64BE,
Int64LE,
UInt32LE,
UInt32BE,
UInt40LE,
BigInt64LE,
BigUInt64LE,
BVarchar,
Expand Down Expand Up @@ -285,6 +288,163 @@ describe('Int32BE', function() {
});
});

describe('Int64LE', function() {
it('parses an signed negative integer', function() {
const parser = new Int64LE();
const result = parser.parse(Buffer.from('FFFF00FF000000FF', 'hex'), 0);
assert.isTrue(result.done);
assert.strictEqual(result.value, -72057589759672320);
assert.strictEqual(result.offset, 8);
});

it('parses an signed positive integer', function() {
const parser = new Int64LE();
const result = parser.parse(Buffer.from('FFFF00FF00000001', 'hex'), 0);
assert.isTrue(result.done);
assert.strictEqual(result.value, 72057589759672320);
assert.strictEqual(result.offset, 8);
});

it('parses a signed integer over multiple buffers', function() {
const parser = new Int64LE();

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}


{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isTrue(result.done);
assert.strictEqual(result.value, -72057589759672320);
assert.strictEqual(result.offset, 1);
}
});
});

describe('Int64BE', function() {
it('parses an signed negative integer', function() {
const parser = new Int64BE();
const result = parser.parse(Buffer.from('FF000000FF00FFFF', 'hex'), 0);
assert.isTrue(result.done);
assert.strictEqual(result.value, -72057589759672320);
assert.strictEqual(result.offset, 8);
});

it('parses an signed positive integer', function() {
const parser = new Int64BE();
const result = parser.parse(Buffer.from('01000000FF00FFFF', 'hex'), 0);
assert.isTrue(result.done);
assert.strictEqual(result.value, 72057589759672320);
assert.strictEqual(result.offset, 8);
});

it('parses a signed integer over multiple buffers', function() {
const parser = new Int64BE();

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isTrue(result.done);
assert.strictEqual(result.value, -72057589759672320);
assert.strictEqual(result.offset, 1);
}
});
});

describe('UInt32LE', function() {
it('parses an unsigned integer', function() {
const parser = new UInt32LE();
Expand Down Expand Up @@ -328,6 +488,56 @@ describe('UInt32LE', function() {
});
});

describe('UInt40LE', function() {
it('parses an unsigned integer', function() {
const parser = new UInt40LE();
const result = parser.parse(Buffer.from('FF00FFFFFF', 'hex'), 0);

assert.isTrue(result.done);
assert.strictEqual(result.value, 1099511562495);
assert.strictEqual(result.offset, 5);
});

it('parses an unsigned integer over multiple buffers', function() {
const parser = new UInt40LE();

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('00', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isFalse(result.done);
assert.isUndefined(result.value);
assert.strictEqual(result.offset, 1);
}

{
const result = parser.parse(Buffer.from('FF', 'hex'), 0);
assert.isTrue(result.done);
assert.strictEqual(result.value, 1099511562495);
assert.strictEqual(result.offset, 1);
}
});
});

describe('UInt32BE', function() {
it('parses an unsigned integer', function() {
const parser = new UInt32BE();
Expand Down