Skip to content

Commit

Permalink
refactor(store): separate SchemaLib into specialized libraries (#1216)
Browse files Browse the repository at this point in the history
  • Loading branch information
dk1a authored Aug 3, 2023
1 parent 131c63e commit c1d2249
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 144 deletions.
1 change: 1 addition & 0 deletions packages/store/abi/Schema.sol/SchemaInstance.abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
14 changes: 7 additions & 7 deletions packages/store/gas-report.json
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@
"file": "test/KeyEncoding.t.sol",
"test": "testRegisterAndGetSchema",
"name": "register KeyEncoding schema",
"gasUsed": 64679
"gasUsed": 65010
},
{
"file": "test/Mixed.t.sol",
Expand All @@ -255,7 +255,7 @@
"file": "test/Mixed.t.sol",
"test": "testRegisterAndGetSchema",
"name": "register Mixed schema",
"gasUsed": 61687
"gasUsed": 61992
},
{
"file": "test/Mixed.t.sol",
Expand All @@ -267,7 +267,7 @@
"file": "test/Mixed.t.sol",
"test": "testSetAndGet",
"name": "get record from Mixed",
"gasUsed": 12438
"gasUsed": 12610
},
{
"file": "test/PackedCounter.t.sol",
Expand Down Expand Up @@ -296,7 +296,7 @@
{
"file": "test/Schema.t.sol",
"test": "testEncodeDecodeSchema",
"name": "encode schema with 6 entries [SchemaLib.encode]",
"name": "encode schema with 6 entries",
"gasUsed": 5639
},
{
Expand Down Expand Up @@ -771,7 +771,7 @@
"file": "test/StoreMetadata.t.sol",
"test": "testSetAndGet",
"name": "get record from StoreMetadataTable",
"gasUsed": 11430
"gasUsed": 11576
},
{
"file": "test/StoreSwitch.t.sol",
Expand Down Expand Up @@ -879,7 +879,7 @@
"file": "test/Vector2.t.sol",
"test": "testRegisterAndGetSchema",
"name": "register Vector2 schema",
"gasUsed": 58890
"gasUsed": 59169
},
{
"file": "test/Vector2.t.sol",
Expand All @@ -891,6 +891,6 @@
"file": "test/Vector2.t.sol",
"test": "testSetAndGet",
"name": "get Vector2 record",
"gasUsed": 4403
"gasUsed": 4549
}
]
84 changes: 17 additions & 67 deletions packages/store/src/Schema.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import { Bytes } from "./Bytes.sol";
// - 28 bytes for 28 schema types (MAX_DYNAMIC_FIELDS allows us to pack the lengths into 1 word)
type Schema is bytes32;

using SchemaLib for Schema global;
using SchemaInstance for Schema global;

/**
* Static functions for Schema
*/
library SchemaLib {
error SchemaLib_InvalidLength(uint256 length);
error SchemaLib_StaticTypeAfterDynamicType();
Expand Down Expand Up @@ -68,66 +71,12 @@ library SchemaLib {

return Schema.wrap(schema);
}
}

// Overrides for encode functions
function encode(SchemaType a) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](1);
schema[0] = a;
return encode(schema);
}

function encode(SchemaType a, SchemaType b) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](2);
schema[0] = a;
schema[1] = b;
return encode(schema);
}

function encode(SchemaType a, SchemaType b, SchemaType c) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](3);
schema[0] = a;
schema[1] = b;
schema[2] = c;
return encode(schema);
}

function encode(SchemaType a, SchemaType b, SchemaType c, SchemaType d) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](4);
schema[0] = a;
schema[1] = b;
schema[2] = c;
schema[3] = d;
return encode(schema);
}

function encode(SchemaType a, SchemaType b, SchemaType c, SchemaType d, SchemaType e) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](5);
schema[0] = a;
schema[1] = b;
schema[2] = c;
schema[3] = d;
schema[4] = e;
return encode(schema);
}

function encode(
SchemaType a,
SchemaType b,
SchemaType c,
SchemaType d,
SchemaType e,
SchemaType f
) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](6);
schema[0] = a;
schema[1] = b;
schema[2] = c;
schema[3] = d;
schema[4] = e;
schema[5] = f;
return encode(schema);
}

