SupremeTechnopriest/whitney-buffers

View on GitHub
src/encode.js

Summary

Maintainability
A
0 mins
Test Coverage
/* eslint-disable no-redeclare */
import Long from 'long'
import { parse as uuidParse } from 'uuid-parse'
import {
  SHORT_INFINITY,
  SHORT_NEG_INFINITY,
  INT_INFINITY,
  INT_NEG_INFINITY,
  SYMBOL_STRING_REGEX
} from '../_private/constants'
import {
  inferType,
  getVals,
  isListOfSamePrimitiveType,
  type2num,
  calcN
} from '../_private/utils'
import { Typed, isTyped } from './typed'
import Buffer from '../buffer'

export default function encode (x) {
  var pos = 0
  var b
  function wb (i) {
    b.writeInt8(i, pos)
    pos += 1
  }
  function wub (i) {
    b.writeUInt8(i, pos)
    pos += 1
  }
  function wr (i) {
    b.writeFloatLE(i, pos)
    pos += 4
  }
  function wf (i) {
    b.writeDoubleLE(i, pos)
    pos += 8
  }
  function wn (n, i) {
    if (n === 1) {
      b.writeInt8(i, pos)
    } else if (n === 2) {
      b.writeInt16LE(i, pos)
    } else if (n === 4) {
      b.writeInt32LE(i, pos)
    } else {
      throw new Error('only n = 1, 2 or 4 is supported')
    }
    pos += n
  }
  function wlongjs (x) {
    wn(4, x.low)
    wn(4, x.high)
  }
  function wboolean (x) {
    wb(x ? 1 : 0)
  }
  function wguid (x) {
    uuidParse(x, b, pos)
    pos += 16
  }
  function wbyte (x) {
    wb(x)
  }
  function wshort (x) {
    if (x === Infinity) {
      wn(2, SHORT_INFINITY)
    } else if (x === -Infinity) {
      wn(2, SHORT_NEG_INFINITY)
    } else {
      wn(2, x)
    }
  }
  function wint (x) {
    if (x === Infinity) {
      wn(4, INT_INFINITY)
    } else if (x === -Infinity) {
      wn(4, INT_NEG_INFINITY)
    } else {
      wn(4, x)
    }
  }
  function wlong (x) {
    if (x === Infinity) {
      wub(255)
      wub(255)
      wub(255)
      wub(255)
      wub(255)
      wub(255)
      wub(255)
      wub(127)
    } else if (x === -Infinity) {
      wub(1)
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(128)
    } else {
      wlongjs(x)
    }
  }
  function wreal (x) {
    if (x === Infinity) {
      wub(0)
      wub(0)
      wub(128)
      wub(127)
    } else if (x === -Infinity) {
      wub(0)
      wub(0)
      wub(128)
      wub(255)
    } else {
      wr(x)
    }
  }
  function wfloat (x) {
    if (x === Infinity) {
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(240)
      wub(127)
    } else if (x === -Infinity) {
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(0)
      wub(240)
      wub(255)
    } else {
      wf(x)
    }
  }
  function wchar (x) {
    wub(x.charCodeAt())
  }
  function wstring (x) {
    pos += b.write(x, pos)
  }
  function wsymbol (x) {
    if (x !== null) {
      wstring(x)
    }
    wb(0)
  }
  function wtimestamp (x) {
    wlong(Long.fromNumber(86400000000000 * (x.getTime() / 86400000 - 10957)))
  }
  function wmonth (x) {
    wn(4, (x.getUTCFullYear() - 2000) * 12 + x.getUTCMonth())
  }
  function wdate (x) {
    wn(4, x.getTime() / 86400000 - 10957)
  }
  function wdatetime (x) {
    wf(x.getTime() / 86400000 - 10957)
  }
  function wtimespan (x) {
    var nanos = (((x.getUTCHours() * 60 + x.getUTCMinutes()) * 60 + x.getUTCSeconds()) * 1000 + x.getUTCMilliseconds()) * 1000 * 1000
    wlong(Long.fromNumber(nanos))
  }
  function wminute (x) {
    wn(4, x.getUTCHours() * 60 + x.getUTCMinutes())
  }
  function wsecond (x) {
    wn(4, (x.getUTCHours() * 60 + x.getUTCMinutes()) * 60 + x.getUTCSeconds())
  }
  function wtime (x) {
    wn(4, ((x.getUTCHours() * 60 + x.getUTCMinutes()) * 60 + x.getUTCSeconds()) * 1000 + x.getUTCMilliseconds())
  }

  var qtype2wfn = {
    boolean: wboolean,
    guid: wguid,
    byte: wbyte,
    short: wshort,
    int: wint,
    long: wlong,
    real: wreal,
    float: wfloat,
    char: wchar,
    symbol: wsymbol,
    timestamp: wtimestamp,
    month: wmonth,
    date: wdate,
    datetime: wdatetime,
    timespan: wtimespan,
    minute: wminute,
    second: wsecond,
    time: wtime
  }

  function type2wfn (t) {
    var wfn = qtype2wfn[t]
    if (wfn === undefined) {
      throw new Error('bad type ' + t)
    }
    return wfn
  }

  function w (x, dt) {
    var t, vt
    if (dt) {
      t = dt
    } else {
      if (isTyped(x)) {
        t = x.type
        if (t === 'typedlist') {
          vt = x.valuetype
        }
        x = x.value
      } else {
        t = inferType(x)
      }
    }
    switch (t) {
      case 'null': // JavaScript only type
      {
        wb(101)
        wb(0)
        return
      }
      case 'dict':
      {
        var k = Object.keys(x)
        wb(99)
        wb(11)
        wb(0)
        wn(4, k.length)
        for (var i = 0; i < k.length; i++) {
          wsymbol(k[i])
        }
        w(getVals(x))
        return
      }
      case 'list':
      {
        if (x.length > 0 && (dt === null || dt === undefined)) {
          var valuetype = inferType(x[0])
          if (isListOfSamePrimitiveType(x, valuetype)) {
            return w(new Typed('typedlist', x.map(function (x1) {
              if (isTyped(x1)) {
                return x1.value
              } else {
                return x1
              }
            }), valuetype))
          }
        }
        return w(x, 'mixedlist')
      }
      case 'mixedlist':
      {
        wb(0)
        wb(0)
        wn(4, x.length)
        for (var i = 0; i < x.length; i++) {
          w(x[i], null)
        }
        return
      }
      case 'typedlist':
      {
        wb(type2num(vt))
        wb(0)
        wn(4, x.length)
        var wfn = type2wfn(vt)
        x.forEach(wfn)
        return
      }
      case 'string': // JavaScript only type => list of char
      {
        if (SYMBOL_STRING_REGEX.test(x)) {
          w(x.substr(1), 'symbol')
        } else {
          wb(10)
          wb(0)
          wn(4, Buffer.byteLength(x, 'utf8'))
          wstring(x)
        }
        return
      }
    }
    var num = type2num(t)
    var wfn = type2wfn(t)
    wb(0 - num)
    wfn(x)
  }
  var n = calcN(x, null)
  b = Buffer.alloc(8 + n)
  wb(1)
  wb(0)
  wb(0)
  wb(0)
  wn(4, b.length)
  w(x, null)
  return b
}