Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(world,store): add updateInField #525

Merged
merged 12 commits into from
Apr 4, 2023
128 changes: 128 additions & 0 deletions packages/cli/contracts/src/tables/Dynamics.sol
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element)));
}

/** Update an element of staticB32 at `_index` */
function updateStaticB32(bytes32 key, uint256 _index, bytes32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 32, abi.encodePacked((_element)));
}

/** Update an element of staticB32 (using the specified store) at `_index` */
function updateStaticB32(IStore _store, bytes32 key, uint256 _index, bytes32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 0, _index * 32, abi.encodePacked((_element)));
}

/** Get staticI32 */
function getStaticI32(bytes32 key) internal view returns (int32[2] memory staticI32) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down Expand Up @@ -190,6 +206,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 1, abi.encodePacked((_element)));
}

/** Update an element of staticI32 at `_index` */
function updateStaticI32(bytes32 key, uint256 _index, int32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 1, _index * 4, abi.encodePacked((_element)));
}

/** Update an element of staticI32 (using the specified store) at `_index` */
function updateStaticI32(IStore _store, bytes32 key, uint256 _index, int32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 1, _index * 4, abi.encodePacked((_element)));
}

/** Get staticU128 */
function getStaticU128(bytes32 key) internal view returns (uint128[3] memory staticU128) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down Expand Up @@ -240,6 +272,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element)));
}

/** Update an element of staticU128 at `_index` */
function updateStaticU128(bytes32 key, uint256 _index, uint128 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 2, _index * 16, abi.encodePacked((_element)));
}

/** Update an element of staticU128 (using the specified store) at `_index` */
function updateStaticU128(IStore _store, bytes32 key, uint256 _index, uint128 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 2, _index * 16, abi.encodePacked((_element)));
}

/** Get staticAddrs */
function getStaticAddrs(bytes32 key) internal view returns (address[4] memory staticAddrs) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down Expand Up @@ -290,6 +338,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 3, abi.encodePacked((_element)));
}

/** Update an element of staticAddrs at `_index` */
function updateStaticAddrs(bytes32 key, uint256 _index, address _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 3, _index * 20, abi.encodePacked((_element)));
}

/** Update an element of staticAddrs (using the specified store) at `_index` */
function updateStaticAddrs(IStore _store, bytes32 key, uint256 _index, address _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 3, _index * 20, abi.encodePacked((_element)));
}

/** Get staticBools */
function getStaticBools(bytes32 key) internal view returns (bool[5] memory staticBools) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down Expand Up @@ -340,6 +404,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 4, abi.encodePacked((_element)));
}

/** Update an element of staticBools at `_index` */
function updateStaticBools(bytes32 key, uint256 _index, bool _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 4, _index * 1, abi.encodePacked((_element)));
}

/** Update an element of staticBools (using the specified store) at `_index` */
function updateStaticBools(IStore _store, bytes32 key, uint256 _index, bool _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 4, _index * 1, abi.encodePacked((_element)));
}

/** Get u64 */
function getU64(bytes32 key) internal view returns (uint64[] memory u64) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down Expand Up @@ -390,6 +470,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 5, abi.encodePacked((_element)));
}

/** Update an element of u64 at `_index` */
function updateU64(bytes32 key, uint256 _index, uint64 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 5, _index * 8, abi.encodePacked((_element)));
}

/** Update an element of u64 (using the specified store) at `_index` */
function updateU64(IStore _store, bytes32 key, uint256 _index, uint64 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 5, _index * 8, abi.encodePacked((_element)));
}

/** Get str */
function getStr(bytes32 key) internal view returns (string memory str) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down Expand Up @@ -440,6 +536,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 6, bytes((_slice)));
}

/** Update a slice of str at `_index` */
function updateStr(bytes32 key, uint256 _index, string memory _slice) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 6, _index * 1, bytes((_slice)));
}

/** Update a slice of str (using the specified store) at `_index` */
function updateStr(IStore _store, bytes32 key, uint256 _index, string memory _slice) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 6, _index * 1, bytes((_slice)));
}

/** Get b */
function getB(bytes32 key) internal view returns (bytes memory b) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down Expand Up @@ -490,6 +602,22 @@ library Dynamics {
_store.pushToField(_tableId, _primaryKeys, 7, bytes((_slice)));
}

