Skip to content

Commit 9d6118f

Browse files
committed
Merge branch 'release/1.0.0'
2 parents 7551f51 + 0ad9b67 commit 9d6118f

10 files changed

+400
-1
lines changed

Diff for: .gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
dev/
3+
npm-debug.log
4+
yarn-error.log
5+
yarn.lock

Diff for: README.md

+84-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,84 @@
1-
# bit-array
1+
# bit-array
2+
3+
Save memory space by using a BitArray! Behind the scene, the library stores bits within elements of a `Uint8Array` resulting in memory space savings.
4+
5+
```typescript
6+
// ~256 bits in memory + overhead
7+
const jsArray = [1, 0, 0, 1, 1, 0, 0, 0];
8+
9+
// ~8 bits in memory + overhead
10+
const bitArray = new BitArray([1, 0, 0, 1, 1, 0, 0, 0]);
11+
```
12+
13+
14+
- [Size](#size)
15+
- [Installation](#installation)
16+
- [Documentation](#documentation)
17+
- [Release notes](#release-notes)
18+
19+
## Size
20+
2.9 KB
21+
22+
## Installation
23+
**via npm**
24+
25+
`npm install bit-array`
26+
27+
**via yarn**
28+
29+
`yarn add bit-array`
30+
31+
**dev**
32+
33+
`npm install`
34+
35+
`yarn install`
36+
37+
`yarn start`
38+
39+
## Documentation
40+
41+
#### Usage
42+
```typescript
43+
import BitArray from "../lib/bit-array";
44+
45+
const arr = new BitArray([0, 1, 0]);
46+
47+
arr.pushAll([1, 1, 1]);
48+
// 0, 1, 0, 1, 1, 1
49+
50+
arr.push(0);
51+
// 0, 1, 0, 1, 1, 1, 0
52+
53+
arr.atIndex(0);
54+
// 0
55+
56+
arr.atIndexRange(0, 3);
57+
// 0, 1, 0
58+
59+
console.log(arr.size)
60+
// 7
61+
```
62+
63+
#### Methods
64+
Method | Description
65+
--- | ---
66+
**`pushAll(values: Array<bit>)`** | Pushes a an array of bits onto the array
67+
**`push(value: bit)`** | Pushes a single bit onto the array
68+
**`atIndex(index: number)`** | Gets the bit at a given index.
69+
**`atIndexRange(index: number, count: number)`** | Gets count bits at a given index
70+
71+
#### Properties
72+
Property | Description
73+
--- | ---
74+
**`size`** | Gets the size of the array
75+
76+
## Release notes
77+
### 1.0.0
78+
Initial release
79+
- `pushAll(values: Array<bit>)`
80+
- `push(value: bit)`
81+
- `atIndex(index: number)`
82+
- `atIndexRange(index: number, count: number)`
83+
- `size`
84+

Diff for: package.json

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "bit-array",
3+
"description": "Bit array implementation in javascript",
4+
"version": "0.1.0",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/Bubblesphere/bit-array.git"
8+
},
9+
"keywords": [
10+
"bit",
11+
"array",
12+
"typescript",
13+
"javascript"
14+
],
15+
"author": "Déric Dallaire",
16+
"license": "MIT",
17+
"bugs": {
18+
"url": "https://github.com/Bubblesphere/bit-array/issues"
19+
},
20+
"homepage": "https://github.com/Bubblesphere/bit-array",
21+
"devDependencies": {
22+
"babel-preset-es2015": "^6.24.1",
23+
"clean-webpack-plugin": "^0.1.19",
24+
"copy-webpack-plugin": "^4.5.1",
25+
"ts-loader": "^4.1.0",
26+
"typescript": "^2.7.2",
27+
"webpack": "^4.1.1",
28+
"webpack-bundle-analyzer": "^2.11.1",
29+
"webpack-cli": "^2.0.12",
30+
"webpack-dev-server": "^3.1.1",
31+
"webpack-merge": "^4.1.2"
32+
},
33+
"scripts": {
34+
"start": "webpack-dev-server --open-page dev/index.html --config webpack.dev.js --mode development ",
35+
"build": "webpack --config webpack.prod.js --mode production"
36+
}
37+
}

Diff for: src/demo/index.html

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<html>
2+
<head>
3+
<title>bit-array</title>
4+
</head>
5+
<body>
6+
7+
<h1>Hello world!</h1>
8+
<p>Look up the console to show the results</p>
9+
<xmp>
10+
import BitArray from "../lib/bit-array";
11+
12+
const arr = new BitArray([0, 1, 0]);
13+
14+
arr.pushAll([1, 1, 1]);
15+
// 0, 1, 0, 1, 1, 1
16+
17+
arr.push(0);
18+
// 0, 1, 0, 1, 1, 1, 0
19+
20+
arr.atIndex(0);
21+
// 0
22+
23+
arr.atIndexRange(0, 3);
24+
// 0, 1, 0
25+
26+
console.log(arr.size)
27+
// 7
28+
</xmp>
29+
<script src="common-bundle.js"></script>
30+
<script src="index-bundle.js"></script>
31+
</body>
32+
</html>

Diff for: src/demo/index.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import BitArray from "../lib/bit-array";
2+
3+
const arr = new BitArray([0, 1, 0]);
4+
5+
arr.pushAll([1, 1, 1]);
6+
console.log(arr.atIndexRange(0, arr.size));
7+
// 0, 1, 0, 1, 1, 1
8+
9+
arr.push(0);
10+
console.log(arr.atIndexRange(0, arr.size));
11+
// 0, 1, 0, 1, 1, 1, 0
12+
13+
arr.atIndex(0);
14+
console.log(arr.atIndex(0));
15+
// 0
16+
17+
arr.atIndexRange(0, 3);
18+
console.log(arr.atIndexRange(0, 3));
19+
// 0, 1, 0
20+
21+
console.log(arr.size)
22+
// 7

Diff for: src/lib/bit-array.ts

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
2+
export type bit = 0 | 1;
3+
type byte = number;
4+
5+
export default class BitArray {
6+
private _array: Uint8Array;
7+
private _size: number;
8+
private _pushedSize: number;
9+
private _pointer: number;
10+
private _bitPerIndex: number = 8;
11+
12+
/**
13+
* Creates a bitArray
14+
* @param values The bits to push onto the array
15+
*/
16+
constructor(values: Array<bit>) {
17+
this._pushedSize = 0;
18+
this._size = values.length;
19+
this._array = new Uint8Array(Math.ceil(this._size/this._bitPerIndex));
20+
this._pointer = 0;
21+
this.pushAll(values);
22+
}
23+
24+
/**
25+
* Gets the size of the array
26+
*/
27+
public get size() {
28+
return this._pushedSize;
29+
}
30+
31+
/**
32+
* Pushes a single bit onto the array
33+
* @param value The bit to push onto the array
34+
*/
35+
public push(value: bit) {
36+
if (this._pointer == this._size) {
37+
this._increaseArraySize();
38+
}
39+
// create a mask for current index.
40+
const mask = this._createMask(this._pointer);
41+
// compare the array with the mask. A value different than 0 means the target bit is set
42+
if (this._matchesMask(mask, this._array[this._arrayIndex(this._pointer)])) {
43+
// bit is set in array. Should we toggle it?
44+
if (value === 0) {
45+
this._array[this._arrayIndex(this._pointer)] ^= mask;
46+
}
47+
} else {
48+
// bit is unset in array. Sould we toggle it?
49+
if (value === 1) {
50+
this._array[this._arrayIndex(this._pointer)] ^= mask;
51+
}
52+
}
53+
// increment pointer for next push
54+
this._pushedSize++;
55+
this._pointer++;
56+
}
57+
58+
/**
59+
* Pushes a an array of bits onto the array
60+
* @param values The bits to push onto the array
61+
*/
62+
public pushAll(values: Array<bit>) {
63+
// TODO: Improve performance
64+
values.forEach((value) => {
65+
this.push(value);
66+
});
67+
}
68+
69+
/**
70+
* Gets the bit at a given index
71+
* @param index The index of the bit to return
72+
*/
73+
public atIndex(index: number): bit {
74+
if (index > this._pushedSize) {
75+
throw `Index (${index}) exceeds the size of the bit array (${this._pushedSize})`;
76+
}
77+
78+
const mask = this._createMask(index);
79+
return this._matchesMask(mask, this._array[this._arrayIndex(index)]) ? 1 : 0;
80+
}
81+
82+
/**
83+
* Gets count bits at a given index
84+
* @param index The index of the first bit to return
85+
* @param count The amount of bits to fetch starting at the index
86+
*/
87+
public atIndexRange(index: number, count: number): bit[] {
88+
if (index + count - 1 > this._pushedSize) {
89+
throw `Index (${index}) exceeds the size of the bit array (${this._pushedSize})`;
90+
}
91+
92+
const values: bit[] = [];
93+
for (let i = 0; i < count; i++) {
94+
const mask = this._createMask(index + i);
95+
values.push(this._matchesMask(mask, this._array[this._arrayIndex(index + i)]) ? 1 : 0);
96+
}
97+
return values;
98+
}
99+
100+
// Checks whether a value has a bit of 1 at the mask position
101+
private _matchesMask(mask: byte, value: byte) {
102+
// returns true if bit is set
103+
return (mask & value) != 0;
104+
}
105+
106+
// Creates a mask for a given index
107+
private _createMask(index: number): byte {
108+
// supposing the index is 2, the created mask is
109+
// 00100000
110+
return 1 << (this._bitPerIndex - 1) - this._arrayIndexOffset(index);
111+
}
112+
113+
// Gets the Uint8Array index
114+
private _arrayIndex(index: number) {
115+
return Math.floor(index / this._bitPerIndex);
116+
}
117+
118+
// Gets the offset within the Uint8Array
119+
private _arrayIndexOffset(index: number) {
120+
return index % this._bitPerIndex;
121+
}
122+
123+
private _increaseArraySize() {
124+
this._size = this._size * 2;
125+
const tempArr = this._array.slice();
126+
this._array = new Uint8Array(Math.ceil(this._size/this._bitPerIndex));
127+
this._array = tempArr.slice();
128+
}
129+
};

Diff for: tsconfig.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"module": "esnext",
4+
"noImplicitAny": true,
5+
"removeComments": true,
6+
"preserveConstEnums": true,
7+
"sourceMap": true,
8+
"target": "es5"
9+
},
10+
"include": [
11+
"src/**/*"
12+
],
13+
"exclude": [
14+
"node_modules",
15+
"**/*.test.ts"
16+
]
17+
}

