|
4 | 4 | * @author Gonçalo Sá <goncalo.sa@consensys.net>
|
5 | 5 | *
|
6 | 6 | * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
|
7 |
| - * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. |
| 7 | + * This is a reduced version of the library. |
8 | 8 | */
|
9 | 9 | pragma solidity >=0.8.0 <0.9.0;
|
10 | 10 |
|
11 | 11 | library BytesLib {
|
12 |
| - function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { |
13 |
| - bytes memory tempBytes; |
14 |
| - |
15 |
| - assembly ("memory-safe") { |
16 |
| - // Get a location of some free memory and store it in tempBytes as |
17 |
| - // Solidity does for memory variables. |
18 |
| - tempBytes := mload(0x40) |
19 |
| - |
20 |
| - // Store the length of the first bytes array at the beginning of |
21 |
| - // the memory for tempBytes. |
22 |
| - let length := mload(_preBytes) |
23 |
| - mstore(tempBytes, length) |
24 |
| - |
25 |
| - // Maintain a memory counter for the current write location in the |
26 |
| - // temp bytes array by adding the 32 bytes for the array length to |
27 |
| - // the starting location. |
28 |
| - let mc := add(tempBytes, 0x20) |
29 |
| - // Stop copying when the memory counter reaches the length of the |
30 |
| - // first bytes array. |
31 |
| - let end := add(mc, length) |
32 |
| - |
33 |
| - for { |
34 |
| - // Initialize a copy counter to the start of the _preBytes data, |
35 |
| - // 32 bytes into its memory. |
36 |
| - let cc := add(_preBytes, 0x20) |
37 |
| - } lt(mc, end) { |
38 |
| - // Increase both counters by 32 bytes each iteration. |
39 |
| - mc := add(mc, 0x20) |
40 |
| - cc := add(cc, 0x20) |
41 |
| - } { |
42 |
| - // Write the _preBytes data into the tempBytes memory 32 bytes |
43 |
| - // at a time. |
44 |
| - mstore(mc, mload(cc)) |
45 |
| - } |
46 |
| - |
47 |
| - // Add the length of _postBytes to the current length of tempBytes |
48 |
| - // and store it as the new length in the first 32 bytes of the |
49 |
| - // tempBytes memory. |
50 |
| - length := mload(_postBytes) |
51 |
| - mstore(tempBytes, add(length, mload(tempBytes))) |
52 |
| - |
53 |
| - // Move the memory counter back from a multiple of 0x20 to the |
54 |
| - // actual end of the _preBytes data. |
55 |
| - mc := end |
56 |
| - // Stop copying when the memory counter reaches the new combined |
57 |
| - // length of the arrays. |
58 |
| - end := add(mc, length) |
59 |
| - |
60 |
| - for { let cc := add(_postBytes, 0x20) } lt(mc, end) { |
61 |
| - mc := add(mc, 0x20) |
62 |
| - cc := add(cc, 0x20) |
63 |
| - } { mstore(mc, mload(cc)) } |
64 |
| - |
65 |
| - // Update the free-memory pointer by padding our last write location |
66 |
| - // to 32 bytes: add 31 bytes to the end of tempBytes to move to the |
67 |
| - // next 32 byte block, then round down to the nearest multiple of |
68 |
| - // 32. If the sum of the length of the two arrays is zero then add |
69 |
| - // one before rounding down to leave a blank 32 bytes (the length block with 0). |
70 |
| - mstore( |
71 |
| - 0x40, |
72 |
| - and( |
73 |
| - add(add(end, iszero(add(length, mload(_preBytes)))), 31), |
74 |
| - not(31) // Round down to the nearest 32 bytes. |
75 |
| - ) |
76 |
| - ) |
77 |
| - } |
78 |
| - |
79 |
| - return tempBytes; |
80 |
| - } |
81 |
| - |
82 |
| - function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { |
83 |
| - assembly ("memory-safe") { |
84 |
| - // Read the first 32 bytes of _preBytes storage, which is the length |
85 |
| - // of the array. (We don't need to use the offset into the slot |
86 |
| - // because arrays use the entire slot.) |
87 |
| - let fslot := sload(_preBytes.slot) |
88 |
| - // Arrays of 31 bytes or less have an even value in their slot, |
89 |
| - // while longer arrays have an odd value. The actual length is |
90 |
| - // the slot divided by two for odd values, and the lowest order |
91 |
| - // byte divided by two for even values. |
92 |
| - // If the slot is even, bitwise and the slot with 255 and divide by |
93 |
| - // two to get the length. If the slot is odd, bitwise and the slot |
94 |
| - // with -1 and divide by two. |
95 |
| - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) |
96 |
| - let mlength := mload(_postBytes) |
97 |
| - let newlength := add(slength, mlength) |
98 |
| - // slength can contain both the length and contents of the array |
99 |
| - // if length < 32 bytes so let's prepare for that |
100 |
| - // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage |
101 |
| - switch add(lt(slength, 32), lt(newlength, 32)) |
102 |
| - case 2 { |
103 |
| - // Since the new array still fits in the slot, we just need to |
104 |
| - // update the contents of the slot. |
105 |
| - // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length |
106 |
| - sstore( |
107 |
| - _preBytes.slot, |
108 |
| - // all the modifications to the slot are inside this |
109 |
| - // next block |
110 |
| - add( |
111 |
| - // we can just add to the slot contents because the |
112 |
| - // bytes we want to change are the LSBs |
113 |
| - fslot, |
114 |
| - add( |
115 |
| - mul( |
116 |
| - div( |
117 |
| - // load the bytes from memory |
118 |
| - mload(add(_postBytes, 0x20)), |
119 |
| - // zero all bytes to the right |
120 |
| - exp(0x100, sub(32, mlength)) |
121 |
| - ), |
122 |
| - // and now shift left the number of bytes to |
123 |
| - // leave space for the length in the slot |
124 |
| - exp(0x100, sub(32, newlength)) |
125 |
| - ), |
126 |
| - // increase length by the double of the memory |
127 |
| - // bytes length |
128 |
| - mul(mlength, 2) |
129 |
| - ) |
130 |
| - ) |
131 |
| - ) |
132 |
| - } |
133 |
| - case 1 { |
134 |
| - // The stored value fits in the slot, but the combined value |
135 |
| - // will exceed it. |
136 |
| - // get the keccak hash to get the contents of the array |
137 |
| - mstore(0x0, _preBytes.slot) |
138 |
| - let sc := add(keccak256(0x0, 0x20), div(slength, 32)) |
139 |
| - |
140 |
| - // save new length |
141 |
| - sstore(_preBytes.slot, add(mul(newlength, 2), 1)) |
142 |
| - |
143 |
| - // The contents of the _postBytes array start 32 bytes into |
144 |
| - // the structure. Our first read should obtain the `submod` |
145 |
| - // bytes that can fit into the unused space in the last word |
146 |
| - // of the stored array. To get this, we read 32 bytes starting |
147 |
| - // from `submod`, so the data we read overlaps with the array |
148 |
| - // contents by `submod` bytes. Masking the lowest-order |
149 |
| - // `submod` bytes allows us to add that value directly to the |
150 |
| - // stored value. |
151 |
| - |
152 |
| - let submod := sub(32, slength) |
153 |
| - let mc := add(_postBytes, submod) |
154 |
| - let end := add(_postBytes, mlength) |
155 |
| - let mask := sub(exp(0x100, submod), 1) |
156 |
| - |
157 |
| - sstore( |
158 |
| - sc, |
159 |
| - add( |
160 |
| - and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00), |
161 |
| - and(mload(mc), mask) |
162 |
| - ) |
163 |
| - ) |
164 |
| - |
165 |
| - for { |
166 |
| - mc := add(mc, 0x20) |
167 |
| - sc := add(sc, 1) |
168 |
| - } lt(mc, end) { |
169 |
| - sc := add(sc, 1) |
170 |
| - mc := add(mc, 0x20) |
171 |
| - } { sstore(sc, mload(mc)) } |
172 |
| - |
173 |
| - mask := exp(0x100, sub(mc, end)) |
174 |
| - |
175 |
| - sstore(sc, mul(div(mload(mc), mask), mask)) |
176 |
| - } |
177 |
| - default { |
178 |
| - // get the keccak hash to get the contents of the array |
179 |
| - mstore(0x0, _preBytes.slot) |
180 |
| - // Start copying to the last used word of the stored array. |
181 |
| - let sc := add(keccak256(0x0, 0x20), div(slength, 32)) |
182 |
| - |
183 |
| - // save new length |
184 |
| - sstore(_preBytes.slot, add(mul(newlength, 2), 1)) |
185 |
| - |
186 |
| - // Copy over the first `submod` bytes of the new data as in |
187 |
| - // case 1 above. |
188 |
| - let slengthmod := mod(slength, 32) |
189 |
| - let mlengthmod := mod(mlength, 32) |
190 |
| - let submod := sub(32, slengthmod) |
191 |
| - let mc := add(_postBytes, submod) |
192 |
| - let end := add(_postBytes, mlength) |
193 |
| - let mask := sub(exp(0x100, submod), 1) |
194 |
| - |
195 |
| - sstore(sc, add(sload(sc), and(mload(mc), mask))) |
196 |
| - |
197 |
| - for { |
198 |
| - sc := add(sc, 1) |
199 |
| - mc := add(mc, 0x20) |
200 |
| - } lt(mc, end) { |
201 |
| - sc := add(sc, 1) |
202 |
| - mc := add(mc, 0x20) |
203 |
| - } { sstore(sc, mload(mc)) } |
204 |
| - |
205 |
| - mask := exp(0x100, sub(mc, end)) |
206 |
| - |
207 |
| - sstore(sc, mul(div(mload(mc), mask), mask)) |
208 |
| - } |
209 |
| - } |
210 |
| - } |
211 |
| - |
212 | 12 | function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
|
213 | 13 | require(_length + 31 >= _length, "slice_overflow");
|
214 | 14 | require(_bytes.length >= _start + _length, "slice_outOfBounds");
|
@@ -323,17 +123,6 @@ library BytesLib {
|
323 | 123 | return tempUint;
|
324 | 124 | }
|
325 | 125 |
|
326 |
| - function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { |
327 |
| - require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); |
328 |
| - uint96 tempUint; |
329 |
| - |
330 |
| - assembly ("memory-safe") { |
331 |
| - tempUint := mload(add(add(_bytes, 0xc), _start)) |
332 |
| - } |
333 |
| - |
334 |
| - return tempUint; |
335 |
| - } |
336 |
| - |
337 | 126 | function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
|
338 | 127 | require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
|
339 | 128 | uint128 tempUint;
|
@@ -366,111 +155,4 @@ library BytesLib {
|
366 | 155 |
|
367 | 156 | return tempBytes32;
|
368 | 157 | }
|
369 |
| - |
370 |
| - function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { |
371 |
| - bool success = true; |
372 |
| - |
373 |
| - assembly ("memory-safe") { |
374 |
| - let length := mload(_preBytes) |
375 |
| - |
376 |
| - // if lengths don't match the arrays are not equal |
377 |
| - switch eq(length, mload(_postBytes)) |
378 |
| - case 1 { |
379 |
| - // cb is a circuit breaker in the for loop since there's |
380 |
| - // no said feature for inline assembly loops |
381 |
| - // cb = 1 - don't breaker |
382 |
| - // cb = 0 - break |
383 |
| - let cb := 1 |
384 |
| - |
385 |
| - let mc := add(_preBytes, 0x20) |
386 |
| - let end := add(mc, length) |
387 |
| - |
388 |
| - for { let cc := add(_postBytes, 0x20) } |
389 |
| - // the next line is the loop condition: |
390 |
| - // while(uint256(mc < end) + cb == 2) |
391 |
| - eq(add(lt(mc, end), cb), 2) { |
392 |
| - mc := add(mc, 0x20) |
393 |
| - cc := add(cc, 0x20) |
394 |
| - } { |
395 |
| - // if any of these checks fails then arrays are not equal |
396 |
| - if iszero(eq(mload(mc), mload(cc))) { |
397 |
| - // unsuccess: |
398 |
| - success := 0 |
399 |
| - cb := 0 |
400 |
| - } |
401 |
| - } |
402 |
| - } |
403 |
| - default { |
404 |
| - // unsuccess: |
405 |
| - success := 0 |
406 |
| - } |
407 |
| - } |
408 |
| - |
409 |
| - return success; |
410 |
| - } |
411 |
| - |
412 |
| - function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { |
413 |
| - bool success = true; |
414 |
| - |
415 |
| - assembly ("memory-safe") { |
416 |
| - // we know _preBytes_offset is 0 |
417 |
| - let fslot := sload(_preBytes.slot) |
418 |
| - // Decode the length of the stored array like in concatStorage(). |
419 |
| - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) |
420 |
| - let mlength := mload(_postBytes) |
421 |
| - |
422 |
| - // if lengths don't match the arrays are not equal |
423 |
| - switch eq(slength, mlength) |
424 |
| - case 1 { |
425 |
| - // slength can contain both the length and contents of the array |
426 |
| - // if length < 32 bytes so let's prepare for that |
427 |
| - // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage |
428 |
| - if iszero(iszero(slength)) { |
429 |
| - switch lt(slength, 32) |
430 |
| - case 1 { |
431 |
| - // blank the last byte which is the length |
432 |
| - fslot := mul(div(fslot, 0x100), 0x100) |
433 |
| - |
434 |
| - if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { |
435 |
| - // unsuccess: |
436 |
| - success := 0 |
437 |
| - } |
438 |
| - } |
439 |
| - default { |
440 |
| - // cb is a circuit breaker in the for loop since there's |
441 |
| - // no said feature for inline assembly loops |
442 |
| - // cb = 1 - don't breaker |
443 |
| - // cb = 0 - break |
444 |
| - let cb := 1 |
445 |
| - |
446 |
| - // get the keccak hash to get the contents of the array |
447 |
| - mstore(0x0, _preBytes.slot) |
448 |
| - let sc := keccak256(0x0, 0x20) |
449 |
| - |
450 |
| - let mc := add(_postBytes, 0x20) |
451 |
| - let end := add(mc, mlength) |
452 |
| - |
453 |
| - // the next line is the loop condition: |
454 |
| - // while(uint256(mc < end) + cb == 2) |
455 |
| - for {} eq(add(lt(mc, end), cb), 2) { |
456 |
| - sc := add(sc, 1) |
457 |
| - mc := add(mc, 0x20) |
458 |
| - } { |
459 |
| - if iszero(eq(sload(sc), mload(mc))) { |
460 |
| - // unsuccess: |
461 |
| - success := 0 |
462 |
| - cb := 0 |
463 |
| - } |
464 |
| - } |
465 |
| - } |
466 |
| - } |
467 |
| - } |
468 |
| - default { |
469 |
| - // unsuccess: |
470 |
| - success := 0 |
471 |
| - } |
472 |
| - } |
473 |
| - |
474 |
| - return success; |
475 |
| - } |
476 | 158 | }
|
0 commit comments