├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── style.yml ├── .gitignore ├── .markdownlint.jsonc ├── .prettierrc.json ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── index.html ├── index.mjs └── util.mjs ├── package-lock.json ├── package.json ├── scripts └── postbuild.sh ├── src └── bit-typedarray.ts ├── test ├── index.ts ├── suite.ts └── test.ts ├── tsconfig.cjs.json ├── tsconfig.esm.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Full documentation can be found at editorconfig.org 2 | # This requires a plugin to be installed in the editor of choice 3 | # Link to info on plugins can be found here - http://editorconfig.org/#download 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.{js,json,ts}] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 2 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 3 | 4 | version: 2 5 | updates: 6 | 7 | # Maintain dependencies for GitHub Actions 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | 13 | # Maintain dependencies for npm 14 | - package-ecosystem: "npm" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | paths-ignore: ['LICENSE', 'example/**', '**.md', '.*'] 6 | pull_request: 7 | paths-ignore: ['LICENSE', 'example/**', '**.md', '.*'] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | node-version: [20.x, 22.x, 24.x] 14 | os: [ubuntu-latest, macOS-latest] # , windows-latest 15 | 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | cache: 'npm' 25 | - name: Install dependencies 26 | run: npm install # npm ci 27 | - run: npm run build --if-present 28 | - run: npm run test:ci 29 | -------------------------------------------------------------------------------- /.github/workflows/style.yml: -------------------------------------------------------------------------------- 1 | name: conformance with Prettier 2 | 3 | on: 4 | push: 5 | paths: ['src/**.ts', '.prettierrc.json'] 6 | pull_request: 7 | paths: ['src/**.ts', '.prettierrc.json'] 8 | 9 | jobs: 10 | prettier: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: '18' 17 | cache: 'npm' 18 | - name: Install dependencies 19 | run: npm install # npm ci 20 | - name: Run prettier 21 | run: npm run style:check 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | node_modules 3 | -------------------------------------------------------------------------------- /.markdownlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | // keep VSCode default of no line-length limit 4 | // https://github.com/DavidAnson/vscode-markdownlint#markdownlintconfig 5 | "MD013": false, 6 | // those have been deprecated 7 | // https://github.com/DavidAnson/vscode-markdownlint#configure 8 | "MD002": false, 9 | "MD006": false 10 | } 11 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "quoteProps": "as-needed", 8 | "trailingComma": "es5", 9 | "bracketSpacing": true, 10 | "bracketSameLine": false, 11 | "arrowParens": "always", 12 | "proseWrap": "preserve" 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // From VSCode 1.67.1, this is the default 3 | "editor.bracketPairColorization.enabled": true, 4 | "editor.guides.bracketPairs": "active", 5 | 6 | "editor.defaultFormatter": "esbenp.prettier-vscode", 7 | "editor.formatOnSave": true 8 | } 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # @bitarray/typedarray ChangeLog 4 | 5 | ## v1.1.1 6 | 7 | _2022-08-07_ 8 | 9 | - fix: path to .d.ts types file (for node v18?) 10 | - style: run prettier 11 | - chore: add .vscode for automatic prettier run 12 | - build: bump dependency version 13 | 14 | ## v1.1.0 15 | 16 | _2022-05-18_ 17 | 18 | - feat: can now set bit values with booleans 19 | - fix: export type `bit` 20 | - chore: run github actions only when relevant 21 | - chore: explicit markdownlint rules 22 | - chore: move dependabot.yml to correct location 23 | - build: bump dependency version 24 | 25 | ## v1.0.3 26 | 27 | _2022-05-04_ 28 | 29 | - build: move creation of package.json to postbuild 30 | - chore: add github actions for testing ([54c7aa9](https://github.com/swiing/Bit-TypedArray/commit/54c7aa9244bee3b1949a8b34ecccea45f1c69008)), styling ([8c9bdfa](https://github.com/swiing/Bit-TypedArray/commit/8c9bdfaba321b4987cb17eb151f06190aaf3c70e)) and dependabot ([0410dee](https://github.com/swiing/Bit-TypedArray/commit/0410deeeed0466bc651fc9af384337a6233ee0e5)) 31 | - chore: update gitignore ([90749f1](https://github.com/swiing/Bit-TypedArray/commit/90749f12d45d79f1c23b8d587b93e5f4ffea412e)) 32 | - chore: run example from script 33 | - chore: delete .gitattributes 34 | - style: add and run prettier 35 | - docs: reference WIP 36 | - fix: correct path and base url in example 37 | 38 | ## v1.0.2 39 | 40 | _2022-04-20_ 41 | 42 | - build: make typescript happy 43 | - chore: move license to MIT 44 | - docs: cleanup readme 45 | 46 | ## v1.0.1 47 | 48 | _2022-04-03_ 49 | 50 | - docs: improve badges 51 | - docs: improve readme 52 | 53 | ## v1.0.0 54 | 55 | _2022-04-02_ 56 | 57 | ## v0.3.0 58 | 59 | _2022-03-30_ 60 | 61 | ## v0.2.0 62 | 63 | _2021-04-21_ 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 swiing (www.github.com/swiing) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 | 7 |

