diff options
Diffstat (limited to 'packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol')
-rw-r--r-- | packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol | 195 |
1 files changed, 142 insertions, 53 deletions
diff --git a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol index df2221c93..10d7ce41a 100644 --- a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol +++ b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol @@ -18,31 +18,36 @@ pragma solidity ^0.4.24; -contract LibBytes { +import "../LibMem/LibMem.sol"; + +contract LibBytes is + LibMem +{ // Revert reasons - string constant GT_ZERO_LENGTH_REQUIRED = "Length must be greater than 0."; - string constant GTE_4_LENGTH_REQUIRED = "Length must be greater than or equal to 4."; - string constant GTE_20_LENGTH_REQUIRED = "Length must be greater than or equal to 20."; - string constant GTE_32_LENGTH_REQUIRED = "Length must be greater than or equal to 32."; - string constant INDEX_OUT_OF_BOUNDS = "Specified array index is out of bounds."; + string constant GREATER_THAN_ZERO_LENGTH_REQUIRED = "GREATER_THAN_ZERO_LENGTH_REQUIRED"; + string constant GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED"; + string constant GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED"; + string constant GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED"; + string constant GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED"; + string constant GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED"; /// @dev Pops the last byte off of a byte array by modifying its length. /// @param b Byte array that will be modified. /// @return The byte that was popped off. - function popByte(bytes memory b) + function popLastByte(bytes memory b) internal pure returns (bytes1 result) { require( b.length > 0, - GT_ZERO_LENGTH_REQUIRED + GREATER_THAN_ZERO_LENGTH_REQUIRED ); // Store last byte. result = b[b.length - 1]; - + assembly { // Decrement length of byte array. let newLen := sub(mload(b), 1) @@ -54,14 +59,14 @@ contract LibBytes { /// @dev Pops the last 20 bytes off of a byte array by modifying its length. /// @param b Byte array that will be modified. /// @return The 20 byte address that was popped off. - function popAddress(bytes memory b) + function popLast20Bytes(bytes memory b) internal pure returns (address result) { require( b.length >= 20, - GTE_20_LENGTH_REQUIRED + GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED ); // Store last 20 bytes. @@ -75,41 +80,6 @@ contract LibBytes { return result; } - /// @dev Tests equality of two byte arrays. - /// @param lhs First byte array to compare. - /// @param rhs Second byte array to compare. - /// @return True if arrays are the same. False otherwise. - function areBytesEqual( - bytes memory lhs, - bytes memory rhs - ) - internal - pure - returns (bool equal) - { - assembly { - // Get the number of words occupied by <lhs> - let lenFullWords := div(add(mload(lhs), 0x1F), 0x20) - - // Add 1 to the number of words, to account for the length field - lenFullWords := add(lenFullWords, 0x1) - - // Test equality word-by-word. - // Terminates early if there is a mismatch. - for {let i := 0} lt(i, lenFullWords) {i := add(i, 1)} { - let lhsWord := mload(add(lhs, mul(i, 0x20))) - let rhsWord := mload(add(rhs, mul(i, 0x20))) - equal := eq(lhsWord, rhsWord) - if eq(equal, 0) { - // Break - i := lenFullWords - } - } - } - - return equal; - } - /// @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. @@ -124,8 +94,8 @@ contract LibBytes { { require( b.length >= index + 20, // 20 is length of address - GTE_20_LENGTH_REQUIRED - ); + GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED + ); // Add offset to index: // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) @@ -156,8 +126,8 @@ contract LibBytes { { require( b.length >= index + 20, // 20 is length of address - GTE_20_LENGTH_REQUIRED - ); + GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED + ); // Add offset to index: // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) @@ -195,7 +165,7 @@ contract LibBytes { { require( b.length >= index + 32, - GTE_32_LENGTH_REQUIRED + GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED ); // Arrays are prefixed by a 256 bit length parameter @@ -222,7 +192,7 @@ contract LibBytes { { require( b.length >= index + 32, - GTE_32_LENGTH_REQUIRED + GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED ); // Arrays are prefixed by a 256 bit length parameter @@ -274,11 +244,130 @@ contract LibBytes { { require( b.length >= 4, - GTE_4_LENGTH_REQUIRED + GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED ); assembly { result := mload(add(b, 32)) } return result; } + + /// @dev Reads nested bytes from a specific position. + /// @param b Byte array containing nested bytes. + /// @param index Index of nested bytes. + /// @return result Nested bytes. + function readBytes( + bytes memory b, + uint256 index + ) + internal + pure + returns (bytes memory result) + { + // Read length of nested bytes + uint256 nestedBytesLength = readUint256(b, index); + index += 32; + + // Assert length of <b> is valid, given + // length of nested bytes + require( + b.length >= index + nestedBytesLength, + GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED + ); + + // Allocate memory and copy value to result + result = new bytes(nestedBytesLength); + memCopy( + getMemAddress(result) + 32, // +32 skips array length + getMemAddress(b) + index + 32, + nestedBytesLength + ); + + return result; + } + + /// @dev Inserts bytes at a specific position in a byte array. + /// @param b Byte array to insert <input> into. + /// @param index Index in byte array of <input>. + /// @param input bytes to insert. + function writeBytes( + bytes memory b, + uint256 index, + bytes memory input + ) + internal + pure + { + // Assert length of <b> is valid, given + // length of input + require( + b.length >= index + 32 /* 32 bytes to store length */ + input.length, + GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED + ); + + // Copy <input> into <b> + memCopy( + getMemAddress(b) + 32 + index, // +32 to skip length of <b> + getMemAddress(input), // includes length of <input> + input.length + 32 // +32 bytes to store <input> length + ); + } + + /// @dev Tests equality of two byte arrays. + /// @param lhs First byte array to compare. + /// @param rhs Second byte array to compare. + /// @return True if arrays are the same. False otherwise. + function areBytesEqual( + bytes memory lhs, + bytes memory rhs + ) + internal + pure + returns (bool equal) + { + assembly { + // Get the number of words occupied by <lhs> + let lenFullWords := div(add(mload(lhs), 0x1F), 0x20) + + // Add 1 to the number of words, to account for the length field + lenFullWords := add(lenFullWords, 0x1) + + // Test equality word-by-word. + // Terminates early if there is a mismatch. + for {let i := 0} lt(i, lenFullWords) {i := add(i, 1)} { + let lhsWord := mload(add(lhs, mul(i, 0x20))) + let rhsWord := mload(add(rhs, mul(i, 0x20))) + equal := eq(lhsWord, rhsWord) + if eq(equal, 0) { + // Break + i := lenFullWords + } + } + } + + return equal; + } + + /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length. + /// @param dest Byte array that will be overwritten with source bytes. + /// @param source Byte array to copy onto dest bytes. + function deepCopyBytes( + bytes memory dest, + bytes memory source + ) + internal + pure + { + uint256 sourceLen = source.length; + // Dest length must be >= source length, or some bytes would not be copied. + require( + dest.length >= sourceLen, + GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED + ); + memCopy( + getMemAddress(dest) + 32, // +32 to skip length of <dest> + getMemAddress(source) + 32, // +32 to skip length of <source> + sourceLen + ); + } } |