/** Update a slice of b at `_index` */
function updateB(bytes32 key, uint256 _index, bytes memory _slice) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

StoreSwitch.updateInField(_tableId, _primaryKeys, 7, _index * 1, bytes((_slice)));
}

/** Update a slice of b (using the specified store) at `_index` */
function updateB(IStore _store, bytes32 key, uint256 _index, bytes memory _slice) internal {
bytes32[] memory _primaryKeys = new bytes32[](1);
_primaryKeys[0] = bytes32((key));

_store.updateInField(_tableId, _primaryKeys, 7, _index * 1, bytes((_slice)));
}

/** Get the full data */
function get(bytes32 key) internal view returns (DynamicsData memory _table) {
bytes32[] memory _primaryKeys = new bytes32[](1);
Expand Down
42 changes: 42 additions & 0 deletions packages/cli/contracts/src/tables/Singleton.sol
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,20 @@ library Singleton {
_store.pushToField(_tableId, _primaryKeys, 1, abi.encodePacked((_element)));
}

/** Update an element of v2 at `_index` */
function updateV2(uint256 _index, uint32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](0);

StoreSwitch.updateInField(_tableId, _primaryKeys, 1, _index * 4, abi.encodePacked((_element)));
}

/** Update an element of v2 (using the specified store) at `_index` */
function updateV2(IStore _store, uint256 _index, uint32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](0);

_store.updateInField(_tableId, _primaryKeys, 1, _index * 4, abi.encodePacked((_element)));
}

/** Get v3 */
function getV3() internal view returns (uint32[2] memory v3) {
bytes32[] memory _primaryKeys = new bytes32[](0);
Expand Down Expand Up @@ -188,6 +202,20 @@ library Singleton {
_store.pushToField(_tableId, _primaryKeys, 2, abi.encodePacked((_element)));
}

/** Update an element of v3 at `_index` */
function updateV3(uint256 _index, uint32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](0);

StoreSwitch.updateInField(_tableId, _primaryKeys, 2, _index * 4, abi.encodePacked((_element)));
}

/** Update an element of v3 (using the specified store) at `_index` */
function updateV3(IStore _store, uint256 _index, uint32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](0);

_store.updateInField(_tableId, _primaryKeys, 2, _index * 4, abi.encodePacked((_element)));
}

/** Get v4 */
function getV4() internal view returns (uint32[1] memory v4) {
bytes32[] memory _primaryKeys = new bytes32[](0);
Expand Down Expand Up @@ -232,6 +260,20 @@ library Singleton {
_store.pushToField(_tableId, _primaryKeys, 3, abi.encodePacked((_element)));
}

/** Update an element of v4 at `_index` */
function updateV4(uint256 _index, uint32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](0);

StoreSwitch.updateInField(_tableId, _primaryKeys, 3, _index * 4, abi.encodePacked((_element)));
}

/** Update an element of v4 (using the specified store) at `_index` */
function updateV4(IStore _store, uint256 _index, uint32 _element) internal {
bytes32[] memory _primaryKeys = new bytes32[](0);

_store.updateInField(_tableId, _primaryKeys, 3, _index * 4, abi.encodePacked((_element)));
}

