-
Notifications
You must be signed in to change notification settings - Fork 1
/
BytesLib.sol
76 lines (66 loc) · 3.33 KB
/
BytesLib.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;
// creds https://gist.github.com/rmeissner/76d6345796909ee41fb9f36fdaa4d15f - Stackoverflow
library BytesLib {
function slice(
bytes memory _bytes,
uint256 _start,
uint256 _length
)
internal
pure
returns (bytes memory)
{
require(_length + 31 >= _length, "slice_overflow");
require(_bytes.length >= _start + _length, "slice_outOfBounds");
bytes memory tempBytes;
// Check length is 0. `iszero` return 1 for `true` and 0 for `false`.
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)
// Calculate length mod 32 to handle slices that are not a multiple of 32 in size.
let lengthmod := and(_length, 31)
// tempBytes will have the following format in memory: <length><data>
// When copying data we will offset the start forward to avoid allocating additional memory
// Therefore part of the length area will be written, but this will be overwritten later anyways.
// In case no offset is require, the start is set to the data region (0x20 from the tempBytes)
// mc will be used to keep track where to copy the data to.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// Same logic as for mc is applied and additionally the start offset specified for the method is added
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
// increase `mc` and `cc` to read the next word from memory
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
// Copy the data from source (cc location) to the slice data (mc location)
mstore(mc, mload(cc))
}
// Store the length of the slice. This will overwrite any partial data that
// was copied when having slices that are not a multiple of 32.
mstore(tempBytes, _length)
// update free-memory pointer
// allocating the array padded to 32 bytes like the compiler does now
// To set the used memory as a multiple of 32, add 31 to the actual memory usage (mc)
// and remove the modulo 32 (the `and` with `not(31)`)
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)
// update free-memory pointer
// tempBytes uses 32 bytes in memory (even when empty) for the length.
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
}