Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try replace Buffer with Uint8Array #51

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added bun.lockb
Binary file not shown.
27 changes: 17 additions & 10 deletions src/address/ADNLAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import inspectSymbol from 'symbol.inspect';
import { base32Decode, base32Encode } from '../utils/base32';
import { crc16 } from '../utils/crc16';
import { base64ToUint8Array, uint8ArrayEquals, uint8ArrayToHexString } from '../utils/buffer_to_uint8array';

export class ADNLAddress {

Expand All @@ -26,40 +27,46 @@ export class ADNLAddress {
}
let gotHash = decoded.slice(33);
let hash = crc16(decoded.slice(0, 33));
if (!hash.equals(gotHash)) {
if (!uint8ArrayEquals(hash, gotHash)) {
throw Error('Invalid address');
}
return new ADNLAddress(decoded.slice(1, 33));
}

static parseRaw(src: string) {
const data = Buffer.from(src, 'base64');
const data = base64ToUint8Array(src);
return new ADNLAddress(data);
}

readonly address: Buffer;
readonly address: Uint8Array;

constructor(address: Buffer) {
constructor(address: Uint8Array) {
if (address.length !== 32) {
throw Error('Invalid address');
}
this.address = address;
}

equals(b: ADNLAddress) {
return this.address.equals(b.address);
return uint8ArrayEquals(this.address, b.address);
}

toRaw = () => {
return this.address.toString('hex').toUpperCase();
return uint8ArrayToHexString(this.address).toUpperCase();
}

toString = () => {
let data = Buffer.concat([Buffer.from([0x2D]), this.address]);
let data = new Uint8Array(1 + this.address.length);
data[0] = 0x2D;
data.set(this.address, 1);

let hash = crc16(data);
data = Buffer.concat([data, hash]);
return base32Encode(data).slice(1);
let result = new Uint8Array(data.length + hash.length);
result.set(data);
result.set(hash, data.length);

return base32Encode(result).slice(1);
}

[inspectSymbol] = () => this.toString();
}
}
11 changes: 6 additions & 5 deletions src/address/Address.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import { Address } from "./Address";
import { hexStringToUint8Array } from "../utils/buffer_to_uint8array";

describe('Address', () => {
it('should parse addresses in various forms', () => {
Expand All @@ -21,14 +22,14 @@ describe('Address', () => {
expect(address1.address.workChain).toBe(0);
expect(address2.address.workChain).toBe(0);
expect(address3.workChain).toBe(0);
expect(address1.address.hash).toEqual(Buffer.from('2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3', 'hex'));
expect(address2.address.hash).toEqual(Buffer.from('2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3', 'hex'));
expect(address3.hash).toEqual(Buffer.from('2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3', 'hex'));
expect(address1.address.hash).toEqual(hexStringToUint8Array('2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3'));
expect(address2.address.hash).toEqual(hexStringToUint8Array('2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3'));
expect(address3.hash).toEqual(hexStringToUint8Array('2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3'));
expect(address1.address.toRawString()).toBe('0:2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3');
expect(address2.address.toRawString()).toBe('0:2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3');
expect(address3.toRawString()).toBe('0:2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3');
expect(address4.workChain).toBe(-1);
expect(address4.hash).toEqual(Buffer.from('3333333333333333333333333333333333333333333333333333333333333333', 'hex'));
expect(address4.hash).toEqual(hexStringToUint8Array('3333333333333333333333333333333333333333333333333333333333333333'));
});
it('should serialize to friendly form', () => {
let address = Address.parseRaw('0:2cf55953e92efbeadab7ba725c3f93a0b23f842cbba72d7b8e6f510a70e422e3');
Expand Down Expand Up @@ -86,4 +87,4 @@ describe('Address', () => {
Address.parseFriendly('0:EQDXDCFLXgiTrjGSNVBuvKPZVYlPn3J_u96xxLas3_yoRWRk')
}).toThrowError('Unknown address type');
});
});
});
32 changes: 16 additions & 16 deletions src/address/Address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@

import inspectSymbol from 'symbol.inspect';
import { crc16 } from '../utils/crc16';
import { base64ToUint8Array, hexStringToUint8Array, uint8ArrayToHexString, uint8ArrayToBase64, uint8ArrayEquals } from '../utils/buffer_to_uint8array';

const bounceable_tag = 0x11;
const non_bounceable_tag = 0x51;
const test_flag = 0x80;

