Skip to content

Commit

Permalink
improve serialization format for smaller payload
Browse files Browse the repository at this point in the history
  • Loading branch information
mourner committed Aug 14, 2018
1 parent 3b0a1a9 commit ece1649
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 33 deletions.
55 changes: 32 additions & 23 deletions src/util/web_worker_transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { ImageData } = window;

import type {Transferable} from '../types/transferable';

type SerializedObject = { [string]: Serialized }; // eslint-disable-line
export type Serialized =
| null
| void
Expand All @@ -27,7 +28,7 @@ export type Serialized =
| $ArrayBufferView
| ImageData
| Array<Serialized>
| {| name: string, properties: {+[string]: Serialized} |};
| SerializedObject;

type Registry = {
[string]: {
Expand Down Expand Up @@ -68,16 +69,18 @@ export function register<T: any>(name: string, klass: Class<T>, options: Registe

register('Object', Object);

Grid.serialize = function serializeGrid(grid: Grid, transferables?: Array<Transferable>): Serialized {
const ab = grid.toArrayBuffer();
type SerializedGrid = { buffer: ArrayBuffer };

Grid.serialize = function serializeGrid(grid: Grid, transferables?: Array<Transferable>): SerializedGrid {
const buffer = grid.toArrayBuffer();
if (transferables) {
transferables.push(ab);
transferables.push(buffer);
}
return ab;
return {buffer};
};

Grid.deserialize = function deserializeGrid(serialized: ArrayBuffer): Grid {
return new Grid(serialized);
Grid.deserialize = function deserializeGrid(serialized: SerializedGrid): Grid {
return new Grid(serialized.buffer);
};
register('Grid', Grid);

Expand Down Expand Up @@ -161,18 +164,17 @@ export function serialize(input: mixed, transferables?: Array<Transferable>): Se
}
assert(registry[name]);

const properties: {[string]: Serialized} = {};

if (klass.serialize) {
const properties: SerializedObject = klass.serialize ?
// (Temporary workaround) allow a class to provide static
// `serialize()` and `deserialize()` methods to bypass the generic
// approach.
// This temporary workaround lets us use the generic serialization
// approach for objects whose members include instances of dynamic
// StructArray types. Once we refactor StructArray to be static,
// we can remove this complexity.
properties._serialized = (klass.serialize: typeof serialize)(input, transferables);
} else {
(klass.serialize(input, transferables): SerializedObject) : {};

if (!klass.serialize) {
for (const key in input) {
// any cast due to https://github.com/facebook/flow/issues/5393
if (!(input: any).hasOwnProperty(key)) continue;
Expand All @@ -182,13 +184,22 @@ export function serialize(input: mixed, transferables?: Array<Transferable>): Se
property :
serialize(property, transferables);
}

if (input instanceof Error) {
properties.message = input.message;
}
} else {
// make sure statically serialized object survives transfer of $name property
assert(!transferables || properties !== transferables[transferables.length - 1]);
}

return {name, properties};
if (properties.$name) {
throw new Error('$name property is reserved for worker serialization logic.');
}
if (name !== 'Object') {
properties.$name = name;
}

return properties;
}

throw new Error(`can't serialize object of type ${typeof input}`);
Expand All @@ -212,29 +223,27 @@ export function deserialize(input: Serialized): mixed {
}

if (Array.isArray(input)) {
return input.map((i) => deserialize(i));
return input.map(deserialize);
}

if (typeof input === 'object') {
const {name, properties} = (input: any);
if (!name) {
throw new Error(`can't deserialize object of anonymous class`);
}
const name = (input: any).$name || 'Object';

const {klass} = registry[name];
if (!klass) {
throw new Error(`can't deserialize unregistered class ${name}`);
}

if (klass.deserialize) {
return (klass.deserialize: typeof deserialize)(properties._serialized);
return (klass.deserialize: typeof deserialize)(input);
}

const result = Object.create(klass.prototype);

for (const key of Object.keys(properties)) {
result[key] = registry[name].shallow.indexOf(key) >= 0 ?
properties[key] : deserialize(properties[key]);
for (const key of Object.keys(input)) {
if (key === '$name') continue;
const value = (input: SerializedObject)[key];
result[key] = registry[name].shallow.indexOf(key) >= 0 ? value : deserialize(value);
}

return result;
Expand Down
14 changes: 6 additions & 8 deletions test/unit/data/dem_data.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,12 @@ test('DEMData#backfillBorder', (t) => {
const serialized = serialize(dem0);

t.deepEqual(serialized, {
name: 'DEMData',
properties: {
uid: 0,
dim: 4,
border: 2,
stride: 8,
data: dem0.data,
}
$name: 'DEMData',
uid: 0,
dim: 4,
border: 2,
stride: 8,
data: dem0.data,
}, 'serializes DEM');

const transferrables = [];
Expand Down
4 changes: 2 additions & 2 deletions test/unit/util/web_worker_transfer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ test('custom serialization', (t) => {
}

static serialize(b: Bar): Serialized {
return `custom serialization,${b.id}`;
return {foo: `custom serialization,${b.id}`};
}

static deserialize(input: Serialized): Bar {
const b = new Bar((input: any).split(',')[1]);
const b = new Bar((input: any).foo.split(',')[1]);
b._deserialized = true;
return b;
}
Expand Down

0 comments on commit ece1649

Please sign in to comment.