OverviewFilesCommitsIssuesDocsReleasesPeers

mirall-cli / node_modules / bare-subprocess / lib / framers / advanced.js

107 lines · javascript ref:
const structuredClone = require('bare-structured-clone')
const errors = require('../errors')

const MAX_FRAME_SIZE = 0xffffffff

module.exports = class SubprocessAdvancedFramer {
  constructor() {
    this._buffer = []
    this._buffered = 0
    this._bodyLength = -1
  }

  encode(message) {
    const serialized = structuredClone.serialize(message)

    const state = { start: 0, end: 4, buffer: null }

    structuredClone.preencode(state, serialized)

    const bodyLength = state.end - 4

    if (bodyLength > MAX_FRAME_SIZE) {
      throw errors.MESSAGE_TOO_LARGE(
        `IPC message exceeds the maximum frame size of ${MAX_FRAME_SIZE} bytes`
      )
    }

    const frame = Buffer.alloc(state.end)
    state.buffer = frame

    frame.writeUInt32LE(bodyLength, 0)
    state.start += 4

    structuredClone.encode(state, serialized)

    return frame
  }

  push(chunk, onmessage) {
    this._buffer.push(chunk)
    this._buffered += chunk.byteLength

    this._consume(onmessage)
  }

  _consume(onmessage) {
    while (true) {
      if (this._bodyLength === -1) {
        if (this._buffered < 4) return

        const header = this._read(4)
        this._bodyLength = header.readUInt32LE(0)
      }

      if (this._buffered < this._bodyLength) return

      const body = this._read(this._bodyLength)
      this._bodyLength = -1

      let message
      try {
        const state = { start: 0, end: body.byteLength, buffer: body }

        message = structuredClone.deserialize(structuredClone.decode(state))
      } catch (err) {
        throw errors.INVALID_MESSAGE('Invalid IPC message', err)
      }

      onmessage(message)
    }
  }

  _read(n) {
    this._buffered -= n

    if (this._buffer[0].byteLength === n) {
      return this._buffer.shift()
    }

    if (this._buffer[0].byteLength > n) {
      const head = this._buffer[0]
      this._buffer[0] = head.subarray(n)

      return head.subarray(0, n)
    }

    const parts = []
    let remaining = n

    while (remaining > 0) {
      const head = this._buffer[0]

      if (head.byteLength <= remaining) {
        parts.push(head)
        remaining -= head.byteLength
        this._buffer.shift()
      } else {
        parts.push(head.subarray(0, remaining))
        this._buffer[0] = head.subarray(remaining)
        remaining = 0
      }
    }

    return Buffer.concat(parts)
  }
}