diff --git a/foundry/src/lib/LibPrefixLengthEncodedByteArray.sol b/foundry/src/lib/LibPrefixLengthEncodedByteArray.sol index f8053b4..6692b32 100644 --- a/foundry/src/lib/LibPrefixLengthEncodedByteArray.sol +++ b/foundry/src/lib/LibPrefixLengthEncodedByteArray.sol @@ -1,8 +1,17 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -error LibPrefixLengthEncodedByteArray__InvalidEncoding(); - +/** + * @title Propellerheads PrefixLengthEncoded Byte Array Library + * @author PropellerHeads Developers + * @dev Provide a gas efficient encoding for bytes array. + * + * Array of bytes are encoded as a single bytes like this : + * 16 bits ??bits 16bits ??bits ... + * [length(elem1)] [elem1] [length(elem2)] [elem2] ... + * + * This is much more efficient and compact than default solidity encoding. + */ library LibPrefixLengthEncodedByteArray { /** * @dev Pop the first element of an array and returns it with the remaining data. @@ -12,45 +21,35 @@ library LibPrefixLengthEncodedByteArray { pure returns (bytes calldata elem, bytes calldata res) { - // Handle empty input - if (encoded.length == 0) { - return (encoded[:0], encoded[:0]); + assembly { + switch iszero(encoded.length) + case 1 { + elem.offset := 0 + elem.length := 0 + res.offset := 0 + res.length := 0 + } + default { + let l := shr(240, calldataload(encoded.offset)) + elem.offset := add(encoded.offset, 2) + elem.length := l + res.offset := add(elem.offset, l) + res.length := sub(sub(encoded.length, l), 2) + } } - - // Ensure we have at least 2 bytes for length prefix - if (encoded.length < 2) revert LibPrefixLengthEncodedByteArray__InvalidEncoding(); - // Extract the length prefix (first 2 bytes) - uint16 length = uint16(bytes2(encoded[:2])); - - // Check if length is valid - if (2 + length > encoded.length) revert LibPrefixLengthEncodedByteArray__InvalidEncoding(); - - // Extract the element (after length prefix) - elem = encoded[2:2+length]; - - // Extract the remaining data - res = encoded[2+length:]; - - return (elem, res); } /** * @dev Gets the size of the encoded array. */ function size(bytes calldata encoded) internal pure returns (uint256 s) { - uint256 offset = 0; - - while (offset < encoded.length) { - // Ensure we have at least 2 bytes for length prefix - if (offset + 2 > encoded.length) revert LibPrefixLengthEncodedByteArray__InvalidEncoding(); - - uint16 length = uint16(bytes2(encoded[offset:offset + 2])); - - // Check if length is valid - if (offset + 2 + length > encoded.length) revert LibPrefixLengthEncodedByteArray__InvalidEncoding(); - - offset += length + 2; - s++; + assembly { + let offset := encoded.offset + let end := add(encoded.offset, encoded.length) + for {} lt(offset, end) {} { + offset := add(offset, add(shr(240, calldataload(offset)), 2)) + s := add(s, 1) + } } }