| 491 | } |
| 492 | |
| 493 | void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const |
| 494 | { |
| 495 | solAssert(_type.location() == DataLocation::Storage, ""); |
| 496 | solAssert(_type.isDynamicallySized(), ""); |
| 497 | if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32) |
| 498 | solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); |
| 499 | |
| 500 | if (_type.isByteArrayOrString()) |
| 501 | { |
| 502 | m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1; |
| 503 | m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); |
| 504 | util::Whiskers code(R"({ |
| 505 | if iszero(length) { |
| 506 | mstore(0, <panicSelector>) |
| 507 | mstore(4, <emptyArrayPop>) |
| 508 | revert(0, 0x24) |
| 509 | } |
| 510 | switch gt(length, 31) |
| 511 | case 0 { |
| 512 | // short byte array |
| 513 | // Zero-out the suffix including the least significant byte. |
| 514 | let mask := sub(exp(0x100, sub(33, length)), 1) |
| 515 | length := sub(length, 1) |
| 516 | slot_value := or(and(not(mask), slot_value), mul(length, 2)) |
| 517 | } |
| 518 | case 1 { |
| 519 | // long byte array |
| 520 | mstore(0, ref) |
| 521 | let slot := keccak256(0, 0x20) |
| 522 | switch length |
| 523 | case 32 |
| 524 | { |
| 525 | let data := sload(slot) |
| 526 | sstore(slot, 0) |
| 527 | data := and(data, not(0xff)) |
| 528 | slot_value := or(data, 62) |
| 529 | } |
| 530 | default |
| 531 | { |
| 532 | let offset_inside_slot := and(sub(length, 1), 0x1f) |
| 533 | slot := add(slot, div(sub(length, 1), 32)) |
| 534 | let data := sload(slot) |
| 535 | |
| 536 | // Zero-out the suffix of the byte array by masking it. |
| 537 | // ((1<<(8 * (32 - offset))) - 1) |
| 538 | let mask := sub(exp(0x100, sub(32, offset_inside_slot)), 1) |
| 539 | data := and(not(mask), data) |
| 540 | sstore(slot, data) |
| 541 | |
| 542 | // Reduce the length by 1 |
| 543 | slot_value := sub(slot_value, 2) |
| 544 | } |
| 545 | } |
| 546 | sstore(ref, slot_value) |
| 547 | })"); |
| 548 | code("panicSelector", util::selectorFromSignatureU256("Panic(uint256)").str()); |
| 549 | code("emptyArrayPop", std::to_string(unsigned(util::PanicCode::EmptyArrayPop))); |
| 550 | m_context.appendInlineAssembly(code.render(), {"ref", "slot_value", "length"}); |
no test coverage detected