Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
12 changes: 12 additions & 0 deletions .github/workflows/borsh.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Borsh Tests
on: [push]
jobs:
prettier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
- run: yarn install
- run: yarn pretty:check
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/*.d.ts
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"tabWidth": 4,
"useTabs": false,
"singleQuote": true,
"printWidth": 120
}
74 changes: 45 additions & 29 deletions borsh-ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import bs58 from 'bs58';

// TODO: Make sure this polyfill not included when not required
import * as encoding from 'text-encoding-utf-8';
const TextDecoder = (typeof (global as any).TextDecoder !== 'function') ? encoding.TextDecoder : (global as any).TextDecoder;
const TextDecoder =
typeof (global as any).TextDecoder !== 'function' ? encoding.TextDecoder : (global as any).TextDecoder;
const textDecoder = new TextDecoder('utf-8', { fatal: true });

export function baseEncode(value: Uint8Array | string): string {
if (typeof(value) === 'string') {
if (typeof value === 'string') {
value = Buffer.from(value, 'utf8');
}
return bs58.encode(Buffer.from(value));
Expand Down Expand Up @@ -93,7 +94,11 @@ export class BinaryWriter {

private writeBuffer(buffer: Buffer) {
// Buffer.from is needed as this.buf.subarray can return plain Uint8Array in browser
this.buf = Buffer.concat([Buffer.from(this.buf.subarray(0, this.length)), buffer, Buffer.alloc(INITIAL_LENGTH)]);
this.buf = Buffer.concat([
Buffer.from(this.buf.subarray(0, this.length)),
buffer,
Buffer.alloc(INITIAL_LENGTH),
]);
this.length += buffer.length;
}

Expand Down Expand Up @@ -194,7 +199,7 @@ export class BinaryReader {
}

private readBuffer(len: number): Buffer {
if ((this.offset + len) > this.buf.length) {
if (this.offset + len > this.buf.length) {
throw new BorshError(`Expected buffer length ${len} isn't within bounds`);
}
const result = this.buf.slice(this.offset, this.offset + len);
Expand Down Expand Up @@ -239,35 +244,41 @@ function serializeField(schema: Schema, fieldName: string, value: any, fieldType
// TODO: Handle missing values properly (make sure they never result in just skipped write)
if (typeof fieldType === 'string') {
writer[`write${capitalizeFirstLetter(fieldType)}`](value);

} else if (fieldType instanceof Array) {
if (typeof fieldType[0] === 'number') {
if (value.length !== fieldType[0]) {
throw new BorshError(`Expecting byte array of length ${fieldType[0]}, but got ${value.length} bytes`);
throw new BorshError(
`Expecting byte array of length ${fieldType[0]}, but got ${value.length} bytes`
);
}
writer.writeFixedArray(value);
} else if(fieldType.length === 2 && typeof fieldType[1] === 'number' ) {
} else if (fieldType.length === 2 && typeof fieldType[1] === 'number') {
if (value.length !== fieldType[1]) {
throw new BorshError(`Expecting byte array of length ${fieldType[1]}, but got ${value.length} bytes`);
throw new BorshError(
`Expecting byte array of length ${fieldType[1]}, but got ${value.length} bytes`
);
}
for(let i = 0; i < fieldType[1]; i++) {
for (let i = 0; i < fieldType[1]; i++) {
serializeField(schema, null, value[i], fieldType[0], writer);
}
} else {
writer.writeArray(value, (item: any) => { serializeField(schema, fieldName, item, fieldType[0], writer); });
writer.writeArray(value, (item: any) => {
serializeField(schema, fieldName, item, fieldType[0], writer);
});
}
} else if (fieldType.kind !== undefined) {
switch (fieldType.kind) {
case 'option': {
if (value === null || value === undefined) {
writer.writeU8(0);
} else {
writer.writeU8(1);
serializeField(schema, fieldName, value, fieldType.type, writer);
case 'option': {
if (value === null || value === undefined) {
writer.writeU8(0);
} else {
writer.writeU8(1);
serializeField(schema, fieldName, value, fieldType.type, writer);
}
break;
}
break;
}
default: throw new BorshError(`FieldType ${fieldType} unrecognized`);
default:
throw new BorshError(`FieldType ${fieldType} unrecognized`);
}
} else {
serializeStruct(schema, value, writer);
Expand Down Expand Up @@ -325,9 +336,9 @@ function deserializeField(schema: Schema, fieldName: string, fieldType: any, rea
if (fieldType instanceof Array) {
if (typeof fieldType[0] === 'number') {
return reader.readFixedArray(fieldType[0]);
} else if(typeof fieldType[1] === 'number') {
} else if (typeof fieldType[1] === 'number') {
const arr = [];
for(let i = 0; i < fieldType[1]; i++) {
for (let i = 0; i < fieldType[1]; i++) {
arr.push(deserializeField(schema, null, fieldType[0], reader));
}
return arr;
Expand All @@ -339,12 +350,7 @@ function deserializeField(schema: Schema, fieldName: string, fieldType: any, rea
if (fieldType.kind === 'option') {
const option = reader.readU8();
if (option) {
return deserializeField(
schema,
fieldName,
fieldType.type,
reader
);
return deserializeField(schema, fieldName, fieldType.type, reader);
}

return undefined;
Expand Down Expand Up @@ -391,7 +397,12 @@ function deserializeStruct(schema: Schema, classType: any, reader: BinaryReader)
}

/// Deserializes object from bytes using schema.
export function deserialize<T>( schema: Schema, classType: { new (args: any): T }, buffer: Buffer, Reader = BinaryReader): T {
export function deserialize<T>(
schema: Schema,
classType: { new (args: any): T },
buffer: Buffer,
Reader = BinaryReader
): T {
const reader = new Reader(buffer);
const result = deserializeStruct(schema, classType, reader);
if (reader.offset < buffer.length) {
Expand All @@ -401,7 +412,12 @@ export function deserialize<T>( schema: Schema, classType: { new (args: any): T
}

/// Deserializes object from bytes using schema, without checking the length read
export function deserializeUnchecked<T>(schema: Schema, classType: {new (args: any): T}, buffer: Buffer, Reader = BinaryReader): T {
export function deserializeUnchecked<T>(
schema: Schema,
classType: { new (args: any): T },
buffer: Buffer,
Reader = BinaryReader
): T {
const reader = new Reader(buffer);
return deserializeStruct(schema, classType, reader);
}
105 changes: 54 additions & 51 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,53 +1,56 @@
{
"name": "borsh",
"version": "0.6.0",
"description": "Binary Object Representation Serializer for Hashing",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"scripts": {
"build": "tsc -p ./tsconfig.json",
"test": "jest test --runInBand",
"fuzz": "jsfuzz borsh-ts/test/fuzz/borsh-roundtrip.js borsh-ts/test/fuzz/corpus/",
"dev": "yarn build -w",
"pretest": "yarn build",
"lint": "eslint borsh-ts/**/*.ts",
"fix": "eslint borsh-ts/**/*.ts --fix"
},
"repository": {
"type": "git",
"url": "git+https://github.com/near/borsh-js.git"
},
"keywords": [
"serializer",
"binary",
"serializer",
"deserializer",
"consistency",
"deterministic"
],
"author": "Near Inc",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/near/borsh-js/issues"
},
"homepage": "https://github.com/near/borsh-js#readme",
"devDependencies": {
"@types/babel__core": "^7.1.2",
"@types/babel__template": "^7.0.2",
"@types/bn.js": "^5.1.0",
"@types/node": "^12.7.3",
"@typescript-eslint/eslint-plugin": "^2.18.0",
"@typescript-eslint/parser": "^2.18.0",
"bs58": "^4.0.0",
"eslint": "^6.5.1",
"jest": "^26.0.1",
"js-sha256": "^0.9.0",
"jsfuzz": "^1.0.14",
"typescript": "^3.6.2"
},
"dependencies": {
"bn.js": "^5.2.0",
"bs58": "^4.0.0",
"text-encoding-utf-8": "^1.0.2"
}
"name": "borsh",
"version": "0.6.0",
"description": "Binary Object Representation Serializer for Hashing",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"scripts": {
"build": "tsc -p ./tsconfig.json",
"test": "jest test --runInBand",
"fuzz": "jsfuzz borsh-ts/test/fuzz/borsh-roundtrip.js borsh-ts/test/fuzz/corpus/",
"dev": "yarn build -w",
"pretest": "yarn build",
"lint": "eslint borsh-ts/**/*.ts",
"pretty": "prettier borsh-ts/**/*.ts package.json",
"pretty:check": "yarn pretty --check",
"fix": "eslint borsh-ts/**/*.ts --fix"
},
"repository": {
"type": "git",
"url": "git+https://github.com/near/borsh-js.git"
},
"keywords": [
"serializer",
"binary",
"serializer",
"deserializer",
"consistency",
"deterministic"
],
"author": "Near Inc",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/near/borsh-js/issues"
},
"homepage": "https://github.com/near/borsh-js#readme",
"devDependencies": {
"@types/babel__core": "^7.1.2",
"@types/babel__template": "^7.0.2",
"@types/bn.js": "^5.1.0",
"@types/node": "^12.7.3",
"@typescript-eslint/eslint-plugin": "^2.18.0",
"@typescript-eslint/parser": "^2.18.0",
"bs58": "^4.0.0",
"eslint": "^6.5.1",
"jest": "^26.0.1",
"js-sha256": "^0.9.0",
"jsfuzz": "^1.0.14",
"prettier": "^2.4.1",
"typescript": "^3.6.2"
},
"dependencies": {
"bn.js": "^5.2.0",
"bs58": "^4.0.0",
"text-encoding-utf-8": "^1.0.2"
}
}