lib/objectRecorder.js

'use strict'

/**
 * Record objects that pass by in a stream.  If the same object is used more
 * than once, it can be value-shared using shared values.
 *
 * @see {@link http://cbor.schmorp.de/value-sharing}
 */
class ObjectRecorder {
  constructor() {
    this.clear()
  }

  /**
   * Clear all of the objects that have been seen.  Revert to recording mode.
   */
  clear() {
    this.map = new WeakMap()
    this.count = 0
    this.recording = true
  }

  /**
   * Stop recording.
   */
  stop() {
    this.recording = false
  }

  /**
   * Determine if wrapping a tag 28 or 29 around an object that has been
   * reused is appropriate.  This method stores state for which objects have
   * been seen.
   *
   * @param {object} obj Any object about to be serialized.
   * @returns {number} If recording: -1 for first use, index for second use.
   *   If not recording, -1 for never-duplicated, -2 for first use, index for
   *   subsequent uses.
   * @throws {Error} Recording does not match playback.
   */
  check(obj) {
    const val = this.map.get(obj)
    if (val) {
      if (val.length > 1) {
        if (val[0] || this.recording) {
          return val[1]
        }

        val[0] = true
        return ObjectRecorder.FIRST
      }
      if (!this.recording) {
        return ObjectRecorder.NEVER
      }
      val.push(this.count++)
      // Second use while recording
      return val[1]
    }
    if (!this.recording) {
      throw new Error('New object detected when not recording')
    }
    this.map.set(obj, [false])
    // First use while recording
    return ObjectRecorder.NEVER
  }
}

ObjectRecorder.NEVER = -1
ObjectRecorder.FIRST = -2

module.exports = ObjectRecorder