Comparing version 0.6.4 to 0.8.0
@@ -11,4 +11,3 @@ "use strict" | ||
const EMPTY_ARRAY = [] | ||
const RECORD_STARTING_ID_PREFIX = 0x9d | ||
const RECORD_STARTING_ID = 40100 | ||
const RECORD_TAG_ID = 0x69 | ||
const STOP_CODE = {} | ||
@@ -18,2 +17,3 @@ let strings = EMPTY_ARRAY | ||
let currentDecoder = {} | ||
let currentStructures | ||
let srcString | ||
@@ -30,3 +30,3 @@ let srcStringStart = 0 | ||
class Decoder { | ||
export class Decoder { | ||
constructor(options) { | ||
@@ -45,3 +45,3 @@ if (options) { | ||
return saveState(() => { | ||
src = null | ||
clearSource() | ||
return this ? this.decode(source, end, continueReading) : Decoder.prototype.decode.call(defaultOptions, source, end, continueReading) | ||
@@ -87,16 +87,40 @@ }) | ||
} finally { | ||
src = null | ||
if (referenceMap) | ||
referenceMap = null | ||
if (position >= srcEnd || !continueReading) { | ||
src = null | ||
if (referenceMap) | ||
referenceMap = null | ||
} | ||
} | ||
} | ||
decodeMultiple(source, forEach) { | ||
try { | ||
let decoder = this | ||
let size = source.length | ||
let value = this ? this.decode(source, size, true) : defaultDecoder.decode(source, size, true) | ||
let values | ||
if (forEach) { | ||
forEach(value) | ||
while(position < size) { | ||
if (forEach(read()) === false) { | ||
return | ||
} | ||
} | ||
} | ||
else { | ||
values = [ value ] | ||
while(position < size) { | ||
values.push(read()) | ||
} | ||
return values | ||
} | ||
} finally { | ||
clearSource() | ||
} | ||
} | ||
} | ||
let currentStructures | ||
exports.Decoder = Decoder | ||
exports.read = read | ||
exports.getPosition = () => { | ||
export function getPosition() { | ||
return position | ||
} | ||
function read() { | ||
export function read() { | ||
let token = src[position++] | ||
@@ -217,3 +241,3 @@ let majorType = token >> 5 | ||
case 6: // extension | ||
if ((token >> 8) == RECORD_STARTING_ID_PREFIX) { // record structures | ||
if ((token >> 8) == RECORD_TAG_ID) { // record structures | ||
let structure = currentStructures[token & 0xff] | ||
@@ -315,3 +339,3 @@ if (structure) { | ||
exports.setExtractor = (extractStrings) => { | ||
export function setExtractor(extractStrings) { | ||
readFixedString = readString(1) | ||
@@ -642,3 +666,3 @@ readString8 = readString(2) | ||
class Tag { | ||
export class Tag { | ||
constructor(value) { | ||
@@ -671,14 +695,19 @@ this.value = value | ||
// the registration of the record definition extension (tag 6) | ||
// the registration of the record definition extension (tag 105) | ||
const recordDefinition = () => { | ||
readArrayHeader(3) | ||
let id = read() | ||
let structure = read() | ||
let definition = read() | ||
let structure = definition[0] | ||
let id = definition[1] | ||
currentStructures[id & 0xff] = structure | ||
structure.read = createStructureReader(structure) | ||
return structure.read() | ||
let object = {} | ||
for (let i = 2,l = definition.length; i < l; i++) { | ||
let key = structure[i - 2] | ||
object[key] = definition[i] | ||
} | ||
return object | ||
} | ||
recordDefinition.handlesRead = true | ||
currentExtensions[40006] = recordDefinition | ||
currentExtensions[RECORD_TAG_ID] = recordDefinition | ||
@@ -720,3 +749,3 @@ currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object | ||
const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array') | ||
export const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array') | ||
const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 81, 82] | ||
@@ -763,18 +792,26 @@ for (let i = 0; i < typedArrays.length; i++) { | ||
} | ||
exports.clearSource = function() { | ||
export function clearSource() { | ||
src = null | ||
referenceMap = null | ||
currentStructures = null | ||
} | ||
exports.addExtension = function(extension) { | ||
export function addExtension(extension) { | ||
currentExtensions[extension.tag] = extension.decode | ||
} | ||
let mult10 = new Array(147) // this is a table matching binary exponents to the multiplier to determine significant digit rounding | ||
export let mult10 = new Array(147) // this is a table matching binary exponents to the multiplier to determine significant digit rounding | ||
for (let i = 0; i < 256; i++) { | ||
mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103)) | ||
} | ||
exports.mult10 = mult10 | ||
exports.typedArrays = typedArrays | ||
exports.useRecords = false | ||
exports.mapsAsObjects = true | ||
exports.Tag = Tag | ||
export const useRecords = false | ||
export const mapsAsObjects = true | ||
let defaultDecoder = new Decoder({ useRecords: false }) | ||
export const decode = defaultDecoder.decode | ||
export const decodeMultiple = defaultDecoder.decodeMultiple | ||
export const FLOAT32_OPTIONS = { | ||
NEVER: 0, | ||
ALWAYS: 1, | ||
DECIMAL_ROUND: 3, | ||
DECIMAL_FIT: 4 | ||
} |
1487
dist/index.js
@@ -1,2 +0,1485 @@ | ||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.CBOR=t():e.CBOR=t()}(window,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=2)}([function(e,t,r){"use strict";let n=r(1),i=n.Decoder,o=n.mult10;const s=n.typedArrays;let u,l,f;try{u=new TextEncoder}catch(e){}const c="undefined"!=typeof Buffer,a=c?Buffer.allocUnsafeSlow:Uint8Array,d=c?Buffer:Uint8Array;let h,g,y,p=0;const w=Symbol("record-id");function b(e,t,r,n,i){for(;n<i;)t[r++]=e[n++]}function O(e,t,r){h[p++]=204;let n=e.byteLength,i=e.byteOffset||0,o=e.buffer||e;r([t,c?Buffer.from(o,i,n):new Uint8Array(o,i,n)])}function U(e){let t=e.byteLength;t<256?(h[p++]=88,h[p++]=t):t<65536?(h[p++]=89,h[p++]=t>>8,h[p++]=255&t):(h[p++]=90,g.setUint32(p,t),p+=4),e.copy?e.copy(h,p):b(e,h,p,0,t),p+=t}t.Encoder=class extends i{constructor(e){let t,r,n,i,s;super(e),this.offset=0;let c=0,O=d.prototype.utf8Write?function(e,t,r){return h.utf8Write(e,t,r)}:!(!u||!u.encodeInto)&&function(e,t){return u.encodeInto(e,h.subarray(t)).written},U=this,m=64,v=e&&e.sequential;v&&(m=0,this.structures=[]);let x=[],j=0,A=0;if(this.structures&&this.structures.length>m)throw new Error("Too many shared structures");this.encode=function(e){if(h||(h=new a(8192),g=new DataView(h.buffer,0,8192),p=0),y=h.length-10,y-p<2048&&(h=new a(h.length),g=new DataView(h.buffer,0,h.length),y=h.length-10,p=0),t=p,s=U.structuredClone?new Map:null,r=U.structures,r){r.uninitialized&&(U.structures=r=U.getStructures());let e=r.length;if(e>m&&!v&&(e=m),!r.transitions){r.transitions=Object.create(null);for(let t=0;t<e;t++){let e=r[t];if(!e)continue;let n,i=r.transitions;for(let t=0,r=e.length;t<r;t++){let r=e[t];n=i[r],n||(n=i[r]=Object.create(null)),i=n}i[w]=t+64}c=r.length}v||(r.nextId=e+64)}n&&(n=!1),i=r||[];try{if(E(e),U.offset=p,s&&s.idsToInsert){p+=6*s.idsToInsert.length,p>y&&D(p),U.offset=p;let e=function(e,t){let r,n=6*t.length,i=e.length-n;t.sort((e,t)=>e.offset>t.offset?1:-1);for(;r=t.pop();){let t=r.offset,o=r.id;e.copyWithin(t+n,t,i),n-=6;let s=t+n;e[s++]=201,e[s++]=26,e[s++]=o<<24,e[s++]=o<<16&255,e[s++]=o<<8&255,e[s++]=255&o,i=t}return e}(h.subarray(t,p),s.idsToInsert);return s=null,e}return h.subarray(t,p)}finally{if(r){if(A<10&&A++,j>1e4)r.transitions=null,A=0,j=0,x.length>0&&(x=[]);else if(x.length>0&&!v){for(let e=0,t=x.length;e<t;e++)x[e][w]=0;x=[]}if(n&&U.saveStructures){if(U.structures.length>m&&(U.structures=U.structures.slice(0,m)),!1===U.saveStructures(U.structures,c))return U.structures=U.getStructures()||[],U.encode(e);c=U.structures.length}}}};const E=e=>{p>y&&(h=D(p));var r,n=typeof e;if("string"===n){let t,n=e.length;t=n<32?1:n<256?2:n<65536?3:5;let i=3*n;if(p+i>y&&(h=D(p+i)),n<64||!O){let i,o,s,u=p+t;for(i=0;i<n;i++)o=e.charCodeAt(i),o<128?h[u++]=o:o<2048?(h[u++]=o>>6|192,h[u++]=63&o|128):55296==(64512&o)&&56320==(64512&(s=e.charCodeAt(i+1)))?(o=65536+((1023&o)<<10)+(1023&s),i++,h[u++]=o>>18|240,h[u++]=o>>12&63|128,h[u++]=o>>6&63|128,h[u++]=63&o|128):(h[u++]=o>>12|224,h[u++]=o>>6&63|128,h[u++]=63&o|128);r=u-p-t}else r=O(e,p+t,i);r<24?h[p++]=96|r:r<256?(t<2&&h.copyWithin(p+2,p+1,p+1+r),h[p++]=120,h[p++]=r):r<65536?(t<3&&h.copyWithin(p+3,p+2,p+2+r),h[p++]=121,h[p++]=r>>8,h[p++]=255&r):(t<5&&h.copyWithin(p+5,p+3,p+3+r),h[p++]=122,g.setUint32(p,r),p+=4),p+=r}else if("number"===n)if(e>>>0===e)e<24?h[p++]=e:e<256?(h[p++]=24,h[p++]=e):e<65536?(h[p++]=25,h[p++]=e>>8,h[p++]=255&e):(h[p++]=26,g.setUint32(p,e),p+=4);else if(e>>0===e)e>=-24?h[p++]=31-e:e>=-256?(h[p++]=56,h[p++]=~e):e>=-65536?(h[p++]=57,g.setUint16(p,~e),p+=2):(h[p++]=58,g.setUint32(p,~e),p+=4);else{let t;if((t=this.useFloat32)>0&&e<4294967296&&e>=-2147483648){let r;if(h[p++]=250,g.setFloat32(p,e),t<4||(r=e*o[(127&h[p])<<1|h[p+1]>>7])>>0===r)return void(p+=4);p--}h[p++]=251,g.setFloat64(p,e),p+=8}else if("object"===n)if(e){if(s){let r=s.get(e);if(r){if(!r.id){let e=s.idsToInsert||(s.idsToInsert=[]);r.id=e.push(r)}return h[p++]=202,h[p++]=26,g.setUint32(p,r.id),void(p+=4)}s.set(e,{offset:p-t})}let n=e.constructor;if(n===Object)S(e,!0);else if(n===Array){(r=e.length)<24?h[p++]=128|r:r<256?(h[p++]=152,h[p++]=r):r<65536?(h[p++]=153,h[p++]=r>>8,h[p++]=255&r):(h[p++]=154,g.setUint32(p,r),p+=4);for(let t=0;t<r;t++)E(e[t])}else if(n===Map){(r=e.size)<24?h[p++]=160|r:r<256?(h[p++]=184,h[p++]=r):r<65536?(h[p++]=185,h[p++]=r>>8,h[p++]=255&r):(h[p++]=186,g.setUint32(p,r),p+=4);for(let[t,r]of e)E(t),E(r)}else{for(let t=0,r=l.length;t<r;t++){if(e instanceof f[t]){let r=l[t],n=r.tag;return n<24?h[p++]=192|n:n<256?(h[p++]=216,h[p++]=n):n<65536?(h[p++]=217,h[p++]=n>>8,h[p++]=255&n):n>-1&&(h[p++]=186,g.setUint32(p,n),p+=4),void r.encode.call(this,e,E)}}S(e,!1)}}else h[p++]=246;else if("boolean"===n)h[p++]=e?245:244;else if("bigint"===n)h[p++]=251,g.setFloat64(p,e),p+=8;else{if("undefined"!==n)throw new Error("Unknown type "+n);h[p++]=247}},S=!1===this.useRecords?this.variableMapSize?e=>{let t,r=Object.keys(e),n=r.length;n<24?h[p++]=160|n:n<256?(h[p++]=184,h[p++]=n):n<65536?(h[p++]=185,h[p++]=n>>8,h[p++]=255&n):(h[p++]=186,g.setUint32(p,n),p+=4);for(let i=0;i<n;i++)E(t=r[i]),E(e[t])}:(e,r)=>{h[p++]=185;let n=p-t;p+=2;let i=0;for(let t in e)(r||e.hasOwnProperty(t))&&(E(t),E(e[t]),i++);h[n+++t]=i>>8,h[n+t]=255&i}:e=>{let t,o=Object.keys(e),s=i.transitions||(i.transitions=Object.create(null)),u=0;for(let e=0,r=o.length;e<r;e++){let r=o[e];t=s[r],t||(t=s[r]=Object.create(null),u++),s=t}let l=s[w];l?(h[p++]=216,h[p++]=l):(l=i.nextId++,l||(l=64,i.nextId=65),l>=256&&(i.nextId=(l=m+64)+1),s[w]=l,i[l-64]=o,r&&r.length<=m?(h[p++]=216,h[p++]=l,n=!0):(h[p++]=198,u&&(j+=A*u),x.length>=192-m&&(x.shift()[w]=0),x.push(s),E([l].concat(o))));for(let t=0,r=o.length;t<r;t++)E(e[o[t]])},D=e=>{let r=1+(Math.max(e-t<<2,h.length-1)>>12)<<12,n=new a(r);return g=new DataView(n.buffer,0,r),h.copy?h.copy(n,0,t,e):b(h,n,0,t,e),p-=t,t=0,y=n.length-10,h=n}}useBuffer(e){h=e,g=new DataView(h.buffer,h.byteOffset,h.byteLength),p=0}},f=[Date,Set,Error,RegExp,ArrayBuffer,Object.getPrototypeOf(Uint8Array.prototype).constructor],l=[{tag:1,encode(e,t){let r=e.getTime()/1e3;(this.useTimestamp32||0===e.getMilliseconds())&&r>=0&&r<4294967296?(h[p++]=26,g.setUint32(p,r),p+=4):(h[p++]=251,g.setFloat64(p,r),p+=8)}},{tag:11,encode(e,t){t(Array.from(e))}},{tag:8,encode(e,t){t([e.name,e.message])}},{tag:13,encode(e,t){t([e.source,e.flags])}},{encode(e,t){this.structuredClone?O(e,16,t):U(c?Buffer.from(e):new Uint8Array(e))}},{encode(e,t){let r=e.constructor;r!==d&&this.structuredClone?O(e,s.indexOf(r.name),t):U(e)}}],t.addExtension=function(e){if(e.Class){if(!e.encode)throw new Error("Extension has no encode function");f.unshift(e.Class),l.unshift(e)}n.addExtension(e)}},function(e,t,r){"use strict";(function(e){let r,n,i;try{r=new TextDecoder}catch(e){}let o=0;const s=[];let u,l,f,c,a=s,d=0,h={},g=0,y=0,p=[],w={useRecords:!1,mapsAsObjects:!0};class b{constructor(e){e&&(!1===e.useRecords&&void 0===e.mapsAsObjects&&(e.mapsAsObjects=!0),e.getStructures&&!e.structures&&((e.structures=[]).uninitialized=!0)),Object.assign(this,e)}decode(e,t,r){if(n)return C(()=>(n=null,this?this.decode(e,t,r):b.prototype.decode.call(w,e,t,r)));if(i=t>-1?t:e.length,o=0,d=0,y=0,u=null,a=s,n=e,f=e.dataView||(e.dataView=new DataView(e.buffer,e.byteOffset,e.byteLength)),this)if(h=this,this.structures){c=this.structures;try{return O()}finally{(o>=i||!r)&&(c=null,n=null,l&&(l=null))}}else(!c||c.length>0)&&(c=[]);else h=w,(!c||c.length>0)&&(c=[]);try{return O()}finally{n=null,l&&(l=null)}}}function O(){let e=n[o++],t=e>>5;if(e&=31,e>23)switch(e){case 24:e=n[o++];break;case 25:if(7==t)return function(){let e,t=n[o++],r=n[o++],i=(t<<8)+r,s=i>>10&31,u=1023&i;e=0==s?Math.exp(u,-24):31!=s?Math.exp(u+1024,s-25):0==u?1/0:NaN;return 32768&i?-e:e}();e=f.getUint16(o),o+=2;break;case 26:if(7==t){let e=f.getFloat32(o);if(h.useFloat32>2){let t=M[(127&n[o])<<1|n[o+1]>>7];return o+=4,(t*e+(e>0?.5:-.5)>>0)/t}return o+=4,e}e=f.getUint32(o),o+=4;break;case 27:if(7==t){let e=f.getFloat64(o);return o+=8,e}if(h.uint64AsNumber)return 72057594037927940*n[o++]+281474976710656*n[o++]+1099511627776*n[o++]+4294967296*n[o++]+16777216*n[o++]+(n[o++]<<16)+(n[o++]<<8)+n[o++];e=f.getBigUint64(o),o+=8;break;default:throw new Error("Unknown token "+e)}switch(t){case 0:return e;case 1:return~e;case 2:return r=e,h.copyBuffers?Uint8Array.prototype.slice.call(n,o,o+=r):n.subarray(o,o+=r);case 3:if(y>=o)return u.slice(o-g,(o+=e)-g);if(0==y&&i<120&&e<16){let t=D(e);if(null!=t)return t}return v(e);case 4:let t=new Array(e);for(let r=0;r<e;r++)t[r]=O();return t;case 5:if(h.mapsAsObjects){let t={};for(let r=0;r<e;r++)t[O()]=O();return t}{let t=new Map;for(let r=0;r<e;r++)t.set(O(),O());return t}case 6:if(e>=64&&e<256){let t=c[e-64];if(t)return t.read||(t.read=m(t)),t.read();if(h.getStructures){let r=C(()=>(n=null,h.getStructures()));return!0===c?h.structures=c=r:c.splice.apply(c,[0,r.length].concat(r)),t=c[e-64],t?(t.read||(t.read=m(t)),t.read()):e}return e}if(p[e])return p[e](O());throw new Error("Unknown extension "+e);case 7:switch(e){case 20:return!1;case 21:return!0;case 22:return null;case 23:return;default:throw new Error("Unknown token "+e)}default:if(isNaN(e)){let e=new Error("Unexpected end of CBOR data");throw e.incomplete=!0,e}throw new Error("Unknown CBOR token "+e)}var r}t.Decoder=b,t.read=O,t.getPosition=()=>o;const U=/^[a-zA-Z_$][a-zA-Z\d_$]*$/;function m(e){function t(){if(t.count++>2)return this.read=new Function("r","return function(){return {"+e.map(e=>U.test(e)?e+":r()":"["+JSON.stringify(e)+"]:r()").join(",")+"}}")(O),this.read();let r={};for(let t=0,n=e.length;t<n;t++){r[e[t]]=O()}return r}return t.count=0,t}let v=E,x=E,j=E,A=E;function E(e){let t;if(e<16&&(t=D(e)))return t;if(e>64&&r)return r.decode(n.subarray(o,o+=e));const i=o+e,s=[];for(t="";o<i;){const e=n[o++];if(0==(128&e))s.push(e);else if(192==(224&e)){const t=63&n[o++];s.push((31&e)<<6|t)}else if(224==(240&e)){const t=63&n[o++],r=63&n[o++];s.push((31&e)<<12|t<<6|r)}else if(240==(248&e)){let t=(7&e)<<18|(63&n[o++])<<12|(63&n[o++])<<6|63&n[o++];t>65535&&(t-=65536,s.push(t>>>10&1023|55296),t=56320|1023&t),s.push(t)}else s.push(e);s.length>=4096&&(t+=S.apply(String,s),s.length=0)}return s.length>0&&(t+=S.apply(String,s)),t}t.setExtractor=e=>{function t(t){return function(t){let r=a[d++];null==r&&(a=e(o,i,t,n),d=0,r=a[d++]);let s=r.length;return s<=t?(o+=t,r):(u=r,g=o,y=o+s,o+=t,r.slice(0,t))}}v=t(1),x=t(2),j=t(3),A=t(5)};let S=String.fromCharCode;function D(e){if(e<4){if(e<2){if(0===e)return"";{let e=n[o++];return(128&e)>1?void(o-=1):S(e)}}{let t=n[o++],r=n[o++];if((128&t)>0||(128&r)>0)return void(o-=2);if(e<3)return S(t,r);let i=n[o++];return(128&i)>0?void(o-=3):S(t,r,i)}}{let t=n[o++],r=n[o++],i=n[o++],s=n[o++];if((128&t)>0||(128&r)>0||(128&i)>0||(128&s)>0)return void(o-=4);if(e<6){if(4===e)return S(t,r,i,s);{let e=n[o++];return(128&e)>0?void(o-=5):S(t,r,i,s,e)}}if(e<8){let u=n[o++],l=n[o++];if((128&u)>0||(128&l)>0)return void(o-=6);if(e<7)return S(t,r,i,s,u,l);let f=n[o++];return(128&f)>0?void(o-=7):S(t,r,i,s,u,l,f)}{let u=n[o++],l=n[o++],f=n[o++],c=n[o++];if((128&u)>0||(128&l)>0||(128&f)>0||(128&c)>0)return void(o-=8);if(e<10){if(8===e)return S(t,r,i,s,u,l,f,c);{let e=n[o++];return(128&e)>0?void(o-=9):S(t,r,i,s,u,l,f,c,e)}}if(e<12){let a=n[o++],d=n[o++];if((128&a)>0||(128&d)>0)return void(o-=10);if(e<11)return S(t,r,i,s,u,l,f,c,a,d);let h=n[o++];return(128&h)>0?void(o-=11):S(t,r,i,s,u,l,f,c,a,d,h)}{let a=n[o++],d=n[o++],h=n[o++],g=n[o++];if((128&a)>0||(128&d)>0||(128&h)>0||(128&g)>0)return void(o-=12);if(e<14){if(12===e)return S(t,r,i,s,u,l,f,c,a,d,h,g);{let e=n[o++];return(128&e)>0?void(o-=13):S(t,r,i,s,u,l,f,c,a,d,h,g,e)}}{let y=n[o++],p=n[o++];if((128&y)>0||(128&p)>0)return void(o-=14);if(e<15)return S(t,r,i,s,u,l,f,c,a,d,h,g,y,p);let w=n[o++];return(128&w)>0?void(o-=15):S(t,r,i,s,u,l,f,c,a,d,h,g,y,p,w)}}}}}let I="object"==typeof window?window:e;p[0]=e=>new Date(e),p[1]=e=>new Date(1e3*e),p[2]=e=>new DataView(e.buffer,e.byteOffset,e.byteLength).getBigUint64(0),p[3]=e=>BigInt(-1)-new DataView(e.buffer,e.byteOffset,e.byteLength).getBigUint64(0),p[6]=e=>{let t=e[0];return e=e.slice(1),c[t-64]=e,e.read=m(e),e.read()},p[8]=e=>(I[e[0]]||Error)(e[1]),p[9]=e=>{let t;l||(l=new Map),t=n[o]>>5==4?[]:{};let r={target:t};l.set(e,r);let i=O();return r.used?Object.assign(t,i):(r.target=i,i)},p[10]=e=>{let t=l.get(e);return t.used=!0,t.target},p[11]=e=>new Set(e);const B=["Int8","Uint8\t","Uint8Clamped","Int16","Uint16","Int32","Uint32","Float32","Float64","BigInt64","BigUint64"].map(e=>e+"Array");function C(e){let t=i,r=o,s=d,p=g,w=y,b=u,O=a,U=l,m=new Uint8Array(n.slice(0,i)),v=c,x=h,j=e();return i=t,o=r,d=s,g=p,y=w,u=b,a=O,l=U,n=m,c=v,h=x,f=new DataView(n.buffer,n.byteOffset,n.byteLength),j}p[12]=e=>{let[t,r]=e,n=B[t];if(!n)throw new Error("Could not find typed array for code "+t);return new I[n](Uint8Array.prototype.slice.call(r,0).buffer)},p[13]=e=>new RegExp(e[0],e[1]),t.clearSource=function(){n=null},t.addExtension=function(e){p[e.tag]=e.decode};let M=new Array(147);for(let e=0;e<256;e++)M[e]=+("1e"+Math.floor(45.15-.30103*e));t.mult10=M,t.typedArrays=B}).call(this,r(3))},function(e,t,r){t.Encoder=r(0).Encoder,t.Decoder=r(1).Decoder,t.addExtension=r(0).addExtension;let n=new t.Encoder({useRecords:!1});t.decode=n.decode,t.encode=n.encode,Object.assign(t,{ALWAYS:1,DECIMAL_ROUND:3,DECIMAL_FIT:4})},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r}])})); | ||
//# sourceMappingURL=index.js.map | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = global || self, factory(global.msgpackr = {})); | ||
}(this, (function (exports) { 'use strict'; | ||
let decoder; | ||
try { | ||
decoder = new TextDecoder(); | ||
} catch(error) {} | ||
let src; | ||
let srcEnd; | ||
let position = 0; | ||
const RECORD_TAG_ID = 0x69; | ||
const STOP_CODE = {}; | ||
let currentDecoder = {}; | ||
let currentStructures; | ||
let srcString; | ||
let srcStringStart = 0; | ||
let srcStringEnd = 0; | ||
let referenceMap; | ||
let currentExtensions = []; | ||
let dataView; | ||
let defaultOptions = { | ||
useRecords: false, | ||
mapsAsObjects: true | ||
}; | ||
class Decoder { | ||
constructor(options) { | ||
if (options) { | ||
if (options.useRecords === false && options.mapsAsObjects === undefined) | ||
options.mapsAsObjects = true; | ||
if (options.getStructures && !options.structures) | ||
(options.structures = []).uninitialized = true; // this is what we use to denote an uninitialized structures | ||
} | ||
Object.assign(this, options); | ||
} | ||
decode(source, end, continueReading) { | ||
if (src) { | ||
// re-entrant execution, save the state and restore it after we do this decode | ||
return saveState(() => { | ||
clearSource(); | ||
return this ? this.decode(source, end, continueReading) : Decoder.prototype.decode.call(defaultOptions, source, end, continueReading) | ||
}) | ||
} | ||
srcEnd = end > -1 ? end : source.length; | ||
position = 0; | ||
srcStringEnd = 0; | ||
srcString = null; | ||
src = source; | ||
// this provides cached access to the data view for a buffer if it is getting reused, which is a recommend | ||
// technique for getting data from a database where it can be copied into an existing buffer instead of creating | ||
// new ones | ||
dataView = source.dataView || (source.dataView = new DataView(source.buffer, source.byteOffset, source.byteLength)); | ||
if (this) { | ||
currentDecoder = this; | ||
if (this.structures) { | ||
currentStructures = this.structures; | ||
try { | ||
return read() | ||
} finally { | ||
if (position >= srcEnd || !continueReading) { | ||
// finished reading this source, cleanup references | ||
currentStructures = null; | ||
src = null; | ||
if (referenceMap) | ||
referenceMap = null; | ||
} | ||
} | ||
} else if (!currentStructures || currentStructures.length > 0) { | ||
currentStructures = []; | ||
} | ||
} else { | ||
currentDecoder = defaultOptions; | ||
if (!currentStructures || currentStructures.length > 0) | ||
currentStructures = []; | ||
} | ||
try { | ||
return read() | ||
} finally { | ||
if (position >= srcEnd || !continueReading) { | ||
src = null; | ||
if (referenceMap) | ||
referenceMap = null; | ||
} | ||
} | ||
} | ||
decodeMultiple(source, forEach) { | ||
try { | ||
let decoder = this; | ||
let size = source.length; | ||
let value = this ? this.decode(source, size, true) : defaultDecoder.decode(source, size, true); | ||
let values; | ||
if (forEach) { | ||
forEach(value); | ||
while(position < size) { | ||
if (forEach(read()) === false) { | ||
return | ||
} | ||
} | ||
} | ||
else { | ||
values = [ value ]; | ||
while(position < size) { | ||
values.push(read()); | ||
} | ||
return values | ||
} | ||
} finally { | ||
clearSource(); | ||
} | ||
} | ||
} | ||
function read() { | ||
let token = src[position++]; | ||
let majorType = token >> 5; | ||
token = token & 0x1f; | ||
if (token > 0x17) { | ||
switch (token) { | ||
case 0x18: | ||
token = src[position++]; | ||
break | ||
case 0x19: | ||
if (majorType == 7) { | ||
return getFloat16() | ||
} | ||
token = dataView.getUint16(position); | ||
position += 2; | ||
break | ||
case 0x1a: | ||
if (majorType == 7) { | ||
let value = dataView.getFloat32(position); | ||
if (currentDecoder.useFloat32 > 2) { | ||
// this does rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved | ||
let multiplier = mult10[((src[position] & 0x7f) << 1) | (src[position + 1] >> 7)]; | ||
position += 4; | ||
return ((multiplier * value + (value > 0 ? 0.5 : -0.5)) >> 0) / multiplier | ||
} | ||
position += 4; | ||
return value | ||
} | ||
token = dataView.getUint32(position); | ||
position += 4; | ||
break | ||
case 0x1b: | ||
if (majorType == 7) { | ||
let value = dataView.getFloat64(position); | ||
position += 8; | ||
return value | ||
} | ||
if (currentDecoder.uint64AsNumber) | ||
return src[position++] * 0x100000000000000 + src[position++] * 0x1000000000000 + src[position++] * 0x10000000000 + src[position++] * 0x100000000 + | ||
src[position++] * 0x1000000 + (src[position++] << 16) + (src[position++] << 8) + src[position++] | ||
token = dataView.getBigUint64(position); | ||
position += 8; | ||
break | ||
case 0x1f: | ||
// indefinite length | ||
switch(majorType) { | ||
case 2: // byte string | ||
case 3: // text string | ||
case 4: // array | ||
let array = []; | ||
let value, i = 0; | ||
while ((value = read()) != STOP_CODE) { | ||
array[i++] = value; | ||
} | ||
return majorType == 4 ? array : majorType == 3 ? array.join('') : Buffer.concat(array) | ||
case 5: // map | ||
let key; | ||
if (currentDecoder.mapsAsObjects) { | ||
let object = {}; | ||
while ((key = readKey()) != STOP_CODE) | ||
object[key] = read(); | ||
return object | ||
} else { | ||
let map = new Map(); | ||
while ((key = read()) != STOP_CODE) | ||
map.set(key, read()); | ||
return map | ||
} | ||
case 7: | ||
return STOP_CODE | ||
default: | ||
throw new Error('Invalid major type for indefinite length ' + majorType) | ||
} | ||
default: | ||
throw new Error('Unknown token ' + token) | ||
} | ||
} | ||
switch (majorType) { | ||
case 0: // positive int | ||
return token | ||
case 1: // negative int | ||
return ~token | ||
case 2: // buffer | ||
return readBin(token) | ||
case 3: // string | ||
if (srcStringEnd >= position) { | ||
return srcString.slice(position - srcStringStart, (position += token) - srcStringStart) | ||
} | ||
if (srcStringEnd == 0 && srcEnd < 140 && token < 32) { | ||
// for small blocks, avoiding the overhead of the extract call is helpful | ||
let string = token < 16 ? shortStringInJS(token) : longStringInJS(token); | ||
if (string != null) | ||
return string | ||
} | ||
return readFixedString(token) | ||
case 4: // array | ||
let array = new Array(token); | ||
for (let i = 0; i < token; i++) { | ||
array[i] = read(); | ||
} | ||
return array | ||
case 5: // map | ||
if (currentDecoder.mapsAsObjects) { | ||
let object = {}; | ||
for (let i = 0; i < token; i++) { | ||
object[readKey()] = read(); | ||
} | ||
return object | ||
} else { | ||
let map = new Map(); | ||
for (let i = 0; i < token; i++) { | ||
map.set(read(), read()); | ||
} | ||
return map | ||
} | ||
case 6: // extension | ||
if ((token >> 8) == RECORD_TAG_ID) { // record structures | ||
let structure = currentStructures[token & 0xff]; | ||
if (structure) { | ||
if (!structure.read) | ||
structure.read = createStructureReader(structure); | ||
return structure.read() | ||
} else if (currentDecoder.getStructures) { | ||
let updatedStructures = saveState(() => { | ||
// save the state in case getStructures modifies our buffer | ||
src = null; | ||
return currentDecoder.getStructures() | ||
}); | ||
if (currentStructures === true) | ||
currentDecoder.structures = currentStructures = updatedStructures; | ||
else | ||
currentStructures.splice.apply(currentStructures, [0, updatedStructures.length].concat(updatedStructures)); | ||
structure = currentStructures[token & 0xff]; | ||
if (structure) { | ||
if (!structure.read) | ||
structure.read = createStructureReader(structure); | ||
return structure.read() | ||
} else | ||
return token | ||
} else | ||
return token | ||
} else { | ||
let extension = currentExtensions[token]; | ||
if (extension) { | ||
if (extension.handlesRead) | ||
return extension(read) | ||
else | ||
return extension(read()) | ||
} | ||
else | ||
return new Tag(read()) | ||
} | ||
case 7: // fixed value | ||
switch (token) { | ||
case 0x14: return false | ||
case 0x15: return true | ||
case 0x16: return null | ||
case 0x17: return; // undefined | ||
case 0x1f: | ||
default: | ||
throw new Error('Unknown token ' + token) | ||
} | ||
default: // negative int | ||
if (isNaN(token)) { | ||
let error = new Error('Unexpected end of CBOR data'); | ||
error.incomplete = true; | ||
throw error | ||
} | ||
throw new Error('Unknown CBOR token ' + token) | ||
} | ||
} | ||
const validName = /^[a-zA-Z_$][a-zA-Z\d_$]*$/; | ||
function createStructureReader(structure) { | ||
let l = structure.length; | ||
function readObject() { | ||
// This initial function is quick to instantiate, but runs slower. After several iterations pay the cost to build the faster function | ||
if (readObject.count++ > 2) { | ||
this.read = (new Function('a', 'r', 'return function(){a();return {' + structure.map(key => validName.test(key) ? key + ':r()' : ('[' + JSON.stringify(key) + ']:r()')).join(',') + '}}'))(readArrayHeader, read); | ||
return this.read() | ||
} | ||
readArrayHeader(); | ||
let object = {}; | ||
for (let i = 0; i < l; i++) { | ||
let key = structure[i]; | ||
object[key] = read(); | ||
} | ||
return object | ||
} | ||
readObject.count = 0; | ||
return readObject | ||
} | ||
function readArrayHeader(expectedLength) { | ||
// consume the array header, TODO: check expected length | ||
let token = src[position++]; | ||
//let majorType = token >> 5 | ||
token = token & 0x1f; | ||
if (token > 0x17) { | ||
switch (token) { | ||
case 0x18: position++; | ||
break | ||
case 0x19: position += 2; | ||
break | ||
case 0x1a: position += 4; | ||
} | ||
} | ||
} | ||
let readFixedString = readStringJS; | ||
function readStringJS(length) { | ||
let result; | ||
if (length < 16) { | ||
if (result = shortStringInJS(length)) | ||
return result | ||
} | ||
if (length > 64 && decoder) | ||
return decoder.decode(src.subarray(position, position += length)) | ||
const end = position + length; | ||
const units = []; | ||
result = ''; | ||
while (position < end) { | ||
const byte1 = src[position++]; | ||
if ((byte1 & 0x80) === 0) { | ||
// 1 byte | ||
units.push(byte1); | ||
} else if ((byte1 & 0xe0) === 0xc0) { | ||
// 2 bytes | ||
const byte2 = src[position++] & 0x3f; | ||
units.push(((byte1 & 0x1f) << 6) | byte2); | ||
} else if ((byte1 & 0xf0) === 0xe0) { | ||
// 3 bytes | ||
const byte2 = src[position++] & 0x3f; | ||
const byte3 = src[position++] & 0x3f; | ||
units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3); | ||
} else if ((byte1 & 0xf8) === 0xf0) { | ||
// 4 bytes | ||
const byte2 = src[position++] & 0x3f; | ||
const byte3 = src[position++] & 0x3f; | ||
const byte4 = src[position++] & 0x3f; | ||
let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4; | ||
if (unit > 0xffff) { | ||
unit -= 0x10000; | ||
units.push(((unit >>> 10) & 0x3ff) | 0xd800); | ||
unit = 0xdc00 | (unit & 0x3ff); | ||
} | ||
units.push(unit); | ||
} else { | ||
units.push(byte1); | ||
} | ||
if (units.length >= 0x1000) { | ||
result += fromCharCode.apply(String, units); | ||
units.length = 0; | ||
} | ||
} | ||
if (units.length > 0) { | ||
result += fromCharCode.apply(String, units); | ||
} | ||
return result | ||
} | ||
let fromCharCode = String.fromCharCode; | ||
function longStringInJS(length) { | ||
let start = position; | ||
let bytes = new Array(length); | ||
for (let i = 0; i < length; i++) { | ||
const byte = src[position++]; | ||
if ((byte & 0x80) > 0) { | ||
position = start; | ||
return | ||
} | ||
bytes[i] = byte; | ||
} | ||
return fromCharCode.apply(String, bytes) | ||
} | ||
function shortStringInJS(length) { | ||
if (length < 4) { | ||
if (length < 2) { | ||
if (length === 0) | ||
return '' | ||
else { | ||
let a = src[position++]; | ||
if ((a & 0x80) > 1) { | ||
position -= 1; | ||
return | ||
} | ||
return fromCharCode(a) | ||
} | ||
} else { | ||
let a = src[position++]; | ||
let b = src[position++]; | ||
if ((a & 0x80) > 0 || (b & 0x80) > 0) { | ||
position -= 2; | ||
return | ||
} | ||
if (length < 3) | ||
return fromCharCode(a, b) | ||
let c = src[position++]; | ||
if ((c & 0x80) > 0) { | ||
position -= 3; | ||
return | ||
} | ||
return fromCharCode(a, b, c) | ||
} | ||
} else { | ||
let a = src[position++]; | ||
let b = src[position++]; | ||
let c = src[position++]; | ||
let d = src[position++]; | ||
if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) { | ||
position -= 4; | ||
return | ||
} | ||
if (length < 6) { | ||
if (length === 4) | ||
return fromCharCode(a, b, c, d) | ||
else { | ||
let e = src[position++]; | ||
if ((e & 0x80) > 0) { | ||
position -= 5; | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e) | ||
} | ||
} else if (length < 8) { | ||
let e = src[position++]; | ||
let f = src[position++]; | ||
if ((e & 0x80) > 0 || (f & 0x80) > 0) { | ||
position -= 6; | ||
return | ||
} | ||
if (length < 7) | ||
return fromCharCode(a, b, c, d, e, f) | ||
let g = src[position++]; | ||
if ((g & 0x80) > 0) { | ||
position -= 7; | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g) | ||
} else { | ||
let e = src[position++]; | ||
let f = src[position++]; | ||
let g = src[position++]; | ||
let h = src[position++]; | ||
if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) { | ||
position -= 8; | ||
return | ||
} | ||
if (length < 10) { | ||
if (length === 8) | ||
return fromCharCode(a, b, c, d, e, f, g, h) | ||
else { | ||
let i = src[position++]; | ||
if ((i & 0x80) > 0) { | ||
position -= 9; | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i) | ||
} | ||
} else if (length < 12) { | ||
let i = src[position++]; | ||
let j = src[position++]; | ||
if ((i & 0x80) > 0 || (j & 0x80) > 0) { | ||
position -= 10; | ||
return | ||
} | ||
if (length < 11) | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j) | ||
let k = src[position++]; | ||
if ((k & 0x80) > 0) { | ||
position -= 11; | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k) | ||
} else { | ||
let i = src[position++]; | ||
let j = src[position++]; | ||
let k = src[position++]; | ||
let l = src[position++]; | ||
if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) { | ||
position -= 12; | ||
return | ||
} | ||
if (length < 14) { | ||
if (length === 12) | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l) | ||
else { | ||
let m = src[position++]; | ||
if ((m & 0x80) > 0) { | ||
position -= 13; | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m) | ||
} | ||
} else { | ||
let m = src[position++]; | ||
let n = src[position++]; | ||
if ((m & 0x80) > 0 || (n & 0x80) > 0) { | ||
position -= 14; | ||
return | ||
} | ||
if (length < 15) | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n) | ||
let o = src[position++]; | ||
if ((o & 0x80) > 0) { | ||
position -= 15; | ||
return | ||
} | ||
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
function readBin(length) { | ||
return currentDecoder.copyBuffers ? | ||
// specifically use the copying slice (not the node one) | ||
Uint8Array.prototype.slice.call(src, position, position += length) : | ||
src.subarray(position, position += length) | ||
} | ||
function getFloat16() { | ||
let byte0 = src[position++]; | ||
let byte1 = src[position++]; | ||
let half = (byte0 << 8) + byte1; | ||
let exp = (half >> 10) & 0x1f; | ||
let mant = half & 0x3ff; | ||
let val; | ||
if (exp == 0) val = Math.exp(mant, -24); | ||
else if (exp != 31) val = Math.exp(mant + 1024, exp - 25); | ||
else val = mant == 0 ? Infinity : NaN; | ||
return half & 0x8000 ? -val : val | ||
} | ||
let keyCache = new Array(4096); | ||
function readKey() { | ||
let length = src[position++]; | ||
if (length >= 0x60 && length < 0x78) { | ||
// fixstr, potentially use key cache | ||
length = length - 0x60; | ||
if (srcStringEnd >= position) // if it has been extracted, must use it (and faster anyway) | ||
return srcString.slice(position - srcStringStart, (position += length) - srcStringStart) | ||
else if (!(srcStringEnd == 0 && srcEnd < 180)) | ||
return readFixedString(length) | ||
} else { // not cacheable, go back and do a standard read | ||
position--; | ||
return read() | ||
} | ||
let key = ((length << 5) ^ (length > 1 ? dataView.getUint16(position) : length > 0 ? src[position] : 0)) & 0xfff; | ||
let entry = keyCache[key]; | ||
let checkPosition = position; | ||
let end = position + length - 3; | ||
let chunk; | ||
let i = 0; | ||
if (entry && entry.bytes == length) { | ||
while (checkPosition < end) { | ||
chunk = dataView.getUint32(checkPosition); | ||
if (chunk != entry[i++]) { | ||
checkPosition = 0x70000000; | ||
break | ||
} | ||
checkPosition += 4; | ||
} | ||
end += 3; | ||
while (checkPosition < end) { | ||
chunk = src[checkPosition++]; | ||
if (chunk != entry[i++]) { | ||
checkPosition = 0x70000000; | ||
break | ||
} | ||
} | ||
if (checkPosition === end) { | ||
position = checkPosition; | ||
return entry.string | ||
} | ||
end -= 3; | ||
checkPosition = position; | ||
} | ||
entry = []; | ||
keyCache[key] = entry; | ||
entry.bytes = length; | ||
while (checkPosition < end) { | ||
chunk = dataView.getUint32(checkPosition); | ||
entry.push(chunk); | ||
checkPosition += 4; | ||
} | ||
end += 3; | ||
while (checkPosition < end) { | ||
chunk = src[checkPosition++]; | ||
entry.push(chunk); | ||
} | ||
// for small blocks, avoiding the overhead of the extract call is helpful | ||
let string = length < 16 ? shortStringInJS(length) : longStringInJS(length); | ||
if (string != null) | ||
return entry.string = string | ||
return entry.string = readFixedString(length) | ||
} | ||
class Tag { | ||
constructor(value) { | ||
this.value = value; | ||
} | ||
} | ||
let glbl = typeof window == 'object' ? window : global; | ||
currentExtensions[0] = (dateString) => { | ||
// string date extension | ||
return new Date(dateString) | ||
}; | ||
currentExtensions[1] = (epochSec) => { | ||
// numeric date extension | ||
return new Date(epochSec * 1000) | ||
}; | ||
currentExtensions[2] = (buffer) => { | ||
// bigint extension | ||
return new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0) | ||
}; | ||
currentExtensions[3] = (buffer) => { | ||
// negative bigint extension | ||
return BigInt(-1) - (new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getBigUint64(0)) | ||
}; | ||
// the registration of the record definition extension (tag 105) | ||
const recordDefinition = () => { | ||
let definition = read(); | ||
let structure = definition[0]; | ||
let id = definition[1]; | ||
currentStructures[id & 0xff] = structure; | ||
structure.read = createStructureReader(structure); | ||
let object = {}; | ||
for (let i = 2,l = definition.length; i < l; i++) { | ||
let key = structure[i - 2]; | ||
object[key] = definition[i]; | ||
} | ||
return object | ||
}; | ||
recordDefinition.handlesRead = true; | ||
currentExtensions[RECORD_TAG_ID] = recordDefinition; | ||
currentExtensions[27] = (data) => { // http://cbor.schmorp.de/generic-object | ||
return (glbl[data[0]] || Error)(data[1], data[2]) | ||
}; | ||
currentExtensions[40009] = (id) => { | ||
// id extension (for structured clones) | ||
if (!referenceMap) | ||
referenceMap = new Map(); | ||
let token = src[position]; | ||
let target; | ||
// TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read | ||
// ahead past references to record structure definitions | ||
if ((token >> 5) == 4) | ||
target = []; | ||
else | ||
target = {}; | ||
let refEntry = { target }; // a placeholder object | ||
referenceMap.set(id, refEntry); | ||
let targetProperties = read(); // read the next value as the target object to id | ||
if (refEntry.used) // there is a cycle, so we have to assign properties to original target | ||
return Object.assign(target, targetProperties) | ||
refEntry.target = targetProperties; // the placeholder wasn't used, replace with the deserialized one | ||
return targetProperties // no cycle, can just use the returned read object | ||
}; | ||
currentExtensions[40010] = (id) => { | ||
// pointer extension (for structured clones) | ||
let refEntry = referenceMap.get(id); | ||
refEntry.used = true; | ||
return refEntry.target | ||
}; | ||
currentExtensions[258] = (array) => new Set(array); // https://github.com/input-output-hk/cbor-sets-spec/blob/master/CBOR_SETS.md | ||
const typedArrays = ['Uint8', 'Uint8Clamped', 'Uint16', 'Uint32', 'BigUint64','Int8', 'Int16', 'Int32', 'BigInt64', 'Float32', 'Float64'].map(type => type + 'Array'); | ||
const typedArrayTags = [64, 68, 69, 70, 71, 72, 77, 78, 79, 81, 82]; | ||
for (let i = 0; i < typedArrays.length; i++) { | ||
registerTypedArray(typedArrays[i], typedArrayTags[i]); | ||
} | ||
function registerTypedArray(typedArrayName, tag) { | ||
currentExtensions[tag] = (buffer) => { | ||
if (!typedArrayName) | ||
throw new Error('Could not find typed array for code ' + typeCode) | ||
// we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned | ||
return new glbl[typedArrayName](Uint8Array.prototype.slice.call(buffer, 0).buffer) | ||
}; | ||
} | ||
function saveState(callback) { | ||
let savedSrcEnd = srcEnd; | ||
let savedPosition = position; | ||
let savedSrcStringStart = srcStringStart; | ||
let savedSrcStringEnd = srcStringEnd; | ||
let savedSrcString = srcString; | ||
let savedReferenceMap = referenceMap; | ||
// TODO: We may need to revisit this if we do more external calls to user code (since it could be slow) | ||
let savedSrc = new Uint8Array(src.slice(0, srcEnd)); // we copy the data in case it changes while external data is processed | ||
let savedStructures = currentStructures; | ||
let savedDecoder = currentDecoder; | ||
let value = callback(); | ||
srcEnd = savedSrcEnd; | ||
position = savedPosition; | ||
srcStringStart = savedSrcStringStart; | ||
srcStringEnd = savedSrcStringEnd; | ||
srcString = savedSrcString; | ||
referenceMap = savedReferenceMap; | ||
src = savedSrc; | ||
currentStructures = savedStructures; | ||
currentDecoder = savedDecoder; | ||
dataView = new DataView(src.buffer, src.byteOffset, src.byteLength); | ||
return value | ||
} | ||
function clearSource() { | ||
src = null; | ||
referenceMap = null; | ||
currentStructures = null; | ||
} | ||
function addExtension(extension) { | ||
currentExtensions[extension.tag] = extension.decode; | ||
} | ||
let mult10 = new Array(147); // this is a table matching binary exponents to the multiplier to determine significant digit rounding | ||
for (let i = 0; i < 256; i++) { | ||
mult10[i] = +('1e' + Math.floor(45.15 - i * 0.30103)); | ||
} | ||
let defaultDecoder = new Decoder({ useRecords: false }); | ||
const decode = defaultDecoder.decode; | ||
const decodeMultiple = defaultDecoder.decodeMultiple; | ||
const FLOAT32_OPTIONS = { | ||
NEVER: 0, | ||
ALWAYS: 1, | ||
DECIMAL_ROUND: 3, | ||
DECIMAL_FIT: 4 | ||
}; | ||
let textEncoder; | ||
try { | ||
textEncoder = new TextEncoder(); | ||
} catch (error) {} | ||
let extensions, extensionClasses; | ||
const hasNodeBuffer = typeof Buffer !== 'undefined'; | ||
const ByteArrayAllocate = hasNodeBuffer ? Buffer.allocUnsafeSlow : Uint8Array; | ||
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array; | ||
const RECORD_STARTING_ID_PREFIX = 0x69; // tag 105/0x69 | ||
const MAX_STRUCTURES = 0x100; | ||
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000; | ||
let target; | ||
let targetView; | ||
let position$1 = 0; | ||
let safeEnd; | ||
const RECORD_SYMBOL = Symbol('record-id'); | ||
class Encoder extends Decoder { | ||
constructor(options) { | ||
super(options); | ||
this.offset = 0; | ||
let start; | ||
let sharedStructures; | ||
let hasSharedUpdate; | ||
let structures; | ||
let referenceMap; | ||
let lastSharedStructuresLength = 0; | ||
let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position, maxBytes) { | ||
return target.utf8Write(string, position, maxBytes) | ||
} : (textEncoder && textEncoder.encodeInto) ? | ||
function(string, position) { | ||
return textEncoder.encodeInto(string, target.subarray(position)).written | ||
} : false; | ||
let encoder = this; | ||
let maxSharedStructures = 64; | ||
let isSequential = options && options.sequential; | ||
if (isSequential) { | ||
maxSharedStructures = 0; | ||
this.structures = []; | ||
} | ||
let recordIdsToRemove = []; | ||
let transitionsCount = 0; | ||
let serializationsSinceTransitionRebuild = 0; | ||
if (this.structures && this.structures.length > maxSharedStructures) { | ||
throw new Error('Too many shared structures') | ||
} | ||
this.encode = function(value) { | ||
if (!target) { | ||
target = new ByteArrayAllocate(8192); | ||
targetView = new DataView(target.buffer, 0, 8192); | ||
position$1 = 0; | ||
} | ||
safeEnd = target.length - 10; | ||
if (safeEnd - position$1 < 0x800) { | ||
// don't start too close to the end, | ||
target = new ByteArrayAllocate(target.length); | ||
targetView = new DataView(target.buffer, 0, target.length); | ||
safeEnd = target.length - 10; | ||
position$1 = 0; | ||
} | ||
start = position$1; | ||
referenceMap = encoder.structuredClone ? new Map() : null; | ||
sharedStructures = encoder.structures; | ||
if (sharedStructures) { | ||
if (sharedStructures.uninitialized) | ||
encoder.structures = sharedStructures = encoder.getStructures(); | ||
let sharedStructuresLength = sharedStructures.length; | ||
if (sharedStructuresLength > maxSharedStructures && !isSequential) | ||
sharedStructuresLength = maxSharedStructures; | ||
if (!sharedStructures.transitions) { | ||
// rebuild our structure transitions | ||
sharedStructures.transitions = Object.create(null); | ||
for (let i = 0; i < sharedStructuresLength; i++) { | ||
let keys = sharedStructures[i]; | ||
if (!keys) | ||
continue | ||
let nextTransition, transition = sharedStructures.transitions; | ||
for (let i =0, l = keys.length; i < l; i++) { | ||
let key = keys[i]; | ||
nextTransition = transition[key]; | ||
if (!nextTransition) { | ||
nextTransition = transition[key] = Object.create(null); | ||
} | ||
transition = nextTransition; | ||
} | ||
transition[RECORD_SYMBOL] = i; | ||
} | ||
lastSharedStructuresLength = sharedStructures.length; | ||
} | ||
if (!isSequential) | ||
sharedStructures.nextId = sharedStructuresLength; | ||
} | ||
if (hasSharedUpdate) | ||
hasSharedUpdate = false; | ||
structures = sharedStructures || []; | ||
try { | ||
encode(value); | ||
encoder.offset = position$1; // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially | ||
if (referenceMap && referenceMap.idsToInsert) { | ||
position$1 += referenceMap.idsToInsert.length * 8; | ||
if (position$1 > safeEnd) | ||
makeRoom(position$1); | ||
encoder.offset = position$1; | ||
let serialized = insertIds(target.subarray(start, position$1), referenceMap.idsToInsert); | ||
referenceMap = null; | ||
return serialized | ||
} | ||
return target.subarray(start, position$1) // position can change if we call encode again in saveStructures, so we get the buffer now | ||
} finally { | ||
if (sharedStructures) { | ||
if (serializationsSinceTransitionRebuild < 10) | ||
serializationsSinceTransitionRebuild++; | ||
if (transitionsCount > 10000) { | ||
// force a rebuild occasionally after a lot of transitions so it can get cleaned up | ||
sharedStructures.transitions = null; | ||
serializationsSinceTransitionRebuild = 0; | ||
transitionsCount = 0; | ||
if (recordIdsToRemove.length > 0) | ||
recordIdsToRemove = []; | ||
} else if (recordIdsToRemove.length > 0 && !isSequential) { | ||
for (let i = 0, l = recordIdsToRemove.length; i < l; i++) { | ||
recordIdsToRemove[i][RECORD_SYMBOL] = 0; | ||
} | ||
recordIdsToRemove = []; | ||
} | ||
if (hasSharedUpdate && encoder.saveStructures) { | ||
if (encoder.structures.length > maxSharedStructures) { | ||
encoder.structures = encoder.structures.slice(0, maxSharedStructures); | ||
} | ||
if (encoder.saveStructures(encoder.structures, lastSharedStructuresLength) === false) { | ||
// get updated structures and try again if the update failed | ||
encoder.structures = encoder.getStructures() || []; | ||
return encoder.encode(value) | ||
} | ||
lastSharedStructuresLength = encoder.structures.length; | ||
} | ||
} | ||
} | ||
}; | ||
const encode = (value) => { | ||
if (position$1 > safeEnd) | ||
target = makeRoom(position$1); | ||
var type = typeof value; | ||
var length; | ||
if (type === 'string') { | ||
let strLength = value.length; | ||
let headerSize; | ||
// first we estimate the header size, so we can write to the correct location | ||
if (strLength < 0x20) { | ||
headerSize = 1; | ||
} else if (strLength < 0x100) { | ||
headerSize = 2; | ||
} else if (strLength < 0x10000) { | ||
headerSize = 3; | ||
} else { | ||
headerSize = 5; | ||
} | ||
let maxBytes = strLength * 3; | ||
if (position$1 + maxBytes > safeEnd) | ||
target = makeRoom(position$1 + maxBytes); | ||
if (strLength < 0x40 || !encodeUtf8) { | ||
let i, c1, c2, strPosition = position$1 + headerSize; | ||
for (i = 0; i < strLength; i++) { | ||
c1 = value.charCodeAt(i); | ||
if (c1 < 0x80) { | ||
target[strPosition++] = c1; | ||
} else if (c1 < 0x800) { | ||
target[strPosition++] = c1 >> 6 | 0xc0; | ||
target[strPosition++] = c1 & 0x3f | 0x80; | ||
} else if ( | ||
(c1 & 0xfc00) === 0xd800 && | ||
((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00 | ||
) { | ||
c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff); | ||
i++; | ||
target[strPosition++] = c1 >> 18 | 0xf0; | ||
target[strPosition++] = c1 >> 12 & 0x3f | 0x80; | ||
target[strPosition++] = c1 >> 6 & 0x3f | 0x80; | ||
target[strPosition++] = c1 & 0x3f | 0x80; | ||
} else { | ||
target[strPosition++] = c1 >> 12 | 0xe0; | ||
target[strPosition++] = c1 >> 6 & 0x3f | 0x80; | ||
target[strPosition++] = c1 & 0x3f | 0x80; | ||
} | ||
} | ||
length = strPosition - position$1 - headerSize; | ||
} else { | ||
length = encodeUtf8(value, position$1 + headerSize, maxBytes); | ||
} | ||
if (length < 0x18) { | ||
target[position$1++] = 0x60 | length; | ||
} else if (length < 0x100) { | ||
if (headerSize < 2) { | ||
target.copyWithin(position$1 + 2, position$1 + 1, position$1 + 1 + length); | ||
} | ||
target[position$1++] = 0x78; | ||
target[position$1++] = length; | ||
} else if (length < 0x10000) { | ||
if (headerSize < 3) { | ||
target.copyWithin(position$1 + 3, position$1 + 2, position$1 + 2 + length); | ||
} | ||
target[position$1++] = 0x79; | ||
target[position$1++] = length >> 8; | ||
target[position$1++] = length & 0xff; | ||
} else { | ||
if (headerSize < 5) { | ||
target.copyWithin(position$1 + 5, position$1 + 3, position$1 + 3 + length); | ||
} | ||
target[position$1++] = 0x7a; | ||
targetView.setUint32(position$1, length); | ||
position$1 += 4; | ||
} | ||
position$1 += length; | ||
} else if (type === 'number') { | ||
if (value >>> 0 === value) {// positive integer, 32-bit or less | ||
// positive uint | ||
if (value < 0x18) { | ||
target[position$1++] = value; | ||
} else if (value < 0x100) { | ||
target[position$1++] = 0x18; | ||
target[position$1++] = value; | ||
} else if (value < 0x10000) { | ||
target[position$1++] = 0x19; | ||
target[position$1++] = value >> 8; | ||
target[position$1++] = value & 0xff; | ||
} else { | ||
target[position$1++] = 0x1a; | ||
targetView.setUint32(position$1, value); | ||
position$1 += 4; | ||
} | ||
} else if (value >> 0 === value) { // negative integer | ||
if (value >= -0x18) { | ||
target[position$1++] = 0x1f - value; | ||
} else if (value >= -0x100) { | ||
target[position$1++] = 0x38; | ||
target[position$1++] = ~value; | ||
} else if (value >= -0x10000) { | ||
target[position$1++] = 0x39; | ||
targetView.setUint16(position$1, ~value); | ||
position$1 += 2; | ||
} else { | ||
target[position$1++] = 0x3a; | ||
targetView.setUint32(position$1, ~value); | ||
position$1 += 4; | ||
} | ||
} else { | ||
let useFloat32; | ||
if ((useFloat32 = this.useFloat32) > 0 && value < 0x100000000 && value >= -0x80000000) { | ||
target[position$1++] = 0xfa; | ||
targetView.setFloat32(position$1, value); | ||
let xShifted; | ||
if (useFloat32 < 4 || | ||
// this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved | ||
((xShifted = value * mult10[((target[position$1] & 0x7f) << 1) | (target[position$1 + 1] >> 7)]) >> 0) === xShifted) { | ||
position$1 += 4; | ||
return | ||
} else | ||
position$1--; // move back into position for writing a double | ||
} | ||
target[position$1++] = 0xfb; | ||
targetView.setFloat64(position$1, value); | ||
position$1 += 8; | ||
} | ||
} else if (type === 'object') { | ||
if (!value) | ||
target[position$1++] = 0xf6; | ||
else { | ||
if (referenceMap) { | ||
let referee = referenceMap.get(value); | ||
if (referee) { | ||
if (!referee.id) { | ||
let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = []); | ||
referee.id = idsToInsert.push(referee); | ||
} | ||
target[position$1++] = 0xd9; | ||
target[position$1++] = 40010 >> 8; | ||
target[position$1++] = 40010 & 0xff; | ||
target[position$1++] = 0x1a; // uint32 | ||
targetView.setUint32(position$1, referee.id); | ||
position$1 += 4; | ||
return | ||
} else | ||
referenceMap.set(value, { offset: position$1 - start }); | ||
} | ||
let constructor = value.constructor; | ||
if (constructor === Object) { | ||
writeObject(value, true); | ||
} else if (constructor === Array) { | ||
length = value.length; | ||
if (length < 0x18) { | ||
target[position$1++] = 0x80 | length; | ||
} else { | ||
writeArrayHeader(length); | ||
} | ||
for (let i = 0; i < length; i++) { | ||
encode(value[i]); | ||
} | ||
} else if (constructor === Map) { | ||
length = value.size; | ||
if (length < 0x18) { | ||
target[position$1++] = 0xa0 | length; | ||
} else if (length < 0x100) { | ||
target[position$1++] = 0xb8; | ||
target[position$1++] = length; | ||
} else if (length < 0x10000) { | ||
target[position$1++] = 0xb9; | ||
target[position$1++] = length >> 8; | ||
target[position$1++] = length & 0xff; | ||
} else { | ||
target[position$1++] = 0xba; | ||
targetView.setUint32(position$1, length); | ||
position$1 += 4; | ||
} | ||
for (let [ key, entryValue ] of value) { | ||
encode(key); | ||
encode(entryValue); | ||
} | ||
} else { | ||
for (let i = 0, l = extensions.length; i < l; i++) { | ||
let extensionClass = extensionClasses[i]; | ||
if (value instanceof extensionClass) { | ||
let extension = extensions[i]; | ||
let tag = extension.tag; | ||
if (tag < 0x18) { | ||
target[position$1++] = 0xc0 | tag; | ||
} else if (tag < 0x100) { | ||
target[position$1++] = 0xd8; | ||
target[position$1++] = tag; | ||
} else if (tag < 0x10000) { | ||
target[position$1++] = 0xd9; | ||
target[position$1++] = tag >> 8; | ||
target[position$1++] = tag & 0xff; | ||
} else if (tag > -1) { | ||
target[position$1++] = 0xda; | ||
targetView.setUint32(position$1, tag); | ||
position$1 += 4; | ||
} // else undefined, don't write tag | ||
extension.encode.call(this, value, encode, makeRoom); | ||
return | ||
} | ||
} | ||
if (value[Symbol.iterator]) { | ||
target[position$1++] = 0x9f; // indefinite length array | ||
for (let entry of value) { | ||
encode(entry); | ||
} | ||
target[position$1++] = 0xff; // stop-code | ||
return | ||
} | ||
// no extension found, write as object | ||
writeObject(value, !value.hasOwnProperty); // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks | ||
} | ||
} | ||
} else if (type === 'boolean') { | ||
target[position$1++] = value ? 0xf5 : 0xf4; | ||
} else if (type === 'bigint') { | ||
if (value < (BigInt(1)<<BigInt(64)) && value >= 0) { | ||
// use an unsigned int as long as it fits | ||
target[position$1++] = 0x1b; | ||
targetView.setBigUint64(position$1, value); | ||
} else if (value > -(BigInt(1)<<BigInt(64)) && value < 0) { | ||
// if we can fit an unsigned int, use that | ||
target[position$1++] = 0x3b; | ||
targetView.setBigUint64(position$1, -value - BigInt(1)); | ||
} else { | ||
// overflow | ||
if (this.largeBigIntToFloat) { | ||
target[position$1++] = 0xfb; | ||
targetView.setFloat64(position$1, Number(value)); | ||
} else { | ||
throw new RangeError(value + ' was too large to fit in CBOR 64-bit integer format, set largeBigIntToFloat to convert to float-64') | ||
} | ||
} | ||
position$1 += 8; | ||
} else if (type === 'undefined') { | ||
//target[position++] = 0xc1 // this is the "never-used" byte | ||
target[position$1++] = 0xf7; | ||
} else { | ||
throw new Error('Unknown type ' + type) | ||
} | ||
}; | ||
const writeObject = this.useRecords === false ? this.variableMapSize ? (object) => { | ||
// this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects) | ||
let keys = Object.keys(object); | ||
let length = keys.length; | ||
if (length < 0x18) { | ||
target[position$1++] = 0xa0 | length; | ||
} else if (length < 0x100) { | ||
target[position$1++] = 0xb8; | ||
target[position$1++] = length; | ||
} else if (length < 0x10000) { | ||
target[position$1++] = 0xb9; | ||
target[position$1++] = length >> 8; | ||
target[position$1++] = length & 0xff; | ||
} else { | ||
target[position$1++] = 0xba; | ||
targetView.setUint32(position$1, length); | ||
position$1 += 4; | ||
} | ||
let key; | ||
for (let i = 0; i < length; i++) { | ||
encode(key = keys[i]); | ||
encode(object[key]); | ||
} | ||
} : | ||
(object, safePrototype) => { | ||
target[position$1++] = 0xb9; // always use map 16, so we can preallocate and set the length afterwards | ||
let objectOffset = position$1 - start; | ||
position$1 += 2; | ||
let size = 0; | ||
for (let key in object) { | ||
if (safePrototype || object.hasOwnProperty(key)) { | ||
encode(key); | ||
encode(object[key]); | ||
size++; | ||
} | ||
} | ||
target[objectOffset++ + start] = size >> 8; | ||
target[objectOffset + start] = size & 0xff; | ||
} : | ||
/* sharedStructures ? // For highly stable structures, using for-in can a little bit faster | ||
(object, safePrototype) => { | ||
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null)) | ||
let objectOffset = position++ - start | ||
let wroteKeys | ||
for (let key in object) { | ||
if (safePrototype || object.hasOwnProperty(key)) { | ||
nextTransition = transition[key] | ||
if (!nextTransition) { | ||
nextTransition = transition[key] = Object.create(null) | ||
nextTransition.__keys__ = (transition.__keys__ || []).concat([key]) | ||
/*let keys = Object.keys(object) | ||
if | ||
let size = 0 | ||
let startBranch = transition.__keys__ ? transition.__keys__.length : 0 | ||
for (let i = 0, l = keys.length; i++) { | ||
let key = keys[i] | ||
size += key.length << 2 | ||
if (i >= startBranch) { | ||
nextTransition = nextTransition[key] = Object.create(null) | ||
nextTransition.__keys__ = keys.slice(0, i + 1) | ||
} | ||
} | ||
makeRoom(position + size) | ||
nextTransition = transition[key] | ||
target.copy(target, ) | ||
objectOffset | ||
} | ||
transition = nextTransition | ||
encode(object[key]) | ||
} | ||
} | ||
let id = transition.id | ||
if (!id) { | ||
id = transition.id = structures.push(transition.__keys__) + 63 | ||
if (sharedStructures.onUpdate) | ||
sharedStructures.onUpdate(id, transition.__keys__) | ||
} | ||
target[objectOffset + start] = id | ||
}*/ | ||
(object) => { | ||
let keys = Object.keys(object); | ||
let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null)); | ||
let newTransitions = 0; | ||
let length = keys.length; | ||
for (let i =0; i < length; i++) { | ||
let key = keys[i]; | ||
nextTransition = transition[key]; | ||
if (!nextTransition) { | ||
nextTransition = transition[key] = Object.create(null); | ||
newTransitions++; | ||
} | ||
transition = nextTransition; | ||
} | ||
let recordId = transition[RECORD_SYMBOL]; | ||
if (recordId) { | ||
target[position$1++] = 0xd9; // tag two byte | ||
target[position$1++] = RECORD_STARTING_ID_PREFIX; | ||
target[position$1++] = recordId; | ||
} else { | ||
recordId = structures.nextId++; | ||
if (!recordId) { | ||
recordId = 0; | ||
structures.nextId = 1; | ||
} | ||
if (recordId >= MAX_STRUCTURES) {// cycle back around | ||
structures.nextId = (recordId = maxSharedStructures) + 1; | ||
} | ||
transition[RECORD_SYMBOL] = recordId; | ||
structures[recordId] = keys; | ||
if (sharedStructures && sharedStructures.length <= maxSharedStructures) { | ||
target[position$1++] = 0xd9; // tag two byte | ||
target[position$1++] = RECORD_STARTING_ID_PREFIX; | ||
target[position$1++] = recordId; // tag number | ||
hasSharedUpdate = true; | ||
} else { | ||
target[position$1++] = 0xd8; | ||
target[position$1++] = RECORD_STARTING_ID_PREFIX; | ||
if (newTransitions) | ||
transitionsCount += serializationsSinceTransitionRebuild * newTransitions; | ||
// record the removal of the id, we can maintain our shared structure | ||
if (recordIdsToRemove.length >= MAX_STRUCTURES - maxSharedStructures) | ||
recordIdsToRemove.shift()[RECORD_SYMBOL] = 0; // we are cycling back through, and have to remove old ones | ||
recordIdsToRemove.push(transition); | ||
if (length < 0x16) | ||
target[position$1++] = 0x82 + length; // array header, length of values + 2 | ||
else | ||
writeArrayHeader(length + 2); | ||
encode(keys); | ||
target[position$1++] = 0x19; // uint16 | ||
target[position$1++] = RECORD_STARTING_ID_PREFIX; | ||
target[position$1++] = recordId; | ||
// now write the values | ||
for (let i =0; i < length; i++) | ||
encode(object[keys[i]]); | ||
return | ||
} | ||
} | ||
if (length < 0x18) { // write the array header | ||
target[position$1++] = 0x80 | length; | ||
} else { | ||
writeArrayHeader(length); | ||
} | ||
for (let i =0; i < length; i++) | ||
encode(object[keys[i]]); | ||
}; | ||
const makeRoom = (end) => { | ||
let newSize; | ||
if (end > 0x1000000) { | ||
// special handling for really large buffers | ||
if ((end - start) > MAX_BUFFER_SIZE) | ||
throw new Error('Encoded buffer would be larger than maximum buffer size') | ||
newSize = Math.min(MAX_BUFFER_SIZE, | ||
Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x1000000) / 0x1000) * 0x1000); | ||
} else // faster handling for smaller buffers | ||
newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12; | ||
let newBuffer = new ByteArrayAllocate(newSize); | ||
targetView = new DataView(newBuffer.buffer, 0, newSize); | ||
if (target.copy) | ||
target.copy(newBuffer, 0, start, end); | ||
else | ||
newBuffer.set(target.slice(start, end)); | ||
position$1 -= start; | ||
start = 0; | ||
safeEnd = newBuffer.length - 10; | ||
return target = newBuffer | ||
}; | ||
} | ||
useBuffer(buffer) { | ||
// this means we are finished using our own buffer and we can write over it safely | ||
target = buffer; | ||
targetView = new DataView(target.buffer, target.byteOffset, target.byteLength); | ||
position$1 = 0; | ||
} | ||
} | ||
function writeArrayHeader(length) { | ||
if (length < 0x100) { | ||
target[position$1++] = 0x98; | ||
target[position$1++] = length; | ||
} else if (length < 0x10000) { | ||
target[position$1++] = 0x99; | ||
target[position$1++] = length >> 8; | ||
target[position$1++] = length & 0xff; | ||
} else { | ||
target[position$1++] = 0x9a; | ||
targetView.setUint32(position$1, length); | ||
position$1 += 4; | ||
} | ||
} | ||
extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, ByteArray, | ||
Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array, BigUint64Array, Int8Array, Int16Array, Int32Array, BigInt64Array, | ||
Float32Array, Float64Array]; | ||
//Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/ | ||
extensions = [{ | ||
tag: 1, | ||
encode(date, encode) { | ||
let seconds = date.getTime() / 1000; | ||
if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) { | ||
// Timestamp 32 | ||
target[position$1++] = 0x1a; | ||
targetView.setUint32(position$1, seconds); | ||
position$1 += 4; | ||
} else { | ||
// Timestamp float64 | ||
target[position$1++] = 0xfb; | ||
targetView.setFloat64(position$1, seconds); | ||
position$1 += 8; | ||
} | ||
} | ||
}, { | ||
tag: 258, // https://github.com/input-output-hk/cbor-sets-spec/blob/master/CBOR_SETS.md | ||
encode(set, encode) { | ||
let array = Array.from(set); | ||
encode(array); | ||
} | ||
}, { | ||
tag: 27, // http://cbor.schmorp.de/generic-object | ||
encode(error, encode) { | ||
encode([ error.name, error.message ]); | ||
} | ||
}, { | ||
tag: 27, // http://cbor.schmorp.de/generic-object | ||
encode(regex, encode) { | ||
encode([ 'RegExp', regex.source, regex.flags ]); | ||
} | ||
}, { | ||
encode(arrayBuffer, encode, makeRoom) { | ||
writeBuffer(arrayBuffer, makeRoom); | ||
} | ||
}, { | ||
encode(arrayBuffer, encode, makeRoom) { | ||
writeBuffer(arrayBuffer, makeRoom); | ||
} | ||
}, typedArrayEncoder(64), | ||
typedArrayEncoder(68), | ||
typedArrayEncoder(69), | ||
typedArrayEncoder(70), | ||
typedArrayEncoder(71), | ||
typedArrayEncoder(72), | ||
typedArrayEncoder(77), | ||
typedArrayEncoder(78), | ||
typedArrayEncoder(79), | ||
typedArrayEncoder(81), | ||
typedArrayEncoder(82)]; | ||
function typedArrayEncoder(tag) { | ||
return { | ||
tag: tag, | ||
encode: function writeExtBuffer(typedArray, encode) { | ||
let length = typedArray.byteLength; | ||
let offset = typedArray.byteOffset || 0; | ||
let buffer = typedArray.buffer || typedArray; | ||
encode(hasNodeBuffer ? Buffer.from(buffer, offset, length) : | ||
new Uint8Array(buffer, offset, length)); | ||
} | ||
} | ||
} | ||
function writeBuffer(buffer, makeRoom) { | ||
let length = buffer.byteLength; | ||
if (length < 0x100) { | ||
target[position$1++] = 0x58; | ||
target[position$1++] = length; | ||
} else if (length < 0x10000) { | ||
target[position$1++] = 0x59; | ||
target[position$1++] = length >> 8; | ||
target[position$1++] = length & 0xff; | ||
} else { | ||
target[position$1++] = 0x5a; | ||
targetView.setUint32(position$1, length); | ||
position$1 += 4; | ||
} | ||
if (position$1 + length >= target.length) { | ||
makeRoom(position$1 + length); | ||
} | ||
target.set(buffer, position$1); | ||
position$1 += length; | ||
} | ||
function insertIds(serialized, idsToInsert) { | ||
// insert the ids that need to be referenced for structured clones | ||
let nextId; | ||
let distanceToMove = idsToInsert.length * 8; | ||
let lastEnd = serialized.length - distanceToMove; | ||
idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1); | ||
while (nextId = idsToInsert.pop()) { | ||
let offset = nextId.offset; | ||
let id = nextId.id; | ||
serialized.copyWithin(offset + distanceToMove, offset, lastEnd); | ||
distanceToMove -= 8; | ||
let position = offset + distanceToMove; | ||
serialized[position++] = 0xd9; | ||
serialized[position++] = 40009 >> 8; | ||
serialized[position++] = 40009 & 0xff; | ||
serialized[position++] = 0x1a; // uint32 | ||
serialized[position++] = id >> 24; | ||
serialized[position++] = (id >> 16) & 0xff; | ||
serialized[position++] = (id >> 8) & 0xff; | ||
serialized[position++] = id & 0xff; | ||
lastEnd = offset; | ||
} | ||
return serialized | ||
} | ||
function addExtension$1(extension) { | ||
if (extension.Class) { | ||
if (!extension.encode) | ||
throw new Error('Extension has no encode function') | ||
extensionClasses.unshift(extension.Class); | ||
extensions.unshift(extension); | ||
} | ||
addExtension(extension); | ||
} | ||
let defaultEncoder = new Encoder({ useRecords: false }); | ||
const encode = defaultEncoder.encode; | ||
const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS; | ||
const useRecords = false; | ||
const mapsAsObjects = true; | ||
exports.ALWAYS = ALWAYS; | ||
exports.DECIMAL_FIT = DECIMAL_FIT; | ||
exports.DECIMAL_ROUND = DECIMAL_ROUND; | ||
exports.Decoder = Decoder; | ||
exports.Encoder = Encoder; | ||
exports.FLOAT32_OPTIONS = FLOAT32_OPTIONS; | ||
exports.NEVER = NEVER; | ||
exports.Tag = Tag; | ||
exports.addExtension = addExtension$1; | ||
exports.decode = decode; | ||
exports.decodeMultiple = decodeMultiple; | ||
exports.encode = encode; | ||
exports.mapsAsObjects = mapsAsObjects; | ||
exports.useRecords = useRecords; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); |
@@ -1,6 +0,2 @@ | ||
"use strict" | ||
let decoderModule = require('./decode') | ||
let Decoder = decoderModule.Decoder | ||
let mult10 = decoderModule.mult10 | ||
const typedArrays = decoderModule.typedArrays | ||
import { Decoder, mult10, Tag, typedArrays, addExtension as decodeAddExtension } from './decode.js' | ||
let textEncoder | ||
@@ -14,4 +10,5 @@ try { | ||
const ByteArray = hasNodeBuffer ? Buffer : Uint8Array | ||
const RECORD_STARTING_ID_PREFIX = 0x9d | ||
const RECORD_STARTING_ID_PREFIX = 0x69 // tag 105/0x69 | ||
const MAX_STRUCTURES = 0x100 | ||
const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000 | ||
let target | ||
@@ -22,3 +19,3 @@ let targetView | ||
const RECORD_SYMBOL = Symbol('record-id') | ||
class Encoder extends Decoder { | ||
export class Encoder extends Decoder { | ||
constructor(options) { | ||
@@ -350,3 +347,3 @@ super(options) | ||
} // else undefined, don't write tag | ||
extension.encode.call(this, value, encode) | ||
extension.encode.call(this, value, encode, makeRoom) | ||
return | ||
@@ -364,3 +361,3 @@ } | ||
// no extension found, write as object | ||
writeObject(value, false) | ||
writeObject(value, !value.hasOwnProperty) // if it doesn't have hasOwnProperty, don't do hasOwnProperty checks | ||
} | ||
@@ -371,7 +368,19 @@ } | ||
} else if (type === 'bigint') { | ||
target[position++] = 0xfb | ||
/*if (value < 9223372036854776000 && value > -9223372036854776000) | ||
targetView.setBigInt64(position, value) | ||
else*/ | ||
targetView.setFloat64(position, value) | ||
if (value < (BigInt(1)<<BigInt(64)) && value >= 0) { | ||
// use an unsigned int as long as it fits | ||
target[position++] = 0x1b | ||
targetView.setBigUint64(position, value) | ||
} else if (value > -(BigInt(1)<<BigInt(64)) && value < 0) { | ||
// if we can fit an unsigned int, use that | ||
target[position++] = 0x3b | ||
targetView.setBigUint64(position, -value - BigInt(1)) | ||
} else { | ||
// overflow | ||
if (this.largeBigIntToFloat) { | ||
target[position++] = 0xfb | ||
targetView.setFloat64(position, Number(value)) | ||
} else { | ||
throw new RangeError(value + ' was too large to fit in CBOR 64-bit integer format, set largeBigIntToFloat to convert to float-64') | ||
} | ||
} | ||
position += 8 | ||
@@ -502,5 +511,4 @@ } else if (type === 'undefined') { | ||
} else { | ||
target[position++] = 0xd9 | ||
target[position++] = 40006 >> 8 | ||
target[position++] = 40006 & 0xff | ||
target[position++] = 0xd8 | ||
target[position++] = RECORD_STARTING_ID_PREFIX | ||
if (newTransitions) | ||
@@ -512,7 +520,14 @@ transitionsCount += serializationsSinceTransitionRebuild * newTransitions | ||
recordIdsToRemove.push(transition) | ||
target[position++] = 0x83 // array header, length 3 | ||
if (length < 0x16) | ||
target[position++] = 0x82 + length // array header, length of values + 2 | ||
else | ||
writeArrayHeader(length + 2) | ||
encode(keys) | ||
target[position++] = 0x19 // uint16 | ||
target[position++] = RECORD_STARTING_ID_PREFIX | ||
target[position++] = recordId | ||
encode(keys) | ||
// now write the values | ||
for (let i =0; i < length; i++) | ||
encode(object[keys[i]]) | ||
return | ||
} | ||
@@ -525,3 +540,2 @@ } | ||
} | ||
// now write the values | ||
for (let i =0; i < length; i++) | ||
@@ -531,3 +545,11 @@ encode(object[keys[i]]) | ||
const makeRoom = (end) => { | ||
let newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12 | ||
let newSize | ||
if (end > 0x1000000) { | ||
// special handling for really large buffers | ||
if ((end - start) > MAX_BUFFER_SIZE) | ||
throw new Error('Encoded buffer would be larger than maximum buffer size') | ||
newSize = Math.min(MAX_BUFFER_SIZE, | ||
Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x1000000) / 0x1000) * 0x1000) | ||
} else // faster handling for smaller buffers | ||
newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12 | ||
let newBuffer = new ByteArrayAllocate(newSize) | ||
@@ -538,3 +560,3 @@ targetView = new DataView(newBuffer.buffer, 0, newSize) | ||
else | ||
copyBinary(target, newBuffer, 0, start, end) | ||
newBuffer.set(target.slice(start, end)) | ||
position -= start | ||
@@ -553,3 +575,2 @@ start = 0 | ||
} | ||
exports.Encoder = Encoder | ||
@@ -615,8 +636,8 @@ function copyBinary(source, target, targetOffset, offset, endOffset) { | ||
}, { | ||
encode(arrayBuffer, encode) { | ||
writeBuffer(arrayBuffer) | ||
encode(arrayBuffer, encode, makeRoom) { | ||
writeBuffer(arrayBuffer, makeRoom) | ||
} | ||
}, { | ||
encode(arrayBuffer, encode) { | ||
writeBuffer(arrayBuffer) | ||
encode(arrayBuffer, encode, makeRoom) { | ||
writeBuffer(arrayBuffer, makeRoom) | ||
} | ||
@@ -647,3 +668,3 @@ }, typedArrayEncoder(64), | ||
} | ||
function writeBuffer(buffer) { | ||
function writeBuffer(buffer, makeRoom) { | ||
let length = buffer.byteLength | ||
@@ -662,6 +683,6 @@ if (length < 0x100) { | ||
} | ||
if (buffer.copy) | ||
buffer.copy(target, position) | ||
else | ||
copyBinary(buffer, target, position, 0, length) | ||
if (position + length >= target.length) { | ||
makeRoom(position + length) | ||
} | ||
target.set(buffer, position) | ||
position += length | ||
@@ -695,3 +716,3 @@ } | ||
exports.addExtension = function(extension) { | ||
export function addExtension(extension) { | ||
if (extension.Class) { | ||
@@ -703,3 +724,8 @@ if (!extension.encode) | ||
} | ||
decoderModule.addExtension(extension) | ||
decodeAddExtension(extension) | ||
} | ||
let defaultEncoder = new Encoder({ useRecords: false }) | ||
export const encode = defaultEncoder.encode | ||
export { FLOAT32_OPTIONS } from './decode.js' | ||
import { FLOAT32_OPTIONS } from './decode.js' | ||
export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS |
@@ -1,44 +0,10 @@ | ||
declare module 'cbor-x' { | ||
interface Options { | ||
useFloat32?: 0 | typeof ALWAYS | typeof DECIMAL_ROUND | typeof DECIMAL_FIT | ||
useRecords?: boolean | ||
structures?: {}[] | ||
structuredClone?: boolean | ||
mapsAsObjects?: boolean | ||
variableMapSize?: boolean | ||
copyBuffers?: boolean | ||
useTimestamp32?: boolean | ||
getStructures?(): {}[] | ||
saveStructures?(structures: {}[]): boolean | void | ||
} | ||
export class Decoder { | ||
constructor(options?: Options) | ||
decode(messagePack: Buffer): any | ||
} | ||
export class Encoder extends Decoder { | ||
encode(value: any): Buffer | ||
resetMemory(): void | ||
} | ||
export { Decoder, decode, addExtension, FLOAT32_OPTIONS } from './decode.js' | ||
export { Encoder, encode } from './encode.js' | ||
interface Extension { | ||
Class: Function | ||
type: number | ||
encode(value: any): Buffer | ||
decode(messagePack: Buffer): any | ||
} | ||
export function decode(messagePack: Buffer): any | ||
export function encode(value: any): Buffer | ||
export function addExtension(extension: Extension) | ||
export const ALWAYS = 1 | ||
export const DECIMAL_ROUND = 3 | ||
export const DECIMAL_FIT = 4 | ||
export class DecoderStream { | ||
} | ||
export class EncoderStream { | ||
write(value) | ||
end(value?) | ||
} | ||
export class Tag { | ||
value: any | ||
} | ||
} | ||
export as namespace msgpackr; | ||
export class DecoderStream { | ||
} | ||
export class EncoderStream { | ||
write(value: any): void | ||
end(value?: any): void | ||
} |
34
index.js
@@ -1,31 +0,5 @@ | ||
exports.Encoder = require('./encode').Encoder | ||
exports.addExtension = require('./encode').addExtension | ||
let decodeModule = require('./decode') | ||
let extractor = tryRequire('cbor-extract') | ||
if (extractor) | ||
decodeModule.setExtractor(extractor.extractStrings) | ||
exports.Decoder = decodeModule.Decoder | ||
exports.EncoderStream = require('./stream').EncoderStream | ||
exports.DecoderStream = require('./stream').DecoderStream | ||
let encoder = new exports.Encoder({ useRecords: false }) | ||
exports.decode = encoder.decode | ||
exports.encode = encoder.encode | ||
exports.Tag = decodeModule.Tag | ||
exports.useRecords = false | ||
exports.mapsAsObjects = true | ||
Object.assign(exports, { | ||
ALWAYS:1, | ||
DECIMAL_ROUND: 3, | ||
DECIMAL_FIT: 4 | ||
}) | ||
export { Encoder, addExtension, encode, NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } from './encode.js' | ||
export { Tag, Decoder, decodeMultiple, decode, FLOAT32_OPTIONS } from './decode.js' | ||
export const useRecords = false | ||
export const mapsAsObjects = true | ||
function tryRequire(moduleId) { | ||
try { | ||
return require(moduleId) | ||
} catch (error) { | ||
if (typeof window == 'undefined') | ||
console.warn('Native extraction module not loaded, cbor-x will still run, but with decreased performance. ' + error.message) | ||
else | ||
console.warn('For browser usage, directly use encode/decode modules. ' + error.message.split('\n')[0]) | ||
} | ||
} |
{ | ||
"name": "cbor-x", | ||
"author": "Kris Zyp", | ||
"version": "0.6.4", | ||
"version": "0.8.0", | ||
"description": "Ultra-fast CBOR implementation with tag extensions for records and structured cloning", | ||
@@ -20,13 +20,39 @@ "license": "MIT", | ||
"benchmark": "node ./tests/benchmark.js", | ||
"test": "./node_modules/.bin/mocha tests/test*.js -u tdd" | ||
"build": "rollup -c", | ||
"prepare": "npm run build", | ||
"test": "./node_modules/.bin/mocha --experimental-json-modules tests/test.js -u tdd" | ||
}, | ||
"type": "commonjs", | ||
"module": "./index.mjs", | ||
"main": "./index.js", | ||
"type": "module", | ||
"main": "./dist/node.cjs", | ||
"exports": { | ||
"import": "./index.mjs", | ||
"require": "./index.js" | ||
".": { | ||
"node": { | ||
"require": "./dist/node.cjs", | ||
"import": "./node.js" | ||
}, | ||
"default": { | ||
"import": "./index.js" | ||
} | ||
}, | ||
"./pack": { | ||
"node": { | ||
"import": "./index.js", | ||
"require": "./dist/node.cjs" | ||
}, | ||
"default": { | ||
"import": "./pack.js" | ||
} | ||
}, | ||
"./unpack": { | ||
"node": { | ||
"import": "./index.js", | ||
"require": "./dist/node.cjs" | ||
}, | ||
"default": { | ||
"import": "./unpack.js" | ||
} | ||
} | ||
}, | ||
"optionalDependencies": { | ||
"cbor-extract": "^0.2.1" | ||
"cbor-extract": "^0.2.2" | ||
}, | ||
@@ -39,7 +65,8 @@ "devDependencies": { | ||
"chai": "^4", | ||
"mocha": "^4", | ||
"webpack": "^4.44.1", | ||
"webpack-cli": "^3.3.2", | ||
"webpack-command": "^0.4.1" | ||
"esm": "^3.2.25", | ||
"mocha": "^8.1.3", | ||
"rollup": "^1.20.3", | ||
"rollup-plugin-babel-minify": "^9.0.0", | ||
"@rollup/plugin-json": "^4.1.0" | ||
} | ||
} |
@@ -43,3 +43,3 @@ # cbor-x | ||
let receivingStream = new DecoderStream(); | ||
// we just piping to our own stream, but normally you would send and | ||
// we are just piping to our own stream, but normally you would send and | ||
// receive over some type of inter-process or network connection. | ||
@@ -54,2 +54,5 @@ sendingStream.pipe(receivingStream); | ||
## Deno Usage | ||
Msgpackr modules are standard ESM modules and can be loaded directly from github (https://raw.githubusercontent.com/kriszyp/msgpackr/master/index.js) or downloaded and used directly in Deno. The standard pack/encode and unpack/decode functionality is available on Deno, like other platforms. | ||
## Browser Usage | ||
@@ -92,3 +95,2 @@ Cbor-x works as standalone JavaScript as well, and runs on modern browsers. It includes a bundled script, at `dist/index.js` for ease of direct loading: | ||
encoder.encode(myBigData); | ||
``` | ||
@@ -101,3 +103,3 @@ | ||
### Shared Record Structures | ||
Another useful way of using cbor-x, and the record extension, is for storing data in a databases, files, or other storage systems. If a number of objects with common data structures are being stored, a shared structure can be used to greatly improve data storage and deserialization efficiency. We just need to provide a way to store the generated shared structure so it is available to deserialize stored data in the future: | ||
Another useful way of using cbor-x, and the record extension, is for storing data in a databases, files, or other storage systems. If a number of objects with common data structures are being stored, a shared structure can be used to greatly improve data storage and deserialization efficiency. In the simplest form, provide a `structures` array, which is updated if any new object structure is encountered: | ||
@@ -126,2 +128,17 @@ ``` | ||
### Reading Multiple Values | ||
If you have a buffer with multiple values sequentially encoded, you can choose to parse and read multiple values. This can be done using the `unpackMultiple` function/method, which can return an array of all the values it can sequentially parse within the provided buffer. For example: | ||
```js | ||
let data = new Uint8Array([1, 2, 3]) // encodings of values 1, 2, and 3 | ||
let values = unpackMultiple(data) // [1, 2, 3] | ||
``` | ||
Alternately, you can provide a callback function that is called as the parsing occurs with each value, and can optionally terminate the parsing by returning `false`: | ||
```js | ||
let data = new Uint8Array([1, 2, 3]) // encodings of values 1, 2, and 3 | ||
unpackMultiple(data, (value) => { | ||
// called for each value | ||
// return false if you wish to end the parsing | ||
}) | ||
``` | ||
## Options | ||
@@ -138,2 +155,3 @@ The following options properties can be provided to the Encoder or Decoder constructor: | ||
* `useTimestamp32` - Encode JS `Date`s in 32-bit format when possible by dropping the milliseconds. This is a more efficient encoding of dates. You can also cause dates to use 32-bit format by manually setting the milliseconds to zero (`date.setMilliseconds(0)`). | ||
* `largeBigIntToFloat` - If a bigint needs to be encoded that is larger than will fit in 64-bit integers, it will be encoded as a float-64 (otherwise will throw a RangeError). | ||
@@ -217,3 +235,26 @@ ### 32-bit Float Options | ||
``` | ||
If you want to use msgpackr to encode and decode the data within your extensions, you can use the `read` and `write` functions and read and write data/objects that will be encoded and decoded by msgpackr, which can be easier and faster than creating and receiving separate buffers (note that you can't just return the instance from `write` or msgpackr will recursively try to use extension infinitely): | ||
```js | ||
import { addExtension, Packr } from 'msgpackr'; | ||
class MyCustomClass {...} | ||
let extPackr = new Packr(); | ||
addExtension({ | ||
Class: MyCustomClass, | ||
type: 11, // register your own extension code (a type code from 1-100) | ||
write(instance) { | ||
// define how your custom class should be encoded | ||
return instance.myData; // return some data to be encoded | ||
} | ||
read(data) { | ||
// define how your custom class should be decoded, | ||
// data will already be unpacked/decoded | ||
let instance = new MyCustomClass(); | ||
instance.myData = data; | ||
return instance; // return decoded value | ||
} | ||
}); | ||
``` | ||
## Unknown Tags | ||
@@ -253,3 +294,3 @@ If no extension is registered for a tag, the decoder will return an instance of the `Tag` class, where the value provided for the tag will be available in the `value` property of the `Tag` instance. The `Tag` class is an export of the package and decode module. | ||
### Browser Consideration | ||
It is worth noting that while cbor-x works well in modern browsers, the CBOR format itself is often not an ideal format for web use. If you want compact data, brotli or gzip are most effective in compressing, and CBOR's character frequency tends to defeat Huffman encoding used by these standard compression algorithms, resulting in less compact data than compressed JSON. The modern browser architecture is heavily optimized for parsing JSON from HTTP traffic, and it is difficult to achieve the same level of overall efficiency and ease with CBOR. | ||
CBOR can be a great choice for high-performance data delivery to browsers, as reasonable data size is possible without compression. And msgpackr works very well in modern browsers. However, it is worth noting that if you want highly compact data, brotli or gzip are most effective in compressing, and CBOR's character frequency tends to defeat Huffman encoding used by these standard compression algorithms, resulting in less compact data than compressed JSON. | ||
@@ -256,0 +297,0 @@ ### Credits |
@@ -1,8 +0,7 @@ | ||
"use strict" | ||
var Transform = require('stream').Transform | ||
var Encoder = require('./encode').Encoder | ||
const { read, getPosition, Decoder, clearSource } = require('./decode') | ||
import { Transform } from 'stream' | ||
import { Encoder } from './encode.js' | ||
import { read, getPosition, Decoder, clearSource } from './decode.js' | ||
var DEFAULT_OPTIONS = {objectMode: true} | ||
class EncoderStream extends Transform { | ||
export class EncoderStream extends Transform { | ||
constructor(options) { | ||
@@ -26,3 +25,3 @@ if (!options) | ||
class DecoderStream extends Transform { | ||
export class DecoderStream extends Transform { | ||
constructor(options) { | ||
@@ -62,4 +61,1 @@ if (!options) | ||
} | ||
exports.EncoderStream = EncoderStream | ||
exports.DecoderStream = DecoderStream |
@@ -1,4 +0,6 @@ | ||
var data = require('./example4.json'); | ||
import * as data from './example4.json'; | ||
import * as msgpackr from '..' | ||
import * as chai from 'chai' | ||
function tryRequire(module) { | ||
/*function tryRequire(module) { | ||
try { | ||
@@ -9,3 +11,3 @@ return require(module) | ||
} | ||
if (typeof chai === 'undefined') { chai = require('chai') } | ||
//if (typeof chai === 'undefined') { chai = require('chai') } | ||
assert = chai.assert | ||
@@ -16,2 +18,7 @@ if (typeof cborX === 'undefined') { cborX = require('..') } | ||
var encode = cborX.encode | ||
//if (typeof msgpackr === 'undefined') { msgpackr = require('..') } | ||
var msgpack_msgpack = tryRequire('@msgpack/msgpack'); | ||
var msgpack_lite = tryRequire('msgpack-lite');*/ | ||
var unpack = msgpackr.unpack | ||
var pack = msgpackr.pack | ||
@@ -18,0 +25,0 @@ addCompatibilitySuite = (data) => () => { |
@@ -1,4 +0,6 @@ | ||
//var inspector = require('inspector') | ||
//inspector.open(9330, null, true) | ||
import * as CBOR from '../index.js' | ||
import chai from 'chai' | ||
//import('./test.mjs') | ||
import sampleData from './example4.json' | ||
//import inspector from 'inspector'; inspector.open(9330, null, true); debugger | ||
function tryRequire(module) { | ||
@@ -11,5 +13,4 @@ try { | ||
} | ||
if (typeof chai === 'undefined') { chai = require('chai') } | ||
assert = chai.assert | ||
if (typeof CBOR === 'undefined') { CBOR = require('..') } | ||
var assert = chai.assert | ||
var Encoder = CBOR.Encoder | ||
@@ -30,3 +31,2 @@ var EncoderStream = CBOR.EncoderStream | ||
var constants = zlib.constants | ||
import('./test.mjs') | ||
try { | ||
@@ -36,12 +36,3 @@ // var { decode, encode } = require('msgencode-lite') | ||
if (typeof XMLHttpRequest === 'undefined') { | ||
var fs = require('fs') | ||
var sampleData = JSON.parse(fs.readFileSync(__dirname + '/example5.json')) | ||
} else { | ||
var xhr = new XMLHttpRequest() | ||
xhr.open('GET', 'example4.json', false) | ||
xhr.send() | ||
var sampleData = JSON.parse(xhr.responseText) | ||
} | ||
var ITERATIONS = 10000 | ||
var ITERATIONS = 4000 | ||
@@ -117,4 +108,36 @@ suite('CBOR basic tests', function(){ | ||
}) | ||
if (typeof Buffer != 'undefined') | ||
test('replace data', function(){ | ||
var data1 = { | ||
data: [ | ||
{ a: 1, name: 'one', type: 'odd', isOdd: true, a: '13 characters' }, | ||
{ a: 2, name: 'two', type: 'even', a: '11 characte' }, | ||
{ a: 3, name: 'three', type: 'odd', isOdd: true, a: '12 character' }, | ||
{ a: 4, name: 'four', type: 'even', a: '9 charact'}, | ||
{ a: 5, name: 'five', type: 'odd', isOdd: true, a: '14 characters!' }, | ||
{ a: 6, name: 'six', type: 'even', isOdd: null } | ||
], | ||
} | ||
var data2 = { | ||
data: [ | ||
{ foo: 7, name: 'one', type: 'odd', isOdd: true }, | ||
{ foo: 8, name: 'two', type: 'even'}, | ||
{ foo: 9, name: 'three', type: 'odd', isOdd: true }, | ||
{ foo: 10, name: 'four', type: 'even'}, | ||
{ foo: 11, name: 'five', type: 'odd', isOdd: true }, | ||
{ foo: 12, name: 'six', type: 'even', isOdd: null } | ||
], | ||
} | ||
var serialized1 = encode(data1) | ||
var serialized2 = encode(data2) | ||
var b = Buffer.alloc(8000) | ||
serialized1.copy(b) | ||
var deserialized1 = decode(b, serialized1.length) | ||
serialized2.copy(b) | ||
var deserialized2 = decode(b, serialized2.length) | ||
assert.deepEqual(deserialized1, data1) | ||
assert.deepEqual(deserialized2, data2) | ||
}) | ||
test('extended class', function(){ | ||
test('extended class encode/decode', function(){ | ||
function Extended() { | ||
@@ -154,2 +177,3 @@ | ||
}) | ||
test.skip('text decoder', function() { | ||
@@ -195,3 +219,3 @@ let td = new TextDecoder('ISO-8859-15') | ||
test('structured cloning: types', function() { | ||
let b = Buffer.alloc(20) | ||
let b = typeof Buffer != 'undefined' ? Buffer.alloc(20) : new Uint8Array(20) | ||
let fa = new Float32Array(b.buffer, 8, 2) | ||
@@ -223,2 +247,32 @@ fa[0] = 2.25 | ||
test('object without prototype', function(){ | ||
var data = Object.create(null) | ||
data.test = 3 | ||
var serialized = encode(data) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
}) | ||
test('big buffer', function() { | ||
var size = 100000000 | ||
var data = new Uint8Array(size).fill(1) | ||
var encoded = encode(data) | ||
var decoded = decode(encoded) | ||
assert.equal(decoded.length, size) | ||
}) | ||
test('random strings', function(){ | ||
var data = [] | ||
for (var i = 0; i < 2000; i++) { | ||
var str = 'test' | ||
while (Math.random() < 0.7 && str.length < 0x100000) { | ||
str = str + String.fromCharCode(90/(Math.random() + 0.01)) + str | ||
} | ||
data.push(str) | ||
} | ||
var serialized = encode(data) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
}) | ||
test('map/date', function(){ | ||
@@ -282,2 +336,20 @@ var map = new Map() | ||
}) | ||
test('strings', function() { | ||
var data = [''] | ||
var serialized = encode(data) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
// do multiple times | ||
var serialized = encode(data) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
data = 'decode this: ᾜ' | ||
var serialized = encode(data) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
data = 'decode this that is longer but without any non-latin characters' | ||
var serialized = encode(data) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
}) | ||
test('decimal float32', function() { | ||
@@ -294,3 +366,3 @@ var data = { | ||
var serialized = encoder.encode(data) | ||
assert.equal(serialized.length, 37) | ||
assert.equal(serialized.length, 35) | ||
var deserialized = encoder.decode(serialized) | ||
@@ -327,2 +399,24 @@ assert.deepEqual(deserialized, data) | ||
}) | ||
test('bigint', function(){ | ||
var data = { | ||
bigintSmall: 352n, | ||
bigintSmallNegative: -333335252n, | ||
bigintBig: 2n**64n - 1n, // biggest possible | ||
bigintBigNegative: -(2n**63n), // largest negative | ||
mixedWithNormal: 44, | ||
} | ||
var serialized = encode(data) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
var tooBigInt = { | ||
tooBig: 2n**66n | ||
} | ||
assert.throws(function(){ serialized = encode(tooBigInt) }) | ||
let encoder = new Encoder({ | ||
largeBigIntToFloat: true | ||
}) | ||
serialized = encoder.encode(tooBigInt) | ||
deserialized = decode(serialized) | ||
assert.isTrue(deserialized.tooBig > 2n**65n) | ||
}) | ||
@@ -364,35 +458,12 @@ test('buffers', function() { | ||
var serialized = encode(data) | ||
deserialized = decode(serialized) | ||
var deserialized = decode(serialized) | ||
assert.deepEqual(deserialized, data) | ||
}) | ||
if (EncoderStream) { | ||
test('serialize/parse stream', () => { | ||
const serializeStream = new EncoderStream({ | ||
}) | ||
const parseStream = new DecoderStream() | ||
serializeStream.pipe(parseStream) | ||
const received = [] | ||
parseStream.on('data', data => { | ||
received.push(data) | ||
}) | ||
const messages = [{ | ||
name: 'first' | ||
}, { | ||
name: 'second' | ||
}, { | ||
name: 'third' | ||
}, { | ||
name: 'third', | ||
extra: [1, 3, { foo: 'hi'}, 'bye'] | ||
}] | ||
for (const message of messages) | ||
serializeStream.write(message) | ||
return new Promise((resolve, reject) => { | ||
setTimeout(() => { | ||
assert.deepEqual(received, messages) | ||
resolve() | ||
}, 10) | ||
}) | ||
}) | ||
} | ||
test('decodeMultiple', () => { | ||
let values = CBOR.decodeMultiple(new Uint8Array([1, 2, 3, 4])) | ||
assert.deepEqual(values, [1, 2, 3, 4]) | ||
values = [] | ||
CBOR.decodeMultiple(new Uint8Array([1, 2, 3, 4]), value => values.push(value)) | ||
assert.deepEqual(values, [1, 2, 3, 4]) | ||
}) | ||
@@ -436,3 +507,3 @@ }) | ||
let encoder = new Encoder({ structures }) | ||
let buffer = Buffer.alloc(0x10000) | ||
let buffer = typeof Buffer != 'undefined' ? Buffer.alloc(0x10000) : new Uint8Array(0x10000) | ||
@@ -439,0 +510,0 @@ for (var i = 0; i < ITERATIONS; i++) { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
371827
36
7253
292
Yes
10
9
2