Home Reference Source Test

src/common/helpers.js

import {
  isArray,
  isBlob,
  isBoolean,
  isFloat,
  isInfinity,
  isInt,
  isNull,
  isString,
} from './utils'

/**
 * Checks type of given object and returns the regarding OSC
 * Type tag character
 * @param {*} item Any object
 * @return {string} OSC Type tag character
 */
export function typeTag(item) {
  if (isInt(item)) {
    return 'i'
  } else if (isFloat(item)) {
    return 'f'
  } else if (isString(item)) {
    return 's'
  } else if (isBlob(item)) {
    return 'b'
  } else if (isBoolean(item)) {
    return item ? 'T' : 'F'
  } else if (isNull(item)) {
    return 'N'
  } else if (isInfinity(item)) {
    return 'I'
  }

  throw new Error('OSC typeTag() found unknown value type')
}

/**
 * Sanitizes an OSC-ready Address Pattern
 * @param {string[]|string} obj Address as string or array of strings
 * @return {string} Corrected address string
 *
 * @example
 * // all calls return '/test/path' string:
 * prepareAddress('test/path')
 * prepareAddress('/test/path/')
 * prepareAddress([test, path])
 */
export function prepareAddress(obj) {
  let address = ''

  if (isArray(obj)) {
    return `/${obj.join('/')}`
  } else if (isString(obj)) {
    address = obj

    // remove slash at ending of address
    if (address.length > 1 && address[address.length - 1] === '/') {
      address = address.slice(0, address.length - 1)
    }

    // add slash at beginning of address
    if (address.length > 1 && address[0] !== '/') {
      address = `/${address}`
    }

    return address
  }

  throw new Error('OSC prepareAddress() needs addresses of type array or string')
}

/**
 * Make an OSC address pattern javascript-regex-ready
 * @param {string} str OSC address pattern
 * @return {string} Javascript RegEx string
 */
export function prepareRegExPattern(str) {
  let pattern

  if (!(isString(str))) {
    throw new Error('OSC prepareRegExPattern() needs strings')
  }

  pattern = str.replace(/\./g, '\\.')
  pattern = pattern.replace(/\(/g, '\\(')
  pattern = pattern.replace(/\)/g, '\\)')

  pattern = pattern.replace(/\{/g, '(')
  pattern = pattern.replace(/\}/g, ')')
  pattern = pattern.replace(/,/g, '|')

  pattern = pattern.replace(/\[!/g, '[^')

  pattern = pattern.replace(/\?/g, '.')
  pattern = pattern.replace(/\*/g, '.*')

  return pattern
}

/**
 * Holds a list of items and helps to merge them
 * into a single array of packed binary data
 */
export default class EncodeHelper {
  /**
   * Create a new EncodeHelper instance
   */
  constructor() {
    /** @type {array} data */
    this.data = []
    /** @type {number} byteLength */
    this.byteLength = 0
  }

  /**
   * Packs an item and adds it to the list
   * @param {*} item Any object
   * @return {EncodeHelper}
   */
  add(item) {
    // Skip encoding items which do not need a payload as they are constants
    if (isBoolean(item) || isInfinity(item) || isNull(item)) {
      return this
    }

    const buffer = item.pack()
    this.byteLength += buffer.byteLength
    this.data.push(buffer)

    return this
  }

  /**
   * Merge all added items into one Uint8Array
   * @return {Uint8Array} Merged binary data array of all items
   */
  merge() {
    const result = new Uint8Array(this.byteLength)
    let offset = 0

    this.data.forEach((data) => {
      result.set(data, offset)
      offset += data.byteLength
    })

    return result
  }
}