Copy byte array to storage.

This commit is contained in:
chriseth 2020-10-20 17:05:58 +02:00
parent 14d7ca49c0
commit 1066af3b98
10 changed files with 100 additions and 1 deletions

View File

@ -1468,7 +1468,8 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
""
);
solUnimplementedAssert(!_fromType.isByteArray(), "");
if (_fromType.isByteArray())
return copyByteArrayToStorageFunction(_fromType, _toType);
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
@ -1561,6 +1562,84 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
});
}
string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
{
solAssert(
*_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast<ReferenceType const&>(_toType),
""
);
solAssert(_fromType.isByteArray(), "");
solAssert(_toType.isByteArray(), "");
solUnimplementedAssert(!_fromType.dataStoredIn(DataLocation::Storage), "");
string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
return m_functionCollector.createFunction(functionName, [&](){
Whiskers templ(R"(
function <functionName>(slot, src<?fromCalldata>, len</fromCalldata>) {
let newLen := <arrayLength>(src<?fromCalldata>, len</fromCalldata>)
// Make sure array length is sane
if gt(newLen, 0xffffffffffffffff) { <panic>() }
let oldLen := <byteArrayLength>(sload(slot))
<?fromMemory>
src := add(src, 0x20)
</fromMemory>
// This is not needed in all branches.
let dstDataArea
if or(gt(oldLen, 31), gt(newLen, 31)) {
dstDataArea := <dstDataLocation>(slot)
}
if gt(oldLen, 31) {
// potentially truncate data
let deleteStart := add(dstDataArea, div(add(newLen, 31), 32))
if lt(newLen, 32) { deleteStart := dstDataArea }
<clearStorageRange>(deleteStart, add(dstDataArea, div(add(oldLen, 31), 32)))
}
switch gt(newLen, 31)
case 1 {
let loopEnd := and(newLen, not(0x1f))
let dstPtr := dstDataArea
let i := 0
for { } lt(i, loopEnd) { i := add(i, 32) } {
sstore(dstPtr, <readFromCalldataOrMemory>(add(src, i)))
dstPtr := add(dstPtr, 1)
}
if lt(loopEnd, newLen) {
let lastValue := <readFromCalldataOrMemory>(add(src, i))
sstore(dstPtr, <maskBytes>(lastValue, and(newLen, 0x1f)))
}
sstore(slot, add(mul(newLen, 2), 1))
}
default {
let value := 0
if newLen {
value := <readFromCalldataOrMemory>(src)
}
sstore(slot, <byteArrayCombineShort>(value, newLen))
}
}
)");
templ("functionName", functionName);
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
templ("fromCalldata", fromCalldata);
templ("arrayLength", arrayLengthFunction(_fromType));
templ("panic", panicFunction());
templ("byteArrayLength", extractByteArrayLengthFunction());
templ("dstDataLocation", arrayDataAreaFunction(_toType));
templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType()));
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*TypeProvider::uint256(), fromCalldata));
templ("maskBytes", maskBytesFunctionDynamic());
templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction());
return templ.render();
});
}
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
{
string functionName = "array_convert_length_to_size_" + _type.identifier();

View File

@ -190,6 +190,10 @@ public:
/// signature (to_slot, from_ptr) ->
std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// @returns the name of a function that will copy a byte array from calldata or memory to storage
/// signature (to_slot, from_ptr) ->
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
/// Returns the name of a function that will convert a given length to the
/// size in memory (number of storage slots or calldata/memory bytes) it
/// will require.

View File

@ -20,5 +20,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb

View File

@ -7,5 +7,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg"

View File

@ -14,5 +14,7 @@ contract c {
uint8(data[97]) == 97;
}
}
// ====
// compileViaYul: also
// ----
// test1() -> true

View File

@ -10,6 +10,8 @@ contract c {
bytes data;
}
// ====
// compileViaYul: also
// ----
// getLength() -> 0
// set(): 1, 2 -> true

View File

@ -6,5 +6,7 @@ contract C {
return s[0];
}
}
// ====
// compileViaYul: also
// ----
// f() -> "a"

View File

@ -5,5 +5,7 @@ contract C {
return s[0];
}
}
// ====
// compileViaYul: also
// ----
// f(bytes): 0x20, 0x08, "abcdefgh" -> "a"

View File

@ -32,5 +32,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f(bytes): 0x20, 0x5, "abcde" -> 0

View File

@ -8,6 +8,8 @@ contract C {
bytes savedData;
}
// ====
// compileViaYul: also
// ----
// save() -> 24 # empty copy loop #
// save(): "abcdefg" -> 24