-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds (unoptimized) functions to MerkleTrie.sol (#160)
* Added MerkleTrie.sol and deps * Added various randomly generated tests * Removed an unused logging line * Removed dummy package script * Removed unused variable * Various stylistic fixes * Adding some temp changes * Fixed various bugs * Removed old merkle trie file * Added initial comments * Linted and added comments to tests * Added more comments
- Loading branch information
1 parent
0594b1d
commit f810f3b
Showing
9 changed files
with
2,273 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
/* | ||
* @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; | ||
|
||
|
||
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 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 slice( | ||
bytes memory _bytes, | ||
uint256 _start | ||
) | ||
internal | ||
pure | ||
returns (bytes memory) | ||
{ | ||
if (_bytes.length - _start == 0) { | ||
return bytes(''); | ||
} | ||
|
||
return slice(_bytes, _start, _bytes.length - _start); | ||
} | ||
|
||
function toBytes32(bytes memory _bytes) internal pure returns (bytes32) { | ||
bytes32 ret; | ||
assembly { | ||
ret := mload(add(_bytes, 32)) | ||
} | ||
return ret; | ||
} | ||
|
||
function toUint256(bytes memory _bytes) internal pure returns (uint256) { | ||
return uint256(toBytes32(_bytes)); | ||
} | ||
|
||
function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) { | ||
bytes memory nibbles = new bytes(_bytes.length * 2); | ||
|
||
for (uint256 i = 0; i < _bytes.length; i++) { | ||
nibbles[i * 2] = _bytes[i] >> 4; | ||
nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); | ||
} | ||
|
||
return nibbles; | ||
} | ||
|
||
function fromNibbles(bytes memory _bytes) internal pure returns (bytes memory) { | ||
bytes memory ret = new bytes(_bytes.length / 2); | ||
|
||
for (uint256 i = 0; i < ret.length; i++) { | ||
ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { | ||
return keccak256(_bytes) == keccak256(_other); | ||
} | ||
} |
Oops, something went wrong.