text: /**
   * Efficient schema-less binary encoding with support for variable length encoding.
   *
   * Use [lib0/encoding] with [lib0/decoding]. Every encoding function has a corresponding decoding function.
   *
   * Encodes numbers in little-endian order (least to most significant byte order)
   * and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
   * which is also used in Protocol Buffers.
   *
   * ```js
   * // encoding step
   * const encoder = new encoding.createEncoder()
   * encoding.writeVarUint(encoder, 256)
   * encoding.writeVarString(encoder, 'Hello world!')
   * const buf = encoding.toUint8Array(encoder)
   * ```
   *
   * ```js
   * // decoding step
   * const decoder = new decoding.createDecoder(buf)
   * decoding.readVarUint(decoder) // => 256
   * decoding.readVarString(decoder) // => 'Hello world!'
   * decoding.hasContent(decoder) // => false - all data is read
   * ```
   *
   * @module encoding
   */
  
  import * as buffer from './buffer.js'
  import * as math from './math.js'
  import * as number from './number.js'
  import * as binary from './binary.js'
  import * as string from './string.js'
  
  /**
   * A BinaryEncoder handles the encoding to an Uint8Array.
   */
  export class Encoder {
    constructor () {
      this.cpos = 0
      this.cbuf = new Uint8Array(100)
      /**
       * @type {Array<Uint8Array>}
       */
      this.bufs = []
    }
  }
  
  /**
   * @function
   * @return {Encoder}
   */
  export const createEncoder = () => new Encoder()
  
  /**
   * The current length of the encoded data.
   *
   * @function
   * @param {Encoder} encoder
   * @return {number}
   */
  export const length = encoder => {
    let len = encoder.cpos
    for (let i = 0; i < encoder.bufs.length; i++) {
      len += encoder.bufs[i].length
    }
    return len
  }
  
  /**
   * Transform to Uint8Array.
   *
   * @function
   * @param {Encoder} encoder
   * @return {Uint8Array} The created ArrayBuffer.
   */
  export const toUint8Array = encoder => {
    const uint8arr = new Uint8Array(length(encoder))
    let curPos = 0
    for (let i = 0; i < encoder.bufs.length; i++) {
      const d = encoder.bufs[i]
      uint8arr.set(d, curPos)
      curPos += d.length
    }
    uint8arr.set(buffer.createUint8ArrayViewFromArrayBuffer(encoder.cbuf.buffer, 0, encoder.cpos), curPos)
    return uint8arr
  }
  
  /**
   * Verify that it is possible to write `len` bytes wtihout checking. If
   * necessary, a new Buffer with the required length is attached.
   *
   * @param {Encoder} encoder
   * @param {number} len
   */
  const verifyLen = (encoder, len) => {
    const bufferLen = encoder.cbuf.length
    if (bufferLen - encoder.cpos < len) {
      encoder.bufs.push(buffer.createUint8ArrayViewFromArrayBuffer(encoder.cbuf.buffer, 0, encoder.cpos))
      encoder.cbuf = new Uint8Array(math.max(bufferLen, len) * 2)
      encoder.cpos = 0
    }
  }
  
  /**
   * Write one byte to the encoder.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} num The byte that is to be encoded.
   */
  export const write = (encoder, num) => {
    const bufferLen = encoder.cbuf.length
    if (encoder.cpos === bufferLen) {
      encoder.bufs.push(encoder.cbuf)
      encoder.cbuf = new Uint8Array(bufferLen * 2)
      encoder.cpos = 0
    }
    encoder.cbuf[encoder.cpos++] = num
  }
  
  /**
   * Write one byte at a specific position.
   * Position must already be written (i.e. encoder.length > pos)
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} pos Position to which to write data
   * @param {number} num Unsigned 8-bit integer
   */
  export const set = (encoder, pos, num) => {
    let buffer = null
    // iterate all buffers and adjust position
    for (let i = 0; i < encoder.bufs.length && buffer === null; i++) {
      const b = encoder.bufs[i]
      if (pos < b.length) {
        buffer = b // found buffer
      } else {
        pos -= b.length
      }
    }
    if (buffer === null) {
      // use current buffer
      buffer = encoder.cbuf
    }
    buffer[pos] = num
  }
  
  /**
   * Write one byte as an unsigned integer.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} num The number that is to be encoded.
   */
  export const writeUint8 = write
  
  /**
   * Write one byte as an unsigned Integer at a specific location.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} pos The location where the data will be written.
   * @param {number} num The number that is to be encoded.
   */
  export const setUint8 = set
  
  /**
   * Write two bytes as an unsigned integer.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} num The number that is to be encoded.
   */
  export const writeUint16 = (encoder, num) => {
    write(encoder, num & binary.BITS8)
    write(encoder, (num >>> 8) & binary.BITS8)
  }
  /**
   * Write two bytes as an unsigned integer at a specific location.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} pos The location where the data will be written.
   * @param {number} num The number that is to be encoded.
   */
  export const setUint16 = (encoder, pos, num) => {
    set(encoder, pos, num & binary.BITS8)
    set(encoder, pos + 1, (num >>> 8) & binary.BITS8)
  }
  
  /**
   * Write two bytes as an unsigned integer
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} num The number that is to be encoded.
   */
  export const writeUint32 = (encoder, num) => {
    for (let i = 0; i < 4; i++) {
      write(encoder, num & binary.BITS8)
      num >>>= 8
    }
  }
  
  /**
   * Write two bytes as an unsigned integer in big endian order.
   * (most significant byte first)
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} num The number that is to be encoded.
   */
  export const writeUint32BigEndian = (encoder, num) => {
    for (let i = 3; i >= 0; i--) {
      write(encoder, (num >>> (8 * i)) & binary.BITS8)
    }
  }
  
  /**
   * Write two bytes as an unsigned integer at a specific location.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} pos The location where the data will be written.
   * @param {number} num The number that is to be encoded.
   */
  export const setUint32 = (encoder, pos, num) => {
    for (let i = 0; i < 4; i++) {
      set(encoder, pos + i, num & binary.BITS8)
      num >>>= 8
    }
  }
  
  /**
   * Write a variable length unsigned integer. Max encodable integer is 2^53.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} num The number that is to be encoded.
   */
  export const writeVarUint = (encoder, num) => {
    while (num > binary.BITS7) {
      write(encoder, binary.BIT8 | (binary.BITS7 & num))
      num = math.floor(num / 128) // shift >>> 7
    }
    write(encoder, binary.BITS7 & num)
  }
  
  /**
   * Write a variable length integer.
   *
   * We use the 7th bit instead for signaling that this is a negative number.
   *
   * @function
   * @param {Encoder} encoder
   * @param {number} num The number that is to be encoded.
   */
  export const writeVarInt = (encoder, num) => {
    const isNegative = math.isNegativeZero(num)
    if (isNegative) {
      num = -num
    }
    //             |- whether to continue reading         |- whether is negative     |- number
    write(encoder, (num > binary.BITS6 ? binary.BIT8 : 0) | (isNegative ? binary.BIT7 : 0) | (binary.BITS6 & num))
    num = math.floor(num / 64) // shift >>> 6
    // We don't need to consider the case of num === 0 so we can use a different
    // pattern here than above.
    while (num > 0) {
      write(encoder, (num > binary.BITS7 ? binary.BIT8 : 0) | (binary.BITS7 & num))
      num = math.floor(num / 128) // shift >>> 7
    }
  }
  
  /**
   * A cache to store strings temporarily
   */
  const _strBuffer = new Uint8Array(30000)
  const _maxStrBSize = _strBuffer.length / 3
  
  /**
   * Write a variable length string.
   *
   * @function
   * @param {Encoder} encoder
   * @param {String} str The string that is to be encoded.
   */
  export const _writeVarStringNative = (encoder, str) => {
    if (str.length < _maxStrBSize) {
      // We can encode the string into the existing buffer
      /* istanbul ignore else */
      const written = string.utf8TextEncoder.encodeInto(str, _strBuffer).written || 0
      writeVarUint(encoder, written)
      for (let i = 0; i < written; i++) {
        write(encoder, _strBuffer[i])
      }
    } else {
      writeVarUint8Array(encoder, string.encodeUtf8(str))
    }
  }
  
  /**
   * Write a variable length string.
   *
   * @function
   * @param {Encoder} encoder
   * @param {String} str The string that is to be encoded.
   */
  export const _writeVarStringPolyfill = (encoder, str) => {
    const encodedString = unescape(encodeURIComponent(str))
    const len = encodedString.length
    writeVarUint(encoder, len)
    for (let i = 0; i < len; i++) {
      write(encoder, /** @type {number} */ (encodedString.codePointAt(i)))
    }
  }
  
  /**
   * Write a variable length string.
   *
   * @function
   * @param {Encoder} encoder
   * @param {String} str The string that is to be encoded.
   */
  /* istanbul ignore next */
  export const writeVarString = string.utf8TextEncoder ? _writeVarStringNative : _writeVarStringPolyfill
  
  /**
   * Write the content of another Encoder.
   *
   * @TODO: can be improved!
   *        - Note: Should consider that when appending a lot of small Encoders, we should rather clone than referencing the old structure.
   *                Encoders start with a rather big initial buffer.
   *
   * @function
   * @param {Encoder} encoder The enUint8Arr
   * @param {Encoder} append The BinaryEncoder to be written.
   */
  export const writeBinaryEncoder = (encoder, append) => writeUint8Array(encoder, toUint8Array(append))
  
  /**
   * Append fixed-length Uint8Array to the encoder.
   *
   * @function
   * @param {Encoder} encoder
   * @param {Uint8Array} uint8Array
   */
  export const writeUint8Array = (encoder, uint8Array) => {
    const bufferLen = encoder.cbuf.length
    const cpos = encoder.cpos
    const leftCopyLen = math.min(bufferLen - cpos, uint8Array.length)
    const rightCopyLen = uint8Array.length - leftCopyLen
    encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos)
    encoder.cpos += leftCopyLen
    if (rightCopyLen > 0) {
      // Still something to write, write right half..
      // Append new buffer
      encoder.bufs.push(encoder.cbuf)
      // must have at least size of remaining buffer
      encoder.cbuf = new Uint8Array(math.max(bufferLen * 2, rightCopyLen))
      // copy array
      encoder.cbuf.set(uint8Array.subarray(leftCopyLen))
      encoder.cpos = rightCopyLen
    }
  }
  
  /**
   * Append an Uint8Array to Encoder.
   *
   * @function
   * @param {Encoder} encoder
   * @param {Uint8Array} uint8Array
   */
  export const writeVarUint8Array = (encoder, uint8Array) => {
    writeVarUint(encoder, uint8Array.byteLength)
    writeUint8Array(encoder, uint8Array)
  }
  
  /**
   * Create an DataView of the next `len` bytes. Use it to write data after
   * calling this function.
   *
   * ```js
   * // write float32 using DataView
   * const dv = writeOnDataView(encoder, 4)
   * dv.setFloat32(0, 1.1)
   * // read float32 using DataView
   * const dv = readFromDataView(encoder, 4)
   * dv.getFloat32(0) // => 1.100000023841858 (leaving it to the reader to find out why this is the correct result)
   * ```
   *
   * @param {Encoder} encoder
   * @param {number} len
   * @return {DataView}
   */
  export const writeOnDataView = (encoder, len) => {
    verifyLen(encoder, len)
    const dview = new DataView(encoder.cbuf.buffer, encoder.cpos, len)
    encoder.cpos += len
    return dview
  }
  
  /**
   * @param {Encoder} encoder
   * @param {number} num
   */
  export const writeFloat32 = (encoder, num) => writeOnDataView(encoder, 4).setFloat32(0, num, false)
  
  /**
   * @param {Encoder} encoder
   * @param {number} num
   */
  export const writeFloat64 = (encoder, num) => writeOnDataView(encoder, 8).setFloat64(0, num, false)
  
  /**
   * @param {Encoder} encoder
   * @param {bigint} num
   */
  export const writeBigInt64 = (encoder, num) => /** @type {any} */ (writeOnDataView(encoder, 8)).setBigInt64(0, num, false)
  
  /**
   * @param {Encoder} encoder
   * @param {bigint} num
   */
  export const writeBigUint64 = (encoder, num) => /** @type {any} */ (writeOnDataView(encoder, 8)).setBigUint64(0, num, false)
  
  const floatTestBed = new DataView(new ArrayBuffer(4))
  /**
   * Check if a number can be encoded as a 32 bit float.
   *
   * @param {number} num
   * @return {boolean}
   */
  const isFloat32 = num => {
    floatTestBed.setFloat32(0, num)
    return floatTestBed.getFloat32(0) === num
  }
  
  /**
   * Encode data with efficient binary format.
   *
   * Differences to JSON:
   * • Transforms data to a binary format (not to a string)
   * • Encodes undefined, NaN, and ArrayBuffer (these can't be represented in JSON)
   * • Numbers are efficiently encoded either as a variable length integer, as a
   *   32 bit float, as a 64 bit float, or as a 64 bit bigint.
   *
   * Encoding table:
   *
   * | Data Type           | Prefix   | Encoding Method    | Comment |
   * | ------------------- | -------- | ------------------ | ------- |
   * | undefined           | 127      |                    | Functions, symbol, and everything that cannot be identified is encoded as undefined |
   * | null                | 126      |                    | |
   * | integer             | 125      | writeVarInt        | Only encodes 32 bit signed integers |
   * | float32             | 124      | writeFloat32       | |
   * | float64             | 123      | writeFloat64       | |
   * | bigint              | 122      | writeBigInt64      | |
   * | boolean (false)     | 121      |                    | True and false are different data types so we save the following byte |
   * | boolean (true)      | 120      |                    | - 0b01111000 so the last bit determines whether true or false |
   * | string              | 119      | writeVarString     | |
   * | object<string,any>  | 118      | custom             | Writes {length} then {length} key-value pairs |
   * | array<any>          | 117      | custom             | Writes {length} then {length} json values |
   * | Uint8Array          | 116      | writeVarUint8Array | We use Uint8Array for any kind of binary data |
   *
   * Reasons for the decreasing prefix:
   * We need the first bit for extendability (later we may want to encode the
   * prefix with writeVarUint). The remaining 7 bits are divided as follows:
   * [0-30]   the beginning of the data range is used for custom purposes
   *          (defined by the function that uses this library)
   * [31-127] the end of the data range is used for data encoding by
   *          lib0/encoding.js
   *
   * @param {Encoder} encoder
   * @param {undefined|null|number|bigint|boolean|string|Object<string,any>|Array<any>|Uint8Array} data
   */
  export const writeAny = (encoder, data) => {
    switch (typeof data) {
      case 'string':
        // TYPE 119: STRING
        write(encoder, 119)
        writeVarString(encoder, data)
        break
      case 'number':
        if (number.isInteger(data) && math.abs(data) <= binary.BITS31) {
          // TYPE 125: INTEGER
          write(encoder, 125)
          writeVarInt(encoder, data)
        } else if (isFloat32(data)) {
          // TYPE 124: FLOAT32
          write(encoder, 124)
          writeFloat32(encoder, data)
        } else {
          // TYPE 123: FLOAT64
          write(encoder, 123)
          writeFloat64(encoder, data)
        }
        break
      case 'bigint':
        // TYPE 122: BigInt
        write(encoder, 122)
        writeBigInt64(encoder, data)
        break
      case 'object':
        if (data === null) {
          // TYPE 126: null
          write(encoder, 126)
        } else if (data instanceof Array) {
          // TYPE 117: Array
          write(encoder, 117)
          writeVarUint(encoder, data.length)
          for (let i = 0; i < data.length; i++) {
            writeAny(encoder, data[i])
          }
        } else if (data instanceof Uint8Array) {
          // TYPE 116: ArrayBuffer
          write(encoder, 116)
          writeVarUint8Array(encoder, data)
        } else {
          // TYPE 118: Object
          write(encoder, 118)
          const keys = Object.keys(data)
          writeVarUint(encoder, keys.length)
          for (let i = 0; i < keys.length; i++) {
            const key = keys[i]
            writeVarString(encoder, key)
            writeAny(encoder, data[key])
          }
        }
        break
      case 'boolean':
        // TYPE 120/121: boolean (true/false)
        write(encoder, data ? 120 : 121)
        break
      default:
        // TYPE 127: undefined
        write(encoder, 127)
    }
  }
  
  /**
   * Now come a few stateful encoder that have their own classes.
   */
  
  /**
   * Basic Run Length Encoder - a basic compression implementation.
   *
   * Encodes [1,1,1,7] to [1,3,7,1] (3 times 1, 1 time 7). This encoder might do more harm than good if there are a lot of values that are not repeated.
   *
   * It was originally used for image compression. Cool .. article http://csbruce.com/cbm/transactor/pdfs/trans_v7_i06.pdf
   *
   * @note T must not be null!
   *
   * @template T
   */
  export class RleEncoder extends Encoder {
    /**
     * @param {function(Encoder, T):void} writer
     */
    constructor (writer) {
      super()
      /**
       * The writer
       */
      this.w = writer
      /**
       * Current state
       * @type {T|null}
       */
      this.s = null
      this.count = 0
    }
  
    /**
     * @param {T} v
     */
    write (v) {
      if (this.s === v) {
        this.count++
      } else {
        if (this.count > 0) {
          // flush counter, unless this is the first value (count = 0)
          writeVarUint(this, this.count - 1) // since count is always > 0, we can decrement by one. non-standard encoding ftw
        }
        this.count = 1
        // write first value
        this.w(this, v)
        this.s = v
      }
    }
  }
  
  /**
   * Basic diff decoder using variable length encoding.
   *
   * Encodes the values [3, 1100, 1101, 1050, 0] to [3, 1097, 1, -51, -1050] using writeVarInt.
   */
  export class IntDiffEncoder extends Encoder {
    /**
     * @param {number} start
     */
    constructor (start) {
      super()
      /**
       * Current state
       * @type {number}
       */
      this.s = start
    }
  
    /**
     * @param {number} v
     */
    write (v) {
      writeVarInt(this, v - this.s)
      this.s = v
    }
  }
  
  /**
   * A combination of IntDiffEncoder and RleEncoder.
   *
   * Basically first writes the IntDiffEncoder and then counts duplicate diffs using RleEncoding.
   *
   * Encodes the values [1,1,1,2,3,4,5,6] as [1,1,0,2,1,5] (RLE([1,0,0,1,1,1,1,1]) ⇒ RleIntDiff[1,1,0,2,1,5])
   */
  export class RleIntDiffEncoder extends Encoder {
    /**
     * @param {number} start
     */
    constructor (start) {
      super()
      /**
       * Current state
       * @type {number}
       */
      this.s = start
      this.count = 0
    }
  
    /**
     * @param {number} v
     */
    write (v) {
      if (this.s === v && this.count > 0) {
        this.count++
      } else {
        if (this.count > 0) {
          // flush counter, unless this is the first value (count = 0)
          writeVarUint(this, this.count - 1) // since count is always > 0, we can decrement by one. non-standard encoding ftw
        }
        this.count = 1
        // write first value
        writeVarInt(this, v - this.s)
        this.s = v
      }
    }
  }
  
  /**
   * @param {UintOptRleEncoder} encoder
   */
  const flushUintOptRleEncoder = encoder => {
    /* istanbul ignore else */
    if (encoder.count > 0) {
      // flush counter, unless this is the first value (count = 0)
      // case 1: just a single value. set sign to positive
      // case 2: write several values. set sign to negative to indicate that there is a length coming
      writeVarInt(encoder.encoder, encoder.count === 1 ? encoder.s : -encoder.s)
      if (encoder.count > 1) {
        writeVarUint(encoder.encoder, encoder.count - 2) // since count is always > 1, we can decrement by one. non-standard encoding ftw
      }
    }
  }
  
  /**
   * Optimized Rle encoder that does not suffer from the mentioned problem of the basic Rle encoder.
   *
   * Internally uses VarInt encoder to write unsigned integers. If the input occurs multiple times, we write
   * write it as a negative number. The UintOptRleDecoder then understands that it needs to read a count.
   *
   * Encodes [1,2,3,3,3] as [1,2,-3,3] (once 1, once 2, three times 3)
   */
  export class UintOptRleEncoder {
    constructor () {
      this.encoder = new Encoder()
      /**
       * @type {number}
       */
      this.s = 0
      this.count = 0
    }
  
    /**
     * @param {number} v
     */
    write (v) {
      if (this.s === v) {
        this.count++
      } else {
        flushUintOptRleEncoder(this)
        this.count = 1
        this.s = v
      }
    }
  
    toUint8Array () {
      flushUintOptRleEncoder(this)
      return toUint8Array(this.encoder)
    }
  }
  
  /**
   * Increasing Uint Optimized RLE Encoder
   *
   * The RLE encoder counts the number of same occurences of the same value.
   * The IncUintOptRle encoder counts if the value increases.
   * I.e. 7, 8, 9, 10 will be encoded as [-7, 4]. 1, 3, 5 will be encoded
   * as [1, 3, 5].
   */
  export class IncUintOptRleEncoder {
    constructor () {
      this.encoder = new Encoder()
      /**
       * @type {number}
       */
      this.s = 0
      this.count = 0
    }
  
    /**
     * @param {number} v
     */
    write (v) {
      if (this.s + this.count === v) {
        this.count++
      } else {
        flushUintOptRleEncoder(this)
        this.count = 1
        this.s = v
      }
    }
  
    toUint8Array () {
      flushUintOptRleEncoder(this)
      return toUint8Array(this.encoder)
    }
  }
  
  /**
   * @param {IntDiffOptRleEncoder} encoder
   */
  const flushIntDiffOptRleEncoder = encoder => {
    if (encoder.count > 0) {
      //          31 bit making up the diff | wether to write the counter
      // const encodedDiff = encoder.diff << 1 | (encoder.count === 1 ? 0 : 1)
      const encodedDiff = encoder.diff * 2 + (encoder.count === 1 ? 0 : 1)
      // flush counter, unless this is the first value (count = 0)
      // case 1: just a single value. set first bit to positive
      // case 2: write several values. set first bit to negative to indicate that there is a length coming
      writeVarInt(encoder.encoder, encodedDiff)
      if (encoder.count > 1) {
        writeVarUint(encoder.encoder, encoder.count - 2) // since count is always > 1, we can decrement by one. non-standard encoding ftw
      }
    }
  }
  
  /**
   * A combination of the IntDiffEncoder and the UintOptRleEncoder.
   *
   * The count approach is similar to the UintDiffOptRleEncoder, but instead of using the negative bitflag, it encodes
   * in the LSB whether a count is to be read. Therefore this Encoder only supports 31 bit integers!
   *
   * Encodes [1, 2, 3, 2] as [3, 1, 6, -1] (more specifically [(1 << 1) | 1, (3 << 0) | 0, -1])
   *
   * Internally uses variable length encoding. Contrary to normal UintVar encoding, the first byte contains:
   * * 1 bit that denotes whether the next value is a count (LSB)
   * * 1 bit that denotes whether this value is negative (MSB - 1)
   * * 1 bit that denotes whether to continue reading the variable length integer (MSB)
   *
   * Therefore, only five bits remain to encode diff ranges.
   *
   * Use this Encoder only when appropriate. In most cases, this is probably a bad idea.
   */
  export class IntDiffOptRleEncoder {
    constructor () {
      this.encoder = new Encoder()
      /**
       * @type {number}
       */
      this.s = 0
      this.count = 0
      this.diff = 0
    }
  
    /**
     * @param {number} v
     */
    write (v) {
      if (this.diff === v - this.s) {
        this.s = v
        this.count++
      } else {
        flushIntDiffOptRleEncoder(this)
        this.count = 1
        this.diff = v - this.s
        this.s = v
      }
    }
  
    toUint8Array () {
      flushIntDiffOptRleEncoder(this)
      return toUint8Array(this.encoder)
    }
  }
  
  /**
   * Optimized String Encoder.
   *
   * Encoding many small strings in a simple Encoder is not very efficient. The function call to decode a string takes some time and creates references that must be eventually deleted.
   * In practice, when decoding several million small strings, the GC will kick in more and more often to collect orphaned string objects (or maybe there is another reason?).
   *
   * This string encoder solves the above problem. All strings are concatenated and written as a single string using a single encoding call.
   *
   * The lengths are encoded using a UintOptRleEncoder.
   */
  export class StringEncoder {
    constructor () {
      /**
       * @type {Array<string>}
       */
      this.sarr = []
      this.s = ''
      this.lensE = new UintOptRleEncoder()
    }
  
    /**
     * @param {string} string
     */
    write (string) {
      this.s += string
      if (this.s.length > 19) {
        this.sarr.push(this.s)
        this.s = ''
      }
      this.lensE.write(string.length)
    }
  
    toUint8Array () {
      const encoder = new Encoder()
      this.sarr.push(this.s)
      this.s = ''
      writeVarString(encoder, this.sarr.join(''))
      writeUint8Array(encoder, this.lensE.toUint8Array())
      return toUint8Array(encoder)
    }
  }
  