Diff for: webpack.common.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
var path = require("path");
2+
const webpack = require('webpack');
3+
const CleanWebpackPlugin = require('clean-webpack-plugin');
4+
const CopyWebpackPlugin = require('copy-webpack-plugin');
5+
6+
module.exports = {
7+
target: 'web',
8+
resolve: {
9+
extensions: ['.ts', '.tsx', '.js']
10+
},
11+
entry: {
12+
index: "./src/demo/index.ts"
13+
},
14+
output: {
15+
filename: "[name]-bundle.js",
16+
path: path.resolve(__dirname, "dev"),
17+
publicPath: "/dev/"
18+
},
19+
// Demos use the same library, bundle the code in common into a diff file
20+
optimization: {
21+
splitChunks: {
22+
chunks: "all",
23+
minSize: 1000,
24+
name: "common"
25+
}
26+
},
27+
plugins: [
28+
new CopyWebpackPlugin([{
29+
from: 'src/demo/*.html',
30+
flatten: true
31+
}])
32+
],
33+
module: {
34+
rules: [
35+
{
36+
test: /\.tsx?$/,
37+
loader: "ts-loader",
38+
}
39+
]
40+
}
41+
};

Diff for: webpack.dev.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const merge = require('webpack-merge');
2+
const common = require('./webpack.common.js');
3+
4+
module.exports = merge(common, {
5+
devtool: 'inline-source-map',
6+
devServer: {
7+
stats: {
8+
assets: false,
9+
hash: false,
10+
chunks: false,
11+
errors: true,
12+
errorDetails: true,
13+
},
14+
overlay: true,
15+
}
16+
});

0 commit comments

Comments
 (0)