/** Get the full data */
function get() internal view returns (int256 v1, uint32[2] memory v2, uint32[2] memory v3, uint32[1] memory v4) {
bytes32[] memory _primaryKeys = new bytes32[](0);
Expand Down
27 changes: 25 additions & 2 deletions packages/cli/src/render-solidity/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ export function renderFieldMethods(options: RenderTableOptions) {
`
);

// TODO: this is super inefficient right now, need to add support for pushing to arrays to the store core library to avoid reading/writing the entire array
// (see https://github.com/latticexyz/mud/issues/438)
if (field.isDynamic) {
const portionData = fieldPortionData(field);

Expand All @@ -61,6 +59,29 @@ export function renderFieldMethods(options: RenderTableOptions) {
}
`
);

result += renderWithStore(
storeArgument,
(_typedStore, _store, _commentSuffix) => `
/** Update ${portionData.title} of ${field.name}${_commentSuffix} at \`_index\` */
function update${field.methodNameSuffix}(${renderArguments([
_typedStore,
_typedTableId,
_typedKeyArgs,
"uint256 _index",
`${portionData.typeWithLocation} ${portionData.name}`,
])}) internal {
${_primaryKeysDefinition}
${_store}.updateInField(
_tableId,
_primaryKeys,
${index},
_index * ${portionData.elementLength},
${portionData.encoded}
);
}
`
);
}
}
return result;
Expand Down Expand Up @@ -109,6 +130,7 @@ function fieldPortionData(field: RenderTableField) {
name: "_element",
encoded: renderEncodeField({ ...field.arrayElement, arrayElement: undefined, name, methodNameSuffix }),
title: "an element",
elementLength: field.arrayElement.staticByteLength,
};
} else {
const name = "_slice";
Expand All @@ -117,6 +139,7 @@ function fieldPortionData(field: RenderTableField) {
name,
encoded: renderEncodeField({ ...field, name, methodNameSuffix }),
title: "a slice",
elementLength: 1,
};
}
}
Expand Down
22 changes: 12 additions & 10 deletions packages/store/gas-report.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
(test/Gas.t.sol) | pass abi encoded bytes to external contract [someContract.doSomethingWithBytes(abiEncoded)]: 6554
(test/Gas.t.sol) | pass custom encoded bytes to external contract [someContract.doSomethingWithBytes(customEncoded)]: 1381
(test/Mixed.t.sol) | store Mixed struct in storage (native solidity) [testMixed = mixed]: 92050
(test/Mixed.t.sol) | register Mixed schema [Mixed.registerSchema()]: 61173
(test/Mixed.t.sol) | set record in Mixed [Mixed.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 112013
(test/Mixed.t.sol) | get record from Mixed [MixedData memory mixed = Mixed.get(key)]: 13400
(test/Mixed.t.sol) | register Mixed schema [Mixed.registerSchema()]: 61195
(test/Mixed.t.sol) | set record in Mixed [Mixed.set({ key: key, u32: 1, u128: 2, a32: a32, s: s })]: 112057
(test/Mixed.t.sol) | get record from Mixed [MixedData memory mixed = Mixed.get(key)]: 13422
(test/PackedCounter.t.sol) | get value at index of PackedCounter [packedCounter.atIndex(3)]: 261
(test/PackedCounter.t.sol) | set value at index of PackedCounter [packedCounter = packedCounter.setAtIndex(2, 5)]: 799
(test/PackedCounter.t.sol) | pack uint16 array into PackedCounter [PackedCounter packedCounter = PackedCounterLib.pack(counters)]: 2152
Expand Down Expand Up @@ -51,16 +51,16 @@
(test/StoreCore.t.sol) | delete record (complex data, 3 slots) [StoreCore.deleteRecord(table, key)]: 10963
(test/StoreCore.t.sol) | Check for existence of table (existent) [StoreCore.hasTable(table)]: 1117
(test/StoreCore.t.sol) | check for existence of table (non-existent) [StoreCore.hasTable(table2)]: 3143
(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65439
(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65488
(test/StoreCore.t.sol) | set record on table with subscriber [StoreCore.setRecord(table, key, data)]: 73772
(test/StoreCore.t.sol) | set static field on table with subscriber [StoreCore.setField(table, key, 0, data)]: 31929
(test/StoreCore.t.sol) | delete record on table with subscriber [StoreCore.deleteRecord(table, key)]: 24371
(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65439
(test/StoreCore.t.sol) | register subscriber [StoreCore.registerStoreHook(table, subscriber)]: 65488
(test/StoreCore.t.sol) | set (dynamic) record on table with subscriber [StoreCore.setRecord(table, key, data)]: 167179
(test/StoreCore.t.sol) | set (dynamic) field on table with subscriber [StoreCore.setField(table, key, 1, arrayDataBytes)]: 35031
(test/StoreCore.t.sol) | delete (dynamic) record on table with subscriber [StoreCore.deleteRecord(table, key)]: 25851
(test/StoreCore.t.sol) | push to field (1 slot, 1 uint32 item) [StoreCore.pushToField(table, key, 1, secondDataToPush)]: 16958
(test/StoreCore.t.sol) | push to field (2 slots, 10 uint32 items) [StoreCore.pushToField(table, key, 2, thirdDataToPush)]: 39680
(test/StoreCore.t.sol) | push to field (1 slot, 1 uint32 item) [StoreCore.pushToField(table, key, 1, secondDataToPush)]: 17007
(test/StoreCore.t.sol) | push to field (2 slots, 10 uint32 items) [StoreCore.pushToField(table, key, 2, thirdDataToPush)]: 39729
(test/StoreCore.t.sol) | StoreCore: register schema [StoreCore.registerSchema(table, schema, keySchema)]: 54866
(test/StoreCore.t.sol) | StoreCore: get schema (warm) [Schema loadedSchema = StoreCore.getSchema(table)]: 1145
(test/StoreCore.t.sol) | StoreCore: get key schema (warm) [Schema loadedKeySchema = StoreCore.getKeySchema(table)]: 1213
Expand All @@ -83,9 +83,11 @@
(test/StoreCore.t.sol) | get static record (1 slot) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 1335
(test/StoreCore.t.sol) | set static record (2 slots) [StoreCore.setRecord(table, key, data)]: 59833
(test/StoreCore.t.sol) | get static record (2 slots) [bytes memory loadedData = StoreCore.getRecord(table, key, schema)]: 1580
(test/StoreCore.t.sol) | StoreCore: set table metadata [StoreCore.setMetadata(table, tableName, fieldNames)]: 251710
(test/StoreMetadata.t.sol) | set record in StoreMetadataTable [StoreMetadata.set({ tableId: tableId, tableName: tableName, abiEncodedFieldNames: abi.encode(fieldNames) })]: 250184
(test/StoreMetadata.t.sol) | get record from StoreMetadataTable [StoreMetadataData memory metadata = StoreMetadata.get(tableId)]: 12133
(test/StoreCore.t.sol) | StoreCore: set table metadata [StoreCore.setMetadata(table, tableName, fieldNames)]: 251702
(test/StoreCore.t.sol) | update in field (1 slot, 1 uint32 item) [StoreCore.updateInField(table, key, 1, 4 * 1, secondDataForUpdate)]: 16544
(test/StoreCore.t.sol) | push to field (2 slots, 6 uint64 items) [StoreCore.updateInField(table, key, 2, 8 * 1, thirdDataForUpdate)]: 17646
(test/StoreMetadata.t.sol) | set record in StoreMetadataTable [StoreMetadata.set({ tableId: tableId, tableName: tableName, abiEncodedFieldNames: abi.encode(fieldNames) })]: 250220
(test/StoreMetadata.t.sol) | get record from StoreMetadataTable [StoreMetadataData memory metadata = StoreMetadata.get(tableId)]: 12155
(test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 671
(test/StoreSwitch.t.sol) | check if delegatecall [isDelegate = StoreSwitch.isDelegateCall()]: 627
(test/Vector2.t.sol) | register Vector2 schema [Vector2.registerSchema()]: 57971
Expand Down
3 changes: 2 additions & 1 deletion packages/store/src/IErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ interface IErrors {
error StoreCore_TableNotFound(uint256 tableId, string tableIdString);

error StoreCore_NotImplemented();
error StoreCore_NotDynamicField();
error StoreCore_InvalidDataLength(uint256 expected, uint256 received);
error StoreCore_InvalidFieldNamesLength(uint256 expected, uint256 received);
error StoreCore_NotDynamicField();
error StoreCore_DataIndexOverflow(uint256 length, uint256 received);
}
9 changes: 9 additions & 0 deletions packages/store/src/IStore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ interface IStore is IErrors {
// Push encoded items to the dynamic field at schema index
function pushToField(uint256 table, bytes32[] calldata key, uint8 schemaIndex, bytes calldata dataToPush) external;

// Change encoded items within the dynamic field at schema index
function updateInField(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a better name for this? spliceField or setFieldSlice or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to make splice actually, but the proper startIndex->delete->push can't work well in solidity, and update-only splice looks kinda misleading.
setFieldSlice isn't bad but I'd also want to consider its tablegen pair as well. So we have get, set, push, and:
update / slice / setSlice / setItem ...
updateCol1 / sliceCol1 / setSliceCol1 / setItemCol1 ...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this to v2 tracker so we can come back to it

uint256 table,
bytes32[] calldata key,
uint8 schemaIndex,
uint256 startByteIndex,
bytes calldata dataToSet
) external;

// Set full record (including full dynamic data)
function deleteRecord(uint256 table, bytes32[] memory key) external;

Expand Down
Loading