pack.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. import { Unpackr, mult10, C1Type, typedArrays, addExtension as unpackAddExtension } from './unpack.js'
  2. let textEncoder
  3. try {
  4. textEncoder = new TextEncoder()
  5. } catch (error) {}
  6. let extensions, extensionClasses
  7. const hasNodeBuffer = typeof Buffer !== 'undefined'
  8. const ByteArrayAllocate = hasNodeBuffer ?
  9. function(length) { return Buffer.allocUnsafeSlow(length) } : Uint8Array
  10. const ByteArray = hasNodeBuffer ? Buffer : Uint8Array
  11. const MAX_BUFFER_SIZE = hasNodeBuffer ? 0x100000000 : 0x7fd00000
  12. let target, keysTarget
  13. let targetView
  14. let position = 0
  15. let safeEnd
  16. let bundledStrings = null
  17. let writeStructSlots
  18. const MAX_BUNDLE_SIZE = 0x5500 // maximum characters such that the encoded bytes fits in 16 bits.
  19. const hasNonLatin = /[\u0080-\uFFFF]/
  20. export const RECORD_SYMBOL = Symbol('record-id')
  21. export class Packr extends Unpackr {
  22. constructor(options) {
  23. super(options)
  24. this.offset = 0
  25. let typeBuffer
  26. let start
  27. let hasSharedUpdate
  28. let structures
  29. let referenceMap
  30. let encodeUtf8 = ByteArray.prototype.utf8Write ? function(string, position) {
  31. return target.utf8Write(string, position, target.byteLength - position)
  32. } : (textEncoder && textEncoder.encodeInto) ?
  33. function(string, position) {
  34. return textEncoder.encodeInto(string, target.subarray(position)).written
  35. } : false
  36. let packr = this
  37. if (!options)
  38. options = {}
  39. let isSequential = options && options.sequential
  40. let hasSharedStructures = options.structures || options.saveStructures
  41. let maxSharedStructures = options.maxSharedStructures
  42. if (maxSharedStructures == null)
  43. maxSharedStructures = hasSharedStructures ? 32 : 0
  44. if (maxSharedStructures > 8160)
  45. throw new Error('Maximum maxSharedStructure is 8160')
  46. if (options.structuredClone && options.moreTypes == undefined) {
  47. this.moreTypes = true
  48. }
  49. let maxOwnStructures = options.maxOwnStructures
  50. if (maxOwnStructures == null)
  51. maxOwnStructures = hasSharedStructures ? 32 : 64
  52. if (!this.structures && options.useRecords != false)
  53. this.structures = []
  54. // two byte record ids for shared structures
  55. let useTwoByteRecords = maxSharedStructures > 32 || (maxOwnStructures + maxSharedStructures > 64)
  56. let sharedLimitId = maxSharedStructures + 0x40
  57. let maxStructureId = maxSharedStructures + maxOwnStructures + 0x40
  58. if (maxStructureId > 8256) {
  59. throw new Error('Maximum maxSharedStructure + maxOwnStructure is 8192')
  60. }
  61. let recordIdsToRemove = []
  62. let transitionsCount = 0
  63. let serializationsSinceTransitionRebuild = 0
  64. this.pack = this.encode = function(value, encodeOptions) {
  65. if (!target) {
  66. target = new ByteArrayAllocate(8192)
  67. targetView = target.dataView || (target.dataView = new DataView(target.buffer, 0, 8192))
  68. position = 0
  69. }
  70. safeEnd = target.length - 10
  71. if (safeEnd - position < 0x800) {
  72. // don't start too close to the end,
  73. target = new ByteArrayAllocate(target.length)
  74. targetView = target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))
  75. safeEnd = target.length - 10
  76. position = 0
  77. } else
  78. position = (position + 7) & 0x7ffffff8 // Word align to make any future copying of this buffer faster
  79. start = position
  80. if (encodeOptions & RESERVE_START_SPACE) position += (encodeOptions & 0xff)
  81. referenceMap = packr.structuredClone ? new Map() : null
  82. if (packr.bundleStrings && typeof value !== 'string') {
  83. bundledStrings = []
  84. bundledStrings.size = Infinity // force a new bundle start on first string
  85. } else
  86. bundledStrings = null
  87. structures = packr.structures
  88. if (structures) {
  89. if (structures.uninitialized)
  90. structures = packr._mergeStructures(packr.getStructures())
  91. let sharedLength = structures.sharedLength || 0
  92. if (sharedLength > maxSharedStructures) {
  93. //if (maxSharedStructures <= 32 && structures.sharedLength > 32) // TODO: could support this, but would need to update the limit ids
  94. throw new Error('Shared structures is larger than maximum shared structures, try increasing maxSharedStructures to ' + structures.sharedLength)
  95. }
  96. if (!structures.transitions) {
  97. // rebuild our structure transitions
  98. structures.transitions = Object.create(null)
  99. for (let i = 0; i < sharedLength; i++) {
  100. let keys = structures[i]
  101. if (!keys)
  102. continue
  103. let nextTransition, transition = structures.transitions
  104. for (let j = 0, l = keys.length; j < l; j++) {
  105. let key = keys[j]
  106. nextTransition = transition[key]
  107. if (!nextTransition) {
  108. nextTransition = transition[key] = Object.create(null)
  109. }
  110. transition = nextTransition
  111. }
  112. transition[RECORD_SYMBOL] = i + 0x40
  113. }
  114. this.lastNamedStructuresLength = sharedLength
  115. }
  116. if (!isSequential) {
  117. structures.nextId = sharedLength + 0x40
  118. }
  119. }
  120. if (hasSharedUpdate)
  121. hasSharedUpdate = false
  122. let encodingError;
  123. try {
  124. if (packr.randomAccessStructure && value && value.constructor && value.constructor === Object)
  125. writeStruct(value);
  126. else
  127. pack(value)
  128. let lastBundle = bundledStrings;
  129. if (bundledStrings)
  130. writeBundles(start, pack, 0)
  131. if (referenceMap && referenceMap.idsToInsert) {
  132. let idsToInsert = referenceMap.idsToInsert.sort((a, b) => a.offset > b.offset ? 1 : -1);
  133. let i = idsToInsert.length;
  134. let incrementPosition = -1;
  135. while (lastBundle && i > 0) {
  136. let insertionPoint = idsToInsert[--i].offset + start;
  137. if (insertionPoint < (lastBundle.stringsPosition + start) && incrementPosition === -1)
  138. incrementPosition = 0;
  139. if (insertionPoint > (lastBundle.position + start)) {
  140. if (incrementPosition >= 0)
  141. incrementPosition += 6;
  142. } else {
  143. if (incrementPosition >= 0) {
  144. // update the bundle reference now
  145. targetView.setUint32(lastBundle.position + start,
  146. targetView.getUint32(lastBundle.position + start) + incrementPosition)
  147. incrementPosition = -1; // reset
  148. }
  149. lastBundle = lastBundle.previous;
  150. i++;
  151. }
  152. }
  153. if (incrementPosition >= 0 && lastBundle) {
  154. // update the bundle reference now
  155. targetView.setUint32(lastBundle.position + start,
  156. targetView.getUint32(lastBundle.position + start) + incrementPosition)
  157. }
  158. position += idsToInsert.length * 6;
  159. if (position > safeEnd)
  160. makeRoom(position)
  161. packr.offset = position
  162. let serialized = insertIds(target.subarray(start, position), idsToInsert)
  163. referenceMap = null
  164. return serialized
  165. }
  166. packr.offset = position // update the offset so next serialization doesn't write over our buffer, but can continue writing to same buffer sequentially
  167. if (encodeOptions & REUSE_BUFFER_MODE) {
  168. target.start = start
  169. target.end = position
  170. return target
  171. }
  172. return target.subarray(start, position) // position can change if we call pack again in saveStructures, so we get the buffer now
  173. } catch(error) {
  174. encodingError = error;
  175. throw error;
  176. } finally {
  177. if (structures) {
  178. resetStructures();
  179. if (hasSharedUpdate && packr.saveStructures) {
  180. let sharedLength = structures.sharedLength || 0
  181. // we can't rely on start/end with REUSE_BUFFER_MODE since they will (probably) change when we save
  182. let returnBuffer = target.subarray(start, position)
  183. let newSharedData = prepareStructures(structures, packr);
  184. if (!encodingError) { // TODO: If there is an encoding error, should make the structures as uninitialized so they get rebuilt next time
  185. if (packr.saveStructures(newSharedData, newSharedData.isCompatible) === false) {
  186. // get updated structures and try again if the update failed
  187. return packr.pack(value, encodeOptions)
  188. }
  189. packr.lastNamedStructuresLength = sharedLength
  190. // don't keep large buffers around
  191. if (target.length > 0x40000000) target = null
  192. return returnBuffer
  193. }
  194. }
  195. }
  196. // don't keep large buffers around, they take too much memory and cause problems (limit at 1GB)
  197. if (target.length > 0x40000000) target = null
  198. if (encodeOptions & RESET_BUFFER_MODE)
  199. position = start
  200. }
  201. }
  202. const resetStructures = () => {
  203. if (serializationsSinceTransitionRebuild < 10)
  204. serializationsSinceTransitionRebuild++
  205. let sharedLength = structures.sharedLength || 0
  206. if (structures.length > sharedLength && !isSequential)
  207. structures.length = sharedLength
  208. if (transitionsCount > 10000) {
  209. // force a rebuild occasionally after a lot of transitions so it can get cleaned up
  210. structures.transitions = null
  211. serializationsSinceTransitionRebuild = 0
  212. transitionsCount = 0
  213. if (recordIdsToRemove.length > 0)
  214. recordIdsToRemove = []
  215. } else if (recordIdsToRemove.length > 0 && !isSequential) {
  216. for (let i = 0, l = recordIdsToRemove.length; i < l; i++) {
  217. recordIdsToRemove[i][RECORD_SYMBOL] = 0
  218. }
  219. recordIdsToRemove = []
  220. }
  221. }
  222. const packArray = (value) => {
  223. var length = value.length
  224. if (length < 0x10) {
  225. target[position++] = 0x90 | length
  226. } else if (length < 0x10000) {
  227. target[position++] = 0xdc
  228. target[position++] = length >> 8
  229. target[position++] = length & 0xff
  230. } else {
  231. target[position++] = 0xdd
  232. targetView.setUint32(position, length)
  233. position += 4
  234. }
  235. for (let i = 0; i < length; i++) {
  236. pack(value[i])
  237. }
  238. }
  239. const pack = (value) => {
  240. if (position > safeEnd)
  241. target = makeRoom(position)
  242. var type = typeof value
  243. var length
  244. if (type === 'string') {
  245. let strLength = value.length
  246. if (bundledStrings && strLength >= 4 && strLength < 0x1000) {
  247. if ((bundledStrings.size += strLength) > MAX_BUNDLE_SIZE) {
  248. let extStart
  249. let maxBytes = (bundledStrings[0] ? bundledStrings[0].length * 3 + bundledStrings[1].length : 0) + 10
  250. if (position + maxBytes > safeEnd)
  251. target = makeRoom(position + maxBytes)
  252. let lastBundle
  253. if (bundledStrings.position) { // here we use the 0x62 extension to write the last bundle and reserve space for the reference pointer to the next/current bundle
  254. lastBundle = bundledStrings
  255. target[position] = 0xc8 // ext 16
  256. position += 3 // reserve for the writing bundle size
  257. target[position++] = 0x62 // 'b'
  258. extStart = position - start
  259. position += 4 // reserve for writing bundle reference
  260. writeBundles(start, pack, 0) // write the last bundles
  261. targetView.setUint16(extStart + start - 3, position - start - extStart)
  262. } else { // here we use the 0x62 extension just to reserve the space for the reference pointer to the bundle (will be updated once the bundle is written)
  263. target[position++] = 0xd6 // fixext 4
  264. target[position++] = 0x62 // 'b'
  265. extStart = position - start
  266. position += 4 // reserve for writing bundle reference
  267. }
  268. bundledStrings = ['', ''] // create new ones
  269. bundledStrings.previous = lastBundle;
  270. bundledStrings.size = 0
  271. bundledStrings.position = extStart
  272. }
  273. let twoByte = hasNonLatin.test(value)
  274. bundledStrings[twoByte ? 0 : 1] += value
  275. target[position++] = 0xc1
  276. pack(twoByte ? -strLength : strLength);
  277. return
  278. }
  279. let headerSize
  280. // first we estimate the header size, so we can write to the correct location
  281. if (strLength < 0x20) {
  282. headerSize = 1
  283. } else if (strLength < 0x100) {
  284. headerSize = 2
  285. } else if (strLength < 0x10000) {
  286. headerSize = 3
  287. } else {
  288. headerSize = 5
  289. }
  290. let maxBytes = strLength * 3
  291. if (position + maxBytes > safeEnd)
  292. target = makeRoom(position + maxBytes)
  293. if (strLength < 0x40 || !encodeUtf8) {
  294. let i, c1, c2, strPosition = position + headerSize
  295. for (i = 0; i < strLength; i++) {
  296. c1 = value.charCodeAt(i)
  297. if (c1 < 0x80) {
  298. target[strPosition++] = c1
  299. } else if (c1 < 0x800) {
  300. target[strPosition++] = c1 >> 6 | 0xc0
  301. target[strPosition++] = c1 & 0x3f | 0x80
  302. } else if (
  303. (c1 & 0xfc00) === 0xd800 &&
  304. ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00
  305. ) {
  306. c1 = 0x10000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff)
  307. i++
  308. target[strPosition++] = c1 >> 18 | 0xf0
  309. target[strPosition++] = c1 >> 12 & 0x3f | 0x80
  310. target[strPosition++] = c1 >> 6 & 0x3f | 0x80
  311. target[strPosition++] = c1 & 0x3f | 0x80
  312. } else {
  313. target[strPosition++] = c1 >> 12 | 0xe0
  314. target[strPosition++] = c1 >> 6 & 0x3f | 0x80
  315. target[strPosition++] = c1 & 0x3f | 0x80
  316. }
  317. }
  318. length = strPosition - position - headerSize
  319. } else {
  320. length = encodeUtf8(value, position + headerSize)
  321. }
  322. if (length < 0x20) {
  323. target[position++] = 0xa0 | length
  324. } else if (length < 0x100) {
  325. if (headerSize < 2) {
  326. target.copyWithin(position + 2, position + 1, position + 1 + length)
  327. }
  328. target[position++] = 0xd9
  329. target[position++] = length
  330. } else if (length < 0x10000) {
  331. if (headerSize < 3) {
  332. target.copyWithin(position + 3, position + 2, position + 2 + length)
  333. }
  334. target[position++] = 0xda
  335. target[position++] = length >> 8
  336. target[position++] = length & 0xff
  337. } else {
  338. if (headerSize < 5) {
  339. target.copyWithin(position + 5, position + 3, position + 3 + length)
  340. }
  341. target[position++] = 0xdb
  342. targetView.setUint32(position, length)
  343. position += 4
  344. }
  345. position += length
  346. } else if (type === 'number') {
  347. if (value >>> 0 === value) {// positive integer, 32-bit or less
  348. // positive uint
  349. if (value < 0x20 || (value < 0x80 && this.useRecords === false) || (value < 0x40 && !this.randomAccessStructure)) {
  350. target[position++] = value
  351. } else if (value < 0x100) {
  352. target[position++] = 0xcc
  353. target[position++] = value
  354. } else if (value < 0x10000) {
  355. target[position++] = 0xcd
  356. target[position++] = value >> 8
  357. target[position++] = value & 0xff
  358. } else {
  359. target[position++] = 0xce
  360. targetView.setUint32(position, value)
  361. position += 4
  362. }
  363. } else if (value >> 0 === value) { // negative integer
  364. if (value >= -0x20) {
  365. target[position++] = 0x100 + value
  366. } else if (value >= -0x80) {
  367. target[position++] = 0xd0
  368. target[position++] = value + 0x100
  369. } else if (value >= -0x8000) {
  370. target[position++] = 0xd1
  371. targetView.setInt16(position, value)
  372. position += 2
  373. } else {
  374. target[position++] = 0xd2
  375. targetView.setInt32(position, value)
  376. position += 4
  377. }
  378. } else {
  379. let useFloat32
  380. if ((useFloat32 = this.useFloat32) > 0 && value < 0x100000000 && value >= -0x80000000) {
  381. target[position++] = 0xca
  382. targetView.setFloat32(position, value)
  383. let xShifted
  384. if (useFloat32 < 4 ||
  385. // this checks for rounding of numbers that were encoded in 32-bit float to nearest significant decimal digit that could be preserved
  386. ((xShifted = value * mult10[((target[position] & 0x7f) << 1) | (target[position + 1] >> 7)]) >> 0) === xShifted) {
  387. position += 4
  388. return
  389. } else
  390. position-- // move back into position for writing a double
  391. }
  392. target[position++] = 0xcb
  393. targetView.setFloat64(position, value)
  394. position += 8
  395. }
  396. } else if (type === 'object' || type === 'function') {
  397. if (!value)
  398. target[position++] = 0xc0
  399. else {
  400. if (referenceMap) {
  401. let referee = referenceMap.get(value)
  402. if (referee) {
  403. if (!referee.id) {
  404. let idsToInsert = referenceMap.idsToInsert || (referenceMap.idsToInsert = [])
  405. referee.id = idsToInsert.push(referee)
  406. }
  407. target[position++] = 0xd6 // fixext 4
  408. target[position++] = 0x70 // "p" for pointer
  409. targetView.setUint32(position, referee.id)
  410. position += 4
  411. return
  412. } else
  413. referenceMap.set(value, { offset: position - start })
  414. }
  415. let constructor = value.constructor
  416. if (constructor === Object) {
  417. writeObject(value)
  418. } else if (constructor === Array) {
  419. packArray(value)
  420. } else if (constructor === Map) {
  421. if (this.mapAsEmptyObject) target[position++] = 0x80
  422. else {
  423. length = value.size
  424. if (length < 0x10) {
  425. target[position++] = 0x80 | length
  426. } else if (length < 0x10000) {
  427. target[position++] = 0xde
  428. target[position++] = length >> 8
  429. target[position++] = length & 0xff
  430. } else {
  431. target[position++] = 0xdf
  432. targetView.setUint32(position, length)
  433. position += 4
  434. }
  435. for (let [key, entryValue] of value) {
  436. pack(key)
  437. pack(entryValue)
  438. }
  439. }
  440. } else {
  441. for (let i = 0, l = extensions.length; i < l; i++) {
  442. let extensionClass = extensionClasses[i]
  443. if (value instanceof extensionClass) {
  444. let extension = extensions[i]
  445. if (extension.write) {
  446. if (extension.type) {
  447. target[position++] = 0xd4 // one byte "tag" extension
  448. target[position++] = extension.type
  449. target[position++] = 0
  450. }
  451. let writeResult = extension.write.call(this, value)
  452. if (writeResult === value) { // avoid infinite recursion
  453. if (Array.isArray(value)) {
  454. packArray(value)
  455. } else {
  456. writeObject(value)
  457. }
  458. } else {
  459. pack(writeResult)
  460. }
  461. return
  462. }
  463. let currentTarget = target
  464. let currentTargetView = targetView
  465. let currentPosition = position
  466. target = null
  467. let result
  468. try {
  469. result = extension.pack.call(this, value, (size) => {
  470. // restore target and use it
  471. target = currentTarget
  472. currentTarget = null
  473. position += size
  474. if (position > safeEnd)
  475. makeRoom(position)
  476. return {
  477. target, targetView, position: position - size
  478. }
  479. }, pack)
  480. } finally {
  481. // restore current target information (unless already restored)
  482. if (currentTarget) {
  483. target = currentTarget
  484. targetView = currentTargetView
  485. position = currentPosition
  486. safeEnd = target.length - 10
  487. }
  488. }
  489. if (result) {
  490. if (result.length + position > safeEnd)
  491. makeRoom(result.length + position)
  492. position = writeExtensionData(result, target, position, extension.type)
  493. }
  494. return
  495. }
  496. }
  497. // check isArray after extensions, because extensions can extend Array
  498. if (Array.isArray(value)) {
  499. packArray(value)
  500. } else {
  501. // use this as an alternate mechanism for expressing how to serialize
  502. if (value.toJSON) {
  503. const json = value.toJSON()
  504. // if for some reason value.toJSON returns itself it'll loop forever
  505. if (json !== value)
  506. return pack(json)
  507. }
  508. // if there is a writeFunction, use it, otherwise just encode as undefined
  509. if (type === 'function')
  510. return pack(this.writeFunction && this.writeFunction(value));
  511. // no extension found, write as plain object
  512. writeObject(value)
  513. }
  514. }
  515. }
  516. } else if (type === 'boolean') {
  517. target[position++] = value ? 0xc3 : 0xc2
  518. } else if (type === 'bigint') {
  519. if (value < (BigInt(1)<<BigInt(63)) && value >= -(BigInt(1)<<BigInt(63))) {
  520. // use a signed int as long as it fits
  521. target[position++] = 0xd3
  522. targetView.setBigInt64(position, value)
  523. } else if (value < (BigInt(1)<<BigInt(64)) && value > 0) {
  524. // if we can fit an unsigned int, use that
  525. target[position++] = 0xcf
  526. targetView.setBigUint64(position, value)
  527. } else {
  528. // overflow
  529. if (this.largeBigIntToFloat) {
  530. target[position++] = 0xcb
  531. targetView.setFloat64(position, Number(value))
  532. } else if (this.largeBigIntToString) {
  533. return pack(value.toString());
  534. } else if (this.useBigIntExtension && value < BigInt(2)**BigInt(1023) && value > -(BigInt(2)**BigInt(1023))) {
  535. target[position++] = 0xc7
  536. position++;
  537. target[position++] = 0x42 // "B" for BigInt
  538. let bytes = [];
  539. let alignedSign;
  540. do {
  541. let byte = value & BigInt(0xff);
  542. alignedSign = (byte & BigInt(0x80)) === (value < BigInt(0) ? BigInt(0x80) : BigInt(0));
  543. bytes.push(byte);
  544. value >>= BigInt(8);
  545. } while (!((value === BigInt(0) || value === BigInt(-1)) && alignedSign));
  546. target[position-2] = bytes.length;
  547. for (let i = bytes.length; i > 0;) {
  548. target[position++] = Number(bytes[--i]);
  549. }
  550. return
  551. } else {
  552. throw new RangeError(value + ' was too large to fit in MessagePack 64-bit integer format, use' +
  553. ' useBigIntExtension, or set largeBigIntToFloat to convert to float-64, or set' +
  554. ' largeBigIntToString to convert to string')
  555. }
  556. }
  557. position += 8
  558. } else if (type === 'undefined') {
  559. if (this.encodeUndefinedAsNil)
  560. target[position++] = 0xc0
  561. else {
  562. target[position++] = 0xd4 // a number of implementations use fixext1 with type 0, data 0 to denote undefined, so we follow suite
  563. target[position++] = 0
  564. target[position++] = 0
  565. }
  566. } else {
  567. throw new Error('Unknown type: ' + type)
  568. }
  569. }
  570. const writePlainObject = (this.variableMapSize || this.coercibleKeyAsNumber || this.skipValues) ? (object) => {
  571. // this method is slightly slower, but generates "preferred serialization" (optimally small for smaller objects)
  572. let keys;
  573. if (this.skipValues) {
  574. keys = [];
  575. for (let key in object) {
  576. if ((typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) &&
  577. !this.skipValues.includes(object[key]))
  578. keys.push(key);
  579. }
  580. } else {
  581. keys = Object.keys(object)
  582. }
  583. let length = keys.length
  584. if (length < 0x10) {
  585. target[position++] = 0x80 | length
  586. } else if (length < 0x10000) {
  587. target[position++] = 0xde
  588. target[position++] = length >> 8
  589. target[position++] = length & 0xff
  590. } else {
  591. target[position++] = 0xdf
  592. targetView.setUint32(position, length)
  593. position += 4
  594. }
  595. let key
  596. if (this.coercibleKeyAsNumber) {
  597. for (let i = 0; i < length; i++) {
  598. key = keys[i]
  599. let num = Number(key)
  600. pack(isNaN(num) ? key : num)
  601. pack(object[key])
  602. }
  603. } else {
  604. for (let i = 0; i < length; i++) {
  605. pack(key = keys[i])
  606. pack(object[key])
  607. }
  608. }
  609. } :
  610. (object) => {
  611. target[position++] = 0xde // always using map 16, so we can preallocate and set the length afterwards
  612. let objectOffset = position - start
  613. position += 2
  614. let size = 0
  615. for (let key in object) {
  616. if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
  617. pack(key)
  618. pack(object[key])
  619. size++
  620. }
  621. }
  622. if (size > 0xffff) {
  623. throw new Error('Object is too large to serialize with fast 16-bit map size,' +
  624. ' use the "variableMapSize" option to serialize this object');
  625. }
  626. target[objectOffset++ + start] = size >> 8
  627. target[objectOffset + start] = size & 0xff
  628. }
  629. const writeRecord = this.useRecords === false ? writePlainObject :
  630. (options.progressiveRecords && !useTwoByteRecords) ? // this is about 2% faster for highly stable structures, since it only requires one for-in loop (but much more expensive when new structure needs to be written)
  631. (object) => {
  632. let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
  633. let objectOffset = position++ - start
  634. let wroteKeys
  635. for (let key in object) {
  636. if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
  637. nextTransition = transition[key]
  638. if (nextTransition)
  639. transition = nextTransition
  640. else {
  641. // record doesn't exist, create full new record and insert it
  642. let keys = Object.keys(object)
  643. let lastTransition = transition
  644. transition = structures.transitions
  645. let newTransitions = 0
  646. for (let i = 0, l = keys.length; i < l; i++) {
  647. let key = keys[i]
  648. nextTransition = transition[key]
  649. if (!nextTransition) {
  650. nextTransition = transition[key] = Object.create(null)
  651. newTransitions++
  652. }
  653. transition = nextTransition
  654. }
  655. if (objectOffset + start + 1 == position) {
  656. // first key, so we don't need to insert, we can just write record directly
  657. position--
  658. newRecord(transition, keys, newTransitions)
  659. } else // otherwise we need to insert the record, moving existing data after the record
  660. insertNewRecord(transition, keys, objectOffset, newTransitions)
  661. wroteKeys = true
  662. transition = lastTransition[key]
  663. }
  664. pack(object[key])
  665. }
  666. }
  667. if (!wroteKeys) {
  668. let recordId = transition[RECORD_SYMBOL]
  669. if (recordId)
  670. target[objectOffset + start] = recordId
  671. else
  672. insertNewRecord(transition, Object.keys(object), objectOffset, 0)
  673. }
  674. } :
  675. (object) => {
  676. let nextTransition, transition = structures.transitions || (structures.transitions = Object.create(null))
  677. let newTransitions = 0
  678. for (let key in object) if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
  679. nextTransition = transition[key]
  680. if (!nextTransition) {
  681. nextTransition = transition[key] = Object.create(null)
  682. newTransitions++
  683. }
  684. transition = nextTransition
  685. }
  686. let recordId = transition[RECORD_SYMBOL]
  687. if (recordId) {
  688. if (recordId >= 0x60 && useTwoByteRecords) {
  689. target[position++] = ((recordId -= 0x60) & 0x1f) + 0x60
  690. target[position++] = recordId >> 5
  691. } else
  692. target[position++] = recordId
  693. } else {
  694. newRecord(transition, transition.__keys__ || Object.keys(object), newTransitions)
  695. }
  696. // now write the values
  697. for (let key in object)
  698. if (typeof object.hasOwnProperty !== 'function' || object.hasOwnProperty(key)) {
  699. pack(object[key])
  700. }
  701. }
  702. // create reference to useRecords if useRecords is a function
  703. const checkUseRecords = typeof this.useRecords == 'function' && this.useRecords;
  704. const writeObject = checkUseRecords ? (object) => {
  705. checkUseRecords(object) ? writeRecord(object) : writePlainObject(object)
  706. } : writeRecord
  707. const makeRoom = (end) => {
  708. let newSize
  709. if (end > 0x1000000) {
  710. // special handling for really large buffers
  711. if ((end - start) > MAX_BUFFER_SIZE)
  712. throw new Error('Packed buffer would be larger than maximum buffer size')
  713. newSize = Math.min(MAX_BUFFER_SIZE,
  714. Math.round(Math.max((end - start) * (end > 0x4000000 ? 1.25 : 2), 0x400000) / 0x1000) * 0x1000)
  715. } else // faster handling for smaller buffers
  716. newSize = ((Math.max((end - start) << 2, target.length - 1) >> 12) + 1) << 12
  717. let newBuffer = new ByteArrayAllocate(newSize)
  718. targetView = newBuffer.dataView || (newBuffer.dataView = new DataView(newBuffer.buffer, 0, newSize))
  719. end = Math.min(end, target.length)
  720. if (target.copy)
  721. target.copy(newBuffer, 0, start, end)
  722. else
  723. newBuffer.set(target.slice(start, end))
  724. position -= start
  725. start = 0
  726. safeEnd = newBuffer.length - 10
  727. return target = newBuffer
  728. }
  729. const newRecord = (transition, keys, newTransitions) => {
  730. let recordId = structures.nextId
  731. if (!recordId)
  732. recordId = 0x40
  733. if (recordId < sharedLimitId && this.shouldShareStructure && !this.shouldShareStructure(keys)) {
  734. recordId = structures.nextOwnId
  735. if (!(recordId < maxStructureId))
  736. recordId = sharedLimitId
  737. structures.nextOwnId = recordId + 1
  738. } else {
  739. if (recordId >= maxStructureId)// cycle back around
  740. recordId = sharedLimitId
  741. structures.nextId = recordId + 1
  742. }
  743. let highByte = keys.highByte = recordId >= 0x60 && useTwoByteRecords ? (recordId - 0x60) >> 5 : -1
  744. transition[RECORD_SYMBOL] = recordId
  745. transition.__keys__ = keys
  746. structures[recordId - 0x40] = keys
  747. if (recordId < sharedLimitId) {
  748. keys.isShared = true
  749. structures.sharedLength = recordId - 0x3f
  750. hasSharedUpdate = true
  751. if (highByte >= 0) {
  752. target[position++] = (recordId & 0x1f) + 0x60
  753. target[position++] = highByte
  754. } else {
  755. target[position++] = recordId
  756. }
  757. } else {
  758. if (highByte >= 0) {
  759. target[position++] = 0xd5 // fixext 2
  760. target[position++] = 0x72 // "r" record defintion extension type
  761. target[position++] = (recordId & 0x1f) + 0x60
  762. target[position++] = highByte
  763. } else {
  764. target[position++] = 0xd4 // fixext 1
  765. target[position++] = 0x72 // "r" record defintion extension type
  766. target[position++] = recordId
  767. }
  768. if (newTransitions)
  769. transitionsCount += serializationsSinceTransitionRebuild * newTransitions
  770. // record the removal of the id, we can maintain our shared structure
  771. if (recordIdsToRemove.length >= maxOwnStructures)
  772. recordIdsToRemove.shift()[RECORD_SYMBOL] = 0 // we are cycling back through, and have to remove old ones
  773. recordIdsToRemove.push(transition)
  774. pack(keys)
  775. }
  776. }
  777. const insertNewRecord = (transition, keys, insertionOffset, newTransitions) => {
  778. let mainTarget = target
  779. let mainPosition = position
  780. let mainSafeEnd = safeEnd
  781. let mainStart = start
  782. target = keysTarget
  783. position = 0
  784. start = 0
  785. if (!target)
  786. keysTarget = target = new ByteArrayAllocate(8192)
  787. safeEnd = target.length - 10
  788. newRecord(transition, keys, newTransitions)
  789. keysTarget = target
  790. let keysPosition = position
  791. target = mainTarget
  792. position = mainPosition
  793. safeEnd = mainSafeEnd
  794. start = mainStart
  795. if (keysPosition > 1) {
  796. let newEnd = position + keysPosition - 1
  797. if (newEnd > safeEnd)
  798. makeRoom(newEnd)
  799. let insertionPosition = insertionOffset + start
  800. target.copyWithin(insertionPosition + keysPosition, insertionPosition + 1, position)
  801. target.set(keysTarget.slice(0, keysPosition), insertionPosition)
  802. position = newEnd
  803. } else {
  804. target[insertionOffset + start] = keysTarget[0]
  805. }
  806. }
  807. const writeStruct = (object) => {
  808. let newPosition = writeStructSlots(object, target, start, position, structures, makeRoom, (value, newPosition, notifySharedUpdate) => {
  809. if (notifySharedUpdate)
  810. return hasSharedUpdate = true;
  811. position = newPosition;
  812. let startTarget = target;
  813. pack(value);
  814. resetStructures();
  815. if (startTarget !== target) {
  816. return { position, targetView, target }; // indicate the buffer was re-allocated
  817. }
  818. return position;
  819. }, this);
  820. if (newPosition === 0) // bail and go to a msgpack object
  821. return writeObject(object);
  822. position = newPosition;
  823. }
  824. }
  825. useBuffer(buffer) {
  826. // this means we are finished using our own buffer and we can write over it safely
  827. target = buffer
  828. target.dataView || (target.dataView = new DataView(target.buffer, target.byteOffset, target.byteLength))
  829. position = 0
  830. }
  831. set position (value) {
  832. position = value;
  833. }
  834. get position() {
  835. return position;
  836. }
  837. clearSharedData() {
  838. if (this.structures)
  839. this.structures = []
  840. if (this.typedStructs)
  841. this.typedStructs = []
  842. }
  843. }
  844. extensionClasses = [ Date, Set, Error, RegExp, ArrayBuffer, Object.getPrototypeOf(Uint8Array.prototype).constructor /*TypedArray*/, C1Type ]
  845. extensions = [{
  846. pack(date, allocateForWrite, pack) {
  847. let seconds = date.getTime() / 1000
  848. if ((this.useTimestamp32 || date.getMilliseconds() === 0) && seconds >= 0 && seconds < 0x100000000) {
  849. // Timestamp 32
  850. let { target, targetView, position} = allocateForWrite(6)
  851. target[position++] = 0xd6
  852. target[position++] = 0xff
  853. targetView.setUint32(position, seconds)
  854. } else if (seconds > 0 && seconds < 0x100000000) {
  855. // Timestamp 64
  856. let { target, targetView, position} = allocateForWrite(10)
  857. target[position++] = 0xd7
  858. target[position++] = 0xff
  859. targetView.setUint32(position, date.getMilliseconds() * 4000000 + ((seconds / 1000 / 0x100000000) >> 0))
  860. targetView.setUint32(position + 4, seconds)
  861. } else if (isNaN(seconds)) {
  862. if (this.onInvalidDate) {
  863. allocateForWrite(0)
  864. return pack(this.onInvalidDate())
  865. }
  866. // Intentionally invalid timestamp
  867. let { target, targetView, position} = allocateForWrite(3)
  868. target[position++] = 0xd4
  869. target[position++] = 0xff
  870. target[position++] = 0xff
  871. } else {
  872. // Timestamp 96
  873. let { target, targetView, position} = allocateForWrite(15)
  874. target[position++] = 0xc7
  875. target[position++] = 12
  876. target[position++] = 0xff
  877. targetView.setUint32(position, date.getMilliseconds() * 1000000)
  878. targetView.setBigInt64(position + 4, BigInt(Math.floor(seconds)))
  879. }
  880. }
  881. }, {
  882. pack(set, allocateForWrite, pack) {
  883. if (this.setAsEmptyObject) {
  884. allocateForWrite(0);
  885. return pack({})
  886. }
  887. let array = Array.from(set)
  888. let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
  889. if (this.moreTypes) {
  890. target[position++] = 0xd4
  891. target[position++] = 0x73 // 's' for Set
  892. target[position++] = 0
  893. }
  894. pack(array)
  895. }
  896. }, {
  897. pack(error, allocateForWrite, pack) {
  898. let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
  899. if (this.moreTypes) {
  900. target[position++] = 0xd4
  901. target[position++] = 0x65 // 'e' for error
  902. target[position++] = 0
  903. }
  904. pack([ error.name, error.message, error.cause ])
  905. }
  906. }, {
  907. pack(regex, allocateForWrite, pack) {
  908. let { target, position} = allocateForWrite(this.moreTypes ? 3 : 0)
  909. if (this.moreTypes) {
  910. target[position++] = 0xd4
  911. target[position++] = 0x78 // 'x' for regeXp
  912. target[position++] = 0
  913. }
  914. pack([ regex.source, regex.flags ])
  915. }
  916. }, {
  917. pack(arrayBuffer, allocateForWrite) {
  918. if (this.moreTypes)
  919. writeExtBuffer(arrayBuffer, 0x10, allocateForWrite)
  920. else
  921. writeBuffer(hasNodeBuffer ? Buffer.from(arrayBuffer) : new Uint8Array(arrayBuffer), allocateForWrite)
  922. }
  923. }, {
  924. pack(typedArray, allocateForWrite) {
  925. let constructor = typedArray.constructor
  926. if (constructor !== ByteArray && this.moreTypes)
  927. writeExtBuffer(typedArray, typedArrays.indexOf(constructor.name), allocateForWrite)
  928. else
  929. writeBuffer(typedArray, allocateForWrite)
  930. }
  931. }, {
  932. pack(c1, allocateForWrite) { // specific 0xC1 object
  933. let { target, position} = allocateForWrite(1)
  934. target[position] = 0xc1
  935. }
  936. }]
  937. function writeExtBuffer(typedArray, type, allocateForWrite, encode) {
  938. let length = typedArray.byteLength
  939. if (length + 1 < 0x100) {
  940. var { target, position } = allocateForWrite(4 + length)
  941. target[position++] = 0xc7
  942. target[position++] = length + 1
  943. } else if (length + 1 < 0x10000) {
  944. var { target, position } = allocateForWrite(5 + length)
  945. target[position++] = 0xc8
  946. target[position++] = (length + 1) >> 8
  947. target[position++] = (length + 1) & 0xff
  948. } else {
  949. var { target, position, targetView } = allocateForWrite(7 + length)
  950. target[position++] = 0xc9
  951. targetView.setUint32(position, length + 1) // plus one for the type byte
  952. position += 4
  953. }
  954. target[position++] = 0x74 // "t" for typed array
  955. target[position++] = type
  956. if (!typedArray.buffer) typedArray = new Uint8Array(typedArray)
  957. target.set(new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength), position)
  958. }
  959. function writeBuffer(buffer, allocateForWrite) {
  960. let length = buffer.byteLength
  961. var target, position
  962. if (length < 0x100) {
  963. var { target, position } = allocateForWrite(length + 2)
  964. target[position++] = 0xc4
  965. target[position++] = length
  966. } else if (length < 0x10000) {
  967. var { target, position } = allocateForWrite(length + 3)
  968. target[position++] = 0xc5
  969. target[position++] = length >> 8
  970. target[position++] = length & 0xff
  971. } else {
  972. var { target, position, targetView } = allocateForWrite(length + 5)
  973. target[position++] = 0xc6
  974. targetView.setUint32(position, length)
  975. position += 4
  976. }
  977. target.set(buffer, position)
  978. }
  979. function writeExtensionData(result, target, position, type) {
  980. let length = result.length
  981. switch (length) {
  982. case 1:
  983. target[position++] = 0xd4
  984. break
  985. case 2:
  986. target[position++] = 0xd5
  987. break
  988. case 4:
  989. target[position++] = 0xd6
  990. break
  991. case 8:
  992. target[position++] = 0xd7
  993. break
  994. case 16:
  995. target[position++] = 0xd8
  996. break
  997. default:
  998. if (length < 0x100) {
  999. target[position++] = 0xc7
  1000. target[position++] = length
  1001. } else if (length < 0x10000) {
  1002. target[position++] = 0xc8
  1003. target[position++] = length >> 8
  1004. target[position++] = length & 0xff
  1005. } else {
  1006. target[position++] = 0xc9
  1007. target[position++] = length >> 24
  1008. target[position++] = (length >> 16) & 0xff
  1009. target[position++] = (length >> 8) & 0xff
  1010. target[position++] = length & 0xff
  1011. }
  1012. }
  1013. target[position++] = type
  1014. target.set(result, position)
  1015. position += length
  1016. return position
  1017. }
  1018. function insertIds(serialized, idsToInsert) {
  1019. // insert the ids that need to be referenced for structured clones
  1020. let nextId
  1021. let distanceToMove = idsToInsert.length * 6
  1022. let lastEnd = serialized.length - distanceToMove
  1023. while (nextId = idsToInsert.pop()) {
  1024. let offset = nextId.offset
  1025. let id = nextId.id
  1026. serialized.copyWithin(offset + distanceToMove, offset, lastEnd)
  1027. distanceToMove -= 6
  1028. let position = offset + distanceToMove
  1029. serialized[position++] = 0xd6
  1030. serialized[position++] = 0x69 // 'i'
  1031. serialized[position++] = id >> 24
  1032. serialized[position++] = (id >> 16) & 0xff
  1033. serialized[position++] = (id >> 8) & 0xff
  1034. serialized[position++] = id & 0xff
  1035. lastEnd = offset
  1036. }
  1037. return serialized
  1038. }
  1039. function writeBundles(start, pack, incrementPosition) {
  1040. if (bundledStrings.length > 0) {
  1041. targetView.setUint32(bundledStrings.position + start, position + incrementPosition - bundledStrings.position - start)
  1042. bundledStrings.stringsPosition = position - start;
  1043. let writeStrings = bundledStrings
  1044. bundledStrings = null
  1045. pack(writeStrings[0])
  1046. pack(writeStrings[1])
  1047. }
  1048. }
  1049. export function addExtension(extension) {
  1050. if (extension.Class) {
  1051. if (!extension.pack && !extension.write)
  1052. throw new Error('Extension has no pack or write function')
  1053. if (extension.pack && !extension.type)
  1054. throw new Error('Extension has no type (numeric code to identify the extension)')
  1055. extensionClasses.unshift(extension.Class)
  1056. extensions.unshift(extension)
  1057. }
  1058. unpackAddExtension(extension)
  1059. }
  1060. function prepareStructures(structures, packr) {
  1061. structures.isCompatible = (existingStructures) => {
  1062. let compatible = !existingStructures || ((packr.lastNamedStructuresLength || 0) === existingStructures.length)
  1063. if (!compatible) // we want to merge these existing structures immediately since we already have it and we are in the right transaction
  1064. packr._mergeStructures(existingStructures);
  1065. return compatible;
  1066. }
  1067. return structures
  1068. }
  1069. export function setWriteStructSlots(writeSlots, makeStructures) {
  1070. writeStructSlots = writeSlots;
  1071. prepareStructures = makeStructures;
  1072. }
  1073. let defaultPackr = new Packr({ useRecords: false })
  1074. export const pack = defaultPackr.pack
  1075. export const encode = defaultPackr.pack
  1076. export const Encoder = Packr
  1077. export { FLOAT32_OPTIONS } from './unpack.js'
  1078. import { FLOAT32_OPTIONS } from './unpack.js'
  1079. export const { NEVER, ALWAYS, DECIMAL_ROUND, DECIMAL_FIT } = FLOAT32_OPTIONS
  1080. export const REUSE_BUFFER_MODE = 512
  1081. export const RESET_BUFFER_MODE = 1024
  1082. export const RESERVE_START_SPACE = 2048