| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- */
- "use strict";
- /** @import { ObjectDeserializerContext, ObjectSerializerContext } from "./ObjectMiddleware" */
- /** @typedef {ObjectSerializerContext<EXPECTED_ANY>["write"]} CacheAssoc */
- /**
- * Defines the shared type used by this module.
- * @type {WeakMap<CacheAssoc, ObjectStructure<PropertyKey>>}
- */
- const cache = new WeakMap();
- /**
- * Represents ObjectStructure.
- * @template {PropertyKey} K
- */
- class ObjectStructure {
- constructor() {
- /** @type {undefined | K[]} */
- this.keys = undefined;
- /** @type {undefined | Map<K, ObjectStructure<K>>} */
- this.children = undefined;
- }
- /**
- * Returns keys.
- * @param {K[]} keys keys
- * @returns {K[]} keys
- */
- getKeys(keys) {
- if (this.keys === undefined) this.keys = keys;
- return this.keys;
- }
- /**
- * Returns object structure.
- * @param {K} key key
- * @returns {ObjectStructure<K>} object structure
- */
- key(key) {
- if (this.children === undefined) this.children = new Map();
- const child = this.children.get(key);
- if (child !== undefined) return child;
- /** @type {ObjectStructure<K>} */
- const newChild = new ObjectStructure();
- this.children.set(key, newChild);
- return newChild;
- }
- }
- /**
- * Returns keys.
- * @template {PropertyKey} K
- * @param {K[]} keys keys
- * @param {CacheAssoc} cacheAssoc cache assoc fn
- * @returns {K[]} keys
- */
- const getCachedKeys = (keys, cacheAssoc) => {
- let root = /** @type {ObjectStructure<K> | undefined} */ (
- cache.get(cacheAssoc)
- );
- if (root === undefined) {
- root = new ObjectStructure();
- cache.set(cacheAssoc, root);
- }
- let current = root;
- for (const key of keys) {
- current = current.key(key);
- }
- return current.getKeys(keys);
- };
- class PlainObjectSerializer {
- /**
- * Serializes this instance into the provided serializer context.
- * @template {object} T
- * @param {T} obj plain object
- * @param {ObjectSerializerContext<((keyof T)[] | keyof T | null | T[keyof T])[]>} context context
- */
- serialize(obj, context) {
- const keys = /** @type {(keyof T)[]} */ (Object.keys(obj));
- if (keys.length > 128) {
- // Objects with so many keys are unlikely to share structure
- // with other objects
- context.write(keys);
- for (const key of keys) {
- context.write(obj[key]);
- }
- } else if (keys.length > 1) {
- context.write(getCachedKeys(keys, context.write));
- for (const key of keys) {
- context.write(obj[key]);
- }
- } else if (keys.length === 1) {
- const key = keys[0];
- context.write(key);
- context.write(obj[key]);
- } else {
- context.write(null);
- }
- }
- /**
- * Restores this instance from the provided deserializer context.
- * @template {object} T
- * @param {ObjectDeserializerContext<((keyof T)[] | keyof T | null | T[keyof T])[]>} context context
- * @returns {T} plain object
- */
- deserialize(context) {
- const keys = /** @type {(keyof T)[] | keyof T | null} */ (context.read());
- const obj = /** @type {T} */ ({});
- if (Array.isArray(keys)) {
- for (const key of keys) {
- obj[/** @type {keyof T} */ (key)] = /** @type {T[keyof T]} */ (
- context.read()
- );
- }
- } else if (keys !== null) {
- obj[/** @type {keyof T} */ (keys)] = /** @type {T[keyof T]} */ (
- context.read()
- );
- }
- return obj;
- }
- }
- module.exports = PlainObjectSerializer;
|