// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { Uint256Util } from "./Uint256Util.sol";
library BytesUtil {
using BytesUtil for bytes;
/// @dev Gets the memory address for a byte array.
/// @param input Byte array to lookup.
/// @return memoryAddress Memory address of byte array. This
/// points to the header of the byte array which contains
/// the length.
function pointAddress(bytes memory input)
internal
pure
returns (uint256 memoryAddress)
{
assembly {
memoryAddress := input
}
return memoryAddress;
}
/// @dev Gets the memory address for the contents of a byte array.
/// @param input Byte array to lookup.
/// @return memoryAddress Memory address of the contents of the byte array.
function contentPointAddress(bytes memory input)
internal
pure
returns (uint256 memoryAddress)
{
assembly {
memoryAddress := add(input, 32)
}
return memoryAddress;
}
/// @dev Copies `length` bytes from memory location `source` to `dest`.
/// @param dest memory address to copy bytes to.
/// @param source memory address to copy bytes from.
/// @param length number of bytes to copy.
function memCopy(
uint256 dest,
uint256 source,
uint256 length
)
internal
pure
{
if (length < 32) {
// Handle a partial word by reading destination and masking
// off the bits we are interested in.
// This correctly handles overlap, zero lengths and source == dest
assembly {
let mask := sub(exp(256, sub(32, length)), 1)
let s := and(mload(source), not(mask))
let d := and(mload(dest), mask)
mstore(dest, or(s, d))
}
} else {
// Skip the O(length) loop when source == dest.
if (source == dest) {
return;
}
// For large copies we copy whole words at a time. The final
// word is aligned to the end of the range (instead of after the
// previous) to handle partial words. So a copy will look like this:
//
// ####
// ####
// ####
// ####
//
// We handle overlap in the source and destination range by
// changing the copying direction. This prevents us from
// overwriting parts of source that we still need to copy.
//
// This correctly handles source == dest
//
if (source > dest) {
assembly {
// We subtract 32 from `sEnd` and `dEnd` because it
// is easier to compare with in the loop, and these
// are also the addresses we need for copying the
// last bytes.
length := sub(length, 32)
let sEnd := add(source, length)
let dEnd := add(dest, length)
// Remember the last 32 bytes of source
// This needs to be done here and not after the loop
// because we may have overwritten the last bytes in
// source already due to overlap.
let last := mload(sEnd)
// Copy whole words front to back
// Note: the first check is always true,
// this could have been a do-while loop.
// solhint-disable-next-line no-empty-blocks
for {} lt(source, sEnd) {} {
mstore(dest, mload(source))
source := add(source, 32)
dest := add(dest, 32)
}
// Write the last 32 bytes
mstore(dEnd, last)
}
} else {
assembly {
// We subtract 32 from `sEnd` and `dEnd` because those
// are the starting points when copying a word at the end.
length := sub(length, 32)
let sEnd := add(source, length)
let dEnd := add(dest, length)
// Remember the first 32 bytes of source
// This needs to be done here and not after the loop
// because we may have overwritten the first bytes in
// source already due to overlap.
let first := mload(source)
// Copy whole words back to front
// We use a signed comparisson here to allow dEnd to become
// negative (happens when source and dest < 32). Valid
// addresses in local memory will never be larger than
// 2**255, so they can be safely re-interpreted as signed.
// Note: the first check is always true,
// this could have been a do-while loop.
// solhint-disable-next-line no-empty-blocks
for {} slt(dest, dEnd) {} {
mstore(dEnd, mload(sEnd))
sEnd := sub(sEnd, 32)
dEnd := sub(dEnd, 32)
}
// Write the first 32 bytes
mstore(dest, first)
}
}
}
}
/// @dev Returns a slices from a byte array.
/// @param b The byte array to take a slice from.
/// @param from The starting index for the slice (inclusive).
/// @param to The final index for the slice (exclusive).
/// @return result The slice containing bytes at indices [from, to)
function slice(
bytes memory b,
uint256 from,
uint256 to
)
internal
pure
returns (bytes memory result)
{
// Ensure that the from and to positions are valid positions for a slice within
// the byte array that is being used.
require(from <= to, "FromLessThanOrEqualsToRequired");
require(to <= b.length, "ToLessThanOrEqualsLengthRequired");
// Create a new bytes structure and copy contents
result = new bytes(to - from);
memCopy(
result.contentPointAddress(),
b.contentPointAddress() + from,
result.length
);
return result;
}
/// @dev Returns a slice from a byte array without preserving the input.
/// When `from == 0`, the original array will match the slice.
/// In other cases its state will be corrupted.
/// @param b The byte array to take a slice from. Will be destroyed in the process.
/// @param from The starting index for the slice (inclusive).
/// @param to The final index for the slice (exclusive).
/// @return result The slice containing bytes at indices [from, to)
function sliceDestructive(
bytes memory b,
uint256 from,
uint256 to
)
internal
pure
returns (bytes memory result)
{
// Ensure that the from and to positions are valid positions for a slice within
// the byte array that is being used.
require(from <= to, "FromLessThanOrEqualsToRequired");
require(to <= b.length, "ToLessThanOrEqualsLengthRequired");
// Create a new bytes structure around [from, to) in-place.
assembly {
result := add(b, from)
mstore(result, sub(to, from))
}
return result;
}
/// @dev Pops the last byte off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return result The byte that was popped off.
function popLastByte(bytes memory b)
internal
pure
returns (bytes1 result)
{
require(b.length != 0, "LengthGreaterThanZeroRequired");
// Store last byte.
result = b[b.length - 1];
assembly {
// Decrement length of byte array.
let newLen := sub(mload(b), 1)
mstore(b, newLen)
}
return result;
}
/// @dev Tests equality of two byte arrays.
/// @param lhs First byte array to compare.
/// @param rhs Second byte array to compare.
/// @return equal True if arrays are the same. False otherwise.
function equals(
bytes memory lhs,
bytes memory rhs
)
internal
pure
returns (bool equal)
{
// Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.
// We early exit on unequal lengths, but keccak would also correctly
// handle this.
return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);
}
/// @dev Reads an address from a position in a byte array.
/// @param b Byte array containing an address.
/// @param index Index in byte array of address.
/// @return result address from byte array.
function readAddress(
bytes memory b,
uint256 index
)
internal
pure
returns (address result)
{
require(b.length >= index + 20, "LengthGreaterThanOrEqualsTwentyRequired");
// Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
// 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
index += 20;
// Read address from array memory
assembly {
// 1. Add index to address of bytes array
// 2. Load 32-byte word from memory
// 3. Apply 20-byte mask to obtain address
result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
}
return result;
}
/// @dev Writes an address into a specific position in a byte array.
/// @param b Byte array to insert address into.
/// @param index Index in byte array of address.
/// @param input Address to put into byte array.
function writeAddress(
bytes memory b,
uint256 index,
address input
)
internal
pure
{
require(b.length >= index + 20, "LengthGreaterThanOrEqualsTwentyRequired");
// Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
// 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
index += 20;
// Store address into array memory
assembly {
// The address occupies 20 bytes and mstore stores 32 bytes.
// First fetch the 32-byte word where we'll be storing the address, then
// apply a mask so we have only the bytes in the word that the address will not occupy.
// Then combine these bytes with the address and store the 32 bytes back to memory with mstore.
// 1. Add index to address of bytes array
// 2. Load 32-byte word from memory
// 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address
let neighbors := and(
mload(add(b, index)),
0xffffffffffffffffffffffff0000000000000000000000000000000000000000
)
// Make sure input address is clean.
// (Solidity does not guarantee this)
input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
// Store the neighbors and address into memory
mstore(add(b, index), xor(input, neighbors))
}
}
/// @dev Reads a bytes32 value from a position in a byte array.
/// @param b Byte array containing a bytes32 value.
/// @param index Index in byte array of bytes32 value.
/// @return result bytes32 value from byte array.
function readBytes32(
bytes memory b,
uint256 index
)
internal
pure
returns (bytes32 result)
{
require(b.length >= index + 32, "LengthGreaterThanOrEqualsThirtyTwoRequired");
// Arrays are prefixed by a 256 bit length parameter
index += 32;
// Read the bytes32 from array memory
assembly {
result := mload(add(b, index))
}
return result;
}
/// @dev Writes a bytes32 into a specific position in a byte array.
/// @param b Byte array to insert into.
/// @param index Index in byte array of .
/// @param input bytes32 to put into byte array.
function writeBytes32(
bytes memory b,
uint256 index,
bytes32 input
)
internal
pure
{
require(b.length >= index + 32, "LengthGreaterThanOrEqualsThirtyTwoRequired");
// Arrays are prefixed by a 256 bit length parameter
index += 32;
// Read the bytes32 from array memory
assembly {
mstore(add(b, index), input)
}
}
/// @dev Reads a uint256 value from a position in a byte array.
/// @param b Byte array containing a uint256 value.
/// @param index Index in byte array of uint256 value.
/// @return result uint256 value from byte array.
function readUint256(
bytes memory b,
uint256 index
)
internal
pure
returns (uint256 result)
{
result = uint256(readBytes32(b, index));
return result;
}
/// @dev Writes a uint256 into a specific position in a byte array.
/// @param b Byte array to insert into.
/// @param index Index in byte array of .
/// @param input uint256 to put into byte array.
function writeUint256(
bytes memory b,
uint256 index,
uint256 input
)
internal
pure
{
writeBytes32(b, index, bytes32(input));
}
/// @dev Reads an unpadded bytes4 value from a position in a byte array.
/// @param b Byte array containing a bytes4 value.
/// @param index Index in byte array of bytes4 value.
/// @return result bytes4 value from byte array.
function readBytes4(
bytes memory b,
uint256 index
)
internal
pure
returns (bytes4 result)
{
require(b.length >= index + 4, "LengthGreaterThanOrEqualsFourRequired");
// Arrays are prefixed by a 32 byte length field
index += 32;
// Read the bytes4 from array memory
assembly {
result := mload(add(b, index))
// Solidity does not require us to clean the trailing bytes.
// We do it anyway
result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
}
return result;
}
/// @dev Writes a new length to a byte array.
/// Decreasing length will lead to removing the corresponding lower order bytes from the byte array.
/// Increasing length may lead to appending adjacent in-memory bytes to the end of the byte array.
/// @param b Bytes array to write new length to.
/// @param length New length of byte array.
function writeLength(bytes memory b, uint256 length)
internal
pure
{
assembly {
mstore(b, length)
}
}
function recover(bytes memory _msgBytes, uint8 _v, bytes32 _r, bytes32 _s) internal pure returns (address) {
bytes memory fullMessage = concat(
bytes("\x19Ethereum Signed Message:\n"),
bytes(Uint256Util.toString(_msgBytes.length)),
_msgBytes,
new bytes(0), new bytes(0), new bytes(0), new bytes(0)
);
return ecrecover(keccak256(fullMessage), _v, _r, _s);
}
function concat(bytes memory ba, bytes memory bb, bytes memory bc, bytes memory bd, bytes memory be, bytes memory bf, bytes memory bg) internal pure returns (bytes memory) {
bytes memory resultBytes = new bytes(ba.length + bb.length + bc.length + bd.length + be.length + bf.length + bg.length);
uint k = 0;
for (uint i = 0; i < ba.length; i++) resultBytes[k++] = ba[i];
for (uint i = 0; i < bb.length; i++) resultBytes[k++] = bb[i];
for (uint i = 0; i < bc.length; i++) resultBytes[k++] = bc[i];
for (uint i = 0; i < bd.length; i++) resultBytes[k++] = bd[i];
for (uint i = 0; i < be.length; i++) resultBytes[k++] = be[i];
for (uint i = 0; i < bf.length; i++) resultBytes[k++] = bf[i];
for (uint i = 0; i < bg.length; i++) resultBytes[k++] = bg[i];
return resultBytes;
}
function toHexString(bytes memory _value) internal pure returns (string memory) {
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(64);
for (uint256 i = 0; i < _value.length; i++) {
str[i*2] = alphabet[uint8(_value[i] >> 4)];
str[1+i*2] = alphabet[uint8(_value[i] & 0x0f)];
}
return string(str);
}
}