8 | 9 | # @bitarray/typedarray 10 | 11 | [![GitHub package.json dynamic](https://img.shields.io/github/package-json/keywords/swiing/bit-typedarray)](https://github.com/swiing/Bit-TypedArray#bitarraytypedarray) 12 | 13 | A bit array object exhibiting the interface of standard ecmascript [TypedArray](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray)'s. 14 | 15 | > :bulb: If you are looking for easily applying bitwise operations, check out [@bitarray/es6](https://github.com/swiing/BitArray), which builds on top of the present library. Here, we purposely stick to methods and properties described by the ecmascript specification (there are no bitwise operations on arrays specified by ecmascript). 16 | 17 | ## Rationale 18 | 19 | The ecmascript specification has introduced `TypedArray`s for `Int8`, `Uint8`, `Uint8Clamped`, `Int16`, `Uint16`, `Int32`, `Uint32`, `Float32`, `Float64`, `BigInt64` and `BigUint64` types. 20 | 21 | This library adds support for the `Bit` type. It provides a very memory-efficient means 22 | to store sequences of bits, while exposing the familiar, standard interface of typed arrays. 23 | 24 | ## Compatibility 25 | 26 | [![compatibility](https://img.shields.io/badge/compatibility-%3E%3D%20ES6-orange?style=flat)](https://github.com/swiing/Bit-TypedArray#compatibility) 27 | 28 | The library uses a [Proxy](https://caniuse.com/?search=Proxy) object, which is an ES6 (aka ES2015) feature. It can **NOT** be polyfilled (to the extent it is used by the library). 29 | 30 | _Note: standard `TypedArray` is also a feature of ecmascript ES6._ 31 | 32 | ## Installation 33 | 34 | [![npm version](https://badge.fury.io/js/@bitarray%2Ftypedarray.svg)](https://www.npmjs.com/package/@bitarray/typedarray) 35 | 36 | ```sh 37 | npm install @bitarray/typedarray 38 | ``` 39 | 40 | or 41 | 42 | ```sh 43 | yarn add @bitarray/typedarray 44 | ``` 45 | 46 | ## Usage 47 | 48 | Usage is same as for any standard typed array. You may check the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) for details. 49 | 50 | ### Instantiating 51 | 52 | ```js 53 | import BitArray from "@bitarray/typedarray" 54 | 55 | const length = 32; // or whatever length value 56 | const bits = new BitArray(length); 57 | 58 | // Bit arrays can be created from iterables. 59 | // The following are all equivalent 60 | 61 | new BitArray("11001010"); 62 | new BitArray([1, 1, 0, 0, 1, 0, 1, 0]); 63 | new BitArray([true, true, false, false, true, false, true, false]); 64 | 65 | BitArray.from("11001010"); 66 | BitArray.from([1, 1, 0, 0, 1, 0, 1, 0]); 67 | BitArray.from([true, true, false, false, true, false, true, false]); 68 | 69 | BitArray.of(..."11001010"); 70 | BitArray.of(1, 1, 0, 0, 1, 0, 1, 0); 71 | BitArray.of(true, true, false, false, true, false, true, false); 72 | ``` 73 | 74 | ### Reading/writing values 75 | 76 | ```js 77 | bits[1]; // 0 by default 78 | bits[1] = 1; 79 | bits[1]; // 1 80 | bits.at(1); // 1 81 | 82 | // can also take boolean values 83 | // (will be coerced to bit) 84 | bits[1] = false; 85 | bits.at(1); // 0 86 | ``` 87 | 88 | ### Iterating 89 | 90 | ```js 91 | for (let i=0; i { /* do something */ }); 95 | 96 | for (let i in bits) 97 | // do something with bits[i] 98 | 99 | for (let bit of bits) 100 | // do something with bit 101 | ``` 102 | 103 | ### Indexes & values 104 | 105 | ```js 106 | // indexes - following two are the same 107 | Object.keys(bits); // [0, 1, 2, ...] 108 | Object.getOwnPropertyNames(bits); 109 | 110 | // values 111 | Object.values(bits); // [0, 1, 0, 0, 0, ...] 112 | 113 | // entries 114 | Object.entries(bits); // [["0", 0], ["1", 1], ["2", 0], ["3", 0], ...] 115 | ``` 116 | 117 | ### Instance properties 118 | 119 | ```js 120 | // properties 121 | bits.buffer; 122 | bits.byteLength; 123 | bits.byteOffset; 124 | bits.length; 125 | 126 | ``` 127 | 128 | ### static properties 129 | 130 | ```js 131 | BitArray.BYTES_PER_ELEMENT; // 0.125 == 1/8, read-only 132 | BitArray.name; // "BitArray", read-only 133 | BitArray.prototype; // Object {...} 134 | ``` 135 | 136 | ## Implementation notes 137 | 138 | For the most part, mapping the behaviour of standard _methods_ and _properties_ to the case of bit arrays is obvious. There are a few caveats though. 139 | 140 | _Note: not all features of the specification are implemented yet **[[WIP](https://github.com/swiing/Bit-TypedArray/issues/3); PRs welcome!]**._ 141 | 142 | ### Setting values 143 | 144 | In standard typed arrays, except for the Uint8clamped type, values exceeding the limits go round. For instance, setting value 257 to a Uint8 results in the value of 1 (== 257 % 0xFF). Also, non-numerical values become 0. 145 | 146 | With BitArray, values are first coerced to number. If the result is truthy, the bit will be set to 1; 0 otherwise. 147 | 148 | ```js 149 | let arr = new BitArray(2); 150 | 151 | // one would normally set values like this 152 | arr[0] = 0; 153 | arr[1] = 1; 154 | 155 | // or using booleans: 156 | arr[0] = false; 157 | arr[1] = true; 158 | 159 | // this will also work 160 | arr[0] = -.000001; // arr[0] === 1, because Boolean(-.000001) === true 161 | arr[1] = "a"; // arr[1] === 0, because Number("a") === NaN, which is falsy 162 | ``` 163 | 164 | ### `.toString()` method 165 | 166 | The standard method returns a comma-separated list of numbers. In the case of bit sequences, interleaving commas is unnecessarily heavy, for no benefit. Instead, we list 0|1 bits in sequence, grouping them by eight for better clarity (human-reading), and separating groups by a space rather than a comma, to match common practice of text representation of bit sequences. 167 | 168 | ```js 169 | new BitArray(20).toString(); // "00000000 00000000 0000" 170 | ``` 171 | 172 | ## License 173 | 174 | [![license](https://img.shields.io/github/license/swiing/Bit-TypedArray)](https://github.com/swiing/Bit-TypedArray/blob/main/LICENSE) 175 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |

38 | Open the console (ctrl+shift+I) 39 | and reload (F5) to be able to inspect elements in the console. 40 |

41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/index.mjs: -------------------------------------------------------------------------------- 1 | import BitArray from "../dist/esm/bit-typedarray.js"; 2 | import { log as _, logHeader as _$ } from "./util.mjs"; 3 | 4 | const len = 48; // choose any integer value 5 | 6 | _$("Initializing"); 7 | 8 | const randomArray = new BitArray(len); 9 | for( let i=0, bool; i 0.5); // randomly 11 | 12 | _("array is", randomArray); 13 | _("instanceof BitArray ==", randomArray instanceof BitArray); 14 | 15 | _("BitArray.from()", BitArray.from("11001010")); 16 | _("BitArray.of()", BitArray.of(1,1,0,0,1,0,1,0)); 17 | 18 | 19 | _$("Iterating"); 20 | 21 | let res; 22 | 23 | // following three output the same 24 | res=[]; 25 | for( let i=0; i { _("."+prop+":", randomArray[prop] ); }); 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /example/util.mjs: -------------------------------------------------------------------------------- 1 | // style for console output formatting 2 | const styleHeader = new Array(200).fill(".").join("") // make sure the formatting text is not visible on the html document 3 | // (it is far to the right, hence overflows) 4 | + "; color:blue; font-weight: bold; font-size: x-large;" 5 | 6 | function logHeader( arg0, ...rest ) { 7 | console.info( "%c> "+arg0, ...rest, styleHeader ); 8 | } 9 | 10 | function log() { 11 | console.log(...arguments); 12 | } 13 | 14 | export { log, logHeader } 15 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitarray/typedarray", 3 | "version": "1.1.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@bitarray/typedarray", 9 | "version": "1.1.1", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "prettier": "3.5.3", 13 | "ts-node": "^10.7.0", 14 | "typescript": "^5.0.2" 15 | } 16 | }, 17 | "node_modules/@cspotcode/source-map-support": { 18 | "version": "0.8.1", 19 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 20 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 21 | "dev": true, 22 | "dependencies": { 23 | "@jridgewell/trace-mapping": "0.3.9" 24 | }, 25 | "engines": { 26 | "node": ">=12" 27 | } 28 | }, 29 | "node_modules/@jridgewell/resolve-uri": { 30 | "version": "3.1.0", 31 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 32 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 33 | "dev": true, 34 | "engines": { 35 | "node": ">=6.0.0" 36 | } 37 | }, 38 | "node_modules/@jridgewell/sourcemap-codec": { 39 | "version": "1.4.14", 40 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 41 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 42 | "dev": true 43 | }, 44 | "node_modules/@jridgewell/trace-mapping": { 45 | "version": "0.3.9", 46 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 47 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 48 | "dev": true, 49 | "dependencies": { 50 | "@jridgewell/resolve-uri": "^3.0.3", 51 | "@jridgewell/sourcemap-codec": "^1.4.10" 52 | } 53 | }, 54 | "node_modules/@tsconfig/node10": { 55 | "version": "1.0.9", 56 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", 57 | "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", 58 | "dev": true 59 | }, 60 | "node_modules/@tsconfig/node12": { 61 | "version": "1.0.11", 62 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 63 | "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 64 | "dev": true 65 | }, 66 | "node_modules/@tsconfig/node14": { 67 | "version": "1.0.3", 68 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 69 | "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 70 | "dev": true 71 | }, 72 | "node_modules/@tsconfig/node16": { 73 | "version": "1.0.3", 74 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", 75 | "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", 76 | "dev": true 77 | }, 78 | "node_modules/@types/node": { 79 | "version": "18.19.54", 80 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", 81 | "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", 82 | "dev": true, 83 | "license": "MIT", 84 | "peer": true, 85 | "dependencies": { 86 | "undici-types": "~5.26.4" 87 | } 88 | }, 89 | "node_modules/acorn": { 90 | "version": "8.8.2", 91 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", 92 | "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", 93 | "dev": true, 94 | "bin": { 95 | "acorn": "bin/acorn" 96 | }, 97 | "engines": { 98 | "node": ">=0.4.0" 99 | } 100 | }, 101 | "node_modules/acorn-walk": { 102 | "version": "8.2.0", 103 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 104 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 105 | "dev": true, 106 | "engines": { 107 | "node": ">=0.4.0" 108 | } 109 | }, 110 | "node_modules/arg": { 111 | "version": "4.1.3", 112 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 113 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 114 | "dev": true 115 | }, 116 | "node_modules/create-require": { 117 | "version": "1.1.1", 118 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 119 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 120 | "dev": true 121 | }, 122 | "node_modules/diff": { 123 | "version": "4.0.2", 124 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 125 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 126 | "dev": true, 127 | "engines": { 128 | "node": ">=0.3.1" 129 | } 130 | }, 131 | "node_modules/make-error": { 132 | "version": "1.3.6", 133 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 134 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 135 | "dev": true 136 | }, 137 | "node_modules/prettier": { 138 | "version": "3.5.3", 139 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", 140 | "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", 141 | "dev": true, 142 | "license": "MIT", 143 | "bin": { 144 | "prettier": "bin/prettier.cjs" 145 | }, 146 | "engines": { 147 | "node": ">=14" 148 | }, 149 | "funding": { 150 | "url": "https://github.com/prettier/prettier?sponsor=1" 151 | } 152 | }, 153 | "node_modules/ts-node": { 154 | "version": "10.9.2", 155 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 156 | "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 157 | "dev": true, 158 | "dependencies": { 159 | "@cspotcode/source-map-support": "^0.8.0", 160 | "@tsconfig/node10": "^1.0.7", 161 | "@tsconfig/node12": "^1.0.7", 162 | "@tsconfig/node14": "^1.0.0", 163 | "@tsconfig/node16": "^1.0.2", 164 | "acorn": "^8.4.1", 165 | "acorn-walk": "^8.1.1", 166 | "arg": "^4.1.0", 167 | "create-require": "^1.1.0", 168 | "diff": "^4.0.1", 169 | "make-error": "^1.1.1", 170 | "v8-compile-cache-lib": "^3.0.1", 171 | "yn": "3.1.1" 172 | }, 173 | "bin": { 174 | "ts-node": "dist/bin.js", 175 | "ts-node-cwd": "dist/bin-cwd.js", 176 | "ts-node-esm": "dist/bin-esm.js", 177 | "ts-node-script": "dist/bin-script.js", 178 | "ts-node-transpile-only": "dist/bin-transpile.js", 179 | "ts-script": "dist/bin-script-deprecated.js" 180 | }, 181 | "peerDependencies": { 182 | "@swc/core": ">=1.2.50", 183 | "@swc/wasm": ">=1.2.50", 184 | "@types/node": "*", 185 | "typescript": ">=2.7" 186 | }, 187 | "peerDependenciesMeta": { 188 | "@swc/core": { 189 | "optional": true 190 | }, 191 | "@swc/wasm": { 192 | "optional": true 193 | } 194 | } 195 | }, 196 | "node_modules/typescript": { 197 | "version": "5.8.3", 198 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 199 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 200 | "dev": true, 201 | "license": "Apache-2.0", 202 | "bin": { 203 | "tsc": "bin/tsc", 204 | "tsserver": "bin/tsserver" 205 | }, 206 | "engines": { 207 | "node": ">=14.17" 208 | } 209 | }, 210 | "node_modules/undici-types": { 211 | "version": "5.26.5", 212 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 213 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 214 | "dev": true, 215 | "license": "MIT", 216 | "peer": true 217 | }, 218 | "node_modules/v8-compile-cache-lib": { 219 | "version": "3.0.1", 220 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 221 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 222 | "dev": true 223 | }, 224 | "node_modules/yn": { 225 | "version": "3.1.1", 226 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 227 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 228 | "dev": true, 229 | "engines": { 230 | "node": ">=6" 231 | } 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bitarray/typedarray", 3 | "version": "1.1.1", 4 | "description": "A BitArray object exhibiting the interface of standard ecmascript TypedArray's", 5 | "type": "module", 6 | "typings": "./dist/esm/bit-typedarray.d.ts", 7 | "exports": { 8 | ".": { 9 | "types": "./dist/esm/bit-typedarray.d.ts", 10 | "import": "./dist/esm/bit-typedarray.js", 11 | "require": "./dist/cjs/bit-typedarray.js" 12 | } 13 | }, 14 | "directories": { 15 | "example": "example", 16 | "test": "test" 17 | }, 18 | "scripts": { 19 | "build": "npm run build:cjs && npm run build:esm && npm run build:types", 20 | "build:cjs": "tsc -p tsconfig.cjs.json", 21 | "build:esm": "tsc -p tsconfig.esm.json", 22 | "build:types": "tsc --emitDeclarationOnly -p tsconfig.json", 23 | "example": "NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" node example/index.mjs", 24 | "postbuild": "bash ./scripts/postbuild.sh", 25 | "prepare": "npm run build", 26 | "prepublishOnly": "npm run test", 27 | "style:fix": "prettier src/**/*.ts --write", 28 | "style:check": "prettier src/**/*.ts --check", 29 | "test": "node --test --loader ts-node/esm ./test/index.ts", 30 | "test:ci": "set TS_NODE_PROJECT=./tsconfig.esm.json && node --no-warnings=ExperimentalWarning --loader ts-node/esm ./test/index.ts" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/swiing/Bit-TypedArray.git" 35 | }, 36 | "keywords": [ 37 | "bitarray", 38 | "typedarray" 39 | ], 40 | "author": "swiing", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/swiing/Bit-TypedArray/issues" 44 | }, 45 | "homepage": "https://github.com/swiing/Bit-TypedArray#readme", 46 | "devDependencies": { 47 | "prettier": "3.5.3", 48 | "ts-node": "^10.7.0", 49 | "typescript": "^5.0.2" 50 | }, 51 | "files": [ 52 | "dist" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /scripts/postbuild.sh: -------------------------------------------------------------------------------- 1 | cat >dist/cjs/package.json <dist/esm/package.json <(); 19 | 20 | // // We could make it available to the outside world so it can benefit as well. 21 | // // Should we prefer to keep this private, it would also be possible. 22 | // // In such a case, it would be up to the outside world to manage 23 | // // its own instance of a viewer, e.g. if required for performance reasons. 24 | // export { _views }; 25 | 26 | // store ownKeys computation to avoid repeating each time 27 | const _keys = new WeakMap(); 28 | 29 | // for my proxy 30 | const handlers = { 31 | get: function (target: BitArray, prop: string | symbol) { 32 | if (typeof prop === 'string') { 33 | const index = Number(prop); 34 | if (index === Math.trunc(index) && index >= 0 && index < target.length) { 35 | const [intIndex, bitMask] = bitIndex2coord(index); 36 | return Number(Boolean(_views.get(target.buffer)[intIndex] & bitMask)); 37 | } 38 | } 39 | 40 | return Reflect.get(target, prop); 41 | }, 42 | 43 | // In standard typed arrays, except for the Uint8clamped type, values 44 | // exceeding the limits go round. For instance, setting value 257 to 45 | // a Uint8 results in the value 1 (== 257 % 0xFF). 46 | // Here, any value coerced to a boolean that is truthy will result 47 | // in setting value 1; 0 otherwise. 48 | set: function ( 49 | target: BitArray, 50 | prop: string | symbol, 51 | value: bit | boolean 52 | ) { 53 | if (typeof prop === 'string') { 54 | const index = Number(prop); 55 | if (index === Math.trunc(index) && index >= 0 && index < target.length) { 56 | const view = _views.get(target.buffer); 57 | const [intIndex, bitMask] = bitIndex2coord(index); 58 | view[intIndex] = Boolean(value) 59 | ? view[intIndex] | bitMask 60 | : view[intIndex] & ~bitMask; 61 | return true; 62 | } 63 | } 64 | 65 | return Reflect.set(target, prop, value); 66 | }, 67 | 68 | // We want to be able to use e.g. for( let i in ... ) 69 | // so ownKeys must return the indexes 70 | ownKeys: (target: BitArray): string[] => { 71 | let keys: string[] = _keys.get(target); 72 | 73 | if (!keys) 74 | // construct and store keys once for all 75 | _keys.set( 76 | target, 77 | (keys = Array(target.length) 78 | .fill(void 0) 79 | .map((_, i) => i.toString())) 80 | ); 81 | 82 | return keys; 83 | }, 84 | 85 | // Needed to make ownKeys work as expected. 86 | // see https://javascript.info/proxy#iteration-with-ownkeys-and-getownpropertydescriptor 87 | getOwnPropertyDescriptor: (/*target: BitArray, prop*/): object => ({ 88 | enumerable: true, 89 | configurable: true, 90 | }), 91 | }; 92 | 93 | class BitArray implements Iterable { 94 | buffer: ArrayBufferLike; 95 | byteLength: number; 96 | byteOffset: number; 97 | length: number; 98 | prototype: object; 99 | [Symbol.iterator]: () => Iterator; 100 | [index: number]: bit; 101 | 102 | static BYTES_PER_ELEMENT = 1 / 8; 103 | 104 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/from 105 | static from(source: Iterable /*, mapFn?, thisArg?*/) { 106 | return new this(source); 107 | } 108 | 109 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of 110 | static of(...items) { 111 | // let ret = new this( items.length ); 112 | // for( let i in items ) ret[i] = Number( (items[i]) ); 113 | // return ret; 114 | // simplified into 115 | return this.from(items); 116 | } 117 | 118 | /** 119 | * At this stage, only the ( length ) signature is supported. 120 | * @todo: other signatures - cf. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray 121 | * @param length 122 | * @returns a proxy instance 123 | */ 124 | // In order to mimic native TypedArrays, it must be possible to use things 125 | // like myBitArray[i], and for..in loops. The only way to achieve this is 126 | // by using a proxy. The object _itself_ must be a proxy. 127 | // It is NOT possible to extend Proxy's. However, it is possible to return 128 | // a proxy from the constructor. 129 | constructor(arg: number /*| TypedArray @todo */ | Iterable) { 130 | let byteOffset = 0; 131 | let byteLength: number; 132 | let buffer: ArrayBufferLike; 133 | let length = 0; 134 | 135 | const argIsIterable = Symbol.iterator in Object(arg); 136 | 137 | if (argIsIterable) for (let _ of arg as Iterable) length++; 138 | else { 139 | length = Math.trunc(arg as number); 140 | 141 | if (length < 0) throw new RangeError('invalid array length'); 142 | } 143 | 144 | byteLength = (((length - 1) >> 5) + 1) * 4; 145 | buffer = new ArrayBuffer(byteLength); 146 | 147 | // by default, properties are not writable, which is what I need 148 | // however, I need to have them configurable, as per https://stackoverflow.com/a/42876020 149 | Object.defineProperties(this, { 150 | byteOffset: { value: byteOffset, configurable: true }, 151 | byteLength: { value: byteLength, configurable: true }, 152 | buffer: { value: buffer, configurable: true }, 153 | length: { value: length, configurable: true }, 154 | }); 155 | 156 | // store once for all a viewer for this buffer (see above) 157 | // 158 | // Note 1: we can NOT use 'this' as the key for the weakmap, because 159 | // here 'this' refers to the target, while later on, 'this' will refer 160 | // to the proxy (because of the return statement in this constructor 161 | // - see below). Or else, we would need to derive the proxy from the 162 | // target (does not look very nice). 163 | // 164 | // Note 2: this implies, if two instances of BitArray's share the same 165 | // buffer, they will also share the same viewer. It is not a problem 166 | // unless maybe one of the bit arrays is constructed with an offset. 167 | // I have not thought too much about it yet: it seems a very corner 168 | // case when it comes to usage of bit arrays; and such construction 169 | // is not supported at this stage anyway. 170 | _views.set(this.buffer, new Uint32Array(this.buffer)); 171 | 172 | let ret = Reflect.construct(Proxy, [this, handlers]); 173 | 174 | if (argIsIterable) 175 | // beware Boolean("0") === true, so make sure to convert to number first; 176 | for (let i in arg as Iterable) ret[i] = Number(arg[i]); 177 | 178 | return ret; 179 | } 180 | 181 | /** 182 | * Returns a string representation of an array. 183 | */ 184 | // standard TypeArray's return comma-separated values. Here, we deliberately 185 | // choose not to include commas as it makes the string very heavy-weight, 186 | // for no benefit. 187 | toString() { 188 | // Object.values is an es2017 feature => avoid to remain ES6 compliant 189 | // return Object.values(this) 190 | // .map( String ) 191 | // // add one space between each byte - it could be argued 192 | // // if this is the right thing to do. I think it replaces 193 | // // nicely the comma in the native TypedArry's toString() 194 | // // since here we are looking at bits. 195 | // // Most obvious alternative would probably be to remove 196 | // // completely this line. It is hard to read for a human, 197 | // // though. 198 | // .map( (b:"0"|"1", i:number) => (i+1)%8 ? b : b+" " ) 199 | // .join("") 200 | // .trim(); 201 | var ret = ''; 202 | for (let i = 0; i < this.length; i++) 203 | ret += String(this[i]) + ((i + 1) % 8 ? '' : ' '); 204 | 205 | // trimEnd() would be suitable but is an es2019 feature, 206 | // while we want to remain ES6-compatible. 207 | return ret.trim(); 208 | } 209 | 210 | /** 211 | * Performs the specified action for each element in an array. 212 | * @param callbackfn A function that accepts up to three arguments. forEach calls the 213 | * callbackfn function one time for each element in the array. 214 | * @param thisArg An object to which the this keyword can refer in the callbackfn function. 215 | * If thisArg is omitted, undefined is used as the this value. 216 | */ 217 | forEach( 218 | callbackfn: (value: bit, index: number, array: BitArray) => void, 219 | thisArg?: any 220 | ): void { 221 | if (typeof callbackfn !== 'function') 222 | throw new TypeError(callbackfn + ' is not a function'); 223 | 224 | thisArg = thisArg || undefined; 225 | for (let i = 0; i < this.length; i++) 226 | callbackfn.call(thisArg, this[i], i, this); 227 | } 228 | 229 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/at 230 | // 231 | // Note: this is an experimental feature. 232 | // 233 | // Inspired by https://github.com/tc39/proposal-relative-indexing-method#polyfill 234 | at(index: number): bit | undefined { 235 | index = Math.trunc(index) || 0; 236 | 237 | // If a negative number is used, the element returned 238 | // will be found by counting back from the end of the array. 239 | if (index < 0) index += this.length; 240 | 241 | // out-of-bounds access is guaranteed to return undefined 242 | if (index < 0 || index >= this.length) return void 0; 243 | 244 | // const [intIndex, bitMask] = bitIndex2coord( index ); 245 | // return Number( _views.get( this.buffer )[ intIndex ] & bitMask ); 246 | // the above can be simplified as follows 247 | // (avoids duplication of code, though less performant) 248 | return this[index]; 249 | } 250 | 251 | /** 252 | * Sets a value or an array of values. 253 | * @param array A typed or untyped array of values to set. 254 | * @param offset The index in the current array at which the values are to be written. 255 | */ 256 | // cf. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set 257 | // 258 | // Should we enforce value to be a number, or allow anything, 259 | // that would coerce to a number inside the method? 260 | // 261 | // Should we keep allowing an offset? Obviously, this is according to 262 | // the native TypedArray's, but can it have any use for BitArrays? 263 | set(array: ArrayLike | ArrayLike, offset: number = 0) { 264 | if (offset < 0 || offset >= this.length) 265 | throw new RangeError('invalid or out-of-range index'); 266 | 267 | if (offset + array.length > this.length) 268 | throw new RangeError('source array is too long'); 269 | 270 | // const view = _views.get( this.buffer ); 271 | 272 | for (let i = 0; i < array.length; i++) { 273 | // const [intIndex, bitMask] = bitIndex2coord( offset+i ); 274 | // view[ intIndex ] = array[i] ? view[ intIndex ] | bitMask 275 | // : view[ intIndex ] & ~bitMask; 276 | // the above can be simplified as follows 277 | // (avoids duplication of code, though less performant) 278 | // 279 | // @ts-ignore (the proxy handler will manage the case array[i] is a boolean) 280 | this[offset + i] = array[i]; 281 | } 282 | } 283 | 284 | values() { 285 | let currentIndex = 0; 286 | return { 287 | next: () => { 288 | return currentIndex < this.length 289 | ? { done: false, value: this[currentIndex++] } 290 | : ({ done: true } as IteratorResult); 291 | }, 292 | }; 293 | } 294 | } 295 | 296 | // utility function 297 | /** 298 | * 299 | * @param index 300 | * @returns the Uint32 index and bit mask that can be used by a Uint32 viewer 301 | * to access the index'th value of the bit array. 302 | */ 303 | function bitIndex2coord(index: number): [number, number] { 304 | return [ 305 | index >> 5, // divide by 32 306 | 1 << (index & 31), // modulo 32 307 | ]; 308 | } 309 | 310 | // as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/values 311 | // "Array.prototype.values is default implementation of Array.prototype[Symbol.iterator]" 312 | BitArray.prototype[Symbol.iterator] = BitArray.prototype.values; 313 | 314 | // according to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#description 315 | // "ECMAScript 2015 defines a TypedArray constructor that serves as the [[Prototype]] 316 | // of all TypedArray constructors. This constructor is not directly exposed: 317 | // there is no global %TypedArray% or TypedArray property. It is only directly accessible 318 | // through Object.getPrototypeOf(Int8Array) and similar. All TypedArrays constructors 319 | // inherit common properties from the %TypedArray% constructor function. Additionally, 320 | // all typed array prototypes (TypedArray.prototype) have %TypedArray%.prototype as their 321 | // [[Prototype]]." 322 | // 323 | // In practice, it is not very useful because implementation detects BitArray is not 324 | // a true (native) typed array, and trying to call methods on the TypedArray prototype 325 | // throws, e.g. TypeError: CallTypedArrayMethodIfWrapped method called on incompatible Proxy. 326 | // Object.setPrototypeOf( BitArray.prototype , Object.getPrototypeOf(Int8Array).prototype ); 327 | 328 | export default BitArray; 329 | export { bit }; 330 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import suite from './suite.js'; 2 | import test from './test.js'; 3 | 4 | test(suite); 5 | -------------------------------------------------------------------------------- /test/suite.ts: -------------------------------------------------------------------------------- 1 | import BitArray from '../src/bit-typedarray.js'; 2 | 3 | // whatever value >=4 4 | // Some test cases are not accounting for a zero length (and would appear as failures) 5 | // (but, in general, it is obviously allowed to have a zero-size bit array) 6 | const length = 48; 7 | 8 | const bits = new BitArray(length); 9 | 10 | /** suite 1 */ 11 | const instantiating = { 12 | 'new BitArray( length )': bits instanceof BitArray, 13 | 'length is set': bits.length === length, 14 | 'empty bit array has length 0': new BitArray(0).length === 0, 15 | 'new BitArray( iterable )': 16 | new BitArray('011010') instanceof BitArray && 17 | new BitArray('011010').length === 6, 18 | }; 19 | 20 | /** suite 2 */ 21 | const reading_writing = { 22 | 'default value set to 0': bits[0] === 0 && bits[length - 1] === 0, 23 | 'bits[n] = true sets bit value to 1': 24 | // @ts-ignore true type as bit 25 | ((bits[length - 1] = true), bits[length - 1] === 1), 26 | 'bits[n] = 1 sets bit value to 1': 27 | ((bits[length - 1] = 1), bits[length - 1] === 1), 28 | '.at(n)': bits.at(length - 1) === 1, 29 | // this test assumes a minimal length, otherwise we don't run and simply return "pass ok". 30 | '.set([true,false], 2)': 31 | length < 4 32 | ? true 33 | : (bits.set([true, false], 2), bits[2] === 1 && bits[3] === 0), 34 | '.set([1,1], 2)': 35 | length < 4 ? true : (bits.set([1, 1], 2), bits[2] === 1 && bits[3] === 1), 36 | }; 37 | 38 | /** suite 3 */ 39 | // we check only that the last value is as expected, 40 | // and assume this is enough evidence to consider the loop was okay 41 | const iterating = { 42 | 'for loop': (() => { 43 | let item, index; 44 | for (let i = 0; i < length; i++) item = bits[(index = i)]; 45 | return index === length - 1 && item === 1; 46 | })(), 47 | 48 | 'for..in loop': (() => { 49 | let item, index; 50 | for (let i in bits) item = bits[(index = i)]; 51 | return index === '' + (length - 1) && item === 1; 52 | })(), 53 | 54 | '.forEach()': (() => { 55 | let res, index; 56 | bits.forEach((val, i, arr) => { 57 | index = i; 58 | res = arr[i] === val; 59 | }); 60 | return index === length - 1 && res === true; 61 | })(), 62 | 63 | 'for..of loop': (() => { 64 | let item, 65 | index = 0; 66 | for (item of bits) index++; 67 | return item === 1 && index === length; 68 | })(), 69 | }; 70 | 71 | /** suite 4 */ 72 | const formatting = { 73 | '.toString()': 74 | bits.toString() === 75 | new Array(length) 76 | .fill(0) 77 | .map((c, i) => ((i + 1) % 8 ? c : c + ' ')) 78 | .join('') 79 | .trim() 80 | // bits 2 and 3 have been set to 1 in my test cases 81 | .replace(/0000/, '0011') 82 | // last bit have been set to 1 in my test cases 83 | .replace(/.$/, '1'), 84 | }; 85 | 86 | /** suite 5 */ 87 | const static_metods = { 88 | 'BitArray.from( boolean[] )': (() => { 89 | let arr = BitArray.from([true, true, false, false, true]); 90 | return ( 91 | arr instanceof BitArray && 92 | arr.length === 5 && 93 | arr[0] === 1 && 94 | arr[1] === 1 && 95 | arr[2] === 0 && 96 | arr[3] === 0 && 97 | arr[4] === 1 98 | ); 99 | })(), 100 | 101 | 'BitArray.from( number[] )': (() => { 102 | let arr = BitArray.from([1, 1, 0, 0, 1]); 103 | return ( 104 | arr instanceof BitArray && 105 | arr.length === 5 && 106 | arr[0] === 1 && 107 | arr[1] === 1 && 108 | arr[2] === 0 && 109 | arr[3] === 0 && 110 | arr[4] === 1 111 | ); 112 | })(), 113 | 114 | 'BitArray.from( string )': (() => { 115 | let arr = BitArray.from('11001'); 116 | return ( 117 | arr instanceof BitArray && 118 | arr.length === 5 && 119 | arr[0] === 1 && 120 | arr[1] === 1 && 121 | arr[2] === 0 && 122 | arr[3] === 0 && 123 | arr[4] === 1 124 | ); 125 | })(), 126 | 127 | 'BitArray.of( ...booleans )': (() => { 128 | let arr = BitArray.of(true, true, false, false, true); 129 | return ( 130 | arr instanceof BitArray && 131 | arr.length === 5 && 132 | arr[0] === 1 && 133 | arr[1] === 1 && 134 | arr[2] === 0 && 135 | arr[3] === 0 && 136 | arr[4] === 1 137 | ); 138 | })(), 139 | 140 | 'BitArray.of( ...numbers )': (() => { 141 | let arr = BitArray.of(1, 1, 0, 0, 1); 142 | return ( 143 | arr instanceof BitArray && 144 | arr.length === 5 && 145 | arr[0] === 1 && 146 | arr[1] === 1 && 147 | arr[2] === 0 && 148 | arr[3] === 0 && 149 | arr[4] === 1 150 | ); 151 | })(), 152 | }; 153 | 154 | export default { 155 | instantiating, 156 | reading_writing, 157 | iterating, 158 | formatting, 159 | static_metods, 160 | }; 161 | -------------------------------------------------------------------------------- /test/test.ts: -------------------------------------------------------------------------------- 1 | // coloring in node 2 | const 3 | NodeFailure = "\x1b[31m", // red 4 | NodeSuccess = "\x1b[32m", // green 5 | NodeInfo = "\x1b[37m"; // white 6 | 7 | // coloring in browser 8 | const 9 | BrowserFailure = "color:red;", 10 | BrowserSuccess = "color:green;", 11 | BrowserInfo = "color:darkgrey;font-size: x-large;"; 12 | 13 | const log = typeof window === "object" 14 | ? // in browser 15 | function( success: boolean|undefined, text: string, tab: string ) { 16 | success == true ? console.log( "%c" + tab + text, BrowserSuccess ) 17 | : success == false ? console.log( "%c" + tab + text, BrowserFailure ) 18 | : console.log( "%c" + tab + text, BrowserInfo ); 19 | } 20 | : // in node 21 | function( success: boolean|undefined, text: string, tab: string ) { 22 | success == true ? console.log( NodeSuccess + tab + text + NodeInfo ) 23 | : success == false ? console.log( NodeFailure + tab + text + NodeInfo ) 24 | : console.log( tab + text ); 25 | }; 26 | 27 | 28 | export default function test( suite: object, tab="" ) { 29 | 30 | let success: boolean; 31 | 32 | // for( let [desc, success] of Object.entries( suite ) ) // es2017 33 | for( let desc of Object.keys( suite ) ) { 34 | success = suite[desc]; 35 | 36 | if( typeof success === "boolean" ) 37 | log( success, desc, tab ); 38 | else { 39 | log( undefined, desc, tab ); 40 | test( success, tab+"\t" ); 41 | } 42 | 43 | } 44 | 45 | }; 46 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "dist/cjs/" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "es6", 5 | "target": "es6", 6 | "outDir": "dist/esm/", 7 | "esModuleInterop": true, 8 | "moduleResolution": "node" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "declarationDir": "./dist/esm", 6 | "declaration": true, 7 | "module": "es6", 8 | "outDir": "dist/esm/", 9 | "esModuleInterop": true 10 | }, 11 | "ts-node": { "esm": true }, 12 | "files": ["src/bit-typedarray.ts"] 13 | } 14 | --------------------------------------------------------------------------------