refactor: merge serializer into schema
This commit is contained in:
parent
8cd242871a
commit
815d53d1e8
|
@ -1,5 +1,3 @@
|
|||
import Serializer from './serializer.js';
|
||||
|
||||
import Base from './base.js';
|
||||
|
||||
export default class Arbitrary extends Base {
|
||||
|
@ -10,11 +8,6 @@ export default class Arbitrary extends Base {
|
|||
|
||||
Instance;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.serializer = new Serializer(this.constructor.schema);
|
||||
}
|
||||
|
||||
allocateMany(count) {
|
||||
if (!this.Instance) {
|
||||
this.Instance = this.instanceFromSchema();
|
||||
|
@ -52,11 +45,11 @@ export default class Arbitrary extends Base {
|
|||
}
|
||||
|
||||
deserialize(entityId, view, offset) {
|
||||
this.serializer.decode(view, this.get(entityId), offset);
|
||||
this.constructor.schema.deserialize(view, this.get(entityId), offset);
|
||||
}
|
||||
|
||||
serialize(entityId, view, offset) {
|
||||
this.serializer.encode(this.get(entityId), view, offset);
|
||||
this.constructor.schema.serialize(this.get(entityId), view, offset);
|
||||
}
|
||||
|
||||
get(entityId) {
|
||||
|
|
|
@ -50,6 +50,33 @@ export default class Schema {
|
|||
return Object.entries(this.specification).values();
|
||||
}
|
||||
|
||||
deserialize(destination, view, offset = 0) {
|
||||
let cursor = offset;
|
||||
for (const key in this.specification) {
|
||||
const {type} = this.specification[key];
|
||||
const viewGetMethod = Schema.viewGetMethods[type];
|
||||
let value;
|
||||
if (viewGetMethod) {
|
||||
value = view[viewGetMethod](cursor, true);
|
||||
cursor += Schema.sizeOfType(type);
|
||||
}
|
||||
else {
|
||||
switch (type) {
|
||||
case 'string': {
|
||||
const length = view.getUint32(cursor, true);
|
||||
cursor += 4;
|
||||
const {buffer, byteOffset} = view;
|
||||
const decoder = new TextDecoder();
|
||||
value = decoder.decode(new DataView(buffer, byteOffset + cursor, length));
|
||||
cursor += length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
destination[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
static defaultValueForType(type) {
|
||||
switch (type) {
|
||||
case 'uint8': case 'int8':
|
||||
|
@ -107,6 +134,33 @@ export default class Schema {
|
|||
return fullSize;
|
||||
}
|
||||
|
||||
serialize(source, view, offset = 0) {
|
||||
let cursor = offset;
|
||||
for (const key in this.specification) {
|
||||
const {type} = this.specification[key];
|
||||
const viewSetMethod = Schema.viewSetMethods[type];
|
||||
if (viewSetMethod) {
|
||||
view[viewSetMethod](cursor, source[key], true);
|
||||
cursor += Schema.sizeOfType(type);
|
||||
}
|
||||
else {
|
||||
switch (type) {
|
||||
case 'string': {
|
||||
const lengthOffset = cursor;
|
||||
cursor += 4;
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(source[key]);
|
||||
for (let i = 0; i < bytes.length; ++i) {
|
||||
view.setUint8(cursor++, bytes[i]);
|
||||
}
|
||||
view.setUint32(lengthOffset, bytes.length, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.$$size;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,44 @@ test('validates a schema', () => {
|
|||
});
|
||||
|
||||
test('calculates the size of an instance', () => {
|
||||
expect((new Schema({foo: 'uint8', bar: 'uint32'})).sizeOf({foo: 69, bar: 420}))
|
||||
expect(
|
||||
(new Schema({foo: 'uint8', bar: 'uint32'}))
|
||||
.sizeOf({foo: 69, bar: 420})
|
||||
)
|
||||
.to.equal(5);
|
||||
expect((new Schema({foo: 'string'})).sizeOf({foo: 'hi'}))
|
||||
expect(
|
||||
(new Schema({foo: 'string'}))
|
||||
.sizeOf({foo: 'hi'})
|
||||
)
|
||||
.to.equal(4 + (new TextEncoder().encode('hi')).length);
|
||||
});
|
||||
|
||||
test('can encode and decode', () => {
|
||||
const entries = [
|
||||
['uint8', 255],
|
||||
['int8', -128],
|
||||
['int8', 127],
|
||||
['uint16', 65535],
|
||||
['int16', -32768],
|
||||
['int16', 32767],
|
||||
['uint32', 4294967295],
|
||||
['int32', -2147483648],
|
||||
['int32', 2147483647],
|
||||
['uint64', 18446744073709551615n],
|
||||
['int64', -9223372036854775808n],
|
||||
['int64', 9223372036854775807n],
|
||||
['float32', 0.5],
|
||||
['float64', 1.234],
|
||||
['string', 'hello world'],
|
||||
['string', 'α'],
|
||||
];
|
||||
const specification = entries.reduce((r, [type]) => ({...r, [Object.keys(r).length]: type}), {});
|
||||
const data = entries.reduce((r, [, value]) => ({...r, [Object.keys(r).length]: value}), {});
|
||||
const schema = new Schema(specification);
|
||||
const view = new DataView(new ArrayBuffer(schema.sizeOf(data)));
|
||||
schema.serialize(data, view);
|
||||
const result = {};
|
||||
schema.deserialize(result, view);
|
||||
expect(data)
|
||||
.to.deep.equal(result);
|
||||
});
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
import Schema from './schema.js';
|
||||
|
||||
export default class Serializer {
|
||||
|
||||
constructor(schema) {
|
||||
this.schema = schema instanceof Schema ? schema : new Schema(schema);
|
||||
}
|
||||
|
||||
decode(view, destination, offset = 0) {
|
||||
let cursor = offset;
|
||||
for (const [key, {type}] of this.schema) {
|
||||
const viewGetMethod = Schema.viewGetMethods[type];
|
||||
let value;
|
||||
if (viewGetMethod) {
|
||||
value = view[viewGetMethod](cursor, true);
|
||||
cursor += Schema.sizeOfType(type);
|
||||
}
|
||||
else {
|
||||
switch (type) {
|
||||
case 'string': {
|
||||
const length = view.getUint32(cursor, true);
|
||||
cursor += 4;
|
||||
const {buffer, byteOffset} = view;
|
||||
const decoder = new TextDecoder();
|
||||
value = decoder.decode(new DataView(buffer, byteOffset + cursor, length));
|
||||
cursor += length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
destination[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
encode(source, view, offset = 0) {
|
||||
let cursor = offset;
|
||||
for (const [key, {type}] of this.schema) {
|
||||
const viewSetMethod = Schema.viewSetMethods[type];
|
||||
if (viewSetMethod) {
|
||||
view[viewSetMethod](cursor, source[key], true);
|
||||
cursor += Schema.sizeOfType(type);
|
||||
}
|
||||
else {
|
||||
switch (type) {
|
||||
case 'string': {
|
||||
const lengthOffset = cursor;
|
||||
cursor += 4;
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(source[key]);
|
||||
for (let i = 0; i < bytes.length; ++i) {
|
||||
view.setUint8(cursor++, bytes[i]);
|
||||
}
|
||||
view.setUint32(lengthOffset, bytes.length, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import {expect, test} from 'vitest';
|
||||
|
||||
import Serializer from './serializer.js';
|
||||
|
||||
test('can encode and decode', () => {
|
||||
const entries = [
|
||||
['uint8', 255],
|
||||
['int8', -128],
|
||||
['int8', 127],
|
||||
['uint16', 65535],
|
||||
['int16', -32768],
|
||||
['int16', 32767],
|
||||
['uint32', 4294967295],
|
||||
['int32', -2147483648],
|
||||
['int32', 2147483647],
|
||||
['uint64', 18446744073709551615n],
|
||||
['int64', -9223372036854775808n],
|
||||
['int64', 9223372036854775807n],
|
||||
['float32', 0.5],
|
||||
['float64', 1.234],
|
||||
['string', 'hello world'],
|
||||
['string', 'α'],
|
||||
];
|
||||
const schema = entries.reduce((r, [type]) => ({...r, [Object.keys(r).length]: type}), {});
|
||||
const data = entries.reduce((r, [, value]) => ({...r, [Object.keys(r).length]: value}), {});
|
||||
const serializer = new Serializer(schema);
|
||||
const view = new DataView(new ArrayBuffer(serializer.schema.sizeOf(data)));
|
||||
serializer.encode(data, view);
|
||||
const result = {};
|
||||
serializer.decode(view, result);
|
||||
expect(data)
|
||||
.to.deep.equal(result);
|
||||
});
|
Loading…
Reference in New Issue
Block a user