ripple-binary-codec
Version:
XRP Ledger binary codec
153 lines (138 loc) • 4.65 kB
text/typescript
// Even though this comes from NodeJS it is valid in the browser
import TypedArray = NodeJS.TypedArray
/**
* Writes value to array at the specified offset. The value must be a valid unsigned 8-bit integer.
* @param array Uint8Array to be written to
* @param value Number to be written to array.
* @param offset plus the number of bytes written.
*/
export function writeUInt8(
array: Uint8Array,
value: number,
offset: number,
): void {
value = Number(value)
array[offset] = value
}
/**
* Writes value to array at the specified offset as big-endian. The value must be a valid unsigned 16-bit integer.
* @param array Uint8Array to be written to
* @param value Number to be written to array.
* @param offset plus the number of bytes written.
*/
export function writeUInt16BE(
array: Uint8Array,
value: number,
offset: number,
): void {
value = Number(value)
array[offset] = value >>> 8
array[offset + 1] = value
}
/**
* Writes value to array at the specified offset as big-endian. The value must be a valid unsigned 32-bit integer.
* @param array Uint8Array to be written to
* @param value Number to be written to array.
* @param offset plus the number of bytes written.
*/
export function writeUInt32BE(
array: Uint8Array,
value: number,
offset: number,
): void {
array[offset] = (value >>> 24) & 0xff
array[offset + 1] = (value >>> 16) & 0xff
array[offset + 2] = (value >>> 8) & 0xff
array[offset + 3] = value & 0xff
}
/**
* Reads an unsigned, big-endian 16-bit integer from the array at the specified offset.
* @param array Uint8Array to read
* @param offset Number of bytes to skip before starting to read. Must satisfy 0 <= offset <= buf.length - 2
*/
export function readUInt16BE(array: Uint8Array, offset: number): string {
return new DataView(array.buffer).getUint16(offset, false).toString(10)
}
/**
* Reads an unsigned, big-endian 16-bit integer from the array at the specified offset.
* @param array Uint8Array to read
* @param offset Number of bytes to skip before starting to read. Must satisfy 0 <= offset <= buf.length - 4
*/
export function readUInt32BE(array: Uint8Array, offset: number): string {
return new DataView(array.buffer).getUint32(offset, false).toString(10)
}
/**
* Compares two Uint8Array or ArrayBuffers
* @param a first array to compare
* @param b second array to compare
*/
export function equal(
a: Uint8Array | ArrayBuffer,
b: Uint8Array | ArrayBuffer,
): boolean {
const aUInt = a instanceof ArrayBuffer ? new Uint8Array(a, 0) : a
const bUInt = b instanceof ArrayBuffer ? new Uint8Array(b, 0) : b
if (aUInt.byteLength != bUInt.byteLength) return false
if (aligned32(aUInt) && aligned32(bUInt)) return compare32(aUInt, bUInt) === 0
if (aligned16(aUInt) && aligned16(bUInt)) return compare16(aUInt, bUInt) === 0
return compare8(aUInt, bUInt) === 0
}
/**
* Compares two 8 bit aligned arrays
* @param a first array to compare
* @param b second array to compare
*/
function compare8(a, b) {
const ua = new Uint8Array(a.buffer, a.byteOffset, a.byteLength)
const ub = new Uint8Array(b.buffer, b.byteOffset, b.byteLength)
return compare(ua, ub)
}
/**
* Compares two 16 bit aligned arrays
* @param a first array to compare
* @param b second array to compare
*/
function compare16(a: Uint8Array, b: Uint8Array) {
const ua = new Uint16Array(a.buffer, a.byteOffset, a.byteLength / 2)
const ub = new Uint16Array(b.buffer, b.byteOffset, b.byteLength / 2)
return compare(ua, ub)
}
/**
* Compares two 32 bit aligned arrays
* @param a first array to compare
* @param b second array to compare
*/
function compare32(a: Uint8Array, b: Uint8Array) {
const ua = new Uint32Array(a.buffer, a.byteOffset, a.byteLength / 4)
const ub = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / 4)
return compare(ua, ub)
}
/**
* Compare two TypedArrays
* @param a first array to compare
* @param b second array to compare
*/
export function compare(a: TypedArray, b: TypedArray): 1 | -1 | 0 {
if (a.byteLength !== b.byteLength) {
throw new Error('Cannot compare arrays of different length')
}
for (let i = 0; i < a.length - 1; i += 1) {
if (a[i] > b[i]) return 1
if (a[i] < b[i]) return -1
}
return 0
}
/**
* Determine if TypedArray is 16 bit aligned
* @param array The array to check
*/
function aligned16(array: TypedArray) {
return array.byteOffset % 2 === 0 && array.byteLength % 2 === 0
}
/**
* Determine if TypedArray is 32 bit aligned
* @param array The array to check
*/
function aligned32(array: TypedArray) {
return array.byteOffset % 4 === 0 && array.byteLength % 4 === 0
}