OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-booking / node_modules / quickbit-native / index.js

218 lines · javascript ref:
const binding = require('./binding')

exports.get = function get(field, bit) {
  const n = field.byteLength * 8

  if (bit < 0) bit += n
  if (bit < 0 || bit >= n) return false

  return binding.quickbit_napi_get(toBuffer(field), bit) !== 0
}

exports.set = function set(field, bit, value = true) {
  const n = field.byteLength * 8

  if (bit < 0) bit += n
  if (bit < 0 || bit >= n) return false

  return binding.quickbit_napi_set(toBuffer(field), bit, value ? 1 : 0) !== 0
}

exports.fill = function fill(
  field,
  value,
  start = 0,
  end = field.byteLength * 8
) {
  const n = field.byteLength * 8

  if (start < 0) start += n
  if (end < 0) end += n
  if (start < 0 || start >= field.byteLength * 8 || start >= end) return field

  binding.quickbit_napi_fill(toBuffer(field), value ? 1 : 0, start, end)
  return field
}

exports.clear = function clear(field, ...chunks) {
  binding.quickbit_napi_clear(toBuffer(field), chunks.map(toBufferChunk))
}

exports.findFirst = function findFirst(field, value, position = 0) {
  const n = field.byteLength * 8

  if (position < 0) position += n
  if (position < 0) position = 0
  if (position >= n) return -1

  return binding.quickbit_napi_find_first(
    toBuffer(field),
    value ? 1 : 0,
    position
  )
}

exports.findLast = function findLast(
  field,
  value,
  position = field.byteLength * 8 - 1
) {
  const n = field.byteLength * 8

  if (position < 0) position += n
  if (position < 0) return -1
  if (position >= n) position = n - 1

  return binding.quickbit_napi_find_last(
    toBuffer(field),
    value ? 1 : 0,
    position
  )
}

function toBuffer(field) {
  if (field.BYTES_PER_ELEMENT === 1) return field
  return new Uint8Array(field.buffer, field.byteOffset, field.byteLength)
}

function toBufferChunk(chunk) {
  return { field: toBuffer(chunk.field), offset: chunk.offset }
}

class Index {
  static from(fieldOrChunks, byteLength = -1) {
    if (Array.isArray(fieldOrChunks)) {
      return new SparseIndex(fieldOrChunks, byteLength)
    } else {
      return new DenseIndex(fieldOrChunks, byteLength)
    }
  }

  constructor(byteLength) {
    this._byteLength = byteLength
    this.handle = Buffer.allocUnsafe(binding.sizeof_quickbit_index_t)
  }

  get byteLength() {
    return this._byteLength
  }

  skipFirst(value, position = 0) {
    const n = this.byteLength * 8

    if (position < 0) position += n
    if (position < 0) position = 0
    if (position >= n) return n - 1

    return binding.quickbit_napi_skip_first(
      this.handle,
      this.byteLength,
      value ? 1 : 0,
      position
    )
  }

  skipLast(value, position = this.byteLength * 8 - 1) {
    const n = this.byteLength * 8

    if (position < 0) position += n
    if (position < 0) return 0
    if (position >= n) position = n - 1

    return binding.quickbit_napi_skip_last(
      this.handle,
      this.byteLength,
      value ? 1 : 0,
      position
    )
  }
}

exports.Index = Index

class DenseIndex extends Index {
  constructor(field, byteLength) {
    super(byteLength)
    this.field = field

    binding.quickbit_napi_index_init(this.handle, toBuffer(this.field))
  }

  get byteLength() {
    if (this._byteLength !== -1) return this._byteLength
    return this.field.byteLength
  }

  update(bit) {
    const n = this.byteLength * 8

    if (bit < 0) bit += n
    if (bit < 0 || bit >= n) return false

    return (
      binding.quickbit_napi_index_update(
        this.handle,
        toBuffer(this.field),
        bit
      ) !== 0
    )
  }
}

function selectChunk(chunks, offset) {
  for (let i = 0; i < chunks.length; i++) {
    const next = chunks[i]

    const start = next.offset
    const end = next.offset + next.field.byteLength

    if (offset >= start && offset + 16 <= end) {
      return next
    }
  }

  return null
}

class SparseIndex extends Index {
  constructor(chunks, byteLength) {
    super(byteLength)
    this.chunks = chunks

    binding.quickbit_napi_index_init_sparse(
      this.handle,
      this.chunks.map(toBufferChunk)
    )
  }

  get byteLength() {
    if (this._byteLength !== -1) return this._byteLength
    const last = this.chunks[this.chunks.length - 1]
    return last ? last.offset + last.field.byteLength : 0
  }

  update(bit) {
    const n = this.byteLength * 8

    if (bit < 0) bit += n
    if (bit < 0 || bit >= n) return false

    const j = Math.floor(bit / 128)

    const offset = j * 16

    const chunk = selectChunk(this.chunks, offset)

    if (chunk === null) return false

    return (
      binding.quickbit_napi_index_update_sparse(
        this.handle,
        toBuffer(chunk.field),
        chunk.offset,
        bit
      ) !== 0
    )
  }
}