├── .babel-register
├── .gitignore
├── assembly
├── tsconfig.json
├── __tests__
│ ├── example.spec.ts
│ └── as-pect.d.ts
└── index.ts
├── examples
├── browser-example.html
└── run-wasm.js
├── src
├── wasm.js
├── index.d.ts
└── index.js
├── .babelrc
├── LICENSE
├── .github
└── workflows
│ ├── test.yml
│ └── benchmark.yml
├── README.md
├── karma.config.js
├── CHANGELOG.md
├── as-pect.config.js
├── package.json
└── test
├── benchmark.test.js
└── index.spec.js
/.babel-register:
--------------------------------------------------------------------------------
1 | require('@babel/register')
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | yarn-error.log
3 | dist/
4 | lib/
5 | benchmark.txt
6 |
--------------------------------------------------------------------------------
/assembly/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../node_modules/assemblyscript/std/assembly.json",
3 | "include": [
4 | "./**/*.ts"
5 | ]
6 | }
--------------------------------------------------------------------------------
/examples/browser-example.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | title
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/wasm.js:
--------------------------------------------------------------------------------
1 | import loader from "@assemblyscript/loader";
2 | import wasmCode from "../build/optimized.wasm";
3 | import {Buffer} from "buffer";
4 |
5 | const module = new WebAssembly.Module(Buffer.from(wasmCode, 'binary'));
6 |
7 | export function newInstance() {
8 | return loader.instantiateSync(module);
9 | }
10 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "targets": {
7 | "node": "8",
8 | "browsers": ">1%, not ie 11"
9 | },
10 | "exclude": [
11 | "transform-regenerator"
12 | ]
13 | }
14 | ]
15 | ],
16 | "plugins": [
17 | ["@chainsafe/babel-plugin-inline-binary-import", {
18 | "extensions": [
19 | ".wasm"
20 | ]
21 | }]
22 | ]
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 ChainSafe Systems
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
15 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on: [pull_request, push]
4 |
5 | jobs:
6 | test:
7 | name: Test
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v1
11 | - uses: actions/setup-node@v2-beta
12 | with:
13 | node-version: "12.14.1"
14 |
15 | - name: Get yarn cache directory path
16 | id: yarn-cache-dir-path
17 | run: echo "::set-output name=dir::$(yarn cache dir)"
18 | - uses: actions/cache@v2
19 | id: yarn-cache
20 | with:
21 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
22 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
23 | restore-keys: |
24 | ${{ runner.os }}-yarn-
25 |
26 | - name: Build
27 | run: yarn
28 | - name: Unit tests
29 | run: yarn test
30 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * This is a hash representation with 8 numbers, each 4 bytes.
4 | * That makes it 32 bytes, the same to Uint8Array(32).
5 | */
6 | export interface HashObject {
7 | h0: number;
8 | h1: number;
9 | h2: number;
10 | h3: number;
11 | h4: number;
12 | h5: number;
13 | h6: number;
14 | h7: number;
15 | }
16 |
17 | export default class SHA256 {
18 | constructor();
19 | init(): this;
20 | update(data: Uint8Array): this;
21 | final(): Uint8Array;
22 |
23 | static digest(data: Uint8Array): Uint8Array;
24 | static digest64(data: Uint8Array): Uint8Array;
25 | static digestTwoHashObjects(obj1: HashObject, obj2: HashObject): HashObject;
26 | }
27 |
28 | export function hashObjectToByteArray(obj: HashObject, byteArr: ArrayLike, offset: number): void;
29 | export function byteArrayToHashObject(byteArr: ArrayLike): HashObject;
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This package is now maintained at https://github.com/ChainSafe/ssz/tree/master/packages/as-sha256
2 | --
3 |
4 | # as-sha256
5 |
6 | 
7 | 
8 |
9 | AssemblyScript implementation of SHA256.
10 |
11 | ## Usage
12 |
13 | `yarn add @chainsafe/as-sha256`
14 |
15 | ```typescript
16 | import SHA256 from "@chainsafe/as-sha256";
17 |
18 | let hash: Uint8Array;
19 |
20 | // create a new sha256 context
21 | const sha256 = new SHA256();
22 | // with init(), update(data), and final()
23 | hash = sha256.init().update(Buffer.from("Hello world")).final();
24 |
25 | // or use a one-pass interface
26 | hash = SHA256.digest(Buffer.from("Hello world"));
27 |
28 | // or use a faster one-pass interface for hashing (only) 64 bytes
29 | hash = SHA256.digest64(Buffer.from("abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh"));
30 | ```
31 |
32 | ### License
33 |
34 | Apache 2.0
35 |
--------------------------------------------------------------------------------
/karma.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | config.set({
3 | frameworks: ['mocha', 'chai'],
4 | files: [
5 | 'test/index.spec.js'
6 | ],
7 | preprocessors: {
8 | 'test/index.spec.js': ['webpack']
9 | },
10 | webpack: {
11 | mode: "production",
12 | module: {
13 | rules: [
14 | {
15 | test: /\.js?$/,
16 | exclude: /node_modules/,
17 | loader: 'babel-loader',
18 | },
19 | ]
20 | },
21 | resolve: {
22 | extensions: ['.ts', '.js']
23 | }
24 | },
25 | reporters: ['progress'],
26 | port: 9876, // karma web server port
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | browsers: ['ChromeHeadless', 'Firefox', 'FirefoxDeveloper', 'FirefoxNightly'],
30 | autoWatch: false,
31 | concurrency: Infinity,
32 | customLaunchers: {
33 | FirefoxHeadless: {
34 | base: 'Firefox',
35 | flags: ['-headless'],
36 | },
37 | },
38 | plugins: [
39 | require("karma-webpack"),
40 | require("karma-chrome-launcher"),
41 | require("karma-firefox-launcher"),
42 | require("karma-mocha"),
43 | require("karma-chai"),
44 | ]
45 | })
46 | };
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.2.4 (2021-08-18)
2 |
3 | - normal digest mem opt for < 512 bytes ([30f7ec](https://github.com/ChainSafe/as-sha256/commit/30f7ec))
4 |
5 | ## 0.2.3 (2021-08-10)
6 |
7 | - Add digestObjects method ([da5d82](https://github.com/ChainSafe/as-sha256/commit/da5d82))
8 | - Optimised w+k for digest64 ([359555](https://github.com/ChainSafe/as-sha256/commit/359555))
9 |
10 | ## 0.2.2 (2021-05-06)
11 |
12 | ### Bug Fixes
13 |
14 | - Fix static digest method ([a42f89](https://github.com/ChainSafe/as-sha256/commit/a42f89))
15 |
16 | ## 0.2.1 (2021-05-04)
17 |
18 | ### Chores
19 |
20 | - Add performance tests ([9621ea](https://github.com/ChainSafe/as-sha256/commit/9621ea))
21 |
22 | ### Features
23 |
24 | - Add optimized digest64 method for 64 byte input ([4acbea](https://github.com/ChainSafe/as-sha256/commit/4acbea))
25 |
26 |
27 | ## 0.2.0 (2020-02-19)
28 |
29 | ### BREAKING CHANGES
30 |
31 | * new TS and AS exported interface
32 |
33 |
34 | ## 0.1.4 (2020-01-30)
35 |
36 | ### Bug Fixes
37 |
38 | * fix data corruption on hash return ([9dd43f](https://github.com/ChainSafe/as-sha256/commit/9dd43f))
39 |
40 | ### Chores
41 |
42 | * update license to Apache-2.0 ([585b2c6](https://github.com/ChainSafe/as-sha256/commit/585b2c6))
43 |
44 | ### Code Refactoring
45 |
46 | * browser compatible as-sha256 ([e44f1d0](https://github.com/ChainSafe/as-sha256/commit/e44f1d0))
47 | * update README & minor refactorings ([122e2a8](https://github.com/ChainSafe/as-sha256/commit/122e2a8))
48 | * add `toHexString` to public wasm api ([eb3534a](https://github.com/ChainSafe/as-sha256/commit/eb3534a))
49 |
--------------------------------------------------------------------------------
/.github/workflows/benchmark.yml:
--------------------------------------------------------------------------------
1 | name: Benchmark
2 |
3 | # only one can tun at a time.
4 | # Actions access a common cache entry and may corrupt it.
5 | concurrency: cd-benchmark-${{ github.ref }}
6 |
7 | on:
8 | push:
9 | branches:
10 | - master
11 | - benchmark # For debugging
12 | pull_request:
13 | branches:
14 | - master
15 |
16 | jobs:
17 | run:
18 | runs-on: ubuntu-latest
19 | # Don't run on forks. Forks don't have access to the S3 credentials and the workflow will fail
20 | if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
21 |
22 | steps:
23 | # - Uses YAML anchors in the future
24 | - uses: actions/checkout@v2
25 | - uses: actions/setup-node@v2-beta
26 | with:
27 | node-version: "14.16.0"
28 | - name: Restore dependencies
29 | uses: actions/cache@master
30 | id: cache-deps
31 | with:
32 | path: |
33 | node_modules
34 | packages/*/node_modules
35 | key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}-ignore-optional-14.16.0
36 | - name: Install & build
37 | if: steps.cache-deps.outputs.cache-hit != 'true'
38 | run: yarn install --frozen-lockfile --ignore-optional
39 | - name: Build
40 | run: yarn build
41 | if: steps.cache-deps.outputs.cache-hit == 'true'
42 | #
43 |
44 | - name: Run benchmarks
45 | run: yarn benchmark
46 | env:
47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48 | # benchmark options
49 | BENCHMARK_S3: true
50 | BENCHMARK_threshold: 3
51 | # S3 credentials
52 | S3_ACCESS_KEY: ${{ secrets.S3_BENCH_LODESTAR_ACCESS_KEY }}
53 | S3_SECRET_KEY: ${{ secrets.S3_BENCH_LODESTAR_SECRET_KEY }}
54 | S3_REGION: ${{ secrets.S3_BENCH_LODESTAR_REGION }}
55 | S3_BUCKET: ${{ secrets.S3_BENCH_LODESTAR_BUCKET }}
56 | S3_ENDPOINT: ${{ secrets.S3_BENCH_LODESTAR_ENDPOINT }}
57 | # Key prefix to separate benchmark data from multiple repositories
58 | S3_KEY_PREFIX: ${{ github.repository }}/${{ runner.os }}
59 |
--------------------------------------------------------------------------------
/examples/run-wasm.js:
--------------------------------------------------------------------------------
1 | const crypto = require('crypto');
2 | const wasm = require('../src/wasm.js');
3 | const sha = require('../lib/index.js');
4 |
5 | const toHexString = byteArray => byteArray.reduce((acc, val) => (acc + ('0' + val.toString(16)).slice(-2)), '');
6 |
7 | const emptyMessage = new Uint8Array(0);
8 |
9 | const testString = 'testi';
10 | const Message = new Uint8Array(Buffer.from(testString));
11 |
12 | const aMessage = new Uint8Array([97,98,99]);
13 | const randomMessage2048 = new Uint8Array(crypto.randomBytes(2048));
14 | const randomMessage16384 = new Uint8Array(crypto.randomBytes(16384));
15 |
16 | const message = wasm.__retain(wasm.__allocArray(wasm.UINT8ARRAY_ID, Message));
17 | const amessage = wasm.__retain(wasm.__allocArray(wasm.UINT8ARRAY_ID, aMessage));
18 | const emptymessage = wasm.__retain(wasm.__allocArray(wasm.UINT8ARRAY_ID, emptyMessage));
19 | const randomessage2048 = wasm.__retain(wasm.__allocArray(wasm.UINT8ARRAY_ID, randomMessage2048));
20 | const randomessage16384 = wasm.__retain(wasm.__allocArray(wasm.UINT8ARRAY_ID, randomMessage16384));
21 |
22 |
23 | console.time('as (wasm)');
24 | const messageOut = wasm.hash(message);
25 | const amessageOut = wasm.hash(amessage);
26 | const emptymessageOut = wasm.hash(emptymessage);
27 | for (let i = 0; i < 1000; i++) {
28 | wasm.hash(randomessage2048);
29 | }
30 | for (let i = 0; i < 100; i++) {
31 | wasm.hash(randomessage16384);
32 | }
33 | console.timeEnd('as (wasm)');
34 |
35 | console.log(toHexString(wasm.__getUint8Array(messageOut)), '26e19f2b4dd93a3a7c49c3e785ec8932550af6aa6bea13078672a8c81508f18e');
36 | console.log(toHexString(wasm.__getUint8Array(amessageOut)), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad');
37 | console.log(toHexString(wasm.__getUint8Array(emptymessageOut)), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
38 |
39 | const h = sha.default(testString);
40 | console.log(toHexString(h), '26e19f2b4dd93a3a7c49c3e785ec8932550af6aa6bea13078672a8c81508f18e');
41 |
42 | wasm.__release(messageOut);
43 | wasm.__release(amessageOut);
44 | wasm.__release(emptymessageOut);
45 |
46 | wasm.__release(message);
47 | wasm.__release(amessage);
48 | wasm.__release(emptymessage);
49 | wasm.__release(randomessage2048);
50 | wasm.__release(randomessage16384);
--------------------------------------------------------------------------------
/as-pect.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /**
3 | * A set of globs passed to the glob package that qualify typescript files for testing.
4 | */
5 | include: ["assembly/__tests__/**/*.spec.ts"],
6 | /**
7 | * A set of globs passed to the glob package that quality files to be added to each test.
8 | */
9 | add: ["assembly/__tests__/**/*.include.ts"],
10 | /**
11 | * All the compiler flags needed for this test suite. Make sure that a binary file is output.
12 | */
13 | flags: {
14 | /** To output a wat file, uncomment the following line. */
15 | // "--textFile": ["output.wat"],
16 | /** A runtime must be provided here. */
17 | "--runtime": ["full"] // Acceptable values are: full, half, stub (arena), and none
18 | },
19 | /**
20 | * A set of regexp that will disclude source files from testing.
21 | */
22 | disclude: [/node_modules/],
23 | /**
24 | * Add your required AssemblyScript imports here.
25 | */
26 | imports: {},
27 | /**
28 | * All performance statistics reporting can be configured here.
29 | */
30 | performance: {
31 | /** Enable performance statistics gathering for every test. */
32 | enabled: false,
33 | /** Set the maximum number of samples to run for every test. */
34 | maxSamples: 10000,
35 | /** Set the maximum test run time in milliseconds for every test. */
36 | maxTestRunTime: 5000,
37 | /** Report the median time in the default reporter for every test. */
38 | reportMedian: true,
39 | /** Report the average time in milliseconds for every test. */
40 | reportAverage: true,
41 | /** Report the standard deviation for every test. */
42 | reportStandardDeviation: false,
43 | /** Report the maximum run time in milliseconds for every test. */
44 | reportMax: false,
45 | /** Report the minimum run time in milliseconds for every test. */
46 | reportMin: false,
47 | },
48 | /**
49 | * Add a custom reporter here if you want one. The following example is in typescript.
50 | *
51 | * @example
52 | * import { TestReporter, TestGroup, TestResult, TestContext } from "as-pect";
53 | *
54 | * export class CustomReporter extends TestReporter {
55 | * // implement each abstract method here
56 | * public abstract onStart(suite: TestContext): void;
57 | * public abstract onGroupStart(group: TestGroup): void;
58 | * public abstract onGroupFinish(group: TestGroup): void;
59 | * public abstract onTestStart(group: TestGroup, result: TestResult): void;
60 | * public abstract onTestFinish(group: TestGroup, result: TestResult): void;
61 | * public abstract onFinish(suite: TestContext): void;
62 | * }
63 | */
64 | // reporter: new CustomReporter(),
65 | /**
66 | * Specify if the binary wasm file should be written to the file system.
67 | */
68 | outputBinary: false,
69 | };
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@chainsafe/as-sha256",
3 | "version": "0.2.4",
4 | "description": "An AssemblyScript implementation of SHA256",
5 | "author": "ChainSafe Systems",
6 | "license": "Apache-2.0",
7 | "bugs": {
8 | "url": "https://github.com/ChainSafe/as-sha256/issues"
9 | },
10 | "homepage": "https://github.com/ChainSafe/as-sha256#readme",
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/chainsafe/as-sha256.git"
14 | },
15 | "main": "lib/index.js",
16 | "types": "lib/index.d.ts",
17 | "files": [
18 | "lib",
19 | "build"
20 | ],
21 | "scripts": {
22 | "prebuild": "rm -rf ./dist",
23 | "prepublish": "yarn build",
24 | "build": "yarn asbuild:untouched && yarn asbuild:optimized && yarn build:lib",
25 | "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --runtime none --validate --debug",
26 | "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --runtime none --validate -O3z --noAssert",
27 | "build:lib": "babel src -d lib --copy-files",
28 | "build:web": "webpack --mode production --entry ./index.js --output ./dist/as-sha256.min.js",
29 | "test": "yarn run test:as-ci && yarn run test:unit",
30 | "test:unit": "yarn run test:unit:node && yarn run test:unit:browser",
31 | "test:unit:node": "mocha -r .babel-register test/*.spec.js",
32 | "benchmark": "node ./node_modules/.bin/benchmark 'test/benchmark.test.js'",
33 | "benchmark:local": "yarn benchmark --local",
34 | "test:unit:browser": "karma start --single-run --browsers ChromeHeadless,FirefoxHeadless karma.config.js",
35 | "test:ci": "yarn test:as-ci",
36 | "test:as": "asp --nortrace --verbose",
37 | "test:as-ci": "asp --nortrace 2> /dev/null"
38 | },
39 | "dependencies": {
40 | "@assemblyscript/loader": "^0.9.2",
41 | "buffer": "^5.4.3"
42 | },
43 | "devDependencies": {
44 | "@as-pect/assembly": "2.8.1",
45 | "@as-pect/cli": "2.8.1",
46 | "@as-pect/core": "2.8.1",
47 | "@babel/cli": "^7.6.0",
48 | "@babel/core": "^7.6.0",
49 | "@babel/preset-env": "^7.6.0",
50 | "@babel/register": "^7.7.4",
51 | "@chainsafe/babel-plugin-inline-binary-import": "^1.0.3",
52 | "assemblyscript": "0.9.2",
53 | "babel-loader": "^8.0.6",
54 | "benchmark": "^2.1.4",
55 | "chai": "^4.2.0",
56 | "karma": "^4.4.1",
57 | "karma-babel-preprocessor": "^8.0.1",
58 | "karma-chai": "^0.1.0",
59 | "karma-chrome-launcher": "^3.1.0",
60 | "karma-firefox-launcher": "^1.2.0",
61 | "karma-mocha": "^1.3.0",
62 | "karma-webpack": "^4.0.2",
63 | "@dapplion/benchmark": "^0.1.6",
64 | "mocha": "^8.3.0",
65 | "webpack": "^4.39.3",
66 | "webpack-cli": "^3.3.7"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/assembly/__tests__/example.spec.ts:
--------------------------------------------------------------------------------
1 | import { init, update, final, digest64 } from '..';
2 |
3 | export function toHexString(bin: Uint8Array): string {
4 | let bin_len = bin.length;
5 | let hex = "";
6 | for (let i = 0; i < bin_len; i++) {
7 | let bin_i = bin[i] as u32;
8 | let c = bin_i & 0xf;
9 | let b = bin_i >> 4;
10 | let x: u32 = ((87 + c + (((c - 10) >> 8) & ~38)) << 8) |
11 | (87 + b + (((b - 10) >> 8) & ~38));
12 | hex += String.fromCharCode(x as u8);
13 | x >>= 8;
14 | hex += String.fromCharCode(x as u8);
15 | }
16 | return hex;
17 | }
18 |
19 | function hash(data: Uint8Array): Uint8Array {
20 | const output = new Uint8Array(32);
21 | init();
22 | update(changetype(data.buffer), data.length);
23 | final(changetype(output.buffer));
24 | return output;
25 | }
26 |
27 | function hash64(data: Uint8Array): Uint8Array {
28 | const output = new Uint8Array(32);
29 | digest64(changetype(data.buffer),changetype(output.buffer));
30 | return output;
31 | }
32 |
33 | describe("example", () => {
34 | it("Hash: empty array", () => {
35 | let preImgArrayBuffer = new Uint8Array(0);
36 | expect(toHexString(hash(preImgArrayBuffer))).toBe("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
37 | });
38 |
39 | it("Hash: abc", () => {
40 | let preImgString = "abc";
41 | let preImgArrayBuffer = Uint8Array.wrap(String.UTF8.encode(preImgString));
42 | expect(toHexString(hash(preImgArrayBuffer))).toBe("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
43 | });
44 |
45 | it("Hash: lorem ipsum", () => {
46 | let preImgString = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
47 | let preImgArrayBuffer = Uint8Array.wrap(String.UTF8.encode(preImgString));
48 | expect(toHexString(hash(preImgArrayBuffer))).toBe("7321348c8894678447b54c888fdbc4e4b825bf4d1eb0cfb27874286a23ea9fd2");
49 | });
50 |
51 | it("Hash: abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", () => {
52 | let preImgString = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
53 | let preImgArrayBuffer = Uint8Array.wrap(String.UTF8.encode(preImgString));
54 | expect(toHexString(hash(preImgArrayBuffer))).toBe("248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
55 | });
56 |
57 | it("Hash64: gajindergajindergajindergajindergajindergajindergajindergajinder", () => {
58 | let preImgString = "gajindergajindergajindergajindergajindergajindergajindergajinder";
59 | let preImgArrayBuffer = Uint8Array.wrap(String.UTF8.encode(preImgString));
60 | expect(toHexString(hash64(preImgArrayBuffer))).toBe("be39380ff1d0261e6f37dafe4278b662ef611f1cb2f7c0a18348b2d7eb14cf6e");
61 | });
62 |
63 | it("CompareHash64: harkamalharkamalharkamalharkamalharkamalharkamalharkamalharkamal", () => {
64 | let preImgString = "harkamalharkamalharkamalharkamalharkamalharkamalharkamalharkamal";
65 | let preImgArrayBuffer = Uint8Array.wrap(String.UTF8.encode(preImgString));
66 |
67 | const normalHash=toHexString(hash(preImgArrayBuffer));
68 | const fastHash64=toHexString(hash64(preImgArrayBuffer));
69 | expect(fastHash64).toBe(normalHash);
70 |
71 | });
72 |
73 | });
74 |
75 |
--------------------------------------------------------------------------------
/test/benchmark.test.js:
--------------------------------------------------------------------------------
1 | const {itBench, setBenchOpts} = require("@dapplion/benchmark");
2 | const sha256 = require("../lib/index");
3 |
4 | // Aug 10 2021
5 | // digestTwoHashObjects vs digest64 vs digest
6 | // ✓ digestTwoHashObjects 50023 times 19.17366 ops/s 52.15488 ms/op - 1151 runs 60.0 s
7 | // ✓ digest64 50023 times 18.21352 ops/s 54.90425 ms/op - 1093 runs 60.0 s
8 | // ✓ digest 50023 times 10.60461 ops/s 94.29865 ms/op - 637 runs 60.1 s
9 | describe("digestTwoHashObjects vs digest64 vs digest", () => {
10 | setBenchOpts({
11 | maxMs: 100 * 1000,
12 | minMs: 60 * 1000,
13 | runs: 512,
14 | });
15 |
16 | const input = Buffer.from("gajindergajindergajindergajindergajindergajindergajindergajinder", "utf8");
17 | const input1 = "gajindergajindergajindergajinder";
18 | const input2 = "gajindergajindergajindergajinder";
19 | const buffer1 = Buffer.from(input1, "utf-8");
20 | const buffer2 = Buffer.from(input2, "utf-8");
21 | const obj1 = sha256.byteArrayToHashObject(buffer1);
22 | const obj2 = sha256.byteArrayToHashObject(buffer2);
23 | // total number of time running hash for 200000 balances
24 | const iterations = 50023;
25 | itBench(`digestTwoHashObjects ${iterations} times`, () => {
26 | for (let j = 0; j < iterations; j++) sha256.default.digestTwoHashObjects(obj1, obj2);
27 | });
28 |
29 | itBench(`digest64 ${iterations} times`, () => {
30 | for (let j = 0; j < iterations; j++) sha256.default.digest64(input);
31 | });
32 |
33 | itBench(`digest ${iterations} times`, () => {
34 | for (let j = 0; j < iterations; j++) sha256.default.digest(input);
35 | });
36 |
37 | });
38 |
39 | describe("digest different Buffers", () => {
40 | const randomBuffer = (length) => Buffer.from(Array.from({length}, () => Math.round(Math.random() * 255)));
41 |
42 | setBenchOpts({
43 | maxMs: 10 * 1000,
44 | minMs: 5 * 1000,
45 | runs: 512,
46 | });
47 |
48 | for (const length of [32, 64, 128, 256, 512, 1024]) {
49 | const buffer = randomBuffer(length);
50 | itBench(`input length ${length}`, () => {
51 | sha256.default.digest(buffer);
52 | });
53 | }
54 | });
55 |
56 | /**
57 | * time java: 2968 336927.2237196765 hashes/sec
58 | * time apache: 1025 975609.7560975610 hashes/sec
59 | *
60 | * Aug 04 2021
61 | * digest 1000000 times 0.8279731 ops/s 1.207769 s/op - 82 runs 100 s
62 | * => we are at 8279731 hashes/sec
63 | */
64 | describe("hash - compare to java", () => {
65 | // java statistic for same test: https://gist.github.com/scoroberts/a60d61a2cc3afba1e8813b338ecd1501
66 | setBenchOpts({
67 | maxMs: 100 * 1000,
68 | minMs: 60 * 1000,
69 | runs: 512,
70 | });
71 |
72 | const iterations = 1000000;
73 | const input = Buffer.from("lwkjt23uy45pojsdf;lnwo45y23po5i;lknwe;lknasdflnqw3uo5", "utf8");
74 |
75 | itBench(`digest ${iterations} times`, () => {
76 | for (let i=0; i {
85 | setBenchOpts({
86 | maxMs: 100 * 1000,
87 | minMs: 60 * 1000,
88 | runs: 512,
89 | });
90 |
91 | const input1 = "gajindergajindergajindergajinder";
92 | const buffer1 = Buffer.from(input1, "utf-8");
93 | const obj1 = sha256.byteArrayToHashObject(buffer1);
94 |
95 | // total number of time running hash for 200000 balances
96 | const iterations = 50023;
97 |
98 | itBench(`hashObjectToByteArray ${iterations} times`, () => {
99 | const byteArr = new Uint8Array(32);
100 | for (let j = 0; j < iterations; j++) sha256.hashObjectToByteArray(obj1, byteArr, 0);
101 | });
102 |
103 | itBench(`byteArrayToHashObject ${iterations} times`, () => {
104 | for (let j = 0; j < iterations; j++) sha256.byteArrayToHashObject(buffer1);
105 | });
106 | });
107 |
--------------------------------------------------------------------------------
/test/index.spec.js:
--------------------------------------------------------------------------------
1 | const sha256 = require("../src");
2 | const {expect} = require("chai");
3 |
4 | describe("sha256", function () {
5 |
6 | describe("digest function", function () {
7 |
8 | it('abc', function () {
9 | const input = Buffer.from("abc", "utf8");
10 | expect(Buffer.from(sha256.default.digest(input)).toString("hex")).to.be.equal("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
11 | });
12 |
13 | it('empty string', function () {
14 | const input = Buffer.from("", "utf8");
15 | expect(Buffer.from(sha256.default.digest(input)).toString("hex")).to.be.equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
16 | });
17 |
18 | it('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', function () {
19 | const input = Buffer.from("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "utf8");
20 | expect(Buffer.from(sha256.default.digest(input)).toString("hex")).to.be.equal("248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1")
21 | });
22 |
23 | it('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', function () {
24 | const input = Buffer.from("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "utf8");
25 | expect(Buffer.from(sha256.default.digest(input)).toString("hex")).to.be.equal("cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1")
26 | });
27 |
28 | it('gajindergajindergajindergajindergajindergajindergajindergajinder', function () {
29 | const input1 = "gajindergajindergajindergajinder";
30 | const input2 = "gajindergajindergajindergajinder";
31 | const input = Buffer.from(input1 + input2, "utf8");
32 | const output = sha256.default.digest64(input);
33 | const expectedOutput = new Uint8Array([
34 | 190, 57, 56, 15, 241, 208, 38, 30,
35 | 111, 55, 218, 254, 66, 120, 182, 98,
36 | 239, 97, 31, 28, 178, 247, 192, 161,
37 | 131, 72, 178, 215, 235, 20, 207, 110
38 | ]);
39 | expect(output).to.be.deep.equal(expectedOutput, "incorrect digest64 result");
40 | expect(Buffer.from(output).toString("hex")).to.be.equal("be39380ff1d0261e6f37dafe4278b662ef611f1cb2f7c0a18348b2d7eb14cf6e")
41 | // digestTwoHashObjects should be the same to digest64
42 | const buffer1 = Buffer.from(input1, "utf-8");
43 | const buffer2 = Buffer.from(input2, "utf-8");
44 | const obj1 = sha256.byteArrayToHashObject(buffer1);
45 | const obj2 = sha256.byteArrayToHashObject(buffer2);
46 | const obj = sha256.default.digestTwoHashObjects(obj1, obj2);
47 | const output2 = new Uint8Array(32);
48 | sha256.hashObjectToByteArray(obj, output2, 0);
49 | for (let i = 0; i < 32; i++) {
50 | expect(output2[i]).to.be.equal(output[i], "failed at index" + i);
51 | }
52 | expect(output2).to.be.deep.equal(expectedOutput, "incorrect digestTwoHashObjects result");
53 | });
54 |
55 | it('harkamalharkamalharkamalharkamalharkamalharkamalharkamalharkamal', function () {
56 | const input = Buffer.from("harkamalharkamalharkamalharkamalharkamalharkamalharkamalharkamal", "utf8");
57 | const output = Buffer.from(sha256.default.digest(input)).toString("hex");
58 | const output64 = Buffer.from(sha256.default.digest64(input)).toString("hex")
59 | expect(output).to.be.equal(output64)
60 | });
61 |
62 | it('1024 digest test', function () {
63 | let input = "12345678";
64 | input=`${input}${input}${input}${input}${input}${input}${input}${input}`;//64 length
65 | input=`${input}${input}${input}${input}${input}${input}${input}${input}`;//512 length
66 | input=`${input}${input}`;//1024 length
67 | input=Buffer.from(input,"utf8");
68 | expect(input.length).to.be.equal(1024)
69 |
70 | const output = Buffer.from(sha256.default.digest(input)).toString("hex");
71 | expect(output).to.be.equal("54c7cb8a82d68145fd5f5da4103f5a66f422dbea23d9fc9f40f59b1dcf5403a9");
72 | });
73 | })
74 |
75 | });
76 |
77 | describe("sha256.hashObjectToByteArray and sha256.byteArrayToHashObject", function () {
78 | const tcs = [
79 | new Uint8Array([
80 | 190, 57, 56, 15, 241, 208, 38, 30,
81 | 111, 55, 218, 254, 66, 120, 182, 98,
82 | 239, 97, 31, 28, 178, 247, 192, 161,
83 | 131, 72, 178, 215, 235, 20, 207, 110,
84 | ]),
85 | new Uint8Array([
86 | 255, 255, 255, 255, 255, 255, 255, 255,
87 | 255, 255, 255, 255, 255, 255, 255, 255,
88 | 255, 255, 255, 255, 255, 255, 255, 255,
89 | 255, 255, 255, 255, 255, 255, 255, 255,
90 | ]),
91 | new Uint8Array([
92 | 0, 0, 0, 0, 0, 0, 0, 0,
93 | 0, 0, 0, 0, 0, 0, 0, 0,
94 | 0, 0, 0, 0, 0, 0, 0, 0,
95 | 0, 0, 0, 0, 0, 0, 0, 0,
96 | ]),
97 | ];
98 | for (const [i, byteArr] of tcs.entries()) {
99 | it("test case " + i, function () {
100 | const obj = sha256.byteArrayToHashObject(byteArr);
101 | const newByteArr = new Uint8Array(32);
102 | sha256.hashObjectToByteArray(obj, newByteArr, 0);
103 | expect(newByteArr).to.be.deep.equal(byteArr, "failed test case" + i);
104 | });
105 | }
106 | });
107 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import {newInstance} from "./wasm";
2 |
3 | export default class SHA256 {
4 | constructor() {
5 | this.ctx = newInstance();
6 | this.wasmInputValue = this.ctx.input.value;
7 | this.wasmOutputValue = this.ctx.output.value;
8 | this.uint8InputArray = new Uint8Array(this.ctx.memory.buffer, this.wasmInputValue, this.ctx.INPUT_LENGTH);
9 | this.uint8OutputArray = new Uint8Array(this.ctx.memory.buffer, this.wasmOutputValue, 32);
10 | this.uint32InputArray = new Uint32Array(this.ctx.memory.buffer, this.wasmInputValue, this.ctx.INPUT_LENGTH);
11 | // extracting numbers from Uint32Array causes more memory
12 | // this.uint32OutputArray = new Uint32Array(this.ctx.memory.buffer, this.wasmOutputValue, 32);
13 | }
14 | init() {
15 | this.ctx.init();
16 | return this;
17 | }
18 | update(data) {
19 | const INPUT_LENGTH = this.ctx.INPUT_LENGTH;
20 | if (data.length > INPUT_LENGTH) {
21 | for (let i = 0; i < data.length; i += INPUT_LENGTH) {
22 | const sliced = data.slice(i, i + INPUT_LENGTH);
23 | this.uint8InputArray.set(sliced);
24 | this.ctx.update(this.wasmInputValue, sliced.length);
25 | }
26 | } else {
27 | this.uint8InputArray.set(data);
28 | this.ctx.update(this.wasmInputValue, data.length);
29 | }
30 | return this;
31 | }
32 | final() {
33 | this.ctx.final(this.wasmOutputValue);
34 | const output = new Uint8Array(32);
35 | output.set(this.uint8OutputArray);
36 | return output;
37 | }
38 |
39 | static digest(data) {
40 | if (data.length === 64) {
41 | return SHA256.digest64(data);
42 | }
43 | if (data.length <= staticInstance.ctx.INPUT_LENGTH) {
44 | staticInstance.uint8InputArray.set(data);
45 | staticInstance.ctx.digest(data.length);
46 | const output = new Uint8Array(32);
47 | output.set(staticInstance.uint8OutputArray);
48 | return output;
49 | }
50 | return staticInstance.init().update(data).final();
51 | }
52 |
53 | static digest64(data) {
54 | if (data.length === 64) {
55 | staticInstance.uint8InputArray.set(data);
56 | staticInstance.ctx.digest64(staticInstance.wasmInputValue, staticInstance.wasmOutputValue);
57 | const output = new Uint8Array(32);
58 | output.set(staticInstance.uint8OutputArray);
59 | return output;
60 | }
61 | throw new Error("InvalidLengthForDigest64");
62 | }
63 |
64 | /**
65 | * Digest 2 objects, each has 8 properties from h0 to h7.
66 | * The performance is a little bit better than digest64 due to the use of Uint32Array
67 | * and the memory is a little bit better than digest64 due to no temporary Uint8Array.
68 | * @returns
69 | */
70 | static digestTwoHashObjects(obj1, obj2) {
71 | // TODO: expect obj1 and obj2 as HashObject
72 | staticInstance.uint32InputArray[0] = obj1.h0;
73 | staticInstance.uint32InputArray[1] = obj1.h1;
74 | staticInstance.uint32InputArray[2] = obj1.h2;
75 | staticInstance.uint32InputArray[3] = obj1.h3;
76 | staticInstance.uint32InputArray[4] = obj1.h4;
77 | staticInstance.uint32InputArray[5] = obj1.h5;
78 | staticInstance.uint32InputArray[6] = obj1.h6;
79 | staticInstance.uint32InputArray[7] = obj1.h7;
80 | staticInstance.uint32InputArray[8] = obj2.h0;
81 | staticInstance.uint32InputArray[9] = obj2.h1;
82 | staticInstance.uint32InputArray[10] = obj2.h2;
83 | staticInstance.uint32InputArray[11] = obj2.h3;
84 | staticInstance.uint32InputArray[12] = obj2.h4;
85 | staticInstance.uint32InputArray[13] = obj2.h5;
86 | staticInstance.uint32InputArray[14] = obj2.h6;
87 | staticInstance.uint32InputArray[15] = obj2.h7;
88 |
89 | staticInstance.ctx.digest64(staticInstance.wasmInputValue, staticInstance.wasmOutputValue);
90 |
91 | // extracting numbers from Uint32Array causes more memory
92 | return byteArrayToHashObject(staticInstance.uint8OutputArray);
93 | }
94 | }
95 |
96 | const staticInstance = new SHA256();
97 |
98 | /**
99 | * Pass 8 numbers in an object and set that to inputArray.
100 | * This function contains multiple same procedures but we intentionally
101 | * do it step by step to improve performance a bit.
102 | **/
103 | export function hashObjectToByteArray(obj, byteArr, offset) {
104 | let tmp = obj.h0;
105 | byteArr[0 + offset] = tmp & 0xff;
106 | tmp = tmp >> 8;
107 | byteArr[1 + offset] = tmp & 0xff;
108 | tmp = tmp >> 8;
109 | byteArr[2 + offset] = tmp & 0xff;
110 | tmp = tmp >> 8;
111 | byteArr[3 + offset] = tmp & 0xff;
112 |
113 | tmp = obj.h1;
114 | byteArr[4 + offset] = tmp & 0xff;
115 | tmp = tmp >> 8;
116 | byteArr[5 + offset] = tmp & 0xff;
117 | tmp = tmp >> 8;
118 | byteArr[6 + offset] = tmp & 0xff;
119 | tmp = tmp >> 8;
120 | byteArr[7 + offset] = tmp & 0xff;
121 |
122 | tmp = obj.h2;
123 | byteArr[8 + offset] = tmp & 0xff;
124 | tmp = tmp >> 8;
125 | byteArr[9 + offset] = tmp & 0xff;
126 | tmp = tmp >> 8;
127 | byteArr[10 + offset] = tmp & 0xff;
128 | tmp = tmp >> 8;
129 | byteArr[11 + offset] = tmp & 0xff;
130 |
131 | tmp = obj.h3;
132 | byteArr[12 + offset] = tmp & 0xff;
133 | tmp = tmp >> 8;
134 | byteArr[13 + offset] = tmp & 0xff;
135 | tmp = tmp >> 8;
136 | byteArr[14 + offset] = tmp & 0xff;
137 | tmp = tmp >> 8;
138 | byteArr[15 + offset] = tmp & 0xff;
139 |
140 | tmp = obj.h4;
141 | byteArr[16 + offset] = tmp & 0xff;
142 | tmp = tmp >> 8;
143 | byteArr[17 + offset] = tmp & 0xff;
144 | tmp = tmp >> 8;
145 | byteArr[18 + offset] = tmp & 0xff;
146 | tmp = tmp >> 8;
147 | byteArr[19 + offset] = tmp & 0xff;
148 |
149 | tmp = obj.h5;
150 | byteArr[20 + offset] = tmp & 0xff;
151 | tmp = tmp >> 8;
152 | byteArr[21 + offset] = tmp & 0xff;
153 | tmp = tmp >> 8;
154 | byteArr[22 + offset] = tmp & 0xff;
155 | tmp = tmp >> 8;
156 | byteArr[23 + offset] = tmp & 0xff;
157 |
158 | tmp = obj.h6;
159 | byteArr[24 + offset] = tmp & 0xff;
160 | tmp = tmp >> 8;
161 | byteArr[25 + offset] = tmp & 0xff;
162 | tmp = tmp >> 8;
163 | byteArr[26 + offset] = tmp & 0xff;
164 | tmp = tmp >> 8;
165 | byteArr[27 + offset] = tmp & 0xff;
166 |
167 | tmp = obj.h7;
168 | byteArr[28 + offset] = tmp & 0xff;
169 | tmp = tmp >> 8;
170 | byteArr[29 + offset] = tmp & 0xff;
171 | tmp = tmp >> 8;
172 | byteArr[30 + offset] = tmp & 0xff;
173 | tmp = tmp >> 8;
174 | byteArr[31 + offset] = tmp & 0xff;
175 | }
176 |
177 | /**
178 | * Parse outputArray into an object of 8 numbers.
179 | * This is the order that makes Uint32Array the same to Uint8Array
180 | * This function contains multiple same procedures but we intentionally
181 | * do it step by step to improve performance a bit.
182 | **/
183 | export function byteArrayToHashObject(byteArr) {
184 | let tmp = 0;
185 | tmp |= byteArr[3] & 0xff;
186 | tmp = tmp << 8;
187 | tmp |= byteArr[2] & 0xff;
188 | tmp = tmp << 8;
189 | tmp |= byteArr[1] & 0xff;
190 | tmp = tmp << 8;
191 | tmp |= byteArr[0] & 0xff;
192 | const h0 = tmp;
193 |
194 | tmp = 0;
195 | tmp |= byteArr[7] & 0xff;
196 | tmp = tmp << 8;
197 | tmp |= byteArr[6] & 0xff;
198 | tmp = tmp << 8;
199 | tmp |= byteArr[5] & 0xff;
200 | tmp = tmp << 8;
201 | tmp |= byteArr[4] & 0xff;
202 | const h1 = tmp;
203 |
204 | tmp = 0;
205 | tmp |= byteArr[11] & 0xff;
206 | tmp = tmp << 8;
207 | tmp |= byteArr[10] & 0xff;
208 | tmp = tmp << 8;
209 | tmp |= byteArr[9] & 0xff;
210 | tmp = tmp << 8;
211 | tmp |= byteArr[8] & 0xff;
212 | const h2 = tmp;
213 |
214 | tmp = 0;
215 | tmp |= byteArr[15] & 0xff;
216 | tmp = tmp << 8;
217 | tmp |= byteArr[14] & 0xff;
218 | tmp = tmp << 8;
219 | tmp |= byteArr[13] & 0xff;
220 | tmp = tmp << 8;
221 | tmp |= byteArr[12] & 0xff;
222 | const h3 = tmp;
223 |
224 | tmp = 0;
225 | tmp |= byteArr[19] & 0xff;
226 | tmp = tmp << 8;
227 | tmp |= byteArr[18] & 0xff;
228 | tmp = tmp << 8;
229 | tmp |= byteArr[17] & 0xff;
230 | tmp = tmp << 8;
231 | tmp |= byteArr[16] & 0xff;
232 | const h4 = tmp;
233 |
234 | tmp = 0;
235 | tmp |= byteArr[23] & 0xff;
236 | tmp = tmp << 8;
237 | tmp |= byteArr[22] & 0xff;
238 | tmp = tmp << 8;
239 | tmp |= byteArr[21] & 0xff;
240 | tmp = tmp << 8;
241 | tmp |= byteArr[20] & 0xff;
242 | const h5 = tmp;
243 |
244 | tmp = 0;
245 | tmp |= byteArr[27] & 0xff;
246 | tmp = tmp << 8;
247 | tmp |= byteArr[26] & 0xff;
248 | tmp = tmp << 8;
249 | tmp |= byteArr[25] & 0xff;
250 | tmp = tmp << 8;
251 | tmp |= byteArr[24] & 0xff;
252 | const h6 = tmp;
253 |
254 | tmp = 0;
255 | tmp |= byteArr[31] & 0xff;
256 | tmp = tmp << 8;
257 | tmp |= byteArr[30] & 0xff;
258 | tmp = tmp << 8;
259 | tmp |= byteArr[29] & 0xff;
260 | tmp = tmp << 8;
261 | tmp |= byteArr[28] & 0xff;
262 | const h7 = tmp;
263 |
264 | return {
265 | h0,
266 | h1,
267 | h2,
268 | h3,
269 | h4,
270 | h5,
271 | h6,
272 | h7,
273 | };
274 | }
275 |
--------------------------------------------------------------------------------
/assembly/index.ts:
--------------------------------------------------------------------------------
1 |
2 | //https://github.com/dchest/fast-sha256-js/blob/master/src/sha256.ts
3 | const DIGEST_LENGTH = 32;
4 | export const INPUT_LENGTH = 512;
5 |
6 | // constants used in the SHA256 compression function
7 | const K: u32[] = [
8 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
9 | 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
10 | 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
11 | 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
12 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
13 | 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
14 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
15 | 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
16 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
17 | 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
18 | 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
19 | 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
20 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
21 | ];
22 | const kPtr = K.dataStart;
23 |
24 | //precomputed W + K for message block representing length 64 bytes for fixed input of 64 bytes for digest64
25 | const W64: u32[] = [
26 | 0xc28a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
27 | 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
28 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
29 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf374,
30 | 0x649b69c1, 0xf0fe4786, 0x0fe1edc6, 0x240cf254,
31 | 0x4fe9346f, 0x6cc984be, 0x61b9411e, 0x16f988fa,
32 | 0xf2c65152, 0xa88e5a6d, 0xb019fc65, 0xb9d99ec7,
33 | 0x9a1231c3, 0xe70eeaa0, 0xfdb1232b, 0xc7353eb0,
34 | 0x3069bad5, 0xcb976d5f, 0x5a0f118f, 0xdc1eeefd,
35 | 0x0a35b689, 0xde0b7a04, 0x58f4ca9d, 0xe15d5b16,
36 | 0x007f3e86, 0x37088980, 0xa507ea32, 0x6fab9537,
37 | 0x17406110, 0x0d8cd6f1, 0xcdaa3b6d, 0xc0bbbe37,
38 | 0x83613bda, 0xdb48a363, 0x0b02e931, 0x6fd15ca7,
39 | 0x521afaca, 0x31338431, 0x6ed41a95, 0x6d437890,
40 | 0xc39c91f2, 0x9eccabbd, 0xb5c9a0e6, 0x532fb63c,
41 | 0xd2c741c6, 0x07237ea3, 0xa4954b68, 0x4c191d76,
42 | ];
43 | const w64Ptr = W64.dataStart;
44 |
45 | // intermediate hash values stored in H0-H7
46 | var H0: u32, H1: u32, H2: u32, H3: u32, H4: u32, H5: u32, H6: u32, H7: u32;
47 |
48 | // hash registers
49 |
50 | var a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: u32, i: u32, t1: u32, t2: u32;
51 |
52 | // 16 32bit message blocks
53 | const M = new ArrayBuffer(64);
54 | const mPtr = changetype(M);
55 |
56 | // 64 32bit extended message blocks
57 | const W = new ArrayBuffer(256);
58 | const wPtr = changetype(W);
59 |
60 | // input buffer
61 | export const input = new ArrayBuffer(INPUT_LENGTH);
62 | const inputPtr = changetype(input);
63 |
64 | // output buffer
65 | export const output = new ArrayBuffer(DIGEST_LENGTH);
66 | const outputPtr = changetype(output);
67 |
68 | // number of bytes in M buffer
69 | var mLength = 0;
70 |
71 | // number of total bytes hashed
72 | var bytesHashed = 0;
73 |
74 | @inline
75 | function load32(ptr: usize, offset: usize): u32 {
76 | return load(ptr + (offset << alignof()));
77 | }
78 |
79 | @inline
80 | function load32be(ptr: usize, offset: usize): u32 {
81 | const firstOffset = offset << alignof();
82 | return (
83 | (load8(ptr, firstOffset + 0) << 24) |
84 | (load8(ptr, firstOffset + 1) << 16) |
85 | (load8(ptr, firstOffset + 2) << 8) |
86 | (load8(ptr, firstOffset + 3) << 0)
87 | );
88 | }
89 |
90 | @inline
91 | function store32(ptr: usize, offset: usize, u: u32): void {
92 | store(ptr + (offset << alignof()), u);
93 | }
94 |
95 | @inline
96 | function store8(ptr: usize, offset: usize, u: u8): void {
97 | store(ptr + offset, u);
98 | }
99 |
100 | @inline
101 | function load8(ptr: usize, offset: usize): u8 {
102 | return load(ptr + offset);
103 | }
104 |
105 | @inline
106 | function fill(ptr: usize, value: u8, length: u32): void {
107 | const finalPtr = ptr + length;
108 | while(ptr < finalPtr) {
109 | store(ptr, value);
110 | ptr++;
111 | }
112 | }
113 |
114 | @inline
115 | function CH(x: u32, y: u32, z: u32): u32 {
116 | return((x & y) ^ (~x & z));
117 | }
118 |
119 | @inline
120 | function MAJ(x: u32, y: u32, z:u32): u32 {
121 | return ((x & y) ^ (x & z) ^ (y & z));
122 | }
123 |
124 | @inline
125 | function EP0(x: u32): u32 {
126 | return rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
127 | }
128 |
129 | @inline
130 | function EP1(x: u32): u32 {
131 | return rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
132 | }
133 |
134 | @inline
135 | function SIG0(x: u32): u32 {
136 | return rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3);
137 | }
138 |
139 | @inline
140 | function SIG1(x: u32): u32 {
141 | return rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10);
142 | }
143 |
144 | /**
145 | * Expand message blocks (16 32bit blocks), into extended message blocks (64 32bit blocks),
146 | * Apply SHA256 compression function on extended message blocks
147 | * Update intermediate hash values
148 | * @param wPtr pointer to expanded message block memory
149 | * @param mPtr pointer to message block memory, pass 0 if wPtr is precomputed for e.g. in digest64
150 | */
151 | function hashBlocks(wPtr: usize, mPtr: usize): void {
152 | a = H0;
153 | b = H1;
154 | c = H2;
155 | d = H3;
156 | e = H4;
157 | f = H5;
158 | g = H6;
159 | h = H7;
160 |
161 | // Load message blocks into first 16 expanded message blocks
162 | for (i = 0; i < 16; i++) {
163 | store32(wPtr, i,
164 | load32be(mPtr, i)
165 | );
166 | }
167 | // Expand message blocks 17-64
168 | for (i = 16; i < 64; i++) {
169 | store32(wPtr, i,
170 | SIG1(load32(wPtr, i - 2)) +
171 | load32(wPtr, i - 7) +
172 | SIG0(load32(wPtr, i - 15)) +
173 | load32(wPtr, i - 16)
174 | );
175 | }
176 |
177 | // Apply SHA256 compression function on expanded message blocks
178 | for (i = 0; i < 64; i++) {
179 | t1 = h + EP1(e) + CH(e, f, g) + load32(kPtr, i) + load32(wPtr, i);
180 | t2 = EP0(a) + MAJ(a, b, c);
181 | h = g;
182 | g = f;
183 | f = e;
184 | e = d + t1;
185 | d = c;
186 | c = b;
187 | b = a;
188 | a = t1 + t2;
189 | }
190 |
191 | H0 += a;
192 | H1 += b;
193 | H2 += c;
194 | H3 += d;
195 | H4 += e;
196 | H5 += f;
197 | H6 += g;
198 | H7 += h;
199 | }
200 |
201 | function hashPreCompW(wPtr: usize): void {
202 | a = H0;
203 | b = H1;
204 | c = H2;
205 | d = H3;
206 | e = H4;
207 | f = H5;
208 | g = H6;
209 | h = H7;
210 |
211 | // Apply SHA256 compression function on expanded message blocks
212 | for (i = 0; i < 64; i++) {
213 | t1 = h + EP1(e) + CH(e, f, g) + load32(wPtr, i);
214 | t2 = EP0(a) + MAJ(a, b, c);
215 | h = g;
216 | g = f;
217 | f = e;
218 | e = d + t1;
219 | d = c;
220 | c = b;
221 | b = a;
222 | a = t1 + t2;
223 | }
224 |
225 | H0 += a;
226 | H1 += b;
227 | H2 += c;
228 | H3 += d;
229 | H4 += e;
230 | H5 += f;
231 | H6 += g;
232 | H7 += h;
233 | }
234 |
235 | export function init(): void {
236 | H0 = 0x6a09e667;
237 | H1 = 0xbb67ae85;
238 | H2 = 0x3c6ef372;
239 | H3 = 0xa54ff53a;
240 | H4 = 0x510e527f;
241 | H5 = 0x9b05688c;
242 | H6 = 0x1f83d9ab;
243 | H7 = 0x5be0cd19;
244 |
245 | mLength = 0;
246 | bytesHashed = 0;
247 | }
248 |
249 | export function update(dataPtr: usize, dataLength: i32): void {
250 | let dataPos = 0;
251 | bytesHashed += dataLength;
252 | // If message blocks buffer has data, fill to 64
253 | if (mLength) {
254 | if (64 - mLength <= dataLength) {
255 | // we can fully fill the buffer with data left over
256 | memory.copy(mPtr + mLength, dataPtr, 64 - mLength);
257 | mLength += 64 - mLength;
258 | dataPos += 64 - mLength;
259 | dataLength -= 64 - mLength;
260 | hashBlocks(wPtr, mPtr);
261 | mLength = 0;
262 | } else {
263 | // we can't fully fill the buffer but we exhaust the whole data buffer
264 | memory.copy(mPtr + mLength, dataPtr, dataLength);
265 | mLength += dataLength;
266 | dataPos += dataLength;
267 | dataLength -= dataLength;
268 | return;
269 | }
270 | }
271 | // If input has remaining 64-byte chunks, hash those
272 | for (let i = 0; i < dataLength / 64; i++, dataPos += 64) {
273 | hashBlocks(wPtr, dataPtr + dataPos);
274 | }
275 | // If any additional bytes remain, copy into message blocks buffer
276 | if (dataLength & 63) {
277 | memory.copy(mPtr + mLength, dataPtr + dataPos, dataLength & 63);
278 | mLength += dataLength & 63;
279 | }
280 | }
281 |
282 | export function final(outPtr: usize): void {
283 | // one additional round of hashes required
284 | // because padding will not fit
285 | if ((bytesHashed & 63) < 63) {
286 | store8(mPtr, mLength, 0x80);
287 | mLength++;
288 | }
289 | if ((bytesHashed & 63) >= 56) {
290 | fill(mPtr + mLength, 0, 64 - mLength);
291 | hashBlocks(wPtr, mPtr);
292 | mLength = 0;
293 | }
294 | if ((bytesHashed & 63) >= 63) {
295 | store8(mPtr, mLength, 0x80);
296 | mLength++;
297 | }
298 | fill(mPtr + mLength, 0, 64 - mLength - 8);
299 |
300 | store(mPtr + 64 - 8, bswap(bytesHashed / 0x20000000)); // length -- high bits
301 | store(mPtr + 64 - 4, bswap(bytesHashed << 3)); // length -- low bits
302 |
303 | // hash round for padding
304 | hashBlocks(wPtr, mPtr);
305 |
306 | store32(outPtr, 0, bswap(H0));
307 | store32(outPtr, 1, bswap(H1));
308 | store32(outPtr, 2, bswap(H2));
309 | store32(outPtr, 3, bswap(H3));
310 | store32(outPtr, 4, bswap(H4));
311 | store32(outPtr, 5, bswap(H5));
312 | store32(outPtr, 6, bswap(H6));
313 | store32(outPtr, 7, bswap(H7));
314 | }
315 |
316 | export function digest(length: i32): void {
317 | init();
318 | update(inputPtr, length);
319 | final(outputPtr)
320 | }
321 |
322 | export function digest64(inPtr: usize, outPtr: usize): void {
323 | init();
324 | hashBlocks(wPtr,inPtr);
325 | hashPreCompW(w64Ptr);
326 | store32(outPtr, 0, bswap(H0));
327 | store32(outPtr, 1, bswap(H1));
328 | store32(outPtr, 2, bswap(H2));
329 | store32(outPtr, 3, bswap(H3));
330 | store32(outPtr, 4, bswap(H4));
331 | store32(outPtr, 5, bswap(H5));
332 | store32(outPtr, 6, bswap(H6));
333 | store32(outPtr, 7, bswap(H7));
334 | }
335 |
--------------------------------------------------------------------------------
/assembly/__tests__/as-pect.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This function creates a test group in the test loader.
3 | *
4 | * @param {string} description - This is the name of the test group.
5 | * @param {() => void} callback - A function that contains all of the closures for this test group.
6 | *
7 | * @example
8 | *
9 | * ```ts
10 | * describe("my test suite", (): void => {
11 | * // put your tests here
12 | * });
13 | * ```
14 | */
15 | declare function describe(description: string, callback: () => void): void;
16 |
17 | /**
18 | * This function creates a test inside the given test group. It must be placed inside a describe
19 | * block.
20 | *
21 | * @param {string} description - This is the name of the test, and should describe a behavior.
22 | * @param {() => void} callback - A function that contains a set of expectations for this test.
23 | *
24 | * @example
25 | *
26 | * ```ts
27 | * describe("the meaning of life", (): void => {
28 | * it("should be 42", (): void => {
29 | * // put your expectations here
30 | * expect(29 + 13).toBe(42);
31 | * });
32 | * });
33 | * ```
34 | */
35 | declare function it(description: string, callback: () => void): void;
36 |
37 | /**
38 | * A test that does not run, and is longhand equivalent to using todo function without a
39 | * callback. This test does not get run and is reported like a todo.
40 | *
41 | * @param {string} description - This is the name of the test, and should describe a behavior.
42 | * @param {() => void} callback - A function that contains a set of expectations for this test.
43 | */
44 | declare function xit(description: string, callback: () => void): void;
45 |
46 | /**
47 | * A test that does not run, and is longhand equivalent to using todo function without a
48 | * callback. This test does not get run and is reported like a todo.
49 | *
50 | * @param {string} description - This is the name of the test, and should describe a behavior.
51 | * @param {() => void} callback - A function that contains a set of expectations for this test.
52 | */
53 | declare function xtest(description: string, callback: () => void): void;
54 |
55 | /**
56 | * This function creates a test inside the given test group. It must be placed inside a describe
57 | * block.
58 | *
59 | * @param {string} description - This is the name of the test, and should describe a behavior.
60 | * @param {() => void} callback - A function that contains a set of expectations for this test.
61 | *
62 | * @example
63 | * ```ts
64 | * describe("the meaning of life", (): void => {
65 | * test("the value should be 42", (): void => {
66 | * // put your expectations here
67 | * expect(29 + 13).toBe(42);
68 | * });
69 | * });
70 | * ```
71 | */
72 | declare function test(description: string, callback: () => void): void;
73 |
74 | /**
75 | * This function creates a test that is expected to fail. This is useful to verify if a given
76 | * behavior is expected to throw.
77 | *
78 | * @param {string} description - This is the name of the test, and should describe a behavior.
79 | * @param {() => void} callback - A function that contains a set of expectations for this test.
80 | * @param {string?} message - A message that describes why the test should fail.
81 | *
82 | * @example
83 | *
84 | * ```ts
85 | * describe("the meaning of life", (): void => {
86 | * throws("the value should be 42", (): void => {
87 | * // put your expectations here
88 | * expect(29 + 13).not.toBe(42);
89 | * });
90 | * });
91 | * ```
92 | */
93 | declare function throws(description: string, callback: () => void, message?: string): void;
94 |
95 |
96 | /**
97 | * This function creates a test that is expected to fail. This is useful to verify if a given
98 | * behavior is expected to throw.
99 | *
100 | * @param {string} description - This is the name of the test, and should describe a behavior.
101 | * @param {() => void} callback - A function that contains a set of expectations for this test.
102 | * @param {string?} message - A message that describes why the test should fail.
103 | *
104 | * @example
105 | *
106 | * ```ts
107 | * describe("the meaning of life", (): void => {
108 | * itThrows("when the value should be 42", (): void => {
109 | * // put your expectations here
110 | * expect(29 + 13).not.toBe(42);
111 | * }, "The value is actually 42.");
112 | * });
113 | * ```
114 | */
115 | declare function itThrows(description: string, callback: () => void, message?: string): void;
116 |
117 | /**
118 | * This function creates a callback that is called before each individual test is run in this test
119 | * group.
120 | *
121 | * @param {function} callback - The function to be run before each test in the current test group.
122 | *
123 | * @example
124 | *
125 | * ```ts
126 | * // create a global
127 | * var cat: Cat = new Cat();
128 | *
129 | * describe("cats", (): void => {
130 | * beforeEach((): void => {
131 | * cat.meow(1); // meow once per test
132 | * });
133 | * });
134 | * ```
135 | */
136 | declare function beforeEach(callback: () => void): void;
137 |
138 | /**
139 | * This function creates a callback that is called before the whole test group is run, and only
140 | * once.
141 | *
142 | * @param {function} callback - The function to be run before each test in the current test group.
143 | *
144 | * @example
145 | *
146 | * ```ts
147 | * // create a global
148 | * var dog: Dog = null;
149 | * describe("dogs", (): void => {
150 | * beforeAll((): void => {
151 | * dog = new Dog(); // create a single dog once before the tests start
152 | * });
153 | * });
154 | * ```
155 | */
156 | declare function beforeAll(callback: () => void): void;
157 |
158 | /**
159 | * This function creates a callback that is called after each individual test is run in this test
160 | * group.
161 | *
162 | * @param {function} callback - The function to be run after each test in the current test group.
163 | *
164 | * @example
165 | *
166 | * ```ts
167 | * // create a global
168 | * var cat: Cat = new Cat();
169 | *
170 | * describe("cats", (): void => {
171 | * afterEach((): void => {
172 | * cat.sleep(12); // cats sleep a lot
173 | * });
174 | * });
175 | * ```
176 | */
177 | declare function afterEach(callback: () => void): void;
178 |
179 | /**
180 | * This function creates a callback that is called after the whole test group is run, and only
181 | * once.
182 | *
183 | * @param {function} callback - The function to be run after each test in the current test group.
184 | *
185 | * @example
186 | *
187 | * ```ts
188 | * // create a global
189 | * var dog: Dog = null;
190 | * describe("dogs", (): void => {
191 | * afterAll((): void => {
192 | * memory.free(changetype(dog)); // free some memory
193 | * });
194 | * });
195 | * ```
196 | */
197 | declare function afterAll(callback: () => void): void;
198 |
199 | /**
200 | * Describes a value and returns an expectation to test the value.
201 | *
202 | * @type {T} - The expectation's type.
203 | * @param {T} actual - The value being tested.
204 | *
205 | * @example
206 | *
207 | * ```ts
208 | * expect(42).not.toBe(-1, "42 should not be -1");
209 | * expect(19 + 23).toBe(42, "19 + 23 should equal 42");
210 | * ```
211 | */
212 | declare function expect(actual: T | null): Expectation;
213 |
214 | /**
215 | * Describes a void function and returns an expectation to test the function.
216 | *
217 | * @param {() => void} callback - The callback being tested.
218 | *
219 | * @example
220 | *
221 | * ```ts
222 | * expectFn((): void => unreachable()).toThrow("unreachables do not throw");
223 | * expectFn((): void => {
224 | * cat.meow();
225 | * }).not.toThrow("Uhoh, cats can't meow!");;
226 | * ```
227 | */
228 | declare function expectFn(cb: () => void): Expectation<() => void>;
229 |
230 | /**
231 | * Describes a test that needs to be written.
232 | *
233 | * @param {string} description - The description of the test that needs to be written.
234 | */
235 | declare function todo(description: string): void;
236 |
237 | /**
238 | * Logs a single value to the logger, and is stringified. It works for references, values, and
239 | * strings.
240 | *
241 | * @type {T} - The type to be logged.
242 | * @param {T | null} value - The value to be logged.
243 | *
244 | * @example
245 | *
246 | * ```ts
247 | * log("This is a logged value.");
248 | * log(42);
249 | * log(new Vec(1, 2, 3));
250 | * log(null);
251 | * ```
252 | */
253 | declare function log(value: T | null): void;
254 |
255 | /**
256 | * An expectation for a value.
257 | */
258 | // @ts-ignore
259 | declare class Expectation {
260 |
261 | /**
262 | * Create a new expectation.
263 | *
264 | * @param {T | null} actual - The actual value of the expectation.
265 | */
266 | constructor(actual: T | null);
267 |
268 | /**
269 | * This expectation performs a strict equality on value types and reference types.
270 | *
271 | * @param {T | null} expected - The value to be compared.
272 | * @param {string} message - The optional message that describes the expectation.
273 | *
274 | * @example
275 | *
276 | * ```ts
277 | * expect(42).not.toBe(-1, "42 should not be -1");
278 | * expect(19 + 23).toBe(42, "19 + 23 should equal 42");
279 | * ```
280 | */
281 | toBe(expected: T | null, message?: string): void;
282 |
283 | /**
284 | * This expectation performs a strict equality on value types and performs a memcompare on
285 | * reference types. If the reference type `T` has reference types as properties, the comparison does
286 | * not perform property traversal. It will only compare the pointer values in the memory block, and
287 | * only compare `offsetof()` bytes, regardless of the allocated block size.
288 | *
289 | * @param {T | null} expected - The value to be compared.
290 | * @param {string} message - The optional message that describes the expectation.
291 | *
292 | * @example
293 | *
294 | * ```ts
295 | * expect(new Vec3(1, 2, 3)).toStrictEqual(new Vec(1, 2, 3), "Vectors of the same shape should be equal");
296 | * ```
297 | */
298 | toStrictEqual(expected: T | null, message?: string): void;
299 |
300 | /**
301 | * This expectation performs a strict memory block equality based on the allocated block sizes.
302 | *
303 | * @param {T | null} expected - The value to be compared.
304 | * @param {string} message - The optional message that describes the expectation.
305 | *
306 | * @example
307 | *
308 | * ```ts
309 | * expect(new Vec3(1, 2, 3)).toBlockEqual(new Vec(1, 2, 3), "Vectors of the same shape should be equal");
310 | * ```
311 | */
312 | toBlockEqual(expected: T | null, message?: string): void;
313 |
314 | /**
315 | * If the value is callable, it calls the function, and fails the expectation if it throws, or hits
316 | * an unreachable().
317 | *
318 | * @param {string} message - The optional message that describes the expectation.
319 | *
320 | * @example
321 | *
322 | * ```ts
323 | * expectFn((): void => unreachable()).toThrow("unreachable() should throw.");
324 | * expectFn((): void => {
325 | * cat.sleep(100); // cats can sleep quite a lot
326 | * }).not.toThrow("cats should sleep, not throw");
327 | * ```
328 | */
329 | toThrow(message?: string): void;
330 |
331 | /**
332 | * This expecation asserts that the value is truthy, like in javascript. If the value is a string,
333 | * then strings of length 0 are not truthy.
334 | *
335 | * @param {string} message - The optional message that describes the expectation.
336 | *
337 | * @example
338 | *
339 | * ```ts
340 | * expect(true).toBeTruthy("true is truthy.");
341 | * expect(1).toBeTruthy("numeric values that are not 0 are truthy.");
342 | * expect(new Vec3(1, 2, 3)).toBeTruthy("reference types that aren't null are truthy.");
343 | * expect(false).not.toBeTruthy("false is not truthy.");
344 | * expect(0).not.toBeTruthy("0 is not truthy.");
345 | * expect(null).not.toBeTruthy("null is not truthy.");
346 | * ```
347 | */
348 | toBeTruthy(message?: string): void;
349 |
350 | /**
351 | * This expectation tests the value to see if it is null. If the value is a value type, it is
352 | * never null. If the value is a reference type, it performs a strict null comparison.
353 | *
354 | * @param {string} message - The optional message that describes the expectation.
355 | *
356 | * @example
357 | *
358 | * ```ts
359 | * expect(0).not.toBeNull("numbers are never null");
360 | * expect(null).toBeNull("null reference types are null.");
361 | * ```
362 | */
363 | toBeNull(message?: string): void;
364 |
365 | /**
366 | * This expecation assert that the value is falsy, like in javascript. If the value is a string,
367 | * then strings of length 0 are falsy.
368 | *
369 | * @param {string} message - The optional message that describes the expectation.
370 | *
371 | * @example
372 | *
373 | * ```ts
374 | * expect(false).toBeFalsy("false is falsy.");
375 | * expect(0).toBeFalsy("0 is falsy.");
376 | * expect(null).toBeFalsy("null is falsy.");
377 | * expect(true).not.toBeFalsy("true is not falsy.");
378 | * expect(1).not.toBeFalsy("numeric values that are not 0 are not falsy.");
379 | * expect(new Vec3(1, 2, 3)).not.toBeFalsy("reference types that aren't null are not falsy.");
380 | * ```
381 | */
382 | toBeFalsy(message?: string): void;
383 |
384 | /**
385 | * This expectation asserts that the value is greater than the expected value. Since operators can
386 | * be overloaded in assemblyscript, it's possible for this to work on reference types.
387 | *
388 | * @param {T | null} expected - The expected value that the actual value should be greater than.
389 | * @param {string} message - The optional message that describes this expectation.
390 | *
391 | * @example
392 | *
393 | * ```ts
394 | * expect(10).toBeGreaterThan(4);
395 | * expect(12).not.toBeGreaterThan(42);
396 | * ```
397 | */
398 | toBeGreaterThan(expected: T | null, message?: string): void;
399 |
400 | /**
401 | * This expectation asserts that the value is less than the expected value. Since operators can
402 | * be overloaded in assemblyscript, it's possible for this to work on reference types.
403 | *
404 | * @param {T | null} value - The expected value that the actual value should be less than.
405 | * @param {string} message - The optional message that describes this expectation.
406 | *
407 | * @example
408 | *
409 | * ```ts
410 | * expect(10).not.toBeLessThan(4);
411 | * expect(12).toBeLessThan(42);
412 | * ```
413 | */
414 | toBeLessThan(expected: T | null, message?: string): void;
415 |
416 | /**
417 | * This expectation asserts that the value is greater than or equal to the expected value. Since
418 | * operators can be overloaded in assemblyscript, it's possible for this to work on reference
419 | * types.
420 | *
421 | * @param {T | null} value - The expected value that the actual value should be greater than or
422 | * equal to.
423 | * @param {string} message - The optional message that describes this expectation.
424 | *
425 | * @example
426 | *
427 | * ```ts
428 | * expect(42).toBeGreaterThanOrEqual(42);
429 | * expect(10).toBeGreaterThanOrEqual(4);
430 | * expect(12).not.toBeGreaterThanOrEqual(42);
431 | * ```
432 | */
433 | toBeGreaterThanOrEqual(expected: T | null, message?: string): void;
434 |
435 | /**
436 | * This expectation asserts that the value is less than or equal to the expected value. Since
437 | * operators can be overloaded in assemblyscript, it's possible for this to work on reference
438 | * types.
439 | *
440 | * @param {T | null} value - The expected value that the actual value should be less than or equal
441 | * to.
442 | * @param {string} message - The optional message that describes this expectation.
443 | *
444 | * @example
445 | *
446 | * ```ts
447 | * expect(42).toBeLessThanOrEqual(42);
448 | * expect(10).not.toBeLessThanOrEqual(4);
449 | * expect(12).toBeLessThanOrEqual(42);
450 | * ```
451 | */
452 | toBeLessThanOrEqual(expected: T | null, message?: string): void;
453 |
454 | /**
455 | * This expectation asserts that the value is close to another value. Both numbers must be finite,
456 | * and T must extend f64 or f32.
457 | *
458 | * @param {T extends f64 | f32} value - The expected value to be close to.
459 | * @param {i32} decimalPlaces - The number of decimal places used to calculate epsilon. Default is
460 | * 2.
461 | * @param {string} message - The optional message that describes this expectation.
462 | *
463 | * @example
464 | *
465 | * ```ts
466 | * expect(0.1 + 0.2).toBeCloseTo(0.3);
467 | * ```
468 | */
469 | toBeCloseTo(expected: T, decimalPlaces?: number, message?: string): void;
470 |
471 | /**
472 | * This function asserts the float type value is NaN.
473 | *
474 | * @param {string} message - The optional message the describes this expectation.
475 | *
476 | * @example
477 | *
478 | * ```ts
479 | * expect(NaN).toBeNaN();
480 | * expect(42).not.toBeNaN();
481 | * ```
482 | */
483 | toBeNaN(message?: string): void;
484 |
485 | /**
486 | * This function asserts a float is finite.
487 | *
488 | * @param {string} message - The optional message the describes this expectation.
489 | * @example
490 | *
491 | * ```ts
492 | * expect(42).toBeFinite();
493 | * expect(Infinity).not.toBeFinite();
494 | * ```
495 | */
496 | toBeFinite(message?: string): void;
497 |
498 | /**
499 | * This method asserts the item has the expected length.
500 | *
501 | * @param {i32} expected - The expected length.
502 | * @param {string} message - The optional message the describes this expectation.
503 | *
504 | * ```ts
505 | * expect([1, 2, 3]).toHaveLength(3);
506 | * ```
507 | */
508 | toHaveLength(expected: i32, message?: string): void;
509 |
510 | /**
511 | * This method asserts that a given T that extends `Array` has a value/reference included.
512 | *
513 | * @param {valueof} expected - The expected item to be included in the Array.
514 | * @param {string} message - The optional message the describes this expectation.
515 | *
516 | * @example
517 | *
518 | * ```ts
519 | * expect([1, 2, 3]).toInclude(3);
520 | * ```
521 | */
522 | // @ts-ignore: expected value should be known at compile time
523 | toInclude(expected: valueof, message?: string): void;
524 |
525 | /**
526 | * This method asserts that a given T that extends `Array` has a value/reference included.
527 | *
528 | * @param {valueof} expected - The expected item to be included in the Array.
529 | * @param {string} message - The optional message the describes this expectation.
530 | *
531 | * @example
532 | *
533 | * ```ts
534 | * expect([1, 2, 3]).toContain(3);
535 | * ```
536 | */
537 | // @ts-ignore: expected value should be known at compile time
538 | toContain(expected: valueof, message?: string): void;
539 |
540 | /**
541 | * This method asserts that a given T that extends `Array` has a value/reference included and
542 | * compared via memory.compare().
543 | *
544 | * @param {i32} expected - The expected item to be included in the Array.
545 | * @param {string} message - The optional message the describes this expectation.
546 | *
547 | * @example
548 | * ```ts
549 | * expect([new Vec3(1, 2, 3)]).toInclude(new Vec3(1, 2, 3));
550 | * ```
551 | */
552 | // @ts-ignore: expected value should be known at compile time
553 | toIncludeEqual(expected: valueof, message?: string): void;
554 |
555 | /**
556 | * This method asserts that a given T that extends `Array` has a value/reference included and
557 | * compared via memory.compare().
558 | *
559 | * @param {i32} expected - The expected item to be included in the Array.
560 | * @param {string} message - The optional message the describes this expectation.
561 | *
562 | * @example
563 | * ```ts
564 | * expect([new Vec3(1, 2, 3)]).toInclude(new Vec3(1, 2, 3));
565 | * ```
566 | */
567 | // @ts-ignore: expected value should be known at compile time
568 | toContainEqual(expected: valueof, message?: string): void;
569 |
570 | /**
571 | * This computed property is chainable, and negates the existing expectation. It returns itself.
572 | *
573 | * @example
574 | * ```ts
575 | * expect(42).not.toBe(0, "42 is not 0");
576 | */
577 | not: Expectation;
578 |
579 | /**
580 | * The actual value of the expectation.
581 | */
582 | actual: T | null;
583 | }
584 |
585 | /**
586 | * This is called to stop the debugger. e.g. `node --inspect-brk asp`.
587 | */
588 | declare function debug(): void;
589 |
590 | /**
591 | * This class contains a set of methods related to performance configuration.
592 | */
593 | // @ts-ignore
594 | declare class Performance {
595 | /**
596 | * This function call enables performance statistics gathering for the following test.
597 | *
598 | * @param {bool} enabled - The bool to indicate if performance statistics should be gathered.
599 | */
600 | public static enabled(enabled: bool): void;
601 |
602 | /**
603 | * This function call sets the maximum number of samples to complete the following test.
604 | *
605 | * @param {f64} count - The maximum number of samples required.
606 | */
607 | public static maxSamples(count: f64): void;
608 |
609 | /**
610 | * This function call sets the number of decimal places to round to for the following test.
611 | *
612 | * @param {i32} deicmalPlaces - The number of decimal places to round to
613 | */
614 | public static roundDecimalPlaces(count: i32): void;
615 |
616 | /**
617 | * This function call will set the maximum amount of time that should pass before it can stop
618 | * gathering samples for the following test.
619 | *
620 | * @param {f64} time - The ammount of time in milliseconds.
621 | */
622 | public static maxTestRunTime(time: f64): void;
623 |
624 | /**
625 | * This function call enables gathering the average/mean run time of each sample for the following
626 | * test.
627 | *
628 | * @param {bool} enabled - The bool to indicate if the average/mean should be gathered.
629 | */
630 | public static reportAverage(enabled: bool): void;
631 |
632 | /**
633 | * This function call enables gathering the median run time of each sample for the following test.
634 | *
635 | * @param {bool} enabled - The bool to indicate if the median should be gathered.
636 | */
637 | public static reportMedian(value: bool): void;
638 |
639 | /**
640 | * This function call enables gathering the standard deviation of the run times of the samples
641 | * collected for the following test.
642 | *
643 | * @param {bool} enabled - The bool to indicate if the standard deviation should be gathered.
644 | */
645 | public static reportStdDev(value: bool): void;
646 |
647 | /**
648 | * This function call enables gathering the largest run time of the samples collected for the
649 | * following test.
650 | *
651 | * @param {bool} enabled - The bool to indicate if the max should be gathered.
652 | */
653 | public static reportMax(value: bool): void;
654 |
655 | /**
656 | * This function call enables gathering the smallest run time of the samples collected for the
657 | * following test.
658 | *
659 | * @param {bool} enabled - The bool to indicate if the min should be gathered.
660 | */
661 | public static reportMin(value: bool): void;
662 |
663 | /**
664 | * This function call enables gathering the varaince of the samples collected for the following test.
665 | *
666 | * @param {bool} enabled - The bool to indicate if the variance should be calculated.
667 | */
668 | public static reportVariance(value: bool): void;
669 | }
670 | /**
671 | * Assemblyscript uses reference counting to perform garbage collection. This means when you
672 | * allocate a managed object and return it, it's reference count is one. If another variable aliases
673 | * it then the reference count goes up. This static class contains a few convenience methods for
674 | * developers to test the current number of blocks allocated on the heap to make sure you aren't leaking
675 | * references, e.i. keeping references to objects you expect to be collected.
676 | */
677 | declare class RTrace {
678 | /**
679 | * This bool indicates if `RTrace` should call into JavaScript to obtain reference counts.
680 | */
681 | public static enabled: bool;
682 |
683 | /**
684 | * This method returns the current number of active references on the heap.
685 | */
686 | public static count(): i32;
687 |
688 | /**
689 | * This method starts a new refcounting group, and causes the next call to `RTrace.end(label)` to
690 | * return a delta in reference counts on the heap.
691 | *
692 | * @param {i32} label - The numeric label for this refcounting group.
693 | */
694 | public static start(label: i32): void;
695 |
696 | /**
697 | * This method returns a delta of how many new (positive) or collected (negative) are on the heap.
698 | *
699 | * @param {i32} label - The numeric label for this refcounting group.
700 | */
701 | public static end(label: i32): i32;
702 |
703 | /**
704 | * This method returns the number of increments that have occurred over the course of a test
705 | * file.
706 | */
707 | public static increments(): i32;
708 |
709 | /**
710 | * This method returns the number of decrements that have occurred over the course of a test
711 | * file.
712 | */
713 | public static decrements(): i32;
714 |
715 | /**
716 | * This method returns the number of increments that have occurred over the course of a test
717 | * group.
718 | */
719 | public static groupIncrements(): i32;
720 |
721 | /**
722 | * This method returns the number of decrements that have occurred over the course of a test
723 | * group.
724 | */
725 | public static groupDecrements(): i32;
726 |
727 | /**
728 | * This method returns the number of increments that have occurred over the course of a test
729 | * group.
730 | */
731 | public static testIncrements(): i32;
732 |
733 | /**
734 | * This method returns the number of decrements that have occurred over the course of a test
735 | * group.
736 | */
737 | public static testDecrements(): i32;
738 |
739 | /**
740 | * This method returns the number of allocations that have occurred over the course of a test
741 | * file.
742 | */
743 | public static allocations(): i32;
744 |
745 | /**
746 | * This method returns the number of frees that have occurred over the course of a test
747 | * file.
748 | */
749 | public static frees(): i32;
750 |
751 | /**
752 | * This method returns the number of allocations that have occurred over the course of a test
753 | * group.
754 | */
755 | public static groupAllocations(): i32;
756 |
757 | /**
758 | * This method returns the number of frees that have occurred over the course of a test
759 | * group.
760 | */
761 | public static groupFrees(): i32;
762 |
763 | /**
764 | * This method returns the number of allocations that have occurred over the course of a test
765 | * group.
766 | */
767 | public static testAllocations(): i32;
768 |
769 | /**
770 | * This method returns the number of frees that have occurred over the course of a test
771 | * group.
772 | */
773 | public static testFrees(): i32;
774 |
775 | /**
776 | * This method triggers a garbage collection.
777 | */
778 | public static collect(): void;
779 |
780 | /**
781 | * Get the class id of the pointer.
782 | *
783 | * @param {usize} pointer - The pointer.
784 | * @returns {u32} - The class id of the allocated block.
785 | */
786 | public static classIdOf(pointer: usize): u32;
787 |
788 | /**
789 | * Get the size of a block or buffer.
790 | *
791 | * @param {T} reference - The reference.
792 | * @returns {u32} - The size of the allocated block.
793 | */
794 | public static sizeOf(reference: T): u32;
795 |
796 | /**
797 | * Get the currently allocated blocks.
798 | */
799 | public static activeBlocks(): usize[];
800 |
801 | /**
802 | * Get the current groups allocated blocks.
803 | */
804 | public static activeGroupBlocks(): usize[];
805 |
806 | /**
807 | * Get the current tests allocated blocks.
808 | */
809 | public static activeTestBlocks(): usize[];
810 | }
811 |
812 |
813 | /**
814 | * This class is static and contains private global values that contain metadata about the Actual
815 | * value.
816 | *
817 | * @example
818 | * ```ts
819 | * Actual.report("This is an expected string.");
820 | * Actual.report([1, 2, 3]);
821 | * Actual.report(42);
822 | * ```
823 | */
824 | declare class Actual {
825 | /**
826 | * This function performs reporting to javascript what the actual value of this expectation is.
827 | *
828 | * @param {T} actual - The actual value to be reported.
829 | */
830 | public static report(value: T): void;
831 |
832 | /**
833 | * Clear the actual value and release any private memory stored as a global.
834 | */
835 | public static clear(): void;
836 | }
837 |
838 |
839 | /**
840 | * This class is static and contains private global values that contain metadata about the Expected
841 | * value.
842 | *
843 | * @example
844 | * ```ts
845 | * Expected.report("This is an expected string.");
846 | * Expected.report([1, 2, 3]);
847 | * Expected.report(42, i32(true)); // not 42
848 | * ```
849 | */
850 | declare class Expected {
851 | /**
852 | * This function performs reporting to javascript what the expected value of this expectation is.
853 | * It notifies javascript if the expectation is negated.
854 | *
855 | * @param {T} value - The actual value to be reported.
856 | * @param {i32} negated - An indicator if the expectation is negated. Pass `1` to negate the
857 | * expectation. (default: 0)
858 | */
859 | public static report(value: T, negated?: i32): void;
860 |
861 | /**
862 | * Clear the expected value and release any private memory stored as a global.
863 | */
864 | public static clear(): void;
865 | }
866 |
--------------------------------------------------------------------------------