register functions by prefix/intermediate/final bytes

This commit is contained in:
Jörg Breitbart
2019-07-31 04:49:01 +02:00
parent d78c062169
commit 812fc5e6ec
14 changed files with 652 additions and 539 deletions
+81 -44
View File
@@ -4,7 +4,7 @@
*/
import { assert } from 'chai';
import { DcsParser, DcsHandlerFactory } from 'common/parser/DcsParser';
import { IDcsHandler, IParams } from 'common/parser/Types';
import { IDcsHandler, IParams, IFunctionIdentifier } from 'common/parser/Types';
import { utf32ToString, StringToUtf32 } from 'common/input/TextDecoder';
import { Params } from 'common/parser/Params';
import { PAYLOAD_LIMIT } from 'common/parser/Constants';
@@ -16,10 +16,47 @@ function toUtf32(s: string): Uint32Array {
return utf32.subarray(0, length);
}
function identifier(id: IFunctionIdentifier): number {
let res = 0;
if (id.prefix) {
if (id.prefix.length > 1) {
throw new Error('only one byte as prefix supported');
}
res = id.prefix.charCodeAt(0);
if (res && 0x3c > res || res > 0x3f) {
throw new Error('prefix must be in range 0x3c .. 0x3f');
}
}
if (id.intermediates) {
if (id.intermediates.length > 2) {
throw new Error('only two bytes as intermediates are supported');
}
for (let i = 0; i < id.intermediates.length; ++i) {
const intermediate = id.intermediates.charCodeAt(i);
if (0x20 > intermediate || intermediate > 0x2f) {
throw new Error('intermediate must be in range 0x20 .. 0x2f');
}
res <<= 8;
res |= intermediate;
}
}
if (id.final.length !== 1) {
throw new Error('final must be a single byte');
}
const finalCode = id.final.charCodeAt(0);
if (0x40 > finalCode || finalCode > 0x7e) {
throw new Error('final must be in range 0x40 .. 0x7e');
}
res <<= 8;
res |= finalCode;
return res;
}
class TestHandler implements IDcsHandler {
constructor(public output: any[], public msg: string, public returnFalse: boolean = false) {}
hook(collect: string, params: IParams, flag: number): void {
this.output.push([this.msg, 'HOOK', params.toArray(), collect, flag]);
hook(params: IParams): void {
this.output.push([this.msg, 'HOOK', params.toArray()]);
}
put(data: Uint32Array, start: number, end: number): void {
this.output.push([this.msg, 'PUT', utf32ToString(data, start, end)]);
@@ -39,16 +76,16 @@ describe('DcsParser', () => {
reports = [];
parser = new DcsParser();
parser.setDcsHandlerFallback((id, action, data) => {
if (data.params) {
data.params = data.params.toArray();
if (action === 'HOOK') {
data = data.toArray();
}
reports.push([id, action, data]);
});
});
describe('handler registration', () => {
it('setDcsHandler', () => {
parser.setDcsHandler('+p', new TestHandler(reports, 'th'));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32('the mouse!');
@@ -56,16 +93,16 @@ describe('DcsParser', () => {
parser.unhook(true);
assert.deepEqual(reports, [
// messages from TestHandler
['th', 'HOOK', [1, 2, 3], '+', 'p'.charCodeAt(0)],
['th', 'HOOK', [1, 2, 3]],
['th', 'PUT', 'Here comes'],
['th', 'PUT', 'the mouse!'],
['th', 'UNHOOK', true]
]);
});
it('clearDcsHandler', () => {
parser.setDcsHandler('+p', new TestHandler(reports, 'th'));
parser.clearDcsHandler('+p');
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
parser.clearDcsHandler(identifier({intermediates: '+', final: 'p'}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32('the mouse!');
@@ -73,24 +110,24 @@ describe('DcsParser', () => {
parser.unhook(true);
assert.deepEqual(reports, [
// messages from fallback handler
['+p', 'HOOK', {collect: '+', params: [1, 2, 3], flag: 'p'.charCodeAt(0)}],
['+p', 'PUT', 'Here comes'],
['+p', 'PUT', 'the mouse!'],
['+p', 'UNHOOK', true]
[identifier({intermediates: '+', final: 'p'}), 'HOOK', [1, 2, 3]],
[identifier({intermediates: '+', final: 'p'}), 'PUT', 'Here comes'],
[identifier({intermediates: '+', final: 'p'}), 'PUT', 'the mouse!'],
[identifier({intermediates: '+', final: 'p'}), 'UNHOOK', true]
]);
});
it('addDcsHandler', () => {
parser.setDcsHandler('+p', new TestHandler(reports, 'th1'));
parser.addDcsHandler('+p', new TestHandler(reports, 'th2'));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2'));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32('the mouse!');
parser.put(data, 0, data.length);
parser.unhook(true);
assert.deepEqual(reports, [
['th2', 'HOOK', [1, 2, 3], '+', 'p'.charCodeAt(0)],
['th1', 'HOOK', [1, 2, 3], '+', 'p'.charCodeAt(0)],
['th2', 'HOOK', [1, 2, 3]],
['th1', 'HOOK', [1, 2, 3]],
['th2', 'PUT', 'Here comes'],
['th1', 'PUT', 'Here comes'],
['th2', 'PUT', 'the mouse!'],
@@ -100,17 +137,17 @@ describe('DcsParser', () => {
]);
});
it('addDcsHandler with return false', () => {
parser.setDcsHandler('+p', new TestHandler(reports, 'th1'));
parser.addDcsHandler('+p', new TestHandler(reports, 'th2', true));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32('the mouse!');
parser.put(data, 0, data.length);
parser.unhook(true);
assert.deepEqual(reports, [
['th2', 'HOOK', [1, 2, 3], '+', 'p'.charCodeAt(0)],
['th1', 'HOOK', [1, 2, 3], '+', 'p'.charCodeAt(0)],
['th2', 'HOOK', [1, 2, 3]],
['th1', 'HOOK', [1, 2, 3]],
['th2', 'PUT', 'Here comes'],
['th1', 'PUT', 'Here comes'],
['th2', 'PUT', 'the mouse!'],
@@ -120,17 +157,17 @@ describe('DcsParser', () => {
]);
});
it('dispose handlers', () => {
parser.setDcsHandler('+p', new TestHandler(reports, 'th1'));
const dispo = parser.addDcsHandler('+p', new TestHandler(reports, 'th2', true));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
const dispo = parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
dispo.dispose();
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32('the mouse!');
parser.put(data, 0, data.length);
parser.unhook(true);
assert.deepEqual(reports, [
['th1', 'HOOK', [1, 2, 3], '+', 'p'.charCodeAt(0)],
['th1', 'HOOK', [1, 2, 3]],
['th1', 'PUT', 'Here comes'],
['th1', 'PUT', 'the mouse!'],
['th1', 'UNHOOK', true]
@@ -139,8 +176,8 @@ describe('DcsParser', () => {
});
describe('DcsHandlerFactory', () => {
it('should be called once on end(true)', () => {
parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push([params.toArray(), data])));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32(' the mouse!');
@@ -149,8 +186,8 @@ describe('DcsParser', () => {
assert.deepEqual(reports, [[[1, 2, 3], 'Here comes the mouse!']]);
});
it('should not be called on end(false)', () => {
parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push([params.toArray(), data])));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32(' the mouse!');
@@ -159,9 +196,9 @@ describe('DcsParser', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', () => {
parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push(['one', params.toArray(), data])));
const dispo = parser.addDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push(['two', params.toArray(), data])));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push(['one', params.toArray(), data])));
const dispo = parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push(['two', params.toArray(), data])));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32(' the mouse!');
@@ -169,7 +206,7 @@ describe('DcsParser', () => {
parser.unhook(true);
assert.deepEqual(reports, [['two', [1, 2, 3], 'Here comes the mouse!']]);
dispo.dispose();
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
data = toUtf32('some other');
parser.put(data, 0, data.length);
data = toUtf32(' data');
@@ -178,9 +215,9 @@ describe('DcsParser', () => {
assert.deepEqual(reports, [['two', [1, 2, 3], 'Here comes the mouse!'], ['one', [1, 2, 3], 'some other data']]);
});
it('should respect return false', () => {
parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push(['one', params.toArray(), data])));
parser.addDcsHandler('+p', new DcsHandlerFactory((params, data) => { reports.push(['two', params.toArray(), data]); return false; }));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push(['one', params.toArray(), data])));
parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => { reports.push(['two', params.toArray(), data]); return false; }));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
data = toUtf32(' the mouse!');
@@ -190,8 +227,8 @@ describe('DcsParser', () => {
});
it('should work up to payload limit', function(): void {
this.timeout(10000);
parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push([params.toArray(), data])));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
const data = toUtf32('A'.repeat(1000));
for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) {
parser.put(data, 0, data.length);
@@ -201,8 +238,8 @@ describe('DcsParser', () => {
});
it('should abort for payload limit +1', function(): void {
this.timeout(10000);
parser.setDcsHandler('+p', new DcsHandlerFactory((params, data) => reports.push([params.toArray(), data])));
parser.hook('+', Params.fromArray([1, 2, 3]), 'p'.charCodeAt(0));
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('A'.repeat(1000));
for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) {
parser.put(data, 0, data.length);