Skip to content

Commit ceca52e

Browse files
authored
v0.0.3-ac.2
First pass of the review to v0.0.3-ac.1
2 parents 08f2a00 + 131af34 commit ceca52e

File tree

7 files changed

+204
-84
lines changed

7 files changed

+204
-84
lines changed

README.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Bytes tightly packed arrays utility library for ethereum contracts written.
66

77
The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
88

9-
Given this library has an all-internal collection of methods it doesn't make sense in having it reside in the mainnet. Instead it will only be available in EPM as an installable package.
9+
Given this library has an all-internal collection of methods it doesn't make sense having it reside in the mainnet. Instead it will only be available in EPM as an installable package.
1010

1111
## Usage
1212

@@ -49,6 +49,15 @@ TODOs:
4949
* Slicing directly from storage
5050
* Implement inline assembly functions for better readability
5151

52+
### Testing
53+
54+
This project uses Truffle for tests. Truffle's version of `solc` needs to be at least 0.4.19 for the contracts to compile. If you encounter compilation errors, try:
55+
56+
$ cd /usr/local/lib/node_modules/truffle
57+
$ npm install solc@latest
58+
59+
To run the tests, start a `testrpc` instance, then run `truffle test`.
60+
5261
## API
5362

5463
* `function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes)`

contracts/AssertBytes.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pragma solidity 0.4.19;
1+
pragma solidity ^0.4.19;
22

33

