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

Use allocUnsafe where possible #278

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@dapplion/benchmark": "^0.2.2",
"@types/chai": "^4.2.15",
"@types/mocha": "^8.2.2",
"@types/node": "^14.14.17",
"@types/node": "^16.11.55",
"@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0",
"babel-loader": "^8.2.2",
Expand Down
10 changes: 5 additions & 5 deletions packages/as-sha256/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {newInstance} from "./wasm";
import {HashObject, byteArrayToHashObject, hashObjectToByteArray} from "./hashObject";
import SHA256 from "./sha256";
import SHA256, {allocUnsafe} from "./sha256";
export {HashObject, byteArrayToHashObject, hashObjectToByteArray, SHA256};

const ctx = newInstance();
Expand All @@ -18,7 +18,7 @@ export function digest(data: Uint8Array): Uint8Array {
if (data.length <= ctx.INPUT_LENGTH) {
inputUint8Array.set(data);
ctx.digest(data.length);
const output = new Uint8Array(32);
const output = allocUnsafe(32);
output.set(outputUint8Array);
return output;
}
Expand All @@ -32,7 +32,7 @@ export function digest64(data: Uint8Array): Uint8Array {
if (data.length === 64) {
inputUint8Array.set(data);
ctx.digest64(wasmInputValue, wasmOutputValue);
const output = new Uint8Array(32);
const output = allocUnsafe(32);
output.set(outputUint8Array);
return output;
}
Expand All @@ -44,7 +44,7 @@ export function digest2Bytes32(bytes1: Uint8Array, bytes2: Uint8Array): Uint8Arr
inputUint8Array.set(bytes1);
inputUint8Array.set(bytes2, 32);
ctx.digest64(wasmInputValue, wasmOutputValue);
const output = new Uint8Array(32);
const output = allocUnsafe(32);
output.set(outputUint8Array);
return output;
}
Expand Down Expand Up @@ -98,7 +98,7 @@ function update(data: Uint8Array): void {

function final(): Uint8Array {
ctx.final(wasmOutputValue);
const output = new Uint8Array(32);
const output = allocUnsafe(32);
output.set(outputUint8Array);
return output;
}
18 changes: 17 additions & 1 deletion packages/as-sha256/src/sha256.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,24 @@ export default class SHA256 {

final(): Uint8Array {
this.ctx.final(this.wasmOutputValue);
const output = new Uint8Array(32);
const output = allocUnsafe(32);
output.set(this.uint8OutputArray);
return output;
}
}

/**
* Where possible returns a Uint8Array of the requested size that references
* uninitialized memory. Only use if you are certain you will immediately
* overwrite every value in the returned `Uint8Array`.
*
* @param {number} [size]
* @returns {Uint8Array}
*/
export function allocUnsafe(size = 0): Uint8Array {
if (globalThis.Buffer != null && globalThis.Buffer.allocUnsafe != null) {
return globalThis.Buffer.allocUnsafe(size);
}

return new Uint8Array(size);
}
5 changes: 3 additions & 2 deletions packages/persistent-merkle-tree/src/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {
HashObject,
hashObjectToByteArray,
} from "@chainsafe/as-sha256";
import {allocUnsafe} from "./proof/util";

const input = new Uint8Array(64);
const input = allocUnsafe(64);

/**
* Hash two 32 byte arrays
Expand All @@ -25,7 +26,7 @@ export function hashTwoObjects(a: HashObject, b: HashObject): HashObject {
}

export function hashObjectToUint8Array(obj: HashObject): Uint8Array {
const byteArr = new Uint8Array(32);
const byteArr = allocUnsafe(32);
hashObjectToByteArray(obj, byteArr, 0);
return byteArr;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/persistent-merkle-tree/src/proof/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
deserializeTreeOffsetProof,
serializeTreeOffsetProof,
} from "./treeOffset";
import {allocUnsafe} from "./util";

export enum ProofType {
single = "single",
Expand Down Expand Up @@ -128,7 +129,7 @@ export function serializeProof(proof: Proof): Uint8Array {
case ProofType.multi:
throw new Error("Not implemented");
case ProofType.treeOffset: {
const output = new Uint8Array(1 + computeTreeOffsetProofSerializedLength(proof.offsets, proof.leaves));
const output = allocUnsafe(1 + computeTreeOffsetProofSerializedLength(proof.offsets, proof.leaves));
output[0] = ProofTypeSerialized.indexOf(ProofType.treeOffset);
serializeTreeOffsetProof(output, 1, proof.offsets, proof.leaves);
return output;
Expand Down
16 changes: 16 additions & 0 deletions packages/persistent-merkle-tree/src/proof/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,19 @@ export function computeMultiProofBitstrings(
return Array.from(proof);
}
}

/**
* Where possible returns a Uint8Array of the requested size that references
* uninitialized memory. Only use if you are certain you will immediately
* overwrite every value in the returned `Uint8Array`.
*
* @param {number} [size]
* @returns {Uint8Array}
*/
export function allocUnsafe(size = 0): Uint8Array {
if (globalThis.Buffer != null && globalThis.Buffer.allocUnsafe != null) {
return globalThis.Buffer.allocUnsafe(size);
}

return new Uint8Array(size);
}
7 changes: 4 additions & 3 deletions packages/ssz/src/type/abstract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Node} from "@chainsafe/persistent-merkle-tree";
import {alloc} from "../util/byteArray";

/* eslint-disable @typescript-eslint/member-ordering */

Expand Down Expand Up @@ -94,7 +95,7 @@ export abstract class Type<V> {
/** INTERNAL METHOD: Merkleize value to tree */
value_toTree(value: V): Node {
// TODO: Un-performant path but useful for prototyping. Overwrite in Type if performance is important
const uint8Array = new Uint8Array(this.value_serializedSize(value));
const uint8Array = alloc(this.value_serializedSize(value));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.value_serializeToBytes({uint8Array, dataView}, 0, value);
return this.tree_deserializeFromBytes({uint8Array, dataView}, 0, uint8Array.length);
Expand All @@ -103,7 +104,7 @@ export abstract class Type<V> {
/** INTERNAL METHOD: Un-merkleize tree to value */
tree_toValue(node: Node): V {
// TODO: Un-performant path but useful for prototyping. Overwrite in Type if performance is important
const uint8Array = new Uint8Array(this.tree_serializedSize(node));
const uint8Array = alloc(this.tree_serializedSize(node));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.tree_serializeToBytes({uint8Array, dataView}, 0, node);
return this.value_deserializeFromBytes({uint8Array, dataView}, 0, uint8Array.length);
Expand All @@ -116,7 +117,7 @@ export abstract class Type<V> {

/** Serialize a value to binary data */
serialize(value: V): Uint8Array {
const uint8Array = new Uint8Array(this.value_serializedSize(value));
const uint8Array = alloc(this.value_serializedSize(value));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.value_serializeToBytes({uint8Array, dataView}, 0, value);
return uint8Array;
Expand Down
3 changes: 2 additions & 1 deletion packages/ssz/src/type/basic.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {LeafNode} from "@chainsafe/persistent-merkle-tree";
import {alloc} from "../util/byteArray";
import {Type} from "./abstract";

/* eslint-disable @typescript-eslint/member-ordering */
Expand Down Expand Up @@ -31,7 +32,7 @@ export abstract class BasicType<V> extends Type<V> {

hashTreeRoot(value: V): Uint8Array {
// TODO: Optimize
const uint8Array = new Uint8Array(32);
const uint8Array = alloc(32);
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.value_serializeToBytes({uint8Array, dataView}, 0, value);
return uint8Array;
Expand Down
6 changes: 3 additions & 3 deletions packages/ssz/src/type/byteArray.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {concatGindices, Gindex, Node, toGindex, Tree} from "@chainsafe/persistent-merkle-tree";
import {fromHexString, toHexString, byteArrayEquals} from "../util/byteArray";
import {fromHexString, toHexString, byteArrayEquals, alloc} from "../util/byteArray";
import {splitIntoRootChunks} from "../util/merkleize";
import {ByteViews} from "./abstract";
import {CompositeType, LENGTH_GINDEX} from "./composite";
Expand All @@ -22,7 +22,7 @@ export abstract class ByteArrayType extends CompositeType<ByteArray, ByteArray,

defaultValue(): ByteArray {
// Since it's a byte array the minSize is bytes is the default size
return new Uint8Array(this.minSize);
return alloc(this.minSize);
}

getView(tree: Tree): ByteArray {
Expand All @@ -38,7 +38,7 @@ export abstract class ByteArrayType extends CompositeType<ByteArray, ByteArray,
}

commitViewDU(view: ByteArray): Node {
const uint8Array = new Uint8Array(this.value_serializedSize(view));
const uint8Array = alloc(this.value_serializedSize(view));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.value_serializeToBytes({uint8Array, dataView}, 0, view);
return this.tree_deserializeFromBytes({uint8Array, dataView}, 0, uint8Array.length);
Expand Down
5 changes: 3 additions & 2 deletions packages/ssz/src/type/containerNodeStruct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {getContainerTreeViewClass} from "../view/containerNodeStruct";
import {getContainerTreeViewDUClass} from "../viewDU/containerNodeStruct";
import {BranchNodeStruct} from "../branchNodeStruct";
import {ValueOfFields} from "../view/container";
import {alloc} from "../util/byteArray";

/**
* ContainerNodeStruct: ordered heterogeneous collection of values.
Expand Down Expand Up @@ -77,7 +78,7 @@ export class ContainerNodeStructType<Fields extends Record<string, Type<unknown>
tree_fromProofNode(node: Node): {node: Node; done: boolean} {
// TODO: Figure out from `node` alone if it contains complete data.
// Otherwise throw a nice error "ContainerNodeStruct type requires proofs for all its data"
const uint8Array = new Uint8Array(super.tree_serializedSize(node));
const uint8Array = alloc(super.tree_serializedSize(node));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
super.tree_serializeToBytes({uint8Array, dataView}, 0, node);
const value = this.value_deserializeFromBytes({uint8Array, dataView}, 0, uint8Array.length);
Expand All @@ -99,7 +100,7 @@ export class ContainerNodeStructType<Fields extends Record<string, Type<unknown>

// TODO: Optimize conversion
private valueToTree(value: ValueOfFields<Fields>): Node {
const uint8Array = new Uint8Array(this.value_serializedSize(value));
const uint8Array = alloc(this.value_serializedSize(value));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
this.value_serializeToBytes({uint8Array, dataView}, 0, value);
return super.tree_deserializeFromBytes({uint8Array, dataView}, 0, uint8Array.length);
Expand Down
3 changes: 2 additions & 1 deletion packages/ssz/src/type/listBasic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {ArrayBasicType} from "../view/arrayBasic";
import {ListBasicTreeView} from "../view/listBasic";
import {ListBasicTreeViewDU} from "../viewDU/listBasic";
import {ArrayType} from "./array";
import {alloc} from "../util/byteArray";

/* eslint-disable @typescript-eslint/member-ordering */

Expand Down Expand Up @@ -138,7 +139,7 @@ export class ListBasicType<ElementType extends BasicType<unknown>>
}

protected getRoots(value: ValueOf<ElementType>[]): Uint8Array[] {
const uint8Array = new Uint8Array(this.value_serializedSize(value));
const uint8Array = alloc(this.value_serializedSize(value));
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
value_serializeToBytesArrayBasic(this.elementType, value.length, {uint8Array, dataView}, 0, value);
return splitIntoRootChunks(uint8Array);
Expand Down
3 changes: 2 additions & 1 deletion packages/ssz/src/type/vectorBasic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import {ArrayBasicType, ArrayBasicTreeView} from "../view/arrayBasic";
import {ArrayBasicTreeViewDU} from "../viewDU/arrayBasic";
import {ArrayType} from "./array";
import {alloc} from "../util/byteArray";

/* eslint-disable @typescript-eslint/member-ordering */

Expand Down Expand Up @@ -129,7 +130,7 @@ export class VectorBasicType<ElementType extends BasicType<unknown>>
// Merkleization

protected getRoots(value: ValueOf<ElementType>[]): Uint8Array[] {
const uint8Array = new Uint8Array(this.fixedSize);
const uint8Array = alloc(this.fixedSize);
const dataView = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
value_serializeToBytesArrayBasic(this.elementType, this.length, {uint8Array, dataView}, 0, value);
return splitIntoRootChunks(uint8Array);
Expand Down
33 changes: 32 additions & 1 deletion packages/ssz/src/util/byteArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function fromHexString(hex: string): Uint8Array {
}

const byteLen = hex.length / 2;
const bytes = new Uint8Array(byteLen);
const bytes = allocUnsafe(byteLen);
for (let i = 0; i < byteLen; i++) {
const byte = parseInt(hex.slice(i * 2, (i + 1) * 2), 16);
bytes[i] = byte;
Expand All @@ -45,3 +45,34 @@ export function byteArrayEquals(a: Uint8Array, b: Uint8Array): boolean {
}
return true;
}

/**
* Returns a `Uint8Array` of the requested size. Referenced memory will
* be initialized to 0.
*
* @param {number} [size]
* @returns {Uint8Array}
*/
export function alloc(size = 0): Uint8Array {
if (globalThis.Buffer != null && globalThis.Buffer.alloc != null) {
return globalThis.Buffer.alloc(size);
}

return new Uint8Array(size);
}

/**
* Where possible returns a Uint8Array of the requested size that references
* uninitialized memory. Only use if you are certain you will immediately
* overwrite every value in the returned `Uint8Array`.
*
* @param {number} [size]
* @returns {Uint8Array}
*/
export function allocUnsafe(size = 0): Uint8Array {
if (globalThis.Buffer != null && globalThis.Buffer.allocUnsafe != null) {
return globalThis.Buffer.allocUnsafe(size);
}

return new Uint8Array(size);
}
3 changes: 2 additions & 1 deletion packages/ssz/src/util/merkleize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {digest2Bytes32} from "@chainsafe/as-sha256";
import {alloc} from "./byteArray";
import {zeroHash} from "./zeros";

export function hash64(bytes32A: Uint8Array, bytes32B: Uint8Array): Uint8Array {
Expand Down Expand Up @@ -43,7 +44,7 @@ export function splitIntoRootChunks(longChunk: Uint8Array): Uint8Array[] {
const chunks = new Array<Uint8Array>(chunkCount);

for (let i = 0; i < chunkCount; i++) {
const chunk = new Uint8Array(32);
const chunk = alloc(32);
chunk.set(longChunk.slice(i * 32, (i + 1) * 32));
chunks[i] = chunk;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/ssz/src/util/zeros.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {digest2Bytes32} from "@chainsafe/as-sha256";
import {alloc} from "./byteArray";

// create array of "zero hashes", successively hashed zero chunks
const zeroHashes = [new Uint8Array(32)];
const zeroHashes = [alloc(32)];

export function zeroHash(depth: number): Uint8Array {
if (depth >= zeroHashes.length) {
Expand Down
4 changes: 3 additions & 1 deletion packages/ssz/src/value/bitArray.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {alloc} from "../util/byteArray";

/** Globally cache this information. @see getUint8ByteToBitBooleanArray */
const uint8ByteToBitBooleanArrays = new Array<boolean[]>(256);

Expand Down Expand Up @@ -29,7 +31,7 @@ export class BitArray {

/** Returns a zero'ed BitArray of `bitLen` */
static fromBitLen(bitLen: number): BitArray {
return new BitArray(new Uint8Array(Math.ceil(bitLen / 8)), bitLen);
return new BitArray(alloc(Math.ceil(bitLen / 8)), bitLen);
}

/** Returns a BitArray of `bitLen` with a single bit set to true at position `bitIndex` */
Expand Down
3 changes: 2 additions & 1 deletion packages/ssz/src/view/abstract.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Node, Tree, Proof} from "@chainsafe/persistent-merkle-tree";
import {ValueOf, JsonPath} from "../type/abstract";
import {CompositeType} from "../type/composite";
import {alloc} from "../util/byteArray";

/**
* A Tree View is a wrapper around a type and an SSZ Tree that contains:
Expand All @@ -21,7 +22,7 @@ export abstract class TreeView<T extends CompositeType<unknown, unknown, unknown

/** Serialize view to binary data */
serialize(): Uint8Array {
const output = new Uint8Array(this.type.tree_serializedSize(this.node));
const output = alloc(this.type.tree_serializedSize(this.node));
const dataView = new DataView(output.buffer, output.byteOffset, output.byteLength);
this.type.tree_serializeToBytes({uint8Array: output, dataView}, 0, this.node);
return output;
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2414,10 +2414,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da"
integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==

"@types/node@^14.14.17":
version "14.17.14"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.14.tgz#6fda9785b41570eb628bac27be4b602769a3f938"
integrity sha512-rsAj2u8Xkqfc332iXV12SqIsjVi07H479bOP4q94NAcjzmAvapumEhuVIt53koEf7JFrpjgNKjBga5Pnn/GL8A==
"@types/node@^16.11.55":
version "16.11.56"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.56.tgz#dcbb617669481e158e0f1c6204d1c768cd675901"
integrity sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==

"@types/normalize-package-data@^2.4.0":
version "2.4.1"
Expand Down