From d5b73b12666699c442d182ee904fa8747b78fefd Mon Sep 17 00:00:00 2001 From: dk1a Date: Wed, 16 Aug 2023 13:06:12 +0300 Subject: [PATCH] refactor(store): optimize table libraries (#1303) --- .changeset/late-spies-cover.md | 5 + .../src/codegen/tables/NumberList.sol | 48 ++- .../src/codegen/tables/Dynamics1.sol | 292 +++++++++++++---- .../src/codegen/tables/Dynamics2.sol | 167 +++++++--- .../src/codegen/tables/Singleton.sol | 167 +++++++--- packages/store/gas-report.json | 14 +- .../store/src/codegen/tables/Callbacks.sol | 55 +++- packages/store/src/codegen/tables/Hooks.sol | 55 +++- packages/store/src/codegen/tables/Mixed.sol | 115 +++++-- .../src/codegen/tables/StoreMetadata.sol | 115 +++++-- packages/store/ts/codegen/field.ts | 50 +-- packages/store/ts/codegen/record.ts | 36 ++- packages/world/gas-report.json | 24 +- .../src/modules/core/tables/SystemHooks.sol | 55 +++- .../keysintable/tables/KeysInTable.sol | 306 ++++++++++++++---- .../keyswithvalue/tables/KeysWithValue.sol | 55 +++- packages/world/test/tables/AddressArray.sol | 55 +++- 17 files changed, 1205 insertions(+), 409 deletions(-) create mode 100644 .changeset/late-spies-cover.md diff --git a/.changeset/late-spies-cover.md b/.changeset/late-spies-cover.md new file mode 100644 index 0000000000..08f509a719 --- /dev/null +++ b/.changeset/late-spies-cover.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/store": patch +--- + +Optimize autogenerated table libraries diff --git a/e2e/packages/contracts/src/codegen/tables/NumberList.sol b/e2e/packages/contracts/src/codegen/tables/NumberList.sol index 01f3216eb8..fe99301d82 100644 --- a/e2e/packages/contracts/src/codegen/tables/NumberList.sol +++ b/e2e/packages/contracts/src/codegen/tables/NumberList.sol @@ -99,7 +99,9 @@ library NumberList { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } /** Get the length of value (using the specified store) */ @@ -107,23 +109,35 @@ library NumberList { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } - /** Get an item of value (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value + * (unchecked, returns invalid data if index overflows) + */ function getItem(uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } - /** Get an item of value (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItem(IStore _store, uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } /** Push an element to value */ @@ -154,18 +168,28 @@ library NumberList { _store.popFromField(_tableId, _keyTuple, 0, 4); } - /** Update an element of value at `_index` */ + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element))); + } } - /** Update an element of value (using the specified store) at `_index` */ + /** + * Update an element of value (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(IStore _store, uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 4, abi.encodePacked((_element))); + } } /** Tightly pack full data using this table's schema */ diff --git a/packages/cli/contracts/src/codegen/tables/Dynamics1.sol b/packages/cli/contracts/src/codegen/tables/Dynamics1.sol index d6b5ceb57d..19c20c4149 100644 --- a/packages/cli/contracts/src/codegen/tables/Dynamics1.sol +++ b/packages/cli/contracts/src/codegen/tables/Dynamics1.sol @@ -121,7 +121,9 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } /** Get the length of staticB32 (using the specified store) */ @@ -130,25 +132,44 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } - /** Get an item of staticB32 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticB32 + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticB32(bytes32 key, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 0, + getSchema(), + _index * 32, + (_index + 1) * 32 + ); + return (Bytes.slice32(_blob, 0)); + } } - /** Get an item of staticB32 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticB32 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticB32(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); + return (Bytes.slice32(_blob, 0)); + } } /** Push an element to staticB32 */ @@ -183,20 +204,30 @@ library Dynamics1 { _store.popFromField(_tableId, _keyTuple, 0, 32); } - /** Update an element of staticB32 at `_index` */ + /** + * Update an element of staticB32 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticB32(bytes32 key, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + } } - /** Update an element of staticB32 (using the specified store) at `_index` */ + /** + * Update an element of staticB32 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticB32(IStore _store, bytes32 key, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + } } /** Get staticI32 */ @@ -239,7 +270,9 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } /** Get the length of staticI32 (using the specified store) */ @@ -248,25 +281,37 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } - /** Get an item of staticI32 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticI32 + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticI32(bytes32 key, uint256 _index) internal view returns (int32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); + return (int32(uint32(Bytes.slice4(_blob, 0)))); + } } - /** Get an item of staticI32 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticI32 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticI32(IStore _store, bytes32 key, uint256 _index) internal view returns (int32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); - return (int32(uint32(Bytes.slice4(_blob, 0)))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); + return (int32(uint32(Bytes.slice4(_blob, 0)))); + } } /** Push an element to staticI32 */ @@ -301,20 +346,30 @@ library Dynamics1 { _store.popFromField(_tableId, _keyTuple, 1, 4); } - /** Update an element of staticI32 at `_index` */ + /** + * Update an element of staticI32 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticI32(bytes32 key, uint256 _index, int32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + } } - /** Update an element of staticI32 (using the specified store) at `_index` */ + /** + * Update an element of staticI32 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticI32(IStore _store, bytes32 key, uint256 _index, int32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + } } /** Get staticU128 */ @@ -357,7 +412,9 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 16; + unchecked { + return _byteLength / 16; + } } /** Get the length of staticU128 (using the specified store) */ @@ -366,25 +423,44 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 16; + unchecked { + return _byteLength / 16; + } } - /** Get an item of staticU128 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticU128 + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticU128(bytes32 key, uint256 _index) internal view returns (uint128) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 16, (_index + 1) * 16); - return (uint128(Bytes.slice16(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 2, + getSchema(), + _index * 16, + (_index + 1) * 16 + ); + return (uint128(Bytes.slice16(_blob, 0))); + } } - /** Get an item of staticU128 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticU128 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticU128(IStore _store, bytes32 key, uint256 _index) internal view returns (uint128) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 16, (_index + 1) * 16); - return (uint128(Bytes.slice16(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 16, (_index + 1) * 16); + return (uint128(Bytes.slice16(_blob, 0))); + } } /** Push an element to staticU128 */ @@ -419,20 +495,30 @@ library Dynamics1 { _store.popFromField(_tableId, _keyTuple, 2, 16); } - /** Update an element of staticU128 at `_index` */ + /** + * Update an element of staticU128 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticU128(bytes32 key, uint256 _index, uint128 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element))); + } } - /** Update an element of staticU128 (using the specified store) at `_index` */ + /** + * Update an element of staticU128 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticU128(IStore _store, bytes32 key, uint256 _index, uint128 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 2, _index * 16, abi.encodePacked((_element))); + } } /** Get staticAddrs */ @@ -475,7 +561,9 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } /** Get the length of staticAddrs (using the specified store) */ @@ -484,25 +572,44 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } - /** Get an item of staticAddrs (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticAddrs + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticAddrs(bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 3, + getSchema(), + _index * 20, + (_index + 1) * 20 + ); + return (address(Bytes.slice20(_blob, 0))); + } } - /** Get an item of staticAddrs (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticAddrs (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticAddrs(IStore _store, bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 20, (_index + 1) * 20); + return (address(Bytes.slice20(_blob, 0))); + } } /** Push an element to staticAddrs */ @@ -537,20 +644,30 @@ library Dynamics1 { _store.popFromField(_tableId, _keyTuple, 3, 20); } - /** Update an element of staticAddrs at `_index` */ + /** + * Update an element of staticAddrs at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticAddrs(bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element))); + } } - /** Update an element of staticAddrs (using the specified store) at `_index` */ + /** + * Update an element of staticAddrs (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticAddrs(IStore _store, bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 3, _index * 20, abi.encodePacked((_element))); + } } /** Get staticBools */ @@ -593,7 +710,9 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } /** Get the length of staticBools (using the specified store) */ @@ -602,25 +721,37 @@ library Dynamics1 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } - /** Get an item of staticBools (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticBools + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticBools(bytes32 key, uint256 _index) internal view returns (bool) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 4, getSchema(), _index * 1, (_index + 1) * 1); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 4, getSchema(), _index * 1, (_index + 1) * 1); + return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + } } - /** Get an item of staticBools (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of staticBools (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemStaticBools(IStore _store, bytes32 key, uint256 _index) internal view returns (bool) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, getSchema(), _index * 1, (_index + 1) * 1); - return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, getSchema(), _index * 1, (_index + 1) * 1); + return (_toBool(uint8(Bytes.slice1(_blob, 0)))); + } } /** Push an element to staticBools */ @@ -655,20 +786,30 @@ library Dynamics1 { _store.popFromField(_tableId, _keyTuple, 4, 1); } - /** Update an element of staticBools at `_index` */ + /** + * Update an element of staticBools at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticBools(bytes32 key, uint256 _index, bool _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element))); + } } - /** Update an element of staticBools (using the specified store) at `_index` */ + /** + * Update an element of staticBools (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStaticBools(IStore _store, bytes32 key, uint256 _index, bool _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 4, _index * 1, abi.encodePacked((_element))); + } } /** Get the full data */ @@ -734,35 +875,46 @@ library Dynamics1 { set(_store, key, _table.staticB32, _table.staticI32, _table.staticU128, _table.staticAddrs, _table.staticBools); } - /** Decode the tightly packed blob using this table's schema */ + /** + * Decode the tightly packed blob using this table's schema. + * Undefined behaviour for invalid blobs. + */ function decode(bytes memory _blob) internal pure returns (Dynamics1Data memory _table) { // 0 is the total byte length of static data PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, 0)); // Store trims the blob if dynamic fields are all empty if (_blob.length > 0) { - uint256 _start; // skip static data length + dynamic lengths word - uint256 _end = 32; - - _start = _end; - _end += _encodedLengths.atIndex(0); + uint256 _start = 32; + uint256 _end; + unchecked { + _end = 32 + _encodedLengths.atIndex(0); + } _table.staticB32 = toStaticArray_bytes32_1(SliceLib.getSubslice(_blob, _start, _end).decodeArray_bytes32()); _start = _end; - _end += _encodedLengths.atIndex(1); + unchecked { + _end += _encodedLengths.atIndex(1); + } _table.staticI32 = toStaticArray_int32_2(SliceLib.getSubslice(_blob, _start, _end).decodeArray_int32()); _start = _end; - _end += _encodedLengths.atIndex(2); + unchecked { + _end += _encodedLengths.atIndex(2); + } _table.staticU128 = toStaticArray_uint128_3(SliceLib.getSubslice(_blob, _start, _end).decodeArray_uint128()); _start = _end; - _end += _encodedLengths.atIndex(3); + unchecked { + _end += _encodedLengths.atIndex(3); + } _table.staticAddrs = toStaticArray_address_4(SliceLib.getSubslice(_blob, _start, _end).decodeArray_address()); _start = _end; - _end += _encodedLengths.atIndex(4); + unchecked { + _end += _encodedLengths.atIndex(4); + } _table.staticBools = toStaticArray_bool_5(SliceLib.getSubslice(_blob, _start, _end).decodeArray_bool()); } } diff --git a/packages/cli/contracts/src/codegen/tables/Dynamics2.sol b/packages/cli/contracts/src/codegen/tables/Dynamics2.sol index 7ad7f58d48..2de0e96b8b 100644 --- a/packages/cli/contracts/src/codegen/tables/Dynamics2.sol +++ b/packages/cli/contracts/src/codegen/tables/Dynamics2.sol @@ -115,7 +115,9 @@ library Dynamics2 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 8; + unchecked { + return _byteLength / 8; + } } /** Get the length of u64 (using the specified store) */ @@ -124,25 +126,37 @@ library Dynamics2 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 8; + unchecked { + return _byteLength / 8; + } } - /** Get an item of u64 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of u64 + * (unchecked, returns invalid data if index overflows) + */ function getItemU64(bytes32 key, uint256 _index) internal view returns (uint64) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 8, (_index + 1) * 8); - return (uint64(Bytes.slice8(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 8, (_index + 1) * 8); + return (uint64(Bytes.slice8(_blob, 0))); + } } - /** Get an item of u64 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of u64 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemU64(IStore _store, bytes32 key, uint256 _index) internal view returns (uint64) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 8, (_index + 1) * 8); - return (uint64(Bytes.slice8(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 8, (_index + 1) * 8); + return (uint64(Bytes.slice8(_blob, 0))); + } } /** Push an element to u64 */ @@ -177,20 +191,30 @@ library Dynamics2 { _store.popFromField(_tableId, _keyTuple, 0, 8); } - /** Update an element of u64 at `_index` */ + /** + * Update an element of u64 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateU64(bytes32 key, uint256 _index, uint64 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element))); + } } - /** Update an element of u64 (using the specified store) at `_index` */ + /** + * Update an element of u64 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateU64(IStore _store, bytes32 key, uint256 _index, uint64 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 8, abi.encodePacked((_element))); + } } /** Get str */ @@ -233,7 +257,9 @@ library Dynamics2 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } /** Get the length of str (using the specified store) */ @@ -242,25 +268,37 @@ library Dynamics2 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } - /** Get an item of str (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of str + * (unchecked, returns invalid data if index overflows) + */ function getItemStr(bytes32 key, uint256 _index) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); - return (string(_blob)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); + return (string(_blob)); + } } - /** Get an item of str (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of str (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemStr(IStore _store, bytes32 key, uint256 _index) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); - return (string(_blob)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); + return (string(_blob)); + } } /** Push a slice to str */ @@ -295,20 +333,30 @@ library Dynamics2 { _store.popFromField(_tableId, _keyTuple, 1, 1); } - /** Update a slice of str at `_index` */ + /** + * Update a slice of str at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStr(bytes32 key, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + } } - /** Update a slice of str (using the specified store) at `_index` */ + /** + * Update a slice of str (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateStr(IStore _store, bytes32 key, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + } } /** Get b */ @@ -351,7 +399,9 @@ library Dynamics2 { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } /** Get the length of b (using the specified store) */ @@ -360,25 +410,37 @@ library Dynamics2 { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } - /** Get an item of b (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of b + * (unchecked, returns invalid data if index overflows) + */ function getItemB(bytes32 key, uint256 _index) internal view returns (bytes memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 1, (_index + 1) * 1); - return (bytes(_blob)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 1, (_index + 1) * 1); + return (bytes(_blob)); + } } - /** Get an item of b (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of b (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemB(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 1, (_index + 1) * 1); - return (bytes(_blob)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 1, (_index + 1) * 1); + return (bytes(_blob)); + } } /** Push a slice to b */ @@ -413,20 +475,30 @@ library Dynamics2 { _store.popFromField(_tableId, _keyTuple, 2, 1); } - /** Update a slice of b at `_index` */ + /** + * Update a slice of b at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateB(bytes32 key, uint256 _index, bytes memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice))); + } } - /** Update a slice of b (using the specified store) at `_index` */ + /** + * Update a slice of b (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateB(IStore _store, bytes32 key, uint256 _index, bytes memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 2, _index * 1, bytes((_slice))); + } } /** Get the full data */ @@ -477,27 +549,34 @@ library Dynamics2 { set(_store, key, _table.u64, _table.str, _table.b); } - /** Decode the tightly packed blob using this table's schema */ + /** + * Decode the tightly packed blob using this table's schema. + * Undefined behaviour for invalid blobs. + */ function decode(bytes memory _blob) internal pure returns (Dynamics2Data memory _table) { // 0 is the total byte length of static data PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, 0)); // Store trims the blob if dynamic fields are all empty if (_blob.length > 0) { - uint256 _start; // skip static data length + dynamic lengths word - uint256 _end = 32; - - _start = _end; - _end += _encodedLengths.atIndex(0); + uint256 _start = 32; + uint256 _end; + unchecked { + _end = 32 + _encodedLengths.atIndex(0); + } _table.u64 = (SliceLib.getSubslice(_blob, _start, _end).decodeArray_uint64()); _start = _end; - _end += _encodedLengths.atIndex(1); + unchecked { + _end += _encodedLengths.atIndex(1); + } _table.str = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); _start = _end; - _end += _encodedLengths.atIndex(2); + unchecked { + _end += _encodedLengths.atIndex(2); + } _table.b = (bytes(SliceLib.getSubslice(_blob, _start, _end).toBytes())); } } diff --git a/packages/cli/contracts/src/codegen/tables/Singleton.sol b/packages/cli/contracts/src/codegen/tables/Singleton.sol index 8939040967..30fac036d9 100644 --- a/packages/cli/contracts/src/codegen/tables/Singleton.sol +++ b/packages/cli/contracts/src/codegen/tables/Singleton.sol @@ -135,7 +135,9 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } /** Get the length of v2 (using the specified store) */ @@ -143,23 +145,35 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } - /** Get an item of v2 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of v2 + * (unchecked, returns invalid data if index overflows) + */ function getItemV2(uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } - /** Get an item of v2 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of v2 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemV2(IStore _store, uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } /** Push an element to v2 */ @@ -190,18 +204,28 @@ library Singleton { _store.popFromField(_tableId, _keyTuple, 1, 4); } - /** Update an element of v2 at `_index` */ + /** + * Update an element of v2 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateV2(uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + } } - /** Update an element of v2 (using the specified store) at `_index` */ + /** + * Update an element of v2 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateV2(IStore _store, uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 1, _index * 4, abi.encodePacked((_element))); + } } /** Get v3 */ @@ -239,7 +263,9 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } /** Get the length of v3 (using the specified store) */ @@ -247,23 +273,35 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } - /** Get an item of v3 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of v3 + * (unchecked, returns invalid data if index overflows) + */ function getItemV3(uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } - /** Get an item of v3 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of v3 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemV3(IStore _store, uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } /** Push an element to v3 */ @@ -294,18 +332,28 @@ library Singleton { _store.popFromField(_tableId, _keyTuple, 2, 4); } - /** Update an element of v3 at `_index` */ + /** + * Update an element of v3 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateV3(uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + } } - /** Update an element of v3 (using the specified store) at `_index` */ + /** + * Update an element of v3 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateV3(IStore _store, uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + } } /** Get v4 */ @@ -343,7 +391,9 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } /** Get the length of v4 (using the specified store) */ @@ -351,23 +401,35 @@ library Singleton { bytes32[] memory _keyTuple = new bytes32[](0); uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } - /** Get an item of v4 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of v4 + * (unchecked, returns invalid data if index overflows) + */ function getItemV4(uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } - /** Get an item of v4 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of v4 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemV4(IStore _store, uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](0); - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } /** Push an element to v4 */ @@ -398,18 +460,28 @@ library Singleton { _store.popFromField(_tableId, _keyTuple, 3, 4); } - /** Update an element of v4 at `_index` */ + /** + * Update an element of v4 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateV4(uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element))); + } } - /** Update an element of v4 (using the specified store) at `_index` */ + /** + * Update an element of v4 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateV4(IStore _store, uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](0); - _store.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 3, _index * 4, abi.encodePacked((_element))); + } } /** Get the full data */ @@ -448,7 +520,10 @@ library Singleton { _store.setRecord(_tableId, _keyTuple, _data); } - /** Decode the tightly packed blob using this table's schema */ + /** + * Decode the tightly packed blob using this table's schema. + * Undefined behaviour for invalid blobs. + */ function decode( bytes memory _blob ) internal pure returns (int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) { @@ -459,20 +534,24 @@ library Singleton { // Store trims the blob if dynamic fields are all empty if (_blob.length > 32) { - uint256 _start; // skip static data length + dynamic lengths word - uint256 _end = 64; - - _start = _end; - _end += _encodedLengths.atIndex(0); + uint256 _start = 64; + uint256 _end; + unchecked { + _end = 64 + _encodedLengths.atIndex(0); + } v2 = toStaticArray_uint32_2(SliceLib.getSubslice(_blob, _start, _end).decodeArray_uint32()); _start = _end; - _end += _encodedLengths.atIndex(1); + unchecked { + _end += _encodedLengths.atIndex(1); + } v3 = toStaticArray_uint32_2(SliceLib.getSubslice(_blob, _start, _end).decodeArray_uint32()); _start = _end; - _end += _encodedLengths.atIndex(2); + unchecked { + _end += _encodedLengths.atIndex(2); + } v4 = toStaticArray_uint32_1(SliceLib.getSubslice(_blob, _start, _end).decodeArray_uint32()); } } diff --git a/packages/store/gas-report.json b/packages/store/gas-report.json index 65cd41e534..0cb345757d 100644 --- a/packages/store/gas-report.json +++ b/packages/store/gas-report.json @@ -81,7 +81,7 @@ "file": "test/Gas.t.sol", "test": "testCompareAbiEncodeVsCustom", "name": "custom decode", - "gasUsed": 2707 + "gasUsed": 2571 }, { "file": "test/Gas.t.sol", @@ -267,7 +267,7 @@ "file": "test/Mixed.t.sol", "test": "testSetAndGet", "name": "get record from Mixed", - "gasUsed": 12408 + "gasUsed": 12272 }, { "file": "test/PackedCounter.t.sol", @@ -771,7 +771,7 @@ "file": "test/StoreMetadata.t.sol", "test": "testSetAndGet", "name": "get record from StoreMetadataTable", - "gasUsed": 11548 + "gasUsed": 11412 }, { "file": "test/StoreSwitch.t.sol", @@ -837,7 +837,7 @@ "file": "test/tables/Hooks.t.sol", "test": "testTable", "name": "Hooks: update 1 element (warm)", - "gasUsed": 17242 + "gasUsed": 17170 }, { "file": "test/tables/Hooks.t.sol", @@ -867,13 +867,13 @@ "file": "test/tables/HooksColdLoad.t.sol", "test": "testGetItem", "name": "Hooks: get 1 element (cold)", - "gasUsed": 7786 + "gasUsed": 7560 }, { "file": "test/tables/HooksColdLoad.t.sol", "test": "testLength", "name": "Hooks: get length (cold)", - "gasUsed": 7536 + "gasUsed": 7482 }, { "file": "test/tables/HooksColdLoad.t.sol", @@ -885,7 +885,7 @@ "file": "test/tables/HooksColdLoad.t.sol", "test": "testUpdate", "name": "Hooks: update 1 element (cold)", - "gasUsed": 28755 + "gasUsed": 28683 }, { "file": "test/tightcoder/DecodeSlice.t.sol", diff --git a/packages/store/src/codegen/tables/Callbacks.sol b/packages/store/src/codegen/tables/Callbacks.sol index 2383ca033d..e3983aded6 100644 --- a/packages/store/src/codegen/tables/Callbacks.sol +++ b/packages/store/src/codegen/tables/Callbacks.sol @@ -105,7 +105,9 @@ library Callbacks { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 24; + unchecked { + return _byteLength / 24; + } } /** Get the length of value (using the specified store) */ @@ -114,25 +116,44 @@ library Callbacks { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 24; + unchecked { + return _byteLength / 24; + } } - /** Get an item of value (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value + * (unchecked, returns invalid data if index overflows) + */ function getItem(bytes32 key, uint256 _index) internal view returns (bytes24) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 24, (_index + 1) * 24); - return (Bytes.slice24(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 0, + getSchema(), + _index * 24, + (_index + 1) * 24 + ); + return (Bytes.slice24(_blob, 0)); + } } - /** Get an item of value (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItem(IStore _store, bytes32 key, uint256 _index) internal view returns (bytes24) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 24, (_index + 1) * 24); - return (Bytes.slice24(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 24, (_index + 1) * 24); + return (Bytes.slice24(_blob, 0)); + } } /** Push an element to value */ @@ -167,20 +188,30 @@ library Callbacks { _store.popFromField(_tableId, _keyTuple, 0, 24); } - /** Update an element of value at `_index` */ + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(bytes32 key, uint256 _index, bytes24 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element))); + } } - /** Update an element of value (using the specified store) at `_index` */ + /** + * Update an element of value (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(IStore _store, bytes32 key, uint256 _index, bytes24 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 24, abi.encodePacked((_element))); + } } /** Tightly pack full data using this table's schema */ diff --git a/packages/store/src/codegen/tables/Hooks.sol b/packages/store/src/codegen/tables/Hooks.sol index 2e0635f124..4708636baa 100644 --- a/packages/store/src/codegen/tables/Hooks.sol +++ b/packages/store/src/codegen/tables/Hooks.sol @@ -105,7 +105,9 @@ library Hooks { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } /** Get the length of value (using the specified store) */ @@ -114,25 +116,44 @@ library Hooks { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } - /** Get an item of value (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value + * (unchecked, returns invalid data if index overflows) + */ function getItem(bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 0, + getSchema(), + _index * 20, + (_index + 1) * 20 + ); + return (address(Bytes.slice20(_blob, 0))); + } } - /** Get an item of value (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItem(IStore _store, bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); + return (address(Bytes.slice20(_blob, 0))); + } } /** Push an element to value */ @@ -167,20 +188,30 @@ library Hooks { _store.popFromField(_tableId, _keyTuple, 0, 20); } - /** Update an element of value at `_index` */ + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + } } - /** Update an element of value (using the specified store) at `_index` */ + /** + * Update an element of value (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(IStore _store, bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + } } /** Tightly pack full data using this table's schema */ diff --git a/packages/store/src/codegen/tables/Mixed.sol b/packages/store/src/codegen/tables/Mixed.sol index e8c0803d94..23ce59dbc2 100644 --- a/packages/store/src/codegen/tables/Mixed.sol +++ b/packages/store/src/codegen/tables/Mixed.sol @@ -186,7 +186,9 @@ library Mixed { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } /** Get the length of a32 (using the specified store) */ @@ -195,25 +197,37 @@ library Mixed { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 4; + unchecked { + return _byteLength / 4; + } } - /** Get an item of a32 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of a32 + * (unchecked, returns invalid data if index overflows) + */ function getItemA32(bytes32 key, uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } - /** Get an item of a32 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of a32 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemA32(IStore _store, bytes32 key, uint256 _index) internal view returns (uint32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); - return (uint32(Bytes.slice4(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 4, (_index + 1) * 4); + return (uint32(Bytes.slice4(_blob, 0))); + } } /** Push an element to a32 */ @@ -248,20 +262,30 @@ library Mixed { _store.popFromField(_tableId, _keyTuple, 2, 4); } - /** Update an element of a32 at `_index` */ + /** + * Update an element of a32 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateA32(bytes32 key, uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + } } - /** Update an element of a32 (using the specified store) at `_index` */ + /** + * Update an element of a32 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateA32(IStore _store, bytes32 key, uint256 _index, uint32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 2, _index * 4, abi.encodePacked((_element))); + } } /** Get s */ @@ -304,7 +328,9 @@ library Mixed { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } /** Get the length of s (using the specified store) */ @@ -313,25 +339,37 @@ library Mixed { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } - /** Get an item of s (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of s + * (unchecked, returns invalid data if index overflows) + */ function getItemS(bytes32 key, uint256 _index) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 1, (_index + 1) * 1); - return (string(_blob)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 1, (_index + 1) * 1); + return (string(_blob)); + } } - /** Get an item of s (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of s (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemS(IStore _store, bytes32 key, uint256 _index) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 1, (_index + 1) * 1); - return (string(_blob)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 1, (_index + 1) * 1); + return (string(_blob)); + } } /** Push a slice to s */ @@ -366,20 +404,30 @@ library Mixed { _store.popFromField(_tableId, _keyTuple, 3, 1); } - /** Update a slice of s at `_index` */ + /** + * Update a slice of s at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateS(bytes32 key, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice))); + } } - /** Update a slice of s (using the specified store) at `_index` */ + /** + * Update a slice of s (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateS(IStore _store, bytes32 key, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 3, _index * 1, bytes((_slice))); + } } /** Get the full data */ @@ -430,7 +478,10 @@ library Mixed { set(_store, key, _table.u32, _table.u128, _table.a32, _table.s); } - /** Decode the tightly packed blob using this table's schema */ + /** + * Decode the tightly packed blob using this table's schema. + * Undefined behaviour for invalid blobs. + */ function decode(bytes memory _blob) internal pure returns (MixedData memory _table) { // 20 is the total byte length of static data PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, 20)); @@ -441,16 +492,18 @@ library Mixed { // Store trims the blob if dynamic fields are all empty if (_blob.length > 20) { - uint256 _start; // skip static data length + dynamic lengths word - uint256 _end = 52; - - _start = _end; - _end += _encodedLengths.atIndex(0); + uint256 _start = 52; + uint256 _end; + unchecked { + _end = 52 + _encodedLengths.atIndex(0); + } _table.a32 = (SliceLib.getSubslice(_blob, _start, _end).decodeArray_uint32()); _start = _end; - _end += _encodedLengths.atIndex(1); + unchecked { + _end += _encodedLengths.atIndex(1); + } _table.s = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); } } diff --git a/packages/store/src/codegen/tables/StoreMetadata.sol b/packages/store/src/codegen/tables/StoreMetadata.sol index 1fdcb7394e..02012dce67 100644 --- a/packages/store/src/codegen/tables/StoreMetadata.sol +++ b/packages/store/src/codegen/tables/StoreMetadata.sol @@ -112,7 +112,9 @@ library StoreMetadata { _keyTuple[0] = tableId; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } /** Get the length of tableName (using the specified store) */ @@ -121,25 +123,37 @@ library StoreMetadata { _keyTuple[0] = tableId; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } - /** Get an item of tableName (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of tableName + * (unchecked, returns invalid data if index overflows) + */ function getItemTableName(bytes32 tableId, uint256 _index) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 1, (_index + 1) * 1); - return (string(_blob)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 1, (_index + 1) * 1); + return (string(_blob)); + } } - /** Get an item of tableName (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of tableName (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemTableName(IStore _store, bytes32 tableId, uint256 _index) internal view returns (string memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 1, (_index + 1) * 1); - return (string(_blob)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 1, (_index + 1) * 1); + return (string(_blob)); + } } /** Push a slice to tableName */ @@ -174,20 +188,30 @@ library StoreMetadata { _store.popFromField(_tableId, _keyTuple, 0, 1); } - /** Update a slice of tableName at `_index` */ + /** + * Update a slice of tableName at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateTableName(bytes32 tableId, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 1, bytes((_slice))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 1, bytes((_slice))); + } } - /** Update a slice of tableName (using the specified store) at `_index` */ + /** + * Update a slice of tableName (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateTableName(IStore _store, bytes32 tableId, uint256 _index, string memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.updateInField(_tableId, _keyTuple, 0, _index * 1, bytes((_slice))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 1, bytes((_slice))); + } } /** Get abiEncodedFieldNames */ @@ -233,7 +257,9 @@ library StoreMetadata { _keyTuple[0] = tableId; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } /** Get the length of abiEncodedFieldNames (using the specified store) */ @@ -242,19 +268,29 @@ library StoreMetadata { _keyTuple[0] = tableId; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 1; + unchecked { + return _byteLength / 1; + } } - /** Get an item of abiEncodedFieldNames (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of abiEncodedFieldNames + * (unchecked, returns invalid data if index overflows) + */ function getItemAbiEncodedFieldNames(bytes32 tableId, uint256 _index) internal view returns (bytes memory) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); - return (bytes(_blob)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); + return (bytes(_blob)); + } } - /** Get an item of abiEncodedFieldNames (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of abiEncodedFieldNames (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemAbiEncodedFieldNames( IStore _store, bytes32 tableId, @@ -263,8 +299,10 @@ library StoreMetadata { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); - return (bytes(_blob)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 1, (_index + 1) * 1); + return (bytes(_blob)); + } } /** Push a slice to abiEncodedFieldNames */ @@ -299,20 +337,30 @@ library StoreMetadata { _store.popFromField(_tableId, _keyTuple, 1, 1); } - /** Update a slice of abiEncodedFieldNames at `_index` */ + /** + * Update a slice of abiEncodedFieldNames at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateAbiEncodedFieldNames(bytes32 tableId, uint256 _index, bytes memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + } } - /** Update a slice of abiEncodedFieldNames (using the specified store) at `_index` */ + /** + * Update a slice of abiEncodedFieldNames (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateAbiEncodedFieldNames(IStore _store, bytes32 tableId, uint256 _index, bytes memory _slice) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = tableId; - _store.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 1, _index * 1, bytes((_slice))); + } } /** Get the full data */ @@ -363,23 +411,28 @@ library StoreMetadata { set(_store, tableId, _table.tableName, _table.abiEncodedFieldNames); } - /** Decode the tightly packed blob using this table's schema */ + /** + * Decode the tightly packed blob using this table's schema. + * Undefined behaviour for invalid blobs. + */ function decode(bytes memory _blob) internal pure returns (StoreMetadataData memory _table) { // 0 is the total byte length of static data PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, 0)); // Store trims the blob if dynamic fields are all empty if (_blob.length > 0) { - uint256 _start; // skip static data length + dynamic lengths word - uint256 _end = 32; - - _start = _end; - _end += _encodedLengths.atIndex(0); + uint256 _start = 32; + uint256 _end; + unchecked { + _end = 32 + _encodedLengths.atIndex(0); + } _table.tableName = (string(SliceLib.getSubslice(_blob, _start, _end).toBytes())); _start = _end; - _end += _encodedLengths.atIndex(1); + unchecked { + _end += _encodedLengths.atIndex(1); + } _table.abiEncodedFieldNames = (bytes(SliceLib.getSubslice(_blob, _start, _end).toBytes())); } } diff --git a/packages/store/ts/codegen/field.ts b/packages/store/ts/codegen/field.ts index 24b8f94161..7b3c5a5758 100644 --- a/packages/store/ts/codegen/field.ts +++ b/packages/store/ts/codegen/field.ts @@ -61,7 +61,9 @@ export function renderFieldMethods(options: RenderTableOptions) { ])}) internal view returns (uint256) { ${_keyTupleDefinition} uint256 _byteLength = ${_store}.getFieldLength(_tableId, _keyTuple, ${schemaIndex}, getSchema()); - return _byteLength / ${portionData.elementLength}; + unchecked { + return _byteLength / ${portionData.elementLength}; + } } ` ); @@ -69,7 +71,10 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, (_typedStore, _store, _commentSuffix) => ` - /** Get an item of ${field.name}${_commentSuffix} (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of ${field.name}${_commentSuffix} + * (unchecked, returns invalid data if index overflows) + */ function getItem${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, @@ -77,15 +82,17 @@ export function renderFieldMethods(options: RenderTableOptions) { "uint256 _index", ])}) internal view returns (${portionData.typeWithLocation}) { ${_keyTupleDefinition} - bytes memory _blob = ${_store}.getFieldSlice( - _tableId, - _keyTuple, - ${schemaIndex}, - getSchema(), - _index * ${portionData.elementLength}, - (_index + 1) * ${portionData.elementLength} - ); - return ${portionData.decoded}; + unchecked { + bytes memory _blob = ${_store}.getFieldSlice( + _tableId, + _keyTuple, + ${schemaIndex}, + getSchema(), + _index * ${portionData.elementLength}, + (_index + 1) * ${portionData.elementLength} + ); + return ${portionData.decoded}; + } } ` ); @@ -124,7 +131,10 @@ export function renderFieldMethods(options: RenderTableOptions) { result += renderWithStore( storeArgument, (_typedStore, _store, _commentSuffix) => ` - /** Update ${portionData.title} of ${field.name}${_commentSuffix} at \`_index\` */ + /** + * Update ${portionData.title} of ${field.name}${_commentSuffix} at \`_index\` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update${field.methodNameSuffix}(${renderArguments([ _typedStore, _typedTableId, @@ -133,13 +143,15 @@ export function renderFieldMethods(options: RenderTableOptions) { `${portionData.typeWithLocation} ${portionData.name}`, ])}) internal { ${_keyTupleDefinition} - ${_store}.updateInField( - _tableId, - _keyTuple, - ${schemaIndex}, - _index * ${portionData.elementLength}, - ${portionData.encoded} - ); + unchecked { + ${_store}.updateInField( + _tableId, + _keyTuple, + ${schemaIndex}, + _index * ${portionData.elementLength}, + ${portionData.encoded} + ); + } } ` ); diff --git a/packages/store/ts/codegen/record.ts b/packages/store/ts/codegen/record.ts index 342415c65a..73f9865113 100644 --- a/packages/store/ts/codegen/record.ts +++ b/packages/store/ts/codegen/record.ts @@ -94,7 +94,10 @@ function renderDecodeFunction({ structName, fields, staticFields, dynamicFields const totalStaticLength = staticFields.reduce((acc, { staticByteLength }) => acc + staticByteLength, 0); // decode static (optionally) and dynamic data return ` - /** Decode the tightly packed blob using this table's schema */ + /** + * Decode the tightly packed blob using this table's schema. + * Undefined behaviour for invalid blobs. + */ function decode(bytes memory _blob) internal pure returns (${renderedDecodedRecord}) { // ${totalStaticLength} is the total byte length of static data PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, ${totalStaticLength})); @@ -107,16 +110,31 @@ function renderDecodeFunction({ structName, fields, staticFields, dynamicFields )} // Store trims the blob if dynamic fields are all empty if (_blob.length > ${totalStaticLength}) { - uint256 _start; - // skip static data length + dynamic lengths word - uint256 _end = ${totalStaticLength + 32}; ${renderList( dynamicFields, - (field, index) => ` - _start = _end; - _end += _encodedLengths.atIndex(${index}); - ${fieldNamePrefix}${field.name} = ${renderDecodeDynamicFieldPartial(field)}; - ` + // unchecked is only dangerous if _encodedLengths (and _blob) is invalid, + // but it's assumed to be valid, and this function is meant to be mostly used internally + (field, index) => { + if (index === 0) { + return ` + // skip static data length + dynamic lengths word + uint256 _start = ${totalStaticLength + 32}; + uint256 _end; + unchecked { + _end = ${totalStaticLength + 32} + _encodedLengths.atIndex(${index}); + } + ${fieldNamePrefix}${field.name} = ${renderDecodeDynamicFieldPartial(field)}; + `; + } else { + return ` + _start = _end; + unchecked { + _end += _encodedLengths.atIndex(${index}); + } + ${fieldNamePrefix}${field.name} = ${renderDecodeDynamicFieldPartial(field)}; + `; + } + } )} } } diff --git a/packages/world/gas-report.json b/packages/world/gas-report.json index a985bb4c6c..d87ce8e248 100644 --- a/packages/world/gas-report.json +++ b/packages/world/gas-report.json @@ -15,7 +15,7 @@ "file": "test/KeysInTableModule.t.sol", "test": "testInstallGas", "name": "set a record on a table with keysInTableModule installed", - "gasUsed": 186376 + "gasUsed": 186322 }, { "file": "test/KeysInTableModule.t.sol", @@ -39,7 +39,7 @@ "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookCompositeGas", "name": "delete a composite record on a table with keysInTableModule installed", - "gasUsed": 260336 + "gasUsed": 259406 }, { "file": "test/KeysInTableModule.t.sol", @@ -57,7 +57,7 @@ "file": "test/KeysInTableModule.t.sol", "test": "testSetAndDeleteRecordHookGas", "name": "delete a record on a table with keysInTableModule installed", - "gasUsed": 136530 + "gasUsed": 136184 }, { "file": "test/KeysWithValueModule.t.sol", @@ -129,31 +129,31 @@ "file": "test/query.t.sol", "test": "testCombinedHasHasValueNotQuery", "name": "CombinedHasHasValueNotQuery", - "gasUsed": 160409 + "gasUsed": 158213 }, { "file": "test/query.t.sol", "test": "testCombinedHasHasValueQuery", "name": "CombinedHasHasValueQuery", - "gasUsed": 75186 + "gasUsed": 74454 }, { "file": "test/query.t.sol", "test": "testCombinedHasNotQuery", "name": "CombinedHasNotQuery", - "gasUsed": 220085 + "gasUsed": 216199 }, { "file": "test/query.t.sol", "test": "testCombinedHasQuery", "name": "CombinedHasQuery", - "gasUsed": 138370 + "gasUsed": 136120 }, { "file": "test/query.t.sol", "test": "testCombinedHasValueNotQuery", "name": "CombinedHasValueNotQuery", - "gasUsed": 137230 + "gasUsed": 135034 }, { "file": "test/query.t.sol", @@ -165,19 +165,19 @@ "file": "test/query.t.sol", "test": "testHasQuery", "name": "HasQuery", - "gasUsed": 31224 + "gasUsed": 30718 }, { "file": "test/query.t.sol", "test": "testHasQuery1000Keys", "name": "HasQuery with 1000 keys", - "gasUsed": 10687510 + "gasUsed": 10461456 }, { "file": "test/query.t.sol", "test": "testHasQuery100Keys", "name": "HasQuery with 100 keys", - "gasUsed": 997013 + "gasUsed": 974359 }, { "file": "test/query.t.sol", @@ -189,7 +189,7 @@ "file": "test/query.t.sol", "test": "testNotValueQuery", "name": "NotValueQuery", - "gasUsed": 68780 + "gasUsed": 68048 }, { "file": "test/UniqueEntityModule.t.sol", diff --git a/packages/world/src/modules/core/tables/SystemHooks.sol b/packages/world/src/modules/core/tables/SystemHooks.sol index 8b2300a63d..e0aa0f43e0 100644 --- a/packages/world/src/modules/core/tables/SystemHooks.sol +++ b/packages/world/src/modules/core/tables/SystemHooks.sol @@ -105,7 +105,9 @@ library SystemHooks { _keyTuple[0] = resourceSelector; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } /** Get the length of value (using the specified store) */ @@ -114,25 +116,44 @@ library SystemHooks { _keyTuple[0] = resourceSelector; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } - /** Get an item of value (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value + * (unchecked, returns invalid data if index overflows) + */ function getItem(bytes32 resourceSelector, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 0, + getSchema(), + _index * 20, + (_index + 1) * 20 + ); + return (address(Bytes.slice20(_blob, 0))); + } } - /** Get an item of value (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItem(IStore _store, bytes32 resourceSelector, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); + return (address(Bytes.slice20(_blob, 0))); + } } /** Push an element to value */ @@ -167,20 +188,30 @@ library SystemHooks { _store.popFromField(_tableId, _keyTuple, 0, 20); } - /** Update an element of value at `_index` */ + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(bytes32 resourceSelector, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + } } - /** Update an element of value (using the specified store) at `_index` */ + /** + * Update an element of value (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(IStore _store, bytes32 resourceSelector, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = resourceSelector; - _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + } } /** Tightly pack full data using this table's schema */ diff --git a/packages/world/src/modules/keysintable/tables/KeysInTable.sol b/packages/world/src/modules/keysintable/tables/KeysInTable.sol index 274a4b14aa..dde965f55e 100644 --- a/packages/world/src/modules/keysintable/tables/KeysInTable.sol +++ b/packages/world/src/modules/keysintable/tables/KeysInTable.sol @@ -121,7 +121,9 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } /** Get the length of keys0 (using the specified store) */ @@ -130,25 +132,44 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } - /** Get an item of keys0 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys0 + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys0(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 0, + getSchema(), + _index * 32, + (_index + 1) * 32 + ); + return (Bytes.slice32(_blob, 0)); + } } - /** Get an item of keys0 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys0 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys0(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); + return (Bytes.slice32(_blob, 0)); + } } /** Push an element to keys0 */ @@ -183,20 +204,30 @@ library KeysInTable { _store.popFromField(_tableId, _keyTuple, 0, 32); } - /** Update an element of keys0 at `_index` */ + /** + * Update an element of keys0 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys0(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + } } - /** Update an element of keys0 (using the specified store) at `_index` */ + /** + * Update an element of keys0 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys0(IStore _store, bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + } } /** Get keys1 */ @@ -239,7 +270,9 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } /** Get the length of keys1 (using the specified store) */ @@ -248,25 +281,44 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 1, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } - /** Get an item of keys1 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys1 + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys1(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 1, + getSchema(), + _index * 32, + (_index + 1) * 32 + ); + return (Bytes.slice32(_blob, 0)); + } } - /** Get an item of keys1 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys1 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys1(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 1, getSchema(), _index * 32, (_index + 1) * 32); + return (Bytes.slice32(_blob, 0)); + } } /** Push an element to keys1 */ @@ -301,20 +353,30 @@ library KeysInTable { _store.popFromField(_tableId, _keyTuple, 1, 32); } - /** Update an element of keys1 at `_index` */ + /** + * Update an element of keys1 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys1(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element))); + } } - /** Update an element of keys1 (using the specified store) at `_index` */ + /** + * Update an element of keys1 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys1(IStore _store, bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 1, _index * 32, abi.encodePacked((_element))); + } } /** Get keys2 */ @@ -357,7 +419,9 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } /** Get the length of keys2 (using the specified store) */ @@ -366,25 +430,44 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 2, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } - /** Get an item of keys2 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys2 + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys2(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 2, + getSchema(), + _index * 32, + (_index + 1) * 32 + ); + return (Bytes.slice32(_blob, 0)); + } } - /** Get an item of keys2 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys2 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys2(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 2, getSchema(), _index * 32, (_index + 1) * 32); + return (Bytes.slice32(_blob, 0)); + } } /** Push an element to keys2 */ @@ -419,20 +502,30 @@ library KeysInTable { _store.popFromField(_tableId, _keyTuple, 2, 32); } - /** Update an element of keys2 at `_index` */ + /** + * Update an element of keys2 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys2(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element))); + } } - /** Update an element of keys2 (using the specified store) at `_index` */ + /** + * Update an element of keys2 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys2(IStore _store, bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 2, _index * 32, abi.encodePacked((_element))); + } } /** Get keys3 */ @@ -475,7 +568,9 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } /** Get the length of keys3 (using the specified store) */ @@ -484,25 +579,44 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 3, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } - /** Get an item of keys3 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys3 + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys3(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 3, + getSchema(), + _index * 32, + (_index + 1) * 32 + ); + return (Bytes.slice32(_blob, 0)); + } } - /** Get an item of keys3 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys3 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys3(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 3, getSchema(), _index * 32, (_index + 1) * 32); + return (Bytes.slice32(_blob, 0)); + } } /** Push an element to keys3 */ @@ -537,20 +651,30 @@ library KeysInTable { _store.popFromField(_tableId, _keyTuple, 3, 32); } - /** Update an element of keys3 at `_index` */ + /** + * Update an element of keys3 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys3(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element))); + } } - /** Update an element of keys3 (using the specified store) at `_index` */ + /** + * Update an element of keys3 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys3(IStore _store, bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 3, _index * 32, abi.encodePacked((_element))); + } } /** Get keys4 */ @@ -593,7 +717,9 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 4, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } /** Get the length of keys4 (using the specified store) */ @@ -602,25 +728,44 @@ library KeysInTable { _keyTuple[0] = sourceTable; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 4, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } - /** Get an item of keys4 (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys4 + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys4(bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 4, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 4, + getSchema(), + _index * 32, + (_index + 1) * 32 + ); + return (Bytes.slice32(_blob, 0)); + } } - /** Get an item of keys4 (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keys4 (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItemKeys4(IStore _store, bytes32 sourceTable, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 4, getSchema(), _index * 32, (_index + 1) * 32); + return (Bytes.slice32(_blob, 0)); + } } /** Push an element to keys4 */ @@ -655,20 +800,30 @@ library KeysInTable { _store.popFromField(_tableId, _keyTuple, 4, 32); } - /** Update an element of keys4 at `_index` */ + /** + * Update an element of keys4 at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys4(bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element))); + } } - /** Update an element of keys4 (using the specified store) at `_index` */ + /** + * Update an element of keys4 (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function updateKeys4(IStore _store, bytes32 sourceTable, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = sourceTable; - _store.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 4, _index * 32, abi.encodePacked((_element))); + } } /** Get the full data */ @@ -734,35 +889,46 @@ library KeysInTable { set(_store, sourceTable, _table.keys0, _table.keys1, _table.keys2, _table.keys3, _table.keys4); } - /** Decode the tightly packed blob using this table's schema */ + /** + * Decode the tightly packed blob using this table's schema. + * Undefined behaviour for invalid blobs. + */ function decode(bytes memory _blob) internal pure returns (KeysInTableData memory _table) { // 0 is the total byte length of static data PackedCounter _encodedLengths = PackedCounter.wrap(Bytes.slice32(_blob, 0)); // Store trims the blob if dynamic fields are all empty if (_blob.length > 0) { - uint256 _start; // skip static data length + dynamic lengths word - uint256 _end = 32; - - _start = _end; - _end += _encodedLengths.atIndex(0); + uint256 _start = 32; + uint256 _end; + unchecked { + _end = 32 + _encodedLengths.atIndex(0); + } _table.keys0 = (SliceLib.getSubslice(_blob, _start, _end).decodeArray_bytes32()); _start = _end; - _end += _encodedLengths.atIndex(1); + unchecked { + _end += _encodedLengths.atIndex(1); + } _table.keys1 = (SliceLib.getSubslice(_blob, _start, _end).decodeArray_bytes32()); _start = _end; - _end += _encodedLengths.atIndex(2); + unchecked { + _end += _encodedLengths.atIndex(2); + } _table.keys2 = (SliceLib.getSubslice(_blob, _start, _end).decodeArray_bytes32()); _start = _end; - _end += _encodedLengths.atIndex(3); + unchecked { + _end += _encodedLengths.atIndex(3); + } _table.keys3 = (SliceLib.getSubslice(_blob, _start, _end).decodeArray_bytes32()); _start = _end; - _end += _encodedLengths.atIndex(4); + unchecked { + _end += _encodedLengths.atIndex(4); + } _table.keys4 = (SliceLib.getSubslice(_blob, _start, _end).decodeArray_bytes32()); } } diff --git a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol index 209ef4ee8d..7195986076 100644 --- a/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol +++ b/packages/world/src/modules/keyswithvalue/tables/KeysWithValue.sol @@ -106,7 +106,9 @@ library KeysWithValue { _keyTuple[0] = valueHash; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } /** Get the length of keysWithValue (using the specified store) */ @@ -115,25 +117,44 @@ library KeysWithValue { _keyTuple[0] = valueHash; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 32; + unchecked { + return _byteLength / 32; + } } - /** Get an item of keysWithValue (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keysWithValue + * (unchecked, returns invalid data if index overflows) + */ function getItem(bytes32 _tableId, bytes32 valueHash, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 0, + getSchema(), + _index * 32, + (_index + 1) * 32 + ); + return (Bytes.slice32(_blob, 0)); + } } - /** Get an item of keysWithValue (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of keysWithValue (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItem(IStore _store, bytes32 _tableId, bytes32 valueHash, uint256 _index) internal view returns (bytes32) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); - return (Bytes.slice32(_blob, 0)); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 32, (_index + 1) * 32); + return (Bytes.slice32(_blob, 0)); + } } /** Push an element to keysWithValue */ @@ -168,20 +189,30 @@ library KeysWithValue { _store.popFromField(_tableId, _keyTuple, 0, 32); } - /** Update an element of keysWithValue at `_index` */ + /** + * Update an element of keysWithValue at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(bytes32 _tableId, bytes32 valueHash, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + } } - /** Update an element of keysWithValue (using the specified store) at `_index` */ + /** + * Update an element of keysWithValue (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(IStore _store, bytes32 _tableId, bytes32 valueHash, uint256 _index, bytes32 _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = valueHash; - _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 32, abi.encodePacked((_element))); + } } /** Tightly pack full data using this table's schema */ diff --git a/packages/world/test/tables/AddressArray.sol b/packages/world/test/tables/AddressArray.sol index a55dc0553e..c338531100 100644 --- a/packages/world/test/tables/AddressArray.sol +++ b/packages/world/test/tables/AddressArray.sol @@ -102,7 +102,9 @@ library AddressArray { _keyTuple[0] = key; uint256 _byteLength = StoreSwitch.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } /** Get the length of value (using the specified store) */ @@ -111,25 +113,44 @@ library AddressArray { _keyTuple[0] = key; uint256 _byteLength = _store.getFieldLength(_tableId, _keyTuple, 0, getSchema()); - return _byteLength / 20; + unchecked { + return _byteLength / 20; + } } - /** Get an item of value (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value + * (unchecked, returns invalid data if index overflows) + */ function getItem(bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = StoreSwitch.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = StoreSwitch.getFieldSlice( + _tableId, + _keyTuple, + 0, + getSchema(), + _index * 20, + (_index + 1) * 20 + ); + return (address(Bytes.slice20(_blob, 0))); + } } - /** Get an item of value (using the specified store) (unchecked, returns invalid data if index overflows) */ + /** + * Get an item of value (using the specified store) + * (unchecked, returns invalid data if index overflows) + */ function getItem(IStore _store, bytes32 _tableId, bytes32 key, uint256 _index) internal view returns (address) { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); - return (address(Bytes.slice20(_blob, 0))); + unchecked { + bytes memory _blob = _store.getFieldSlice(_tableId, _keyTuple, 0, getSchema(), _index * 20, (_index + 1) * 20); + return (address(Bytes.slice20(_blob, 0))); + } } /** Push an element to value */ @@ -164,20 +185,30 @@ library AddressArray { _store.popFromField(_tableId, _keyTuple, 0, 20); } - /** Update an element of value at `_index` */ + /** + * Update an element of value at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(bytes32 _tableId, bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + unchecked { + StoreSwitch.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + } } - /** Update an element of value (using the specified store) at `_index` */ + /** + * Update an element of value (using the specified store) at `_index` + * (checked only to prevent modifying other tables; can corrupt own data if index overflows) + */ function update(IStore _store, bytes32 _tableId, bytes32 key, uint256 _index, address _element) internal { bytes32[] memory _keyTuple = new bytes32[](1); _keyTuple[0] = key; - _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + unchecked { + _store.updateInField(_tableId, _keyTuple, 0, _index * 20, abi.encodePacked((_element))); + } } /** Tightly pack full data using this table's schema */