A Solidity library designed to cram as many values into as small a space as possible
npm install crambituint256 variable.
bytes container of your choice. It works by creating a list of packing instructions, i.e. an array of PackBytes structs, that tells the pack function
sh
forge install --no-commit joejordan/CramBit
`
Then, add this to your remappings.txt file:
`text
crambit=lib/CramBit/src/
`
$3
`bash
yarn add crambit
or
npm install crambit
`
Getting Started
Import the library into your Solidity contract, i.e.
`solidity
import { CramBit } from "crambit/CramBit.sol";
`
Packing instructions can be created in several different ways. Ultimately, the pack function just needs an array of PackBytes structs so that it knows what values to pack and how much space to give each value. Here is one example that creates a PackBytes array and packs two values into a single bytes1 variable:
`solidity
CramBit.PackBytes1[] memory packInstructions = new CramBit.PackBytes1[](2);
bytes1 value1 = 0x0f; // 4 bytes max (1111)
bytes1 value2 = 0x0f; // 4 bytes max (1111)
// define our packing instructions
packInstructions[0] = CramBit.PackBytes1({ maxBits: 4, data: value1 });
packInstructions[1] = CramBit.PackBytes1({ maxBits: 4, data: value2 });
// pack our data into a bytes1 value
bytes1 packedBytes1 = CramBit.pack(packInstructions);
`
Unpacking is pretty easy as well. CramBit has a helper function called packToUnpackMap that will convert your pack instructions into an unpack map, essentially an array of values that represent the maxBits for each packed value. Unpacking can be done like this:
`solidity
// generate an unpack map from our packInstructions
uint8[] memory unpackMap = CramBit.packToUnpackMap(packInstructions);
// unpack our packed values
uint8[] memory unpackedValues = CramBit.unpackBytes1(packedBytes1, unpackMap);
// assert that unpacked values match the original values
assertEq(unpackedValues[0], uint8(value1));
assertEq(unpackedValues[1], uint8(value2));
`
Check out the test directory for more examples.
One important caveat when creating your packing instructions array is that the total maxBits for all of your entries must add up to the same number of bits as the container it's going in, or you're going to get weird numbers back when unpacking.
For example, if you were packing a number of values into in a bytes1 variable, the total number of maxBits for all entries must add up to 8. If you end up with some space at the end, just remember to add an extra entry of any remaining maxBits you need to fill the container. Here's an example:
`solidity
CramBit.PackBytes1[] memory packInstructions = new CramBit.PackBytes1[](4);
uint8 value1 = 7; // 3 bytes max (111)
uint8 value2 = 2; // 2 bytes max (10)
uint8 value3 = 1; // 1 bytes max (1)
bytes1 UNUSED = 0;
// packed binary value: 111_10_1_??
// define our packing instructions
packInstructions[0] = CramBit.PackBytes1({ maxBits: 3, data: bytes1(value1) });
packInstructions[1] = CramBit.PackBytes1({ maxBits: 2, data: bytes1(value2) });
packInstructions[2] = CramBit.PackBytes1({ maxBits: 1, data: bytes1(value3) });
/// @dev important! when your other values require less than the max of the bytes container,
/// you must include an UNUSED entry with the number of bytes remaining.
packInstructions[3] = CramBit.PackBytes1({ maxBits: 2, data: UNUSED });
// pack our data into a bytes1 value
bytes1 packedBytes1 = CramBit.pack(packInstructions);
`
Contribute
Contributions are welcome! Open an issue or submit a PR. There is always room for improvement. The instructions below will walk you through setting up for contributions.
$3
You will need the following software on your machine:
- Git
- Foundry
- Node.js
- Yarn
$3
Clone this repository:
`bash
$ git clone https://github.com/joejordan/CramBit.git
`
Then, inside the project's directory, run this to install dependencies:
`bash
$ yarn install
``