/**
* Instance functions for Schema
*/
library SchemaInstance {
/************************************************************************
*
* INSTANCE FUNCTIONS
Expand Down Expand Up @@ -178,27 +127,28 @@ library SchemaLib {

function validate(Schema schema, bool allowEmpty) internal pure {
// Schema must not be empty
if (!allowEmpty && schema.isEmpty()) revert SchemaLib_InvalidLength(0);
if (!allowEmpty && schema.isEmpty()) revert SchemaLib.SchemaLib_InvalidLength(0);

// Schema must have no more than MAX_DYNAMIC_FIELDS
uint256 _numDynamicFields = schema.numDynamicFields();
if (_numDynamicFields > MAX_DYNAMIC_FIELDS) revert SchemaLib_InvalidLength(_numDynamicFields);
if (_numDynamicFields > SchemaLib.MAX_DYNAMIC_FIELDS) revert SchemaLib.SchemaLib_InvalidLength(_numDynamicFields);

uint256 _numStaticFields = schema.numStaticFields();
// Schema must not have more than 28 fields in total
if (_numStaticFields + _numDynamicFields > 28) revert SchemaLib_InvalidLength(_numStaticFields + _numDynamicFields);
if (_numStaticFields + _numDynamicFields > 28)
revert SchemaLib.SchemaLib_InvalidLength(_numStaticFields + _numDynamicFields);

// No static field can be after a dynamic field
uint256 countStaticFields;
uint256 countDynamicFields;
for (uint256 i; i < _numStaticFields + _numDynamicFields; ) {
if (schema.atIndex(i).getStaticByteLength() > 0) {
// Static field in dynamic part
if (i >= _numStaticFields) revert SchemaLib_StaticTypeAfterDynamicType();
if (i >= _numStaticFields) revert SchemaLib.SchemaLib_StaticTypeAfterDynamicType();
countStaticFields++;
} else {
// Dynamic field in static part
if (i < _numStaticFields) revert SchemaLib_StaticTypeAfterDynamicType();
if (i < _numStaticFields) revert SchemaLib.SchemaLib_StaticTypeAfterDynamicType();
countDynamicFields++;
}
unchecked {
Expand All @@ -207,10 +157,10 @@ library SchemaLib {
}

// Number of static fields must match
if (countStaticFields != _numStaticFields) revert SchemaLib_InvalidLength(countStaticFields);
if (countStaticFields != _numStaticFields) revert SchemaLib.SchemaLib_InvalidLength(countStaticFields);

// Number of dynamic fields must match
if (countDynamicFields != _numDynamicFields) revert SchemaLib_InvalidLength(countDynamicFields);
if (countDynamicFields != _numDynamicFields) revert SchemaLib.SchemaLib_InvalidLength(countDynamicFields);
}

/**
Expand Down
9 changes: 7 additions & 2 deletions packages/store/src/StoreCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ library StoreCore {
StoreSwitch.setStoreAddress(address(this));

// Register internal schema table
SchemaType[] memory _valueSchema = new SchemaType[](2);
_valueSchema[0] = SchemaType.BYTES32;
_valueSchema[1] = SchemaType.BYTES32;
SchemaType[] memory _keySchema = new SchemaType[](1);
_keySchema[0] = SchemaType.BYTES32;
registerSchema(
StoreCoreInternal.SCHEMA_TABLE,
SchemaLib.encode(SchemaType.BYTES32, SchemaType.BYTES32), // The Schema table's valueSchema is { valueSchema: BYTES32, keySchema: BYTES32 }
SchemaLib.encode(SchemaType.BYTES32) // The Schema table's keySchema is { tableId: BYTES32 }
SchemaLib.encode(_valueSchema), // The Schema table's valueSchema is { valueSchema: BYTES32, keySchema: BYTES32 }
SchemaLib.encode(_keySchema) // The Schema table's keySchema is { tableId: BYTES32 }
);

// Register other internal tables
Expand Down
15 changes: 8 additions & 7 deletions packages/store/test/Schema.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { Test, console } from "forge-std/Test.sol";
import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol";
import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol";
import { Schema, SchemaLib } from "../src/Schema.sol";
import { SchemaEncodeHelper } from "./SchemaEncodeHelper.sol";

// TODO add tests for all schema types
contract SchemaTest is Test, GasReporter {
function testEncodeDecodeSchema() public {
startGasReport("encode schema with 6 entries [SchemaLib.encode]");
Schema schema = SchemaLib.encode(
startGasReport("encode schema with 6 entries");
Schema schema = SchemaEncodeHelper.encode(
SchemaType.UINT8, // 1 byte
SchemaType.UINT16, // 2 bytes
SchemaType.UINT32, // 4 bytes
Expand All @@ -33,7 +34,7 @@ contract SchemaTest is Test, GasReporter {
}

function testFailInvalidSchemaStaticAfterDynamic() public pure {
SchemaLib.encode(SchemaType.UINT8, SchemaType.UINT32_ARRAY, SchemaType.UINT16);
SchemaEncodeHelper.encode(SchemaType.UINT8, SchemaType.UINT32_ARRAY, SchemaType.UINT16);
}

function testEncodeMaxValidLength() public {
Expand Down Expand Up @@ -129,7 +130,7 @@ contract SchemaTest is Test, GasReporter {
}

function testGetStaticSchemaLength() public {
Schema schema = SchemaLib.encode(
Schema schema = SchemaEncodeHelper.encode(
SchemaType.UINT8, // 1 byte
SchemaType.UINT16, // 2 bytes
SchemaType.UINT32, // 4 bytes
Expand All @@ -146,7 +147,7 @@ contract SchemaTest is Test, GasReporter {
}

function testGetNumStaticFields() public {
Schema schema = SchemaLib.encode(
Schema schema = SchemaEncodeHelper.encode(
SchemaType.UINT8, // 1 byte
SchemaType.UINT16, // 2 bytes
SchemaType.UINT32, // 4 bytes
Expand All @@ -163,7 +164,7 @@ contract SchemaTest is Test, GasReporter {
}

function testGetNumDynamicFields() public {
Schema schema = SchemaLib.encode(
Schema schema = SchemaEncodeHelper.encode(
SchemaType.UINT8, // 1 byte
SchemaType.UINT16, // 2 bytes
SchemaType.UINT32, // 4 bytes
Expand Down Expand Up @@ -232,7 +233,7 @@ contract SchemaTest is Test, GasReporter {
}

function testIsEmptyFalse() public {
Schema encodedSchema = SchemaLib.encode(SchemaType.UINT256);
Schema encodedSchema = SchemaEncodeHelper.encode(SchemaType.UINT256);

startGasReport("check if schema is empty (non-empty schema)");
bool empty = encodedSchema.isEmpty();
Expand Down
68 changes: 68 additions & 0 deletions packages/store/test/SchemaEncodeHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol";
import { Schema, SchemaLib } from "../src/Schema.sol";

/**
* Overrides for encode function to simplify tests
*/
library SchemaEncodeHelper {
function encode(SchemaType a) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](1);
schema[0] = a;
return SchemaLib.encode(schema);
}