function parseFriendlyAddress(src: string | Buffer) {
function parseFriendlyAddress(src: string | Uint8Array) {
if (typeof src === 'string' && !Address.isFriendly(src)) {
throw new Error('Unknown address type');
}

const data = Buffer.isBuffer(src) ? src : Buffer.from(src, 'base64');
const data = src instanceof Uint8Array ? src : base64ToUint8Array(src);

// 1byte tag + 1byte workchain + 32 bytes hash + 2 byte crc
if (data.length !== 36) {
Expand Down Expand Up @@ -124,13 +125,12 @@ export class Address {

static parseRaw(source: string) {
let workChain = parseInt(source.split(":")[0]);
let hash = Buffer.from(source.split(":")[1], 'hex');

let hash = hexStringToUint8Array(source.split(":")[1]);
return new Address(workChain, hash);
}

static parseFriendly(source: string | Buffer) {
if (Buffer.isBuffer(source)) {
static parseFriendly(source: string | Uint8Array) {
if (source instanceof Uint8Array) {
let r = parseFriendlyAddress(source);
return {
isBounceable: r.isBounceable,
Expand All @@ -149,9 +149,9 @@ export class Address {
}

readonly workChain: number;
readonly hash: Buffer;
readonly hash: Uint8Array;

constructor(workChain: number, hash: Buffer) {
constructor(workChain: number, hash: Uint8Array) {
if (hash.length !== 32) {
throw new Error('Invalid address hash length: ' + hash.length);
}
Expand All @@ -162,18 +162,18 @@ export class Address {
}

toRawString = () => {
return this.workChain + ':' + this.hash.toString('hex');
return this.workChain + ':' + uint8ArrayToHexString(this.hash);
}

equals(src: Address) {
if (src.workChain !== this.workChain) {
return false;
}
return src.hash.equals(this.hash);
return uint8ArrayEquals(src.hash, this.hash);
}

toRaw = () => {
const addressWithChecksum = Buffer.alloc(36);
const addressWithChecksum = new Uint8Array(36);
addressWithChecksum.set(this.hash);
addressWithChecksum.set([this.workChain, this.workChain, this.workChain, this.workChain], 32);
return addressWithChecksum;
Expand All @@ -188,11 +188,11 @@ export class Address {
tag |= test_flag;
}

const addr = Buffer.alloc(34);
const addr = new Uint8Array(34);
addr[0] = tag;
addr[1] = this.workChain;
addr.set(this.hash, 2);
const addressWithChecksum = Buffer.alloc(36);
const addressWithChecksum = new Uint8Array(36);
addressWithChecksum.set(addr);
addressWithChecksum.set(crc16(addr), 34);
return addressWithChecksum;
Expand All @@ -202,9 +202,9 @@ export class Address {
let urlSafe = (args && args.urlSafe !== undefined) ? args.urlSafe : true;
let buffer = this.toStringBuffer(args);
if (urlSafe) {
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_');
return uint8ArrayToBase64(buffer).replace(/\+/g, '-').replace(/\//g, '_');
} else {
return buffer.toString('base64');
return uint8ArrayToBase64(buffer);
}
}

Expand All @@ -213,4 +213,4 @@ export class Address {

export function address(src: string) {
return Address.parse(src);
}
}
10 changes: 5 additions & 5 deletions src/boc/BitBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import { BitString } from "./BitString";
* Class for building bit strings
*/
export class BitBuilder {
private _buffer: Buffer;
private _buffer: Uint8Array;
private _length: number;

constructor(size: number = 1023) {
this._buffer = Buffer.alloc(Math.ceil(size / 8));
this._buffer = new Uint8Array(Math.ceil(size / 8));
this._length = 0;
}

Expand Down Expand Up @@ -66,14 +66,14 @@ export class BitBuilder {
* Write bits from buffer
* @param src source buffer
*/
writeBuffer(src: Buffer) {
writeBuffer(src: Uint8Array) {

// Special case for aligned offsets
if (this._length % 8 === 0) {
if (this._length + src.length * 8 > this._buffer.length * 8) {
throw new Error("BitBuilder overflow");
}
src.copy(this._buffer, this._length / 8);
this._buffer.set(src, this._length / 8);
this._length += src.length * 8;
} else {
for (let i = 0; i < src.length; i++) {
Expand Down Expand Up @@ -308,4 +308,4 @@ export class BitBuilder {
}
return this._buffer.subarray(0, this._length / 8);
}
}
}
4 changes: 2 additions & 2 deletions src/boc/BitReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ export class BitReader {
return res;
}

private _preloadBuffer(bytes: number, offset: number): Buffer {
private _preloadBuffer(bytes: number, offset: number): Uint8Array {

// Try to load fast
let fastBuffer = this._bits.subbuffer(offset, bytes * 8);
Expand All @@ -463,7 +463,7 @@ export class BitReader {
}

// Load slow
let buf = Buffer.alloc(bytes);
let buf = new Uint8Array(bytes);
for (let i = 0; i < bytes; i++) {
buf[i] = Number(this._preloadUint(8, offset + i * 8));
}
Expand Down
36 changes: 18 additions & 18 deletions src/boc/BitString.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('BitString', () => {
});

it('should read bits', () => {
let bs = new BitString(Buffer.from([0b10101010]), 0, 8);
let bs = new BitString(new Uint8Array([0b10101010]), 0, 8);
expect(bs.at(0)).toBe(true);
expect(bs.at(1)).toBe(false);
expect(bs.at(2)).toBe(true);
Expand All @@ -36,9 +36,9 @@ describe('BitString', () => {
expect(bs.toString()).toEqual('AA');
});
it('should equals', () => {
let a = new BitString(Buffer.from([0b10101010]), 0, 8);
let b = new BitString(Buffer.from([0b10101010]), 0, 8);
let c = new BitString(Buffer.from([0, 0b10101010]), 8, 8);
let a = new BitString(new Uint8Array([0b10101010]), 0, 8);
let b = new BitString(new Uint8Array([0b10101010]), 0, 8);
let c = new BitString(new Uint8Array([0, 0b10101010]), 8, 8);
expect(a.equals(b)).toBe(true);
expect(b.equals(a)).toBe(true);
expect(a.equals(c)).toBe(true);
Expand All @@ -48,49 +48,49 @@ describe('BitString', () => {
expect(c.toString()).toEqual('AA');
});
it('should format strings', () => {
expect(new BitString(Buffer.from([0b00000000]), 0, 1).toString()).toEqual('4_');
expect(new BitString(Buffer.from([0b10000000]), 0, 1).toString()).toEqual('C_');
expect(new BitString(Buffer.from([0b11000000]), 0, 2).toString()).toEqual('E_');
expect(new BitString(Buffer.from([0b11100000]), 0, 3).toString()).toEqual('F_');
expect(new BitString(Buffer.from([0b11100000]), 0, 4).toString()).toEqual('E');
expect(new BitString(Buffer.from([0b11101000]), 0, 5).toString()).toEqual('EC_');
expect(new BitString(new Uint8Array([0b00000000]), 0, 1).toString()).toEqual('4_');
expect(new BitString(new Uint8Array([0b10000000]), 0, 1).toString()).toEqual('C_');
expect(new BitString(new Uint8Array([0b11000000]), 0, 2).toString()).toEqual('E_');
expect(new BitString(new Uint8Array([0b11100000]), 0, 3).toString()).toEqual('F_');
expect(new BitString(new Uint8Array([0b11100000]), 0, 4).toString()).toEqual('E');
expect(new BitString(new Uint8Array([0b11101000]), 0, 5).toString()).toEqual('EC_');
});
it('should do subbuffers', () => {
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs2 = bs.subbuffer(0, 16);
expect(bs2!.length).toBe(2);
});
it('should do substrings', () => {
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs2 = bs.substring(0, 16);
expect(bs2!.length).toBe(16);
});
it('should do empty substrings with requested length 0', () => {
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs2 = bs.substring(bs.length, 0);
expect(bs2!.length).toBe(0);
});
it('should OOB when substring offset is out of bounds', () => {
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
testOOB('substring', bs, bs.length + 1, 0);
testOOB('substring', bs, -1, 0);
});
it('should OOB when subbuffer offset is out of bounds', () => {
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
testOOB('subbuffer', bs, bs.length + 1, 0);
testOOB('subbuffer', bs, -1, 0);
});
it('should OOB when offset is on the end of bitsring and length > 0', () =>{
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
testOOB('substring', bs, bs.length, 1);
});
it('should do empty subbuffers with requested length 0', () => {
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs2 = bs.subbuffer(bs.length, 0);
expect(bs2!.length).toBe(0);
});
it('should OOB when offset is on the end of buffer and length > 0', () => {
let bs = new BitString(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
let bs = new BitString(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 0, 64);
testOOB('subbuffer', bs, bs.length, 1);
});
it('should process monkey strings', () => {
Expand Down
11 changes: 6 additions & 5 deletions src/boc/BitString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@
*/

import { bitsToPaddedBuffer } from "./utils/paddedBits";
import { uint8ArrayToHexString } from '../utils/buffer_to_uint8array';
import inspectSymbol from 'symbol.inspect';

/**
* BitString is a class that represents a bitstring in a buffer with a specified offset and length
*/
export class BitString {

static readonly EMPTY = new BitString(Buffer.alloc(0), 0, 0);
static readonly EMPTY = new BitString(new Uint8Array(0), 0, 0);

// NOTE: We want to hide this fields from the user, but
// using private fields would break the compatibility
// between different versions in typescript

private readonly _offset: number;
private readonly _length: number;
private readonly _data: Buffer;
private readonly _data: Uint8Array;

/**
* Checks if supplied object is BitString
Expand All @@ -38,7 +39,7 @@ export class BitString {
* @param offset offset in bits from the start of the buffer
* @param length length of the bitstring in bits
*/
constructor(data: Buffer, offset: number, length: number) {
constructor(data: Uint8Array, offset: number, length: number) {

// Check bounds
if (length < 0) {
Expand Down Expand Up @@ -168,14 +169,14 @@ export class BitString {
const padded = bitsToPaddedBuffer(this);

if (this._length % 4 === 0) {
const s = padded.subarray(0, Math.ceil(this._length / 8)).toString('hex').toUpperCase();
const s = uint8ArrayToHexString(padded.subarray(0, Math.ceil(this._length / 8))).toUpperCase();
if (this._length % 8 === 0) {
return s;
} else {
return s.substring(0, s.length - 1);
}
} else {
const hex = padded.toString('hex').toUpperCase();
const hex = uint8ArrayToHexString(padded).toUpperCase();
if (this._length % 8 <= 4) {
return hex.substring(0, hex.length - 1) + '_';
} else {
Expand Down
Loading