├── .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 | ![ES Version](https://img.shields.io/badge/ES-2015-yellow) 7 | ![Node Version](https://img.shields.io/badge/node-12.x-green) 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 | --------------------------------------------------------------------------------