DEV Community

Cover image for BitArray & SetFixed (JS) are awesome for compression or drawing | 250 K Booleans => 31 kB
ViperT
ViperT

Posted on

BitArray & SetFixed (JS) are awesome for compression or drawing | 250 K Booleans => 31 kB

Boolean Array (~ 11.3 kB & 0 dep.)

Introduction

Introducing the Ultimate JavaScript Module for Memory-Efficient Boolean Management

πŸš€ Optimized Efficiency: Leveraging the power of this module, you can ensure unparalleled memory efficiency, especially in applications requiring operations that most other solutions simply can't match. With other systems, you could easily find yourself using up to ten times more system resources. With our solution, you can kiss those inefficiencies goodbye.

πŸ” Real-world Applications:

  1. Data Compression: When you're compressing data and need to keep track of repeated patterns, our module excels. Register whether patterns are stored in a table swiftly and efficiently.
  2. Drawing Apps: For those seeking rapid pixel selection in drawing applications, this module is your go-to. Process pixel selections at lightning speed!

πŸ’Ύ Memory Footprint:

  • A staggering 1/4 million entries take up a mere 31kB in memory.
  • Engaging with ten 2D pixel selections of 500x500? That's a featherlight 0.3mB for the entire lot.
  • Store up to eight thousand boolean values in just 1kB.

πŸ” Precision Limitations:

  • Designed for a singular purpose: handling boolean values.
  • Functions within a fixed index size limited to positive whole numbers.
  • Doesn't try to be everything; it just excels at what it's meant to do.

πŸ› οΈ Design Efficiency:

  • Methods and properties are memory-efficient, with intentional design ensuring that the module remains within its internal state. Only the properties and methods with public "getters" extend beyond.
  • Low-level operations are precisely tuned for the JavaScript engine, ensuring minimal computation force. We've rigged numbers to avoid bulky double storage, streamlining memory usage further.

πŸ–‹οΈ Bottom Line: If you're into data compression or drawing applications, this kind of solution is almost indispensable. With its precise focus and memory efficiency, it's time to elevate your operations to the next level with our module.

The source code has many comments as well and the library is very flexible, there is many ways to harness its potential with different ways to use it in your code.

https://www.npmjs.com/package/@asaitama/boolean-array

Example

The SmartRunLengthCompress and SmartRunLengthDecompress functions utilize the SetFixed and BitArray data structures to implement a highly efficient form of run-length encoding. By using a 1-bit matchmaking system, SetFixed in conjunction with BitArray facilitates compression that's almost 8 times more space-efficient for non-repeating values, while only consuming an eighth more space for multiple repetitions. This enables the compression of image data, particularly suitable for pixel art with similar colors, into a much more compact form. The design leverages the concept of a "lazy" run-length algorithm, where lengths are not always mandatory, and values without specific lengths are set to false (0) in the bitarray. Thus, it provides a unique and optimized approach to data compression, which balances space savings with the ability to handle both repeating and non-repeating values.


function SmartRunLengthCompress(data_uintX) {
    var lengths = new Uint8Array(data_uintX.length);
    var lengths_l = 0;
    var values_constructor_bits = data_uintX instanceof Uint32Array ? 32: data_uintX instanceof Uint16Array ? 16: 8;
    var values_constructor = values_constructor_bits === 32 ? Uint32Array: values_constructor_bits === 16 ? Uint16Array: Uint8Array;
    var values = new values_constructor(data_uintX.length);
    var values_l = 0;
    var values_using_compression = new SetFixed(data_uintX.length);

    var current = data_uintX[0], latest = data_uintX[1], repeated = 1;
    var i = 1, l = data_uintX.length;

    for(; (i|0) < (l|0); i = i + 1|0){

        latest = data_uintX[i];

        if((latest|0) != (current|0) || (repeated|0) >= 0xFF){ // The value is new or surpass chunk length
            if((repeated|0) > 1){
                // We set the index of the current value to hold a length
                values_using_compression.add(i-repeated);
                // We add the number of repetition inside the lengths array
                lengths[lengths_l] = repeated|0
                lengths_l = lengths_l+1|0;
                // We add the value inside the values array
                values[values_l] = (current|0)>>>0;
                values_l = values_l+1|0;
            }else {
                // We add the value inside the values array
                values[values_l] = (current|0)>>>0;
                values_l = values_l+1|0;
            }
            repeated = 1;
            current = (latest|0)>>>0;
        }else { // The value is repeated
            repeated = repeated+1|0;
        }
    }

    if((repeated|0) > 1){
        // We set the index of the current value to hold a length
        values_using_compression.add(i-1)
        // We add the number of repetition inside the lengths array
        lengths[lengths_l] = repeated|0
        lengths_l = lengths_l+1|0;
        // We add the value inside the values array
        values[values_l] = (current|0)>>>0;
        values_l = values_l+1|0;
    }else {
        // We add the value inside the values array
        values[values_l] = (current|0)>>>0;
        values_l = values_l+1|0;
    }

    return {
        bits: values_constructor_bits,
        lengths: lengths.slice(0, lengths_l),
        values: values.slice(0, values_l),
        matchmaking: values_using_compression.uint32array
    };
}

function SmartRunLengthDecompress(object) {
    var constructor = object.bits === 32 ? Uint32Array: object.bits === 16 ? Uint16Array: Uint8Array;
    var lengths = object.lengths;
    var values = object.values;
    var matchmaking = new SetFixed(new BitArray(object.matchmaking));
    var total_length = values.length - lengths.length; // values that aren't alone
    for(var i = 0, l = lengths.length; (i|0) < (l|0); i = i + 1|0){
        total_length = (total_length+lengths[i]|0)>>>0; // values that are repeated
    }

    var output = new constructor(total_length), v = 0, l = 0;
    for(var i = 0; (i|0) < (total_length|0);){
        if(matchmaking.has(i)){
            output.fill(values[v], i, i+lengths[l]|0);
            i = i + lengths[l] | 0;
            l = l + 1 |0;
        }else {
            output[i] = values[v];
            i = i + 1 |0;
        }

        v = v + 1 |0;
    }

    return output;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)