44
library AssertBytes {

contracts/BytesLib.sol

+116-45
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
1-
pragma solidity 0.4.19;
1+
pragma solidity ^0.4.19;
22

33

44
library BytesLib {
55
function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes) {
66
bytes memory tempBytes;
7-
7+
88
assembly {
9+
// Get a location of some free memory and store it in tempBytes as
10+
// Solidity does for memory variables.
911
tempBytes := mload(0x40)
1012

13+
// Store the length of the first bytes array at the beginning of
14+
// the memory for tempBytes.
1115
let length := mload(_preBytes)
1216
mstore(tempBytes, length)
13-
17+
18+
// Maintain a memory counter for the current write location in the
19+
// temp bytes array by adding the 32 bytes for the array length to
20+
// the starting location.
1421
let mc := add(tempBytes, 0x20)
22+
// Stop copying when the memory counter reaches the length of the
23+
// first bytes array.
1524
let end := add(mc, length)
16-
25+
1726
for {
27+
// Initialize a copy counter to the start of the _preBytes data,
28+
// 32 bytes into its memory.
1829
let cc := add(_preBytes, 0x20)
1930
} lt(mc, end) {
31+
// Increase both counters by 32 bytes each iteration.
2032
mc := add(mc, 0x20)
2133
cc := add(cc, 0x20)
2234
} {
35+
// Write the _preBytes data into the tempBytes memory 32 bytes
36+
// at a time.
2337
mstore(mc, mload(cc))
2438
}
25-
39+
40+
// Add the length of _postBytes to the current length of tempBytes
41+
// and store it as the new length in the first 32 bytes of the
42+
// tempBytes memory.
2643
length := mload(_postBytes)
2744
mstore(tempBytes, add(length, mload(tempBytes)))
28-
45+
46+
// Move the memory counter back from a multiple of 0x20 to the
47+
// actual end of the _preBytes data.
2948
mc := end
49+
// Stop copying when the memory counter reaches the new combined
50+
// length of the arrays.
3051
end := add(mc, length)
31-
52+
3253
for {
3354
let cc := add(_postBytes, 0x20)
3455
} lt(mc, end) {
@@ -37,21 +58,34 @@ library BytesLib {
3758
} {
3859
mstore(mc, mload(cc))
3960
}
40-
41-
//update free-memory pointer
42-
//allocating the array padded to 32 bytes like the compiler does now
43-
//make an additional check for a resulting zero-length array:
44-
// if (sub - end == 0) then end = end + 1
45-
mstore(0x40, and(add(add(end, iszero(sub(mc, end))), 31), not(31)))
61+
62+
// Update the free-memory pointer by padding our last write location
63+
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
64+
// next 32 byte block, then round down to the nearest multiple of
65+
// 32. If the sum of the length of the two arrays is zero then add
66+
// one before rounding down to leave a blank 32 bytes (the length block with 0).
67+
mstore(0x40, and(
68+
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
69+
not(31) // Round down to the nearest 32 bytes.
70+
))
4671
}
47-
72+
4873
return tempBytes;
4974
}
50-
75+
5176
function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
5277
assembly {
53-
// we know _preBytes_offset is 0
78+
// Read the first 32 bytes of _preBytes storage, which is the length
79+
// of the array. (We don't need to use the offset into the slot
80+
// because arrays use the entire slot.)
5481
let fslot := sload(_preBytes_slot)
82+
// Arrays of 31 bytes or less have an even value in their slot,
83+
// while longer arrays have an odd value. The actual length is
84+
// the slot divided by two for odd values, and the lowest order
85+
// byte divided by two for even values.
86+
// If the slot is even, bitwise and the slot with 255 and divide by
87+
// two to get the length. If the slot is odd, bitwise and the slot
88+
// with -1 and divide by two.
5589
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
5690
let mlength := mload(_postBytes)
5791
let newlength := add(slength, mlength)
@@ -60,13 +94,15 @@ library BytesLib {
6094
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
6195
switch add(lt(slength, 32), lt(newlength, 32))
6296
case 2 {
97+
// Since the new array still fits in the slot, we just need to
98+
// update the contents of the slot.
6399
// uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
64100
sstore(
65101
_preBytes_slot,
66102
// all the modifications to the slot are inside this
67103
// next block
68104
add(
69-
// we can just add to the slot contents because the
105+
// we can just add to the slot contents because the
70106
// bytes we want to change are the LSBs
71107
fslot,
72108
add(
@@ -89,18 +125,29 @@ library BytesLib {
89125
)
90126
}
91127
case 1 {
128+
// The stored value fits in the slot, but the combined value
129+
// will exceed it.
92130
// get the keccak hash to get the contents of the array
93131
mstore(0x0, _preBytes_slot)
94132
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
95-
133+
96134
// save new length
97135
sstore(_preBytes_slot, add(mul(newlength, 2), 1))
98-
136+
137+
// The contents of the _postBytes array start 32 bytes into
138+
// the structure. Our first read should obtain the `submod`
139+
// bytes that can fit into the unused space in the last word
140+
// of the stored array. To get this, we read 32 bytes starting
141+
// from `submod`, so the data we read overlaps with the array
142+
// contents by `submod` bytes. Masking the lowest-order
143+
// `submod` bytes allows us to add that value directly to the
144+
// stored value.
145+
99146
let submod := sub(32, slength)
100147
let mc := add(_postBytes, submod)
101-
let end := add(add(_postBytes, 0x20), mlength)
148+
let end := add(_postBytes, mlength)
102149
let mask := sub(exp(0x100, submod), 1)
103-
150+
104151
sstore(
105152
sc,
106153
add(
@@ -111,8 +158,8 @@ library BytesLib {
111158
and(mload(mc), mask)
112159
)
113160
)
114-
115-
for {
161+
162+
for {
116163
mc := add(mc, 0x20)
117164
sc := add(sc, 1)
118165
} lt(mc, end) {
@@ -121,50 +168,73 @@ library BytesLib {
121168
} {
122169
sstore(sc, mload(mc))
123170
}
171+
172+
mask := exp(0x100, sub(mc, end))
173+
174+
sstore(sc, mul(div(mload(mc), mask), mask))
124175
}
125176
default {
126177
// get the keccak hash to get the contents of the array
127178
mstore(0x0, _preBytes_slot)
179+
// Start copying to the last used word of the stored array.
128180
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
129-
181+
130182
// save new length
131183
sstore(_preBytes_slot, add(mul(newlength, 2), 1))
132-
184+
185+
// Copy over the first `submod` bytes of the new data as in
186+
// case 1 above.
133187
let slengthmod := mod(slength, 32)
188+
let mlengthmod := mod(mlength, 32)
134189
let submod := sub(32, slengthmod)
135190
let mc := add(_postBytes, submod)
136-
let end := add(mc, mlength)
191+
let end := add(_postBytes, mlength)
137192
let mask := sub(exp(0x100, submod), 1)
138-
193+
139194
sstore(sc, add(sload(sc), and(mload(mc), mask)))
140195

141196
for {
197+
sc := add(sc, 1)
142198
mc := add(mc, 0x20)
143199
} lt(mc, end) {
144200
sc := add(sc, 1)
145201
mc := add(mc, 0x20)
146202
} {
147203
sstore(sc, mload(mc))
148204
}
205+
206+
mask := exp(0x100, sub(mc, end))
207+
208+
sstore(sc, mul(div(mload(mc), mask), mask))
149209
}
150210
}
151211
}
152-
212+
153213
function slice(bytes _bytes, uint _start, uint _length) internal pure returns (bytes) {
154214
require(_bytes.length >= (_start + _length));
155-
215+
156216
bytes memory tempBytes;
157-
217+
158218
assembly {
159219
switch iszero(_length)
160220
case 0 {
221+
// Get a location of some free memory and store it in tempBytes as
222+
// Solidity does for memory variables.
161223
tempBytes := mload(0x40)
162-
224+
225+
// The first word of the slice result is potentially a partial
226+
// word read from the original array. To read it, we calculate
227+
// the length of that partial word and start copying that many
228+
// bytes into the array. The first word we copy will start with
229+
// data we don't care about, but the last `lengthmod` bytes will
230+
// land at the beginning of the contents of the new array. When
231+
// we're done copying, we overwrite the full first word with
232+
// the actual length of the slice.
163233
let lengthmod := and(_length, 31)
164-
234+
165235
let mc := add(tempBytes, lengthmod)
166236
let end := add(mc, _length)
167-
237+
168238
for {
169239
let cc := add(add(_bytes, lengthmod), _start)
170240
} lt(mc, end) {
@@ -173,9 +243,9 @@ library BytesLib {
173243
} {
174244
mstore(mc, mload(cc))
175245
}
176-
246+
177247
mstore(tempBytes, _length)
178-
248+
179249
//update free-memory pointer
180250
//allocating the array padded to 32 bytes like the compiler does now
181251
mstore(0x40, and(add(mc, 31), not(31)))
@@ -187,29 +257,29 @@ library BytesLib {
187257
mstore(0x40, add(tempBytes, 0x20))
188258
}
189259
}
190-
260+
191261
return tempBytes;
192262
}
193-
263+
194264
function toAddress(bytes _bytes, uint _start) internal pure returns (address) {
195265
require(_bytes.length >= (_start + 20));
196266
address tempAddress;
197-
267+
198268
assembly {
199269
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
200270
}
201-
271+
202272
return tempAddress;
203273
}
204-
274+
205275
function toUint(bytes _bytes, uint _start) internal pure returns (uint256) {
206276
require(_bytes.length >= (_start + 32));
207277
uint256 tempUint;
208-
278+
209279
assembly {
210280
tempUint := mload(add(add(_bytes, 0x20), _start))
211281
}
212-
282+
213283
return tempUint;
214284
}
215285

@@ -222,7 +292,7 @@ library BytesLib {
222292
// if lengths don't match the arrays are not equal
223293
switch eq(length, mload(_postBytes))
224294
case 1 {
225-
// cb is a circuit breaker in the for loop since there's
295+
// cb is a circuit breaker in the for loop since there's
226296
// no said feature for inline assembly loops
227297
// cb = 1 - don't breaker
228298
// cb = 0 - break
@@ -262,6 +332,7 @@ library BytesLib {
262332
assembly {
263333
// we know _preBytes_offset is 0
264334
let fslot := sload(_preBytes_slot)
335+
// Decode the length of the stored array like in concatStorage().
265336
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
266337
let mlength := mload(_postBytes)
267338

@@ -283,7 +354,7 @@ library BytesLib {
283354
}
284355
}
285356
default {
286-
// cb is a circuit breaker in the for loop since there's
357+
// cb is a circuit breaker in the for loop since there's
287358
// no said feature for inline assembly loops
288359
// cb = 1 - don't breaker
289360
// cb = 0 - break
@@ -292,7 +363,7 @@ library BytesLib {
292363
// get the keccak hash to get the contents of the array
293364
mstore(0x0, _preBytes_slot)
294365
let sc := keccak256(0x0, 0x20)
295-
366+
296367
let mc := add(_postBytes, 0x20)
297368
let end := add(mc, mlength)
298369

ethpm.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"package_name": "bytes",
3-
"version": "0.0.3",
3+
"version": "0.0.3-ac.2",
44
"description": "Solidity bytes tightly packed arrays utility library.",
55
"authors": [
66
"Gonçalo Sá <[email protected]>"

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "solidity-bytes-utils",
3-
"version": "0.0.3",
3+
"version": "0.0.3-ac.2",
44
"description": "Solidity bytes tightly packed arrays utility library.",
55
"main": "truffle.js",
66
"repository": {

0 commit comments

Comments
 (0)