1'use strict'
2// Tar can encode large and negative numbers using a leading byte of
3// 0xff for negative, and 0x80 for positive.
4
5const encode = (num, buf) => {
6  if (!Number.isSafeInteger(num)) {
7  // The number is so large that javascript cannot represent it with integer
8  // precision.
9    throw Error('cannot encode number outside of javascript safe integer range')
10  } else if (num < 0) {
11    encodeNegative(num, buf)
12  } else {
13    encodePositive(num, buf)
14  }
15  return buf
16}
17
18const encodePositive = (num, buf) => {
19  buf[0] = 0x80
20
21  for (var i = buf.length; i > 1; i--) {
22    buf[i - 1] = num & 0xff
23    num = Math.floor(num / 0x100)
24  }
25}
26
27const encodeNegative = (num, buf) => {
28  buf[0] = 0xff
29  var flipped = false
30  num = num * -1
31  for (var i = buf.length; i > 1; i--) {
32    var byte = num & 0xff
33    num = Math.floor(num / 0x100)
34    if (flipped) {
35      buf[i - 1] = onesComp(byte)
36    } else if (byte === 0) {
37      buf[i - 1] = 0
38    } else {
39      flipped = true
40      buf[i - 1] = twosComp(byte)
41    }
42  }
43}
44
45const parse = (buf) => {
46  const pre = buf[0]
47  const value = pre === 0x80 ? pos(buf.slice(1, buf.length))
48    : pre === 0xff ? twos(buf)
49    : null
50  if (value === null) {
51    throw Error('invalid base256 encoding')
52  }
53
54  if (!Number.isSafeInteger(value)) {
55  // The number is so large that javascript cannot represent it with integer
56  // precision.
57    throw Error('parsed number outside of javascript safe integer range')
58  }
59
60  return value
61}
62
63const twos = (buf) => {
64  var len = buf.length
65  var sum = 0
66  var flipped = false
67  for (var i = len - 1; i > -1; i--) {
68    var byte = buf[i]
69    var f
70    if (flipped) {
71      f = onesComp(byte)
72    } else if (byte === 0) {
73      f = byte
74    } else {
75      flipped = true
76      f = twosComp(byte)
77    }
78    if (f !== 0) {
79      sum -= f * Math.pow(256, len - i - 1)
80    }
81  }
82  return sum
83}
84
85const pos = (buf) => {
86  var len = buf.length
87  var sum = 0
88  for (var i = len - 1; i > -1; i--) {
89    var byte = buf[i]
90    if (byte !== 0) {
91      sum += byte * Math.pow(256, len - i - 1)
92    }
93  }
94  return sum
95}
96
97const onesComp = byte => (0xff ^ byte) & 0xff
98
99const twosComp = byte => ((0xff ^ byte) + 1) & 0xff
100
101module.exports = {
102  encode,
103  parse,
104}
105