msgpackr 1.11.1 → 1.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/pack.js CHANGED
@@ -523,11 +523,11 @@ export class Packr extends Unpackr {
523
523
  } else if (type === 'boolean') {
524
524
  target[position++] = value ? 0xc3 : 0xc2
525
525
  } else if (type === 'bigint') {
526
- if (value < (BigInt(1)<<BigInt(63)) && value >= -(BigInt(1)<<BigInt(63))) {
526
+ if (value < 0x8000000000000000 && value >= -0x8000000000000000) {
527
527
  // use a signed int as long as it fits
528
528
  target[position++] = 0xd3
529
529
  targetView.setBigInt64(position, value)
530
- } else if (value < (BigInt(1)<<BigInt(64)) && value > 0) {
530
+ } else if (value < 0x10000000000000000 && value > 0) {
531
531
  // if we can fit an unsigned int, use that
532
532
  target[position++] = 0xcf
533
533
  targetView.setBigUint64(position, value)
@@ -538,22 +538,46 @@ export class Packr extends Unpackr {
538
538
  targetView.setFloat64(position, Number(value))
539
539
  } else if (this.largeBigIntToString) {
540
540
  return pack(value.toString());
541
- } else if (this.useBigIntExtension && value < BigInt(2)**BigInt(1023) && value > -(BigInt(2)**BigInt(1023))) {
542
- target[position++] = 0xc7
543
- position++;
544
- target[position++] = 0x42 // "B" for BigInt
545
- let bytes = [];
546
- let alignedSign;
547
- do {
548
- let byte = value & BigInt(0xff);
549
- alignedSign = (byte & BigInt(0x80)) === (value < BigInt(0) ? BigInt(0x80) : BigInt(0));
550
- bytes.push(byte);
551
- value >>= BigInt(8);
552
- } while (!((value === BigInt(0) || value === BigInt(-1)) && alignedSign));
553
- target[position-2] = bytes.length;
554
- for (let i = bytes.length; i > 0;) {
555
- target[position++] = Number(bytes[--i]);
541
+ } else if (this.useBigIntExtension || this.moreTypes) {
542
+ let empty = value < 0 ? BigInt(-1) : BigInt(0)
543
+
544
+ let array
545
+ if (value >> BigInt(0x10000) === empty) {
546
+ let mask = BigInt(0x10000000000000000) - BigInt(1) // literal would overflow
547
+ let chunks = []
548
+ do {
549
+ chunks.push(value & mask)
550
+ value >>= BigInt(64)
551
+ } while (value !== empty)
552
+
553
+ array = new Uint8Array(new BigUint64Array(chunks).buffer)
554
+ array.reverse()
555
+ } else {
556
+ let invert = value < 0
557
+ let string = (invert ? ~value : value).toString(16)
558
+ if (string.length % 2) {
559
+ string = '0' + string
560
+ } else if (parseInt(string.charAt(0), 16) >= 8) {
561
+ string = '00' + string
562
+ }
563
+
564
+ if (hasNodeBuffer) {
565
+ array = Buffer.from(string, 'hex')
566
+ } else {
567
+ array = new Uint8Array(string.length / 2)
568
+ for (let i = 0; i < array.length; i++) {
569
+ array[i] = parseInt(string.slice(i * 2, i * 2 + 2), 16)
570
+ }
571
+ }
572
+
573
+ if (invert) {
574
+ for (let i = 0; i < array.length; i++) array[i] = ~array[i]
575
+ }
556
576
  }
577
+
578
+ if (array.length + position > safeEnd)
579
+ makeRoom(array.length + position)
580
+ position = writeExtensionData(array, target, position, 0x42)
557
581
  return
558
582
  } else {
559
583
  throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
@@ -857,7 +881,7 @@ export class Packr extends Unpackr {
857
881
  }
858
882
  }
859
883
 
860
- extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ]
884
+ extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, DataView, C1Type ]
861
885
  extensions = [{
862
886
  pack(date, allocateForWrite, pack) {
863
887
  let seconds = date.getTime() / 1000
@@ -944,6 +968,13 @@ extensions = [{
944
968
  else
945
969
  writeBuffer(typedArray, allocateForWrite)
946
970
  }
971
+ }, {
972
+ pack(arrayBuffer, allocateForWrite) {
973
+ if (this.moreTypes)
974
+ writeExtBuffer(arrayBuffer, 0x11, allocateForWrite)
975
+ else
976
+ writeBuffer(hasNodeBuffer ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite)
977
+ }
947
978
  }, {
948
979
  pack(c1, allocateForWrite) { // specific 0xC1 object
949
980
  let { target, position} = allocateForWrite(1)
@@ -986,7 +1017,7 @@ function writeBuffer(buffer, allocateForWrite) {
986
1017
  target[position++] = length >> 8
987
1018
  target[position++] = length & 0xff
988
1019
  } else {
989
- let { target, position, targetView } = allocateForWrite(length + 5)
1020
+ var { target, position, targetView } = allocateForWrite(length + 5)
990
1021
  target[position++] = 0xc6
991
1022
  targetView.setUint32(position, length)
992
1023
  position += 4
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "msgpackr",
3
3
  "author": "Kris Zyp",
4
- "version": "1.11.1",
4
+ "version": "1.11.3",
5
5
  "description": "Ultra-fast MessagePack implementation with extensions for records and structured cloning",
6
6
  "license": "MIT",
7
7
  "types": "./index.d.ts",
8
8
  "main": "./dist/node.cjs",
9
9
  "module": "./index.js",
10
+ "react-native": "./index.js",
10
11
  "keywords": [
11
12
  "MessagePack",
12
13
  "msgpack",
@@ -32,6 +33,7 @@
32
33
  "require": "./index.d.cts",
33
34
  "import": "./index.d.ts"
34
35
  },
36
+ "browser": "./index.js",
35
37
  "node": {
36
38
  "require": "./dist/node.cjs",
37
39
  "import": "./node-index.js"
@@ -47,6 +49,7 @@
47
49
  "require": "./pack.d.cts",
48
50
  "import": "./pack.d.ts"
49
51
  },
52
+ "browser": "./pack.js",
50
53
  "node": {
51
54
  "import": "./index.js",
52
55
  "require": "./dist/node.cjs"
@@ -62,6 +65,7 @@
62
65
  "require": "./unpack.d.cts",
63
66
  "import": "./unpack.d.ts"
64
67
  },
68
+ "browser": "./unpack.js",
65
69
  "node": {
66
70
  "import": "./index.js",
67
71
  "require": "./dist/node.cjs"
package/struct.js CHANGED
@@ -515,10 +515,14 @@ function readStruct(src, position, srcEnd, unpackr) {
515
515
  throw new Error('Could not find typed structure ' + recordId);
516
516
  }
517
517
  var construct = structure.construct;
518
+ var fullConstruct = structure.fullConstruct;
518
519
  if (!construct) {
519
520
  construct = structure.construct = function LazyObject() {
520
521
  }
521
- var prototype = construct.prototype;
522
+ fullConstruct = structure.fullConstruct = function LoadedObject() {
523
+ }
524
+ fullConstruct.prototype = unpackr.structPrototype ?? {};
525
+ var prototype = construct.prototype = unpackr.structPrototype ? Object.create(unpackr.structPrototype) : {};
522
526
  let properties = [];
523
527
  let currentOffset = 0;
524
528
  let lastRefProperty;
@@ -719,12 +723,12 @@ function readStruct(src, position, srcEnd, unpackr) {
719
723
  Object.defineProperty(prototype, property.key, { get: withSource(property.get), enumerable: true });
720
724
  let valueFunction = 'v' + i++;
721
725
  args.push(valueFunction);
722
- objectLiteralProperties.push('[' + JSON.stringify(property.key) + ']:' + valueFunction + '(s)');
726
+ objectLiteralProperties.push('o[' + JSON.stringify(property.key) + ']=' + valueFunction + '(s)');
723
727
  }
724
728
  if (hasInheritedProperties) {
725
729
  objectLiteralProperties.push('__proto__:this');
726
730
  }
727
- let toObject = (new Function(...args, 'return function(s){return{' + objectLiteralProperties.join(',') + '}}')).apply(null, properties.map(prop => prop.get));
731
+ let toObject = (new Function(...args, 'var c=this;return function(s){var o=new c();' + objectLiteralProperties.join(';') + ';return o;}')).apply(fullConstruct, properties.map(prop => prop.get));
728
732
  Object.defineProperty(prototype, 'toJSON', {
729
733
  value(omitUnderscoredProperties) {
730
734
  return toObject.call(this, this[sourceSymbol]);
package/unpack.js CHANGED
@@ -971,7 +971,10 @@ function asSafeString(property) {
971
971
  if (typeof property === 'string') return property;
972
972
  if (typeof property === 'number' || typeof property === 'boolean' || typeof property === 'bigint') return property.toString();
973
973
  if (property == null) return property + '';
974
- throw new Error('Invalid property type for record', typeof property);
974
+ if (currentUnpackr.allowArraysInMapKeys && Array.isArray(property) && property.flat().every(item => ['string', 'number', 'boolean', 'bigint'].includes(typeof item))) {
975
+ return property.flat().toString();
976
+ }
977
+ throw new Error(`Invalid property type for record: ${typeof property}`);
975
978
  }
976
979
  // the registration of the record definition extension (as "r")
977
980
  const recordDefinition = (id, highByte) => {
@@ -996,21 +999,47 @@ const recordDefinition = (id, highByte) => {
996
999
  currentExtensions[0] = () => {} // notepack defines extension 0 to mean undefined, so use that as the default here
997
1000
  currentExtensions[0].noBuffer = true
998
1001
 
999
- currentExtensions[0x42] = (data) => {
1000
- // decode bigint
1001
- let length = data.length;
1002
- let value = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0]);
1003
- for (let i = 1; i < length; i++) {
1004
- value <<= BigInt(8);
1005
- value += BigInt(data[i]);
1002
+ currentExtensions[0x42] = data => {
1003
+ let headLength = (data.byteLength % 8) || 8
1004
+ let head = BigInt(data[0] & 0x80 ? data[0] - 0x100 : data[0])
1005
+ for (let i = 1; i < headLength; i++) {
1006
+ head <<= BigInt(8)
1007
+ head += BigInt(data[i])
1006
1008
  }
1007
- return value;
1009
+ if (data.byteLength !== headLength) {
1010
+ let view = new DataView(data.buffer, data.byteOffset, data.byteLength)
1011
+ let decode = (start, end) => {
1012
+ let length = end - start
1013
+ if (length <= 40) {
1014
+ let out = view.getBigUint64(start)
1015
+ for (let i = start + 8; i < end; i += 8) {
1016
+ out <<= BigInt(64n)
1017
+ out |= view.getBigUint64(i)
1018
+ }
1019
+ return out
1020
+ }
1021
+ // if (length === 8) return view.getBigUint64(start)
1022
+ let middle = start + (length >> 4 << 3)
1023
+ let left = decode(start, middle)
1024
+ let right = decode(middle, end)
1025
+ return (left << BigInt((end - middle) * 8)) | right
1026
+ }
1027
+ head = (head << BigInt((view.byteLength - headLength) * 8)) | decode(headLength, view.byteLength)
1028
+ }
1029
+ return head
1008
1030
  }
1009
1031
 
1010
- let errors = { Error, TypeError, ReferenceError };
1032
+ let errors = {
1033
+ Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, AggregateError: typeof AggregateError === 'function' ? AggregateError : null,
1034
+ }
1011
1035
  currentExtensions[0x65] = () => {
1012
1036
  let data = read()
1013
- return (errors[data[0]] || Error)(data[1], { cause: data[2] })
1037
+ if (!errors[data[0]]) {
1038
+ let error = Error(data[1], { cause: data[2] })
1039
+ error.name = data[0]
1040
+ return error
1041
+ }
1042
+ return errors[data[0]](data[1], { cause: data[2] })
1014
1043
  }
1015
1044
 
1016
1045
  currentExtensions[0x69] = (data) => {
@@ -1021,20 +1050,33 @@ currentExtensions[0x69] = (data) => {
1021
1050
  referenceMap = new Map()
1022
1051
  let token = src[position]
1023
1052
  let target
1024
- // TODO: handle Maps, Sets, and other types that can cycle; this is complicated, because you potentially need to read
1025
- // ahead past references to record structure definitions
1053
+ // TODO: handle any other types that can cycle and make the code more robust if there are other extensions
1026
1054
  if (token >= 0x90 && token < 0xa0 || token == 0xdc || token == 0xdd)
1027
1055
  target = []
1056
+ else if (token >= 0x80 && token < 0x90 || token == 0xde || token == 0xdf)
1057
+ target = new Map()
1058
+ else if ((token >= 0xc7 && token <= 0xc9 || token >= 0xd4 && token <= 0xd8) && src[position + 1] === 0x73)
1059
+ target = new Set()
1028
1060
  else
1029
1061
  target = {}
1030
1062
 
1031
1063
  let refEntry = { target } // a placeholder object
1032
1064
  referenceMap.set(id, refEntry)
1033
1065
  let targetProperties = read() // read the next value as the target object to id
1034
- if (refEntry.used) // there is a cycle, so we have to assign properties to original target
1035
- return Object.assign(target, targetProperties)
1036
- refEntry.target = targetProperties // the placeholder wasn't used, replace with the deserialized one
1037
- return targetProperties // no cycle, can just use the returned read object
1066
+ if (!refEntry.used) {
1067
+ // no cycle, can just use the returned read object
1068
+ return refEntry.target = targetProperties // replace the placeholder with the real one
1069
+ } else {
1070
+ // there is a cycle, so we have to assign properties to original target
1071
+ Object.assign(target, targetProperties)
1072
+ }
1073
+
1074
+ // copy over map/set entries if we're able to
1075
+ if (target instanceof Map)
1076
+ for (let [k, v] of targetProperties.entries()) target.set(k, v)
1077
+ if (target instanceof Set)
1078
+ for (let i of Array.from(targetProperties)) target.add(i)
1079
+ return target
1038
1080
  }
1039
1081
 
1040
1082
  currentExtensions[0x70] = (data) => {
@@ -1053,18 +1095,16 @@ export const typedArrays = ['Int8','Uint8','Uint8Clamped','Int16','Uint16','Int3
1053
1095
  let glbl = typeof globalThis === 'object' ? globalThis : window;
1054
1096
  currentExtensions[0x74] = (data) => {
1055
1097
  let typeCode = data[0]
1098
+ // we always have to slice to get a new ArrayBuffer that is aligned
1099
+ let buffer = Uint8Array.prototype.slice.call(data, 1).buffer
1100
+
1056
1101
  let typedArrayName = typedArrays[typeCode]
1057
1102
  if (!typedArrayName) {
1058
- if (typeCode === 16) {
1059
- let ab = new ArrayBuffer(data.length - 1)
1060
- let u8 = new Uint8Array(ab)
1061
- u8.set(data.subarray(1))
1062
- return ab;
1063
- }
1103
+ if (typeCode === 16) return buffer
1104
+ if (typeCode === 17) return new DataView(buffer)
1064
1105
  throw new Error('Could not find typed array for code ' + typeCode)
1065
1106
  }
1066
- // we have to always slice/copy here to get a new ArrayBuffer that is word/byte aligned
1067
- return new glbl[typedArrayName](Uint8Array.prototype.slice.call(data, 1).buffer)
1107
+ return new glbl[typedArrayName](buffer)
1068
1108
  }
1069
1109
  currentExtensions[0x78] = () => {
1070
1110
  let data = read()
@@ -1092,13 +1132,13 @@ currentExtensions[0xff] = (data) => {
1092
1132
  return new Date(
1093
1133
  ((data[0] << 22) + (data[1] << 14) + (data[2] << 6) + (data[3] >> 2)) / 1000000 +
1094
1134
  ((data[3] & 0x3) * 0x100000000 + data[4] * 0x1000000 + (data[5] << 16) + (data[6] << 8) + data[7]) * 1000)
1095
- else if (data.length == 12)// TODO: Implement support for negative
1135
+ else if (data.length == 12)
1096
1136
  return new Date(
1097
1137
  ((data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]) / 1000000 +
1098
1138
  (((data[4] & 0x80) ? -0x1000000000000 : 0) + data[6] * 0x10000000000 + data[7] * 0x100000000 + data[8] * 0x1000000 + (data[9] << 16) + (data[10] << 8) + data[11]) * 1000)
1099
1139
  else
1100
1140
  return new Date('invalid')
1101
- } // notepack defines extension 0 to mean undefined, so use that as the default here
1141
+ }
1102
1142
  // registration of bulk record definition?
1103
1143
  // currentExtensions[0x52] = () =>
1104
1144