From the Docs, how does this operation create a new "memory end"?
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))
Context:
library GetCode {
function at(address _addr) public view returns (bytes memory o_code) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
o_code := mload(0x40)
// new "memory end" including padding
**mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))**
I can follow the operators and parse the hexadecimal, however I'm never sure if I should be performing these on underlying values or the memory address values themselves (eg. 0x20 + 0x40 = 0x60).