| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- // SPDX-License-Identifier: Unlicense
- /*
- * @title Solidity Bytes Arrays Utils
- * @author Gonçalo Sá <goncalo.sa@consensys.net>
- *
- * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
- * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
- */
- pragma solidity >=0.5.0 <0.7.0;
- library BytesLib {
- function concat(
- bytes memory _preBytes,
- bytes memory _postBytes
- )
- internal
- pure
- returns (bytes memory)
- {
- bytes memory tempBytes;
- assembly {
- // Get a location of some free memory and store it in tempBytes as
- // Solidity does for memory variables.
- tempBytes := mload(0x40)
- // Store the length of the first bytes array at the beginning of
- // the memory for tempBytes.
- let length := mload(_preBytes)
- mstore(tempBytes, length)
- // Maintain a memory counter for the current write location in the
- // temp bytes array by adding the 32 bytes for the array length to
- // the starting location.
- let mc := add(tempBytes, 0x20)
- // Stop copying when the memory counter reaches the length of the
- // first bytes array.
- let end := add(mc, length)
- for {
- // Initialize a copy counter to the start of the _preBytes data,
- // 32 bytes into its memory.
- let cc := add(_preBytes, 0x20)
- } lt(mc, end) {
- // Increase both counters by 32 bytes each iteration.
- mc := add(mc, 0x20)
- cc := add(cc, 0x20)
- } {
- // Write the _preBytes data into the tempBytes memory 32 bytes
- // at a time.
- mstore(mc, mload(cc))
- }
- // Add the length of _postBytes to the current length of tempBytes
- // and store it as the new length in the first 32 bytes of the
- // tempBytes memory.
- length := mload(_postBytes)
- mstore(tempBytes, add(length, mload(tempBytes)))
- // Move the memory counter back from a multiple of 0x20 to the
- // actual end of the _preBytes data.
- mc := end
- // Stop copying when the memory counter reaches the new combined
- // length of the arrays.
- end := add(mc, length)
- for {
- let cc := add(_postBytes, 0x20)
- } lt(mc, end) {
- mc := add(mc, 0x20)
- cc := add(cc, 0x20)
- } {
- mstore(mc, mload(cc))
- }
- // Update the free-memory pointer by padding our last write location
- // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
- // next 32 byte block, then round down to the nearest multiple of
- // 32. If the sum of the length of the two arrays is zero then add
- // one before rounding down to leave a blank 32 bytes (the length block with 0).
- mstore(0x40, and(
- add(add(end, iszero(add(length, mload(_preBytes)))), 31),
- not(31) // Round down to the nearest 32 bytes.
- ))
- }
- return tempBytes;
- }
- function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
- assembly {
- // Read the first 32 bytes of _preBytes storage, which is the length
- // of the array. (We don't need to use the offset into the slot
- // because arrays use the entire slot.)
- let fslot := sload(_preBytes_slot)
- // Arrays of 31 bytes or less have an even value in their slot,
- // while longer arrays have an odd value. The actual length is
- // the slot divided by two for odd values, and the lowest order
- // byte divided by two for even values.
- // If the slot is even, bitwise and the slot with 255 and divide by
- // two to get the length. If the slot is odd, bitwise and the slot
- // with -1 and divide by two.
- let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
- let mlength := mload(_postBytes)
- let newlength := add(slength, mlength)
- // slength can contain both the length and contents of the array
- // if length < 32 bytes so let's prepare for that
- // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
- switch add(lt(slength, 32), lt(newlength, 32))
- case 2 {
- // Since the new array still fits in the slot, we just need to
- // update the contents of the slot.
- // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
- sstore(
- _preBytes_slot,
- // all the modifications to the slot are inside this
- // next block
- add(
- // we can just add to the slot contents because the
- // bytes we want to change are the LSBs
- fslot,
- add(
- mul(
- div(
- // load the bytes from memory
- mload(add(_postBytes, 0x20)),
- // zero all bytes to the right
- exp(0x100, sub(32, mlength))
- ),
- // and now shift left the number of bytes to
- // leave space for the length in the slot
- exp(0x100, sub(32, newlength))
- ),
- // increase length by the double of the memory
- // bytes length
- mul(mlength, 2)
- )
- )
- )
- }
- case 1 {
- // The stored value fits in the slot, but the combined value
- // will exceed it.
- // get the keccak hash to get the contents of the array
- mstore(0x0, _preBytes_slot)
- let sc := add(keccak256(0x0, 0x20), div(slength, 32))
- // save new length
- sstore(_preBytes_slot, add(mul(newlength, 2), 1))
- // The contents of the _postBytes array start 32 bytes into
- // the structure. Our first read should obtain the `submod`
- // bytes that can fit into the unused space in the last word
- // of the stored array. To get this, we read 32 bytes starting
- // from `submod`, so the data we read overlaps with the array
- // contents by `submod` bytes. Masking the lowest-order
- // `submod` bytes allows us to add that value directly to the
- // stored value.
- let submod := sub(32, slength)
- let mc := add(_postBytes, submod)
- let end := add(_postBytes, mlength)
- let mask := sub(exp(0x100, submod), 1)
- sstore(
- sc,
- add(
- and(
- fslot,
- 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
- ),
- and(mload(mc), mask)
- )
- )
- for {
- mc := add(mc, 0x20)
- sc := add(sc, 1)
- } lt(mc, end) {
- sc := add(sc, 1)
- mc := add(mc, 0x20)
- } {
- sstore(sc, mload(mc))
- }
- mask := exp(0x100, sub(mc, end))
- sstore(sc, mul(div(mload(mc), mask), mask))
- }
- default {
- // get the keccak hash to get the contents of the array
- mstore(0x0, _preBytes_slot)
- // Start copying to the last used word of the stored array.
- let sc := add(keccak256(0x0, 0x20), div(slength, 32))
- // save new length
- sstore(_preBytes_slot, add(mul(newlength, 2), 1))
- // Copy over the first `submod` bytes of the new data as in
- // case 1 above.
- let slengthmod := mod(slength, 32)
- let mlengthmod := mod(mlength, 32)
- let submod := sub(32, slengthmod)
- let mc := add(_postBytes, submod)
- let end := add(_postBytes, mlength)
- let mask := sub(exp(0x100, submod), 1)
- sstore(sc, add(sload(sc), and(mload(mc), mask)))
- for {
- sc := add(sc, 1)
- mc := add(mc, 0x20)
- } lt(mc, end) {
- sc := add(sc, 1)
- mc := add(mc, 0x20)
- } {
- sstore(sc, mload(mc))
- }
- mask := exp(0x100, sub(mc, end))
- sstore(sc, mul(div(mload(mc), mask), mask))
- }
- }
- }
- function slice(
- bytes memory _bytes,
- uint256 _start,
- uint256 _length
- )
- internal
- pure
- returns (bytes memory)
- {
- require(_bytes.length >= (_start + _length), "Read out of bounds");
- bytes memory tempBytes;
- assembly {
- switch iszero(_length)
- case 0 {
- // Get a location of some free memory and store it in tempBytes as
- // Solidity does for memory variables.
- tempBytes := mload(0x40)
- // The first word of the slice result is potentially a partial
- // word read from the original array. To read it, we calculate
- // the length of that partial word and start copying that many
- // bytes into the array. The first word we copy will start with
- // data we don't care about, but the last `lengthmod` bytes will
- // land at the beginning of the contents of the new array. When
- // we're done copying, we overwrite the full first word with
- // the actual length of the slice.
- let lengthmod := and(_length, 31)
- // The multiplication in the next line is necessary
- // because when slicing multiples of 32 bytes (lengthmod == 0)
- // the following copy loop was copying the origin's length
- // and then ending prematurely not copying everything it should.
- let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
- let end := add(mc, _length)
- for {
- // The multiplication in the next line has the same exact purpose
- // as the one above.
- let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
- } lt(mc, end) {
- mc := add(mc, 0x20)
- cc := add(cc, 0x20)
- } {
- mstore(mc, mload(cc))
- }
- mstore(tempBytes, _length)
- //update free-memory pointer
- //allocating the array padded to 32 bytes like the compiler does now
- mstore(0x40, and(add(mc, 31), not(31)))
- }
- //if we want a zero-length slice let's just return a zero-length array
- default {
- tempBytes := mload(0x40)
- mstore(0x40, add(tempBytes, 0x20))
- }
- }
- return tempBytes;
- }
- function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
- require(_bytes.length >= (_start + 20), "Read out of bounds");
- address tempAddress;
- assembly {
- tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
- }
- return tempAddress;
- }
- function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
- require(_bytes.length >= (_start + 1), "Read out of bounds");
- uint8 tempUint;
- assembly {
- tempUint := mload(add(add(_bytes, 0x1), _start))
- }
- return tempUint;
- }
- function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
- require(_bytes.length >= (_start + 2), "Read out of bounds");
- uint16 tempUint;
- assembly {
- tempUint := mload(add(add(_bytes, 0x2), _start))
- }
- return tempUint;
- }
- function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
- require(_bytes.length >= (_start + 4), "Read out of bounds");
- uint32 tempUint;
- assembly {
- tempUint := mload(add(add(_bytes, 0x4), _start))
- }
- return tempUint;
- }
- function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
- require(_bytes.length >= (_start + 8), "Read out of bounds");
- uint64 tempUint;
- assembly {
- tempUint := mload(add(add(_bytes, 0x8), _start))
- }
- return tempUint;
- }
- function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
- require(_bytes.length >= (_start + 12), "Read out of bounds");
- uint96 tempUint;
- assembly {
- tempUint := mload(add(add(_bytes, 0xc), _start))
- }
- return tempUint;
- }
- function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
- require(_bytes.length >= (_start + 16), "Read out of bounds");
- uint128 tempUint;
- assembly {
- tempUint := mload(add(add(_bytes, 0x10), _start))
- }
- return tempUint;
- }
- function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
- require(_bytes.length >= (_start + 32), "Read out of bounds");
- uint256 tempUint;
- assembly {
- tempUint := mload(add(add(_bytes, 0x20), _start))
- }
- return tempUint;
- }
- function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
- require(_bytes.length >= (_start + 32), "Read out of bounds");
- bytes32 tempBytes32;
- assembly {
- tempBytes32 := mload(add(add(_bytes, 0x20), _start))
- }
- return tempBytes32;
- }
- function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
- bool success = true;
- assembly {
- let length := mload(_preBytes)
- // if lengths don't match the arrays are not equal
- switch eq(length, mload(_postBytes))
- case 1 {
- // cb is a circuit breaker in the for loop since there's
- // no said feature for inline assembly loops
- // cb = 1 - don't breaker
- // cb = 0 - break
- let cb := 1
- let mc := add(_preBytes, 0x20)
- let end := add(mc, length)
- for {
- let cc := add(_postBytes, 0x20)
- // the next line is the loop condition:
- // while(uint256(mc < end) + cb == 2)
- } eq(add(lt(mc, end), cb), 2) {
- mc := add(mc, 0x20)
- cc := add(cc, 0x20)
- } {
- // if any of these checks fails then arrays are not equal
- if iszero(eq(mload(mc), mload(cc))) {
- // unsuccess:
- success := 0
- cb := 0
- }
- }
- }
- default {
- // unsuccess:
- success := 0
- }
- }
- return success;
- }
- function equalStorage(
- bytes storage _preBytes,
- bytes memory _postBytes
- )
- internal
- view
- returns (bool)
- {
- bool success = true;
- assembly {
- // we know _preBytes_offset is 0
- let fslot := sload(_preBytes_slot)
- // Decode the length of the stored array like in concatStorage().
- let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
- let mlength := mload(_postBytes)
- // if lengths don't match the arrays are not equal
- switch eq(slength, mlength)
- case 1 {
- // slength can contain both the length and contents of the array
- // if length < 32 bytes so let's prepare for that
- // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
- if iszero(iszero(slength)) {
- switch lt(slength, 32)
- case 1 {
- // blank the last byte which is the length
- fslot := mul(div(fslot, 0x100), 0x100)
- if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
- // unsuccess:
- success := 0
- }
- }
- default {
- // cb is a circuit breaker in the for loop since there's
- // no said feature for inline assembly loops
- // cb = 1 - don't breaker
- // cb = 0 - break
- let cb := 1
- // get the keccak hash to get the contents of the array
- mstore(0x0, _preBytes_slot)
- let sc := keccak256(0x0, 0x20)
- let mc := add(_postBytes, 0x20)
- let end := add(mc, mlength)
- // the next line is the loop condition:
- // while(uint256(mc < end) + cb == 2)
- for {} eq(add(lt(mc, end), cb), 2) {
- sc := add(sc, 1)
- mc := add(mc, 0x20)
- } {
- if iszero(eq(sload(sc), mload(mc))) {
- // unsuccess:
- success := 0
- cb := 0
- }
- }
- }
- }
- }
- default {
- // unsuccess:
- success := 0
- }
- }
- return success;
- }
- }
|