[
  {
    "path": "bits.js",
    "content": "const BitArray = (() => {\n    // The BitArray is a simple bit flag implementation that utilizes standard arrays\n    // (for expandability) and Uint32Array types for memory efficiency.\n    // Every value is assumed either true or false (inclusive of uninitialized values),\n    // however a warning is given if a particular block does not exist yet.\n    const BitArray = function (opts) {\n        // if the opts aren't specified, let's just make it an object so we can\n        // safely OR its properties below.\n        if (!opts) {\n            opts = {};\n        }\n\n        // an internal map of Uint32Array blocks.\n        // this has the fault of becoming a hole-d array, which could be bad.\n        // perhaps give the option to use objects rather than arrays?\n        this.map = [];\n        // the size of a block inside of the map of Uint32Array objects.\n        this.binSize = opts.binSize || 100;\n    };\n\n    BitArray.prototype = {\n        // get a flag by its ID.\n        get: function (id) {\n            let b = this.binSize;\n            // faster than `Math.floor`.\n            // this gets the zone (bin) of Uint32Arrays (primary array index).\n            let z = ~~(id / (b * 32));\n            // if a value is inside a block that has already been created, we cannot\n            // *really* tell whether a value has been set, but if the block doesn't\n            // exist yet then it definitely doesn't.\n            if (!this.map[z]) {\n                console.warn(`A value at the index '${id}' does not exist.`);\n                return false;\n            }\n\n            // the bin of Uint32Array objects that we're looking in.\n            let bin = this.map[z];\n            // the actual Uint32Array the boolean exists within.\n            // secondary array index.\n            let slot = ~~((id % (b * 32)) / 32);\n\n            // id & 31 is equivalent to id % 32\n            // 1 << n is equal to 2 ^ x\n            //\n            // for any mod n where n = 2^i, modulo is simply keeping the lower order\n            // bits 0 through i - 1\n            //     00100100 | 36\n            // AND 00011111 | 31\n            // ==> 00000100 | 4\n            return !!(bin[slot] & (1 << (id & 31)));\n        },\n\n        // set a Boolean value into an ID.\n        set: function (id, val) {\n            // this code is copied from above because this function call is expensive\n            // (adds 20%) and can't really be inlined by JIT.\n            let b = this.binSize;\n            let z = ~~(id / (b * 32));\n            if (!this.map[z]) {\n                this.map[z] = new Uint32Array(b);\n            }\n\n            let bin = this.map[z];\n            let slot = ~~((id % (b * 32)) / 32);\n\n            if (val) {\n                // if the value is truthy, use |= which will OR update the flag.\n                //     01010001\n                //  OR 00100000\n                // ==> 01110001\n                bin[slot] |= 1 << (id & 31);\n            } else {\n                // if the value is falsy, use &= ~FLAG to negate the flag.\n                //     01010001\n                // AND 11101111\n                // ==> 01000001\n                bin[slot] &= ~(1 << (id & 31));\n            }\n\n            return bin[slot];\n        },\n\n        // flip a bit to the opposite of its current value.\n        // this will initialize flags that don't exist yet and set them to \"true\".\n        flip: function (id) {\n            let b = this.binSize;\n            let z = ~~(id / (b * 32));\n            if (!this.map[z]) {\n                this.map[z] = new Uint32Array(b);\n            }\n\n            let bin = this.map[z];\n            let slot = ~~((id % (b * 32)) / 32);\n\n            // flip the particular flag.\n            //      01000100\n            // FLIP 00000100\n            // ===> 01000000\n            bin[slot] ^= 1 << (id & 31);\n\n            return bin[slot];\n        }\n    };\n\n    return BitArray;\n})();\n\nif (typeof module !== \"undefined\" && module.exports) {\n    module.exports = BitArray;\n}\n"
  },
  {
    "path": "perf-test.js",
    "content": "const v8 = require(\"v8\"),\n      BitArray = require(\"./bits\");\n\nlet compare = {\n    standard: () => {\n        const arr = [];\n\n        for (let x = 0; x < 1e7; x++) {\n            arr[x] = !!Math.round(Math.random());\n        }\n\n        return arr;\n    },\n\n    fixedArray: () => {\n        const arr = new Array(1e7);\n\n        for (let x = 0; x < 1e7; x++) {\n            arr[x] = !!Math.round(Math.random());\n        }\n\n        return arr;\n    },\n\n    Uint8Array: () => {\n        const arr = new Uint8Array(1e7);\n\n        for (let x = 0; x < 1e7; x++) {\n            arr[x] = !!Math.round(Math.random());\n        }\n\n        return arr;\n    },\n\n    bitArray: () => {\n        const arr = new BitArray({ binSize: 1000 });\n\n        for (let x = 0; x < 1e7; x++) {\n            arr.set(x, !!Math.round(Math.random()));\n        }\n\n        return arr;\n    }\n};\n\nconst TEST = \"bitArray\";\n\nconst s1 = v8.getHeapStatistics(),\n      d = new Date();\nlet arr = compare[TEST]();\nconsole.log(new Date() - d, v8.getHeapStatistics().total_heap_size - s1.total_heap_size);\n"
  },
  {
    "path": "readme.md",
    "content": "# BitArray.js\n\nBitArray.js is a micro-library that creates an array of booleans with less than 2% of the memory consumption of an array of booleans.\n\nBitArray weighs in at just 0.85kb compressed and 0.35kb gzipped, which makes it extremely lightweight to add to any project.\n\n## Tests\n\nThere are three tests for speed vs. memory consumption:\n\n1. Creating an array with 10m elements and adding a boolean to each index.\n\n```js\nconst arr = [];\n\nfor (let x = 0; x < 1e7; x++) {\n    arr[x] = !!Math.round(Math.random());\n}\n\nreturn arr;\n```\n\n2. Creating an array with a pre-assigned length of 10m and adding a boolean to each index.\n\n```js\nconst arr = new Array(1e7);\n\nfor (let x = 0; x < 1e7; x++) {\n    arr[x] = !!Math.round(Math.random());\n}\n\nreturn arr;\n```\n\n3. Testing the BitArray.\n\n```js\nconst arr = new BitArray();\n\nfor (let x = 0; x < 1e7; x++) {\n    arr.set(x, !!Math.round(Math.random()));\n}\n\nreturn arr;\n```\n\n### Results\n\n| Structure      | Time  | Heap Size   | % Memory |\n| -------------- | ----- | ----------  | -------- |\n| Dynamic Array  | 740ms | 335,155,200 | 100%     |\n| Pre-Init Array | 267ms | 81,068,032  | 24.19%   |\n| BitArray       | 479ms | 4,194,304   | 1.25%    |\n\n## Methods\n\nThe BitArray class only has three methods currently: `get`, `set`, and `flip`.\n\n## Usage\n\nBelow is a quick demo of all the cases this can be used with:\n\n```js\nlet array = new BitArray();\narray.get(0);       // false\narray.set(0, true); // true\narray.get(0);       // true\narray.flip(0);      // false\n```\n\n## Under the Hood\n\nThe booleans are stored in a nested array with the signature of `Array<Uint32Array>`. The array is flexible in length whereas the `Uint32Array` by default is fixed. Each boolean is stored as a bit inside of a 32-bit integer.\n\n## Customization\n\nOne option is given, which is `binSize`. This is the size of a `Uint32Array` (a bin of integers). With smaller sets of only a few dozen booleans it would be beneficial to use a small bin size like 1 or 2. When dealing with millions of booleans however, you can do some napkin math to figure out how large the `Uint32Array` should be (given that less separate arrays means less seperate overhead and pointers in memory).\n\nEach integer fits 32 booleans, so a bin of 100 `Uint32Array` objects would be capable of storing 3,200 flags.\n\n### Example\n\nBelow is an example of setting the binSize to be 1000:\n\n```js\nlet binSize = 1e3;\nconst array = new BitArray({ binSize });\n```\n\n## Contribute\n\nIf you have a contribution you'd like to make, submit a pull request! Try to match the style of the code written – it should have the appearance of being written by a single author.\n"
  },
  {
    "path": "tests.js",
    "content": "const BitArray = require(\"./bits\");\n\nconst Test = () => {\n    const results = {\n        pass: 0,\n        fail: 0\n    };\n\n    console.log(\"\\nStarting tests...\\n\");\n\n    return {\n        assert: {\n            equal: (v1, v2) => {\n                let flag = v1 === v2,\n                    str  = flag ? `SUCCESS: ${v1} === ${v2}` : `ERROR: ${v1} !== ${v2}`;\n\n                if (flag)   results.pass++;\n                else        results.fail++;\n\n                console.log(str);\n\n                if (!flag) {\n                    let err = new Error().stack;\n                    console.log(err.split(/\\n/)[2]);\n                }\n            }\n        },\n\n        results: () => {\n            console.log(`\\nPASSED: ${results.pass}\\nFAILED: ${results.fail}\\n`);\n\n            if (results.pass && !results.fail) {\n                console.log(\"All tests passed successfully!\\n\");\n            }\n        }\n    };\n}\n\nconst bits = new BitArray(),\n      test = Test();\n\n// simple bit initialization.\nbits.set(0, true);\nbits.set(1, false);\nbits.set(2000, true);\nbits.set(3, true);\n\ntest.assert.equal(bits.get(0), true);\ntest.assert.equal(bits.get(1), false);\ntest.assert.equal(bits.get(2000), true);\ntest.assert.equal(bits.get(3), true);\n\n// test flipping the value of the bit.\nbits.flip(3);\n\n// test multi-resetting of bit.\nbits.set(234809, true);\nbits.set(234809, false);\nbits.flip(234809);\n\ntest.assert.equal(bits.get(3), false);\ntest.assert.equal(bits.get(234809), true);\n\nconsole.log(test.results());\n"
  }
]