WIP
This commit is contained in:
parent
2b74100b2a
commit
738a47ce90
|
@ -0,0 +1,54 @@
|
||||||
|
"use strict";
|
||||||
|
// Preserves Annotations.
|
||||||
|
|
||||||
|
if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
||||||
|
require('../package.json').version,
|
||||||
|
'annotations.js',
|
||||||
|
module)) return;
|
||||||
|
|
||||||
|
const { Record, List, Map, Set } = require('./values.js');
|
||||||
|
const { PreserveOn, AsPreserve } = require('./symbols.js');
|
||||||
|
|
||||||
|
function Annotated(item) {
|
||||||
|
this.annotations = [];
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotated.prototype[AsPreserve] = function () {
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Annotated.prototype[PreserveOn] = function (encoder) {
|
||||||
|
for (const a of this.annotations) {
|
||||||
|
encoder.header(0, 0, 5);
|
||||||
|
encoder.push(a);
|
||||||
|
}
|
||||||
|
encoder.push(this.item);
|
||||||
|
};
|
||||||
|
|
||||||
|
function stripAnnotations(v, depth) {
|
||||||
|
function step(v, depth) {
|
||||||
|
if (depth === 0) return v;
|
||||||
|
if (!(v instanceof Annotated)) return v;
|
||||||
|
|
||||||
|
const nextDepth = depth - 1;
|
||||||
|
function walk(v) { return step(v, nextDepth); }
|
||||||
|
|
||||||
|
if (v.item instanceof Record) {
|
||||||
|
return Record(walk(v.item.label), v.item.fields.map(walk));
|
||||||
|
} else if (List.isList(v.item)) {
|
||||||
|
return v.item.map(walk);
|
||||||
|
} else if (Set.isSet(v.item)) {
|
||||||
|
return v.item.map(walk);
|
||||||
|
} else if (Map.isMap(v.item)) {
|
||||||
|
return v.item.mapEntries((e) => [walk(e[0]), walk(e[1])]);
|
||||||
|
} else if (v.item instanceof Annotated) {
|
||||||
|
const e = new Error("Improper annotation structure");
|
||||||
|
e.irritant = v;
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
return v.item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step(v, (depth === void 0) ? Infinity : depth);
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
||||||
module)) return;
|
module)) return;
|
||||||
|
|
||||||
const Values = require('./values.js');
|
const Values = require('./values.js');
|
||||||
|
const Annotations = require('./annotations.js');
|
||||||
const { List, Map, Set, Bytes, Record, Single, Double } = Values;
|
const { List, Map, Set, Bytes, Record, Single, Double } = Values;
|
||||||
|
|
||||||
const { PreserveOn } = require('./symbols.js');
|
const { PreserveOn } = require('./symbols.js');
|
||||||
|
@ -27,7 +28,7 @@ class Decoder {
|
||||||
? (packet._view || packet) // strip off Bytes wrapper, if any
|
? (packet._view || packet) // strip off Bytes wrapper, if any
|
||||||
: new Uint8Array(0);
|
: new Uint8Array(0);
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.shortForms = options.shortForms || {};
|
this.includeAnnotations = options.includeAnnotations || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
write(data) {
|
write(data) {
|
||||||
|
@ -75,16 +76,15 @@ class Decoder {
|
||||||
return [major, minor, arg];
|
return [major, minor, arg];
|
||||||
}
|
}
|
||||||
|
|
||||||
peekend(arg) {
|
peekend() {
|
||||||
const [a,i,r] = this.nextop();
|
const result = this.nextop() === 4;
|
||||||
const result = (a === 0) && (i === 3) && (r === arg);
|
|
||||||
if (!result) this.index--;
|
if (!result) this.index--;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
binarystream(arg, minor) {
|
binarystream(minor) {
|
||||||
const result = [];
|
const result = [];
|
||||||
while (!this.peekend(arg)) {
|
while (!this.peekend()) {
|
||||||
const chunk = this.next();
|
const chunk = this.next();
|
||||||
if (ArrayBuffer.isView(chunk)) {
|
if (ArrayBuffer.isView(chunk)) {
|
||||||
result.push(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
|
result.push(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));
|
||||||
|
@ -99,10 +99,10 @@ class Decoder {
|
||||||
return this.decodebinary(minor, Bytes.concat(result));
|
return this.decodebinary(minor, Bytes.concat(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
valuestream(arg, minor, decoder) {
|
valuestream(minor) {
|
||||||
const result = [];
|
const result = [];
|
||||||
while (!this.peekend(arg)) result.push(this.next());
|
while (!this.peekend()) result.push(this.next());
|
||||||
return decoder(minor, result);
|
return this.decodecompound(minor, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeint(bs) {
|
decodeint(bs) {
|
||||||
|
@ -123,23 +123,15 @@ class Decoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decoderecord(minor, vs) {
|
decodecompound(minor, vs) {
|
||||||
if (minor === 3) {
|
|
||||||
if (vs.length === 0) throw new DecodeError("Too few elements in encoded record");
|
|
||||||
return new Record(vs[0], vs.slice(1));
|
|
||||||
} else {
|
|
||||||
const label = this.shortForms[minor];
|
|
||||||
if (label === void 0) throw new DecodeError("Use of unconfigured short form " + minor);
|
|
||||||
return new Record(label, vs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decodecollection(minor, vs) {
|
|
||||||
switch (minor) {
|
switch (minor) {
|
||||||
case 0: return List(vs);
|
case 0: {
|
||||||
case 1: return Set(vs);
|
if (vs.length === 0) throw new DecodeError("Too few elements in encoded record");
|
||||||
case 2: return this.mapFromArray(vs);
|
return new Record(vs[0], vs.slice(1));
|
||||||
case 3: throw new DecodeError("Invalid collection type");
|
}
|
||||||
|
case 1: return List(vs);
|
||||||
|
case 2: return Set(vs);
|
||||||
|
case 3: return this.mapFromArray(vs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,28 +154,31 @@ class Decoder {
|
||||||
case 1: return true;
|
case 1: return true;
|
||||||
case 2: return Single(this.nextbytes(4).getFloat32(0, false));
|
case 2: return Single(this.nextbytes(4).getFloat32(0, false));
|
||||||
case 3: return Double(this.nextbytes(8).getFloat64(0, false));
|
case 3: return Double(this.nextbytes(8).getFloat64(0, false));
|
||||||
|
case 4: throw new DecodeError("Unexpected end-of-stream marker");
|
||||||
|
case 5: ...annotation...;
|
||||||
|
default: throw new DecodeError("Illegal format A lead byte");
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
return (arg > 12) ? arg - 16 : arg;
|
...placeholder...;
|
||||||
case 2: {
|
case 2: {
|
||||||
const t = arg >> 2;
|
const t = arg >> 2;
|
||||||
const n = arg & 3;
|
const n = arg & 3;
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case 0: throw new DecodeError("Invalid format C start byte");
|
case 0: throw new DecodeError("Invalid format C start byte (0)");
|
||||||
case 1: return this.binarystream(arg, n);
|
case 1: return this.binarystream(n);
|
||||||
case 2: return this.valuestream(arg, n, this.decoderecord.bind(this));
|
case 2: return this.valuestream(n);
|
||||||
case 3: return this.valuestream(arg, n, this.decodecollection.bind(this));
|
case 3: throw new DecodeError("Invalid format C start byte (3)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
throw new DecodeError("Invalid format C end byte");
|
return (arg > 12) ? arg - 16 : arg;
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
return this.decodebinary(minor, Bytes.from(this.nextbytes(this.wirelength(arg))));
|
return this.decodebinary(minor, Bytes.from(this.nextbytes(this.wirelength(arg))));
|
||||||
case 2:
|
case 2:
|
||||||
return this.decoderecord(minor, this.nextvalues(this.wirelength(arg)));
|
return this.decodecompound(minor, this.nextvalues(this.wirelength(arg)));
|
||||||
case 3:
|
case 3:
|
||||||
return this.decodecollection(minor, this.nextvalues(this.wirelength(arg)));
|
throw new DecodeError("Invalid lead byte (major 3)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +202,6 @@ class Encoder {
|
||||||
this.chunks = [];
|
this.chunks = [];
|
||||||
this.view = new DataView(new ArrayBuffer(256));
|
this.view = new DataView(new ArrayBuffer(256));
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.shortForms = options.shortForms || {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contents() {
|
contents() {
|
||||||
|
@ -275,7 +269,7 @@ class Encoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
encodecollection(minor, items) {
|
encodecollection(minor, items) {
|
||||||
this.header(3, minor, items.size);
|
this.header(2, minor, items.size);
|
||||||
for (const item of items) { this.push(item); }
|
for (const item of items) { this.push(item); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,10 +277,11 @@ class Encoder {
|
||||||
const tn = ((t & 3) << 2) | (n & 3);
|
const tn = ((t & 3) << 2) | (n & 3);
|
||||||
this.header(0, 2, tn);
|
this.header(0, 2, tn);
|
||||||
for (const item of items) { this.push(item); }
|
for (const item of items) { this.push(item); }
|
||||||
this.header(0, 3, tn);
|
this.header(0, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
push(v) {
|
push(v) {
|
||||||
|
...placeholder...;
|
||||||
if (typeof v === 'object' && v !== null && typeof v[PreserveOn] === 'function') {
|
if (typeof v === 'object' && v !== null && typeof v[PreserveOn] === 'function') {
|
||||||
v[PreserveOn](this);
|
v[PreserveOn](this);
|
||||||
}
|
}
|
||||||
|
@ -295,7 +290,7 @@ class Encoder {
|
||||||
}
|
}
|
||||||
else if (typeof v === 'number') {
|
else if (typeof v === 'number') {
|
||||||
if (v >= -3 && v <= 12) {
|
if (v >= -3 && v <= 12) {
|
||||||
this.leadbyte(0, 1, v >= 0 ? v : v + 16);
|
this.leadbyte(0, 3, v >= 0 ? v : v + 16);
|
||||||
} else {
|
} else {
|
||||||
this.encodeint(v);
|
this.encodeint(v);
|
||||||
}
|
}
|
||||||
|
@ -323,18 +318,18 @@ class Encoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (List.isList(v)) {
|
else if (List.isList(v)) {
|
||||||
this.encodecollection(0, v);
|
|
||||||
}
|
|
||||||
else if (Set.isSet(v)) {
|
|
||||||
this.encodecollection(1, v);
|
this.encodecollection(1, v);
|
||||||
}
|
}
|
||||||
|
else if (Set.isSet(v)) {
|
||||||
|
this.encodecollection(2, v);
|
||||||
|
}
|
||||||
else if (Map.isMap(v)) {
|
else if (Map.isMap(v)) {
|
||||||
this.encodecollection(2, List().withMutations((l) => {
|
this.encodecollection(3, List().withMutations((l) => {
|
||||||
v.forEach((val, key) => { l.push(key).push(val); });
|
v.forEach((val, key) => { l.push(key).push(val); });
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
else if (typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function') {
|
else if (typeof v === 'object' && v !== null && typeof v[Symbol.iterator] === 'function') {
|
||||||
this.encodestream(3, 0, v);
|
this.encodestream(2, 0, v);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new EncodeError("Cannot encode", v);
|
throw new EncodeError("Cannot encode", v);
|
||||||
|
|
|
@ -8,3 +8,4 @@ if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
||||||
Object.assign(module.exports, require('./symbols.js'));
|
Object.assign(module.exports, require('./symbols.js'));
|
||||||
Object.assign(module.exports, require('./codec.js'));
|
Object.assign(module.exports, require('./codec.js'));
|
||||||
Object.assign(module.exports, require('./values.js'));
|
Object.assign(module.exports, require('./values.js'));
|
||||||
|
Object.assign(module.exports, require('./annotations.js'));
|
||||||
|
|
|
@ -10,7 +10,7 @@ if (require('./singletonmodule.js')('leastfixedpoint.com/preserves',
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
|
||||||
const Immutable = require('immutable');
|
const Immutable = require('immutable');
|
||||||
const { List, isList, Map, Set, is } = Immutable;
|
const { List, Map, Set, is } = Immutable;
|
||||||
|
|
||||||
const { PreserveOn, AsPreserve } = require('./symbols.js');
|
const { PreserveOn, AsPreserve } = require('./symbols.js');
|
||||||
|
|
||||||
|
@ -341,16 +341,8 @@ Record.prototype.set = function (index, newValue) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Record.prototype[PreserveOn] = function (encoder) {
|
Record.prototype[PreserveOn] = function (encoder) {
|
||||||
if (is(encoder.shortForms[0], this.label)) {
|
encoder.header(2, 0, this.fields.size + 1);
|
||||||
encoder.header(2, 0, this.fields.size);
|
encoder.push(this.label);
|
||||||
} else if (is(encoder.shortForms[1], this.label)) {
|
|
||||||
encoder.header(2, 1, this.fields.size);
|
|
||||||
} else if (is(encoder.shortForms[2], this.label)) {
|
|
||||||
encoder.header(2, 2, this.fields.size);
|
|
||||||
} else {
|
|
||||||
encoder.header(2, 3, this.fields.size + 1);
|
|
||||||
encoder.push(this.label);
|
|
||||||
}
|
|
||||||
for (const field of this.fields) { encoder.push(field); }
|
for (const field of this.fields) { encoder.push(field); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue