uint8-varint
Version:
Read/write unsigned varints from Uint8Arrays and Uint8ArrayLists
263 lines (227 loc) • 5.15 kB
text/typescript
/* eslint-disable no-fallthrough */
import { allocUnsafe } from 'uint8arrays/alloc'
import type { Uint8ArrayList } from 'uint8arraylist'
const N1 = Math.pow(2, 7)
const N2 = Math.pow(2, 14)
const N3 = Math.pow(2, 21)
const N4 = Math.pow(2, 28)
const N5 = Math.pow(2, 35)
const N6 = Math.pow(2, 42)
const N7 = Math.pow(2, 49)
/** Most significant bit of a byte */
const MSB = 0x80
/** Rest of the bits in a byte */
const REST = 0x7f
export function encodingLength (value: number): number {
if (value < N1) {
return 1
}
if (value < N2) {
return 2
}
if (value < N3) {
return 3
}
if (value < N4) {
return 4
}
if (value < N5) {
return 5
}
if (value < N6) {
return 6
}
if (value < N7) {
return 7
}
if (Number.MAX_SAFE_INTEGER != null && value > Number.MAX_SAFE_INTEGER) {
throw new RangeError('Could not encode varint')
}
return 8
}
export function encodeUint8Array (value: number, buf: Uint8Array, offset: number = 0): Uint8Array {
switch (encodingLength(value)) {
case 8: {
buf[offset++] = (value & 0xFF) | MSB
value /= 128
}
case 7: {
buf[offset++] = (value & 0xFF) | MSB
value /= 128
}
case 6: {
buf[offset++] = (value & 0xFF) | MSB
value /= 128
}
case 5: {
buf[offset++] = (value & 0xFF) | MSB
value /= 128
}
case 4: {
buf[offset++] = (value & 0xFF) | MSB
value >>>= 7
}
case 3: {
buf[offset++] = (value & 0xFF) | MSB
value >>>= 7
}
case 2: {
buf[offset++] = (value & 0xFF) | MSB
value >>>= 7
}
case 1: {
buf[offset++] = (value & 0xFF)
value >>>= 7
break
}
default: throw new Error('unreachable')
}
return buf
}
export function encodeUint8ArrayList (value: number, buf: Uint8ArrayList, offset: number = 0): Uint8ArrayList {
switch (encodingLength(value)) {
case 8: {
buf.set(offset++, (value & 0xFF) | MSB)
value /= 128
}
case 7: {
buf.set(offset++, (value & 0xFF) | MSB)
value /= 128
}
case 6: {
buf.set(offset++, (value & 0xFF) | MSB)
value /= 128
}
case 5: {
buf.set(offset++, (value & 0xFF) | MSB)
value /= 128
}
case 4: {
buf.set(offset++, (value & 0xFF) | MSB)
value >>>= 7
}
case 3: {
buf.set(offset++, (value & 0xFF) | MSB)
value >>>= 7
}
case 2: {
buf.set(offset++, (value & 0xFF) | MSB)
value >>>= 7
}
case 1: {
buf.set(offset++, (value & 0xFF))
value >>>= 7
break
}
default: throw new Error('unreachable')
}
return buf
}
export function decodeUint8Array (buf: Uint8Array, offset: number): number {
let b = buf[offset]
let res = 0
res += b & REST
if (b < MSB) {
return res
}
b = buf[offset + 1]
res += (b & REST) << 7
if (b < MSB) {
return res
}
b = buf[offset + 2]
res += (b & REST) << 14
if (b < MSB) {
return res
}
b = buf[offset + 3]
res += (b & REST) << 21
if (b < MSB) {
return res
}
b = buf[offset + 4]
res += (b & REST) * N4
if (b < MSB) {
return res
}
b = buf[offset + 5]
res += (b & REST) * N5
if (b < MSB) {
return res
}
b = buf[offset + 6]
res += (b & REST) * N6
if (b < MSB) {
return res
}
b = buf[offset + 7]
res += (b & REST) * N7
if (b < MSB) {
return res
}
throw new RangeError('Could not decode varint')
}
export function decodeUint8ArrayList (buf: Uint8ArrayList, offset: number): number {
let b = buf.get(offset)
let res = 0
res += b & REST
if (b < MSB) {
return res
}
b = buf.get(offset + 1)
res += (b & REST) << 7
if (b < MSB) {
return res
}
b = buf.get(offset + 2)
res += (b & REST) << 14
if (b < MSB) {
return res
}
b = buf.get(offset + 3)
res += (b & REST) << 21
if (b < MSB) {
return res
}
b = buf.get(offset + 4)
res += (b & REST) * N4
if (b < MSB) {
return res
}
b = buf.get(offset + 5)
res += (b & REST) * N5
if (b < MSB) {
return res
}
b = buf.get(offset + 6)
res += (b & REST) * N6
if (b < MSB) {
return res
}
b = buf.get(offset + 7)
res += (b & REST) * N7
if (b < MSB) {
return res
}
throw new RangeError('Could not decode varint')
}
export function encode (value: number): Uint8Array
export function encode (value: number, buf: Uint8Array, offset?: number): Uint8Array
export function encode (value: number, buf: Uint8ArrayList, offset?: number): Uint8ArrayList
export function encode <T extends Uint8Array | Uint8ArrayList = Uint8Array> (value: number, buf?: T, offset: number = 0): T {
if (buf == null) {
buf = allocUnsafe(encodingLength(value)) as T
}
if (buf instanceof Uint8Array) {
return encodeUint8Array(value, buf, offset) as T
} else {
return encodeUint8ArrayList(value, buf, offset) as T
}
}
export function decode (buf: Uint8ArrayList | Uint8Array, offset: number = 0): number {
if (buf instanceof Uint8Array) {
return decodeUint8Array(buf, offset)
} else {
return decodeUint8ArrayList(buf, offset)
}
}