161 lines
5.7 KiB
Solidity
161 lines
5.7 KiB
Solidity
// 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.
|
|
* This is a reduced version of the library.
|
|
*/
|
|
pragma solidity >=0.8.0 <0.9.0;
|
|
|
|
library BytesLib {
|
|
function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
|
|
unchecked {
|
|
require(_length + 31 >= _length, "slice_overflow");
|
|
}
|
|
require(_bytes.length >= _start + _length, "slice_outOfBounds");
|
|
|
|
bytes memory tempBytes;
|
|
|
|
assembly ("memory-safe") {
|
|
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)
|
|
//zero out the 32 bytes slice we are about to return
|
|
//we need to do it because Solidity does not garbage collect
|
|
mstore(tempBytes, 0)
|
|
|
|
mstore(0x40, add(tempBytes, 0x20))
|
|
}
|
|
}
|
|
|
|
return tempBytes;
|
|
}
|
|
|
|
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
|
|
require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
|
|
address tempAddress;
|
|
|
|
assembly ("memory-safe") {
|
|
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, "toUint8_outOfBounds");
|
|
uint8 tempUint;
|
|
|
|
assembly ("memory-safe") {
|
|
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, "toUint16_outOfBounds");
|
|
uint16 tempUint;
|
|
|
|
assembly ("memory-safe") {
|
|
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, "toUint32_outOfBounds");
|
|
uint32 tempUint;
|
|
|
|
assembly ("memory-safe") {
|
|
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, "toUint64_outOfBounds");
|
|
uint64 tempUint;
|
|
|
|
assembly ("memory-safe") {
|
|
tempUint := mload(add(add(_bytes, 0x8), _start))
|
|
}
|
|
|
|
return tempUint;
|
|
}
|
|
|
|
function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
|
|
require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
|
|
uint128 tempUint;
|
|
|
|
assembly ("memory-safe") {
|
|
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, "toUint256_outOfBounds");
|
|
uint256 tempUint;
|
|
|
|
assembly ("memory-safe") {
|
|
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, "toBytes32_outOfBounds");
|
|
bytes32 tempBytes32;
|
|
|
|
assembly ("memory-safe") {
|
|
tempBytes32 := mload(add(add(_bytes, 0x20), _start))
|
|
}
|
|
|
|
return tempBytes32;
|
|
}
|
|
}
|