For example in BytesLib: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol#L348 (Edit: Note that the same pattern is used for lengths: 1, 2, 4, 8, 16, and 32)

    function toUint96(bytes memory _bytes, uint _start) internal  pure returns (uint96) {
        require(_bytes.length >= (_start + 12));
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))

        return tempUint;

1 Answer 1


Ok, add(_bytes, 0xc) adds 12 (0x0c) to the address of _bytes.

add(add(_bytes, 0xc), _start) adds 12 and _start to the address of _bytes.

mload(add(add(_bytes, 0xc), _start)) loads onto the stack 32 bytes starting from the address of _bytes increased by 12 and _start. In other words, it loads bytes at offsets (12 + _start) ... (43 + _start) counting from the address of _bytes.

tempUint := mload(add(add(_bytes, 0xc), _start)) converts just loaded 32-byte value into uint96. This means, that only the last 12 bytes of the laded value are assigned to tempUint: the bytes with offsets (32 + _start) ... (43 + _start).

Taking into accounts, that bytes.length occupies the first 32 bytes of _bytes, and _bytes contents starts from offset 32, we conclude, that tempUint will receive 12 bytes at offsets _start ... (11 + _start) of _bytes contents.

  • 1
    Thanks! It took your walk through for me to get it. Dec 25, 2019 at 23:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.