function encode(SchemaType a, SchemaType b) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](2);
schema[0] = a;
schema[1] = b;
return SchemaLib.encode(schema);
}

function encode(SchemaType a, SchemaType b, SchemaType c) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](3);
schema[0] = a;
schema[1] = b;
schema[2] = c;
return SchemaLib.encode(schema);
}

function encode(SchemaType a, SchemaType b, SchemaType c, SchemaType d) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](4);
schema[0] = a;
schema[1] = b;
schema[2] = c;
schema[3] = d;
return SchemaLib.encode(schema);
}

function encode(SchemaType a, SchemaType b, SchemaType c, SchemaType d, SchemaType e) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](5);
schema[0] = a;
schema[1] = b;
schema[2] = c;
schema[3] = d;
schema[4] = e;
return SchemaLib.encode(schema);
}

function encode(
SchemaType a,
SchemaType b,
SchemaType c,
SchemaType d,
SchemaType e,
SchemaType f
) internal pure returns (Schema) {
SchemaType[] memory schema = new SchemaType[](6);
schema[0] = a;
schema[1] = b;
schema[2] = c;
schema[3] = d;
schema[4] = e;
schema[5] = f;
return SchemaLib.encode(schema);
}
}
Loading

0 comments on commit c1d2249

Please sign in to comment.