-
Notifications
You must be signed in to change notification settings - Fork 29
/
zigzag.js
131 lines (119 loc) · 3.88 KB
/
zigzag.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
* Reads the length for a VarInt
*/
function sizeOfVarInt (value) {
value = (value << 1) ^ (value >> 63)
let cursor = 0
while (value & ~0x7F) {
value >>>= 7
cursor++
}
return cursor + 1
}
function sizeOfVarLong (value) {
if (typeof value.valueOf() === 'object') {
value = (BigInt(value[0]) << 32n) | BigInt(value[1])
} else if (typeof value !== 'bigint') value = BigInt(value)
value = (value << 1n) ^ (value >> 63n)
let cursor = 0
while (value > 127n) {
value >>= 7n
cursor++
}
return cursor + 1
}
/**
* Reads a zigzag encoded 64-bit VarInt as a BigInt
*/
function readSignedVarLong (buffer, offset) {
let result = BigInt(0)
let shift = 0n
let cursor = offset
let size = 0
while (true) {
if (cursor + 1 > buffer.length) { throw new Error('unexpected buffer end') }
const b = buffer.readUInt8(cursor)
result |= (BigInt(b) & 0x7fn) << shift // Add the bits to our number, except MSB
cursor++
if (!(b & 0x80)) { // If the MSB is not set, we return the number
size = cursor - offset
break
}
shift += 7n // we only have 7 bits, MSB being the return-trigger
if (shift > 63n) throw new Error(`varint is too big: ${shift}`)
}
// in zigzag encoding, the sign bit is the LSB of the value - remove the bit,
// if 1, then flip the rest of the bits (xor) and set to negative
// Note: bigint has no sign bit; instead if we XOR -0 we get no-op, XOR -1 flips and sets negative
const zigzag = (result >> 1n) ^ -(result & 1n)
return { value: zigzag, size }
}
/**
* Writes a zigzag encoded 64-bit VarInt as a BigInt
*/
function writeSignedVarLong (value, buffer, offset) {
// if an array, turn it into a BigInt
if (typeof value.valueOf() === 'object') {
value = BigInt.asIntN(64, (BigInt(value[0]) << 32n)) | BigInt(value[1])
} else if (typeof value !== 'bigint') value = BigInt(value)
// shift value left and flip if negative (no sign bit, but right shifting beyond value will always be -0b1)
value = (value << 1n) ^ (value >> 63n)
let cursor = 0
while (value > 127n) { // keep writing in 7 bit slices
const num = Number(value & 0xFFn)
buffer.writeUInt8(num | 0x80, offset + cursor)
cursor++
value >>= 7n
}
buffer.writeUInt8(Number(value), offset + cursor)
return offset + cursor + 1
}
/**
* Reads a 32-bit zigzag encoded varint as a Number
*/
function readSignedVarInt (buffer, offset) {
let result = 0
let shift = 0
let cursor = offset
let size = 0
while (true) {
if (cursor + 1 > buffer.length) { throw new Error('unexpected buffer end') }
const b = buffer.readUInt8(cursor)
result |= ((b & 0x7f) << shift) // Add the bits to our number, except MSB
cursor++
if (!(b & 0x80)) { // If the MSB is not set, we return the number
size = cursor - offset
break
}
shift += 7 // we only have 7 bits, MSB being the return-trigger
if (shift > 63) throw new Error(`varint is too big: ${shift}`)
}
const zigzag = ((((result << 63) >> 63) ^ result) >> 1) ^ (result & (1 << 63))
return { value: zigzag, size }
}
/**
* Writes a 32-bit zigzag encoded varint
*/
function writeSignedVarInt (value, buffer, offset) {
value = (value << 1) ^ (value >> 31)
let cursor = 0
while (value & ~0x7F) {
const num = Number((value & 0xFF) | 0x80)
buffer.writeUInt8(num, offset + cursor)
cursor++
value >>>= 7
}
buffer.writeUInt8(value, offset + cursor)
return offset + cursor + 1
}
module.exports = {
compiler: {
Read: { zigzag64: ['native', readSignedVarLong], zigzag32: ['native', readSignedVarInt] },
Write: { zigzag64: ['native', writeSignedVarLong], zigzag32: ['native', writeSignedVarInt] },
SizeOf: { zigzag64: ['native', sizeOfVarLong], zigzag32: ['native', sizeOfVarInt] }
},
interpret: {
zigzag64: [readSignedVarLong, writeSignedVarLong, sizeOfVarLong],
zigzag32: [readSignedVarInt, writeSignedVarInt, sizeOfVarInt]
}
}