diff --git a/crates/dojo-core/src/database/storage.cairo b/crates/dojo-core/src/database/storage.cairo index 18bb054260..648ccfbbed 100644 --- a/crates/dojo-core/src/database/storage.cairo +++ b/crates/dojo-core/src/database/storage.cairo @@ -60,129 +60,3 @@ fn set_many(address_domain: u32, keys: Span, offset: u8, mut value: Spa }; }; } - - -trait StorageSize { - fn unpacked_size() -> usize; - fn packed_size() -> usize; -} - -impl StorageSizeFelt252 of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 252 - } -} - -impl StorageSizeBool of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 1 - } -} - -impl StorageSizeU8 of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 8 - } -} - -impl StorageSizeU16 of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 16 - } -} - -impl StorageSizeU32 of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 32 - } -} - -impl StorageSizeU64 of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 64 - } -} - -impl StorageSizeU128 of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 128 - } -} - -impl StorageSizeU256 of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 2 - } - - #[inline(always)] - fn packed_size() -> usize { - 256 - } -} - -impl StorageSizeContractAddress of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 256 - } -} - -impl StorageSizeClassHash of StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - 1 - } - - #[inline(always)] - fn packed_size() -> usize { - 256 - } -} diff --git a/crates/dojo-core/src/executor_test.cairo b/crates/dojo-core/src/executor_test.cairo index 395ca322fb..f59fc68270 100644 --- a/crates/dojo-core/src/executor_test.cairo +++ b/crates/dojo-core/src/executor_test.cairo @@ -10,7 +10,7 @@ use starknet::class_hash::Felt252TryIntoClassHash; use dojo::executor::{executor, IExecutorDispatcher, IExecutorDispatcherTrait}; use dojo::world::{Context, IWorldDispatcher}; -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Foo { #[key] id: felt252, diff --git a/crates/dojo-core/src/lib.cairo b/crates/dojo-core/src/lib.cairo index 9a42520009..84541ee928 100644 --- a/crates/dojo-core/src/lib.cairo +++ b/crates/dojo-core/src/lib.cairo @@ -1,14 +1,12 @@ mod database; -use database::storage::StorageSize; #[cfg(test)] mod database_test; mod executor; #[cfg(test)] mod executor_test; -mod component; -mod packing; -#[cfg(test)] -mod packing_test; +mod serde; +use serde::SerdeLen; +mod traits; mod world; #[cfg(test)] mod world_test; diff --git a/crates/dojo-core/src/packing.cairo b/crates/dojo-core/src/packing.cairo deleted file mode 100644 index e96304bd38..0000000000 --- a/crates/dojo-core/src/packing.cairo +++ /dev/null @@ -1,145 +0,0 @@ -use starknet::{ClassHash, ContractAddress}; -use array::{ArrayTrait, SpanTrait}; -use traits::{Into, TryInto}; -use integer::{U256BitAnd, U256BitOr, U256BitXor, upcast, downcast, BoundedInt}; -use option::OptionTrait; - -#[derive(Copy, Drop)] -struct LayoutItem { - value: felt252, - size: u8 -} - -fn pack(ref unpacked: Array) -> Span { - let mut packed: Array = ArrayTrait::new(); - let mut packing: felt252 = 0x0; - let mut offset: u8 = 0x0; - loop { - match unpacked.pop_front() { - Option::Some(s) => { - pack_inner(@s.value, s.size, ref packing, ref offset, ref packed); - }, - Option::None(_) => { - break packed.span(); - } - }; - } -} - -fn unpack(ref packed: Span, ref layout: Span) -> Option> { - let mut unpacked: Array = ArrayTrait::new(); - let mut unpacking: felt252 = 0x0; - let mut offset: u8 = 251; - loop { - match layout.pop_front() { - Option::Some(s) => { - match unpack_inner(*s, ref packed, ref unpacking, ref offset) { - Option::Some(u) => { - unpacked.append(u); - }, - Option::None(_) => { - break Option::None(()); - } - } - }, - Option::None(_) => { - break Option::Some(unpacked.span()); - } - }; - } -} - -/// Pack the proposal fields into a single felt252. -fn pack_inner( - self: @felt252, - size: u8, - ref packing: felt252, - ref packing_offset: u8, - ref packed: Array -) { - // Easier to work on u256 rather than felt252. - let self_256: u256 = (*self).into(); - - // Cannot use all 252 bits because some bit arrangements (eg. 11111...11111) are not valid felt252 values. - // Thus only 251 bits are used. ^-252 times-^ - // One could optimize by some conditional alligment mechanism, but it would be an at most 1/252 space-wise improvement. - let remaining_bits: u8 = (251 - packing_offset).into(); - - let mut packing_256: u256 = packing.into(); - - if remaining_bits < size { - let first_part = self_256 & (shl(1, remaining_bits) - 1); - let second_part = shr(self_256, remaining_bits); - - // Pack the first part into the current felt - packing_256 = packing_256 | shl(first_part, packing_offset); - packed.append(packing_256.try_into().unwrap()); - - // Start a new felt and pack the second part into it - packing = second_part.try_into().unwrap(); - packing_offset = size - remaining_bits; - } else { - // Pack the data into the current felt - packing_256 = packing_256 | shl(self_256, packing_offset); - packing = packing_256.try_into().unwrap(); - packing_offset = packing_offset + size; - } -} - -fn unpack_inner( - size: u8, ref packed: Span, ref unpacking: felt252, ref unpacking_offset: u8 -) -> Option { - let remaining_bits: u8 = (251 - unpacking_offset).into(); - - let mut unpacking_256: u256 = unpacking.into(); - - if remaining_bits < size { - match packed.pop_front() { - Option::Some(val) => { - let val_256: u256 = (*val).into(); - - // Get the first part - let first_part = shr(unpacking_256, unpacking_offset); - // Size of the remaining part - let second_size = size - remaining_bits; - let second_part = val_256 & (shl(1, second_size) - 1); - // Move the second part so it fits alongside the first part - let result = first_part | shl(second_part, remaining_bits); - - unpacking = *val; - unpacking_offset = second_size; - return result.try_into(); - }, - Option::None(()) => { - return Option::None(()); - }, - } - } else { - let result = (shl(1, size) - 1) & shr(unpacking_256, unpacking_offset); - unpacking_offset = unpacking_offset + size; - return result.try_into(); - } -} - -fn fpow(x: u256, n: u8) -> u256 { - let y = x; - if n == 0 { - return 1; - } - if n == 1 { - return x; - } - let double = fpow(y * x, n / 2); - if (n % 2) == 1 { - return x * double; - } - return double; -} - -fn shl(x: u256, n: u8) -> u256 { - x * fpow(2, n) -} - -fn shr(x: u256, n: u8) -> u256 { - x / fpow(2, n) -} \ No newline at end of file diff --git a/crates/dojo-core/src/packing_test.cairo b/crates/dojo-core/src/packing_test.cairo deleted file mode 100644 index 2d4583acb7..0000000000 --- a/crates/dojo-core/src/packing_test.cairo +++ /dev/null @@ -1,25 +0,0 @@ -use array::{ArrayTrait, SpanTrait}; -use starknet::{ClassHash, ContractAddress, Felt252TryIntoContractAddress, Felt252TryIntoClassHash}; -use dojo::packing::{shl, shr, fpow}; -use integer::U256BitAnd; -use option::OptionTrait; -use debug::PrintTrait; -use traits::{Into, TryInto}; - -#[test] -#[available_gas(9000000)] -fn test_bit_fpow() { - assert(fpow(2, 250) == 1809251394333065553493296640760748560207343510400633813116524750123642650624_u256, '') -} - -#[test] -#[available_gas(9000000)] -fn test_bit_shift() { - assert(1 == shl(1, 0), 'left == right'); - assert(1 == shr(1, 0), 'left == right'); - - assert(16 == shl(1, 4), 'left == right'); - assert(1 == shr(16, 4), 'left == right'); - - assert(shr(shl(1, 251), 251) == 1, 'left == right') -} diff --git a/crates/dojo-core/src/serde.cairo b/crates/dojo-core/src/serde.cairo new file mode 100644 index 0000000000..6dd1ce0ec3 --- /dev/null +++ b/crates/dojo-core/src/serde.cairo @@ -0,0 +1,74 @@ +trait SerdeLen { + fn len() -> usize; +} + +impl SerdeLenFelt252 of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenBool of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenU8 of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenU16 of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenU32 of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenU64 of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenU128 of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenU256 of SerdeLen { + #[inline(always)] + fn len() -> usize { + 2 + } +} + +impl SerdeLenContractAddress of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + +impl SerdeLenClassHash of SerdeLen { + #[inline(always)] + fn len() -> usize { + 1 + } +} + diff --git a/crates/dojo-core/src/component.cairo b/crates/dojo-core/src/traits.cairo similarity index 100% rename from crates/dojo-core/src/component.cairo rename to crates/dojo-core/src/traits.cairo diff --git a/crates/dojo-core/src/world.cairo b/crates/dojo-core/src/world.cairo index 88592c514a..fea75d96a4 100644 --- a/crates/dojo-core/src/world.cairo +++ b/crates/dojo-core/src/world.cairo @@ -58,7 +58,7 @@ mod world { use dojo::database; use dojo::executor::{IExecutorDispatcher, IExecutorDispatcherTrait}; - use dojo::component::{INamedLibraryDispatcher, INamedDispatcherTrait, }; + use dojo::traits::{INamedLibraryDispatcher, INamedDispatcherTrait, }; use dojo::world::{IWorldDispatcher, IWorld}; use super::Context; diff --git a/crates/dojo-core/src/world_factory_test.cairo b/crates/dojo-core/src/world_factory_test.cairo index 3c7bdf92f5..5ff581b9df 100644 --- a/crates/dojo-core/src/world_factory_test.cairo +++ b/crates/dojo-core/src/world_factory_test.cairo @@ -16,7 +16,7 @@ use dojo::world_factory::{IWorldFactoryDispatcher, IWorldFactoryDispatcherTrait, use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait, world}; -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Foo { #[key] id: felt252, diff --git a/crates/dojo-core/src/world_test.cairo b/crates/dojo-core/src/world_test.cairo index 1b6f3e160f..25862edf7a 100644 --- a/crates/dojo-core/src/world_test.cairo +++ b/crates/dojo-core/src/world_test.cairo @@ -16,7 +16,7 @@ use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait, library_call, world}; // Components and Systems -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Foo { #[key] caller: ContractAddress, @@ -24,7 +24,7 @@ struct Foo { b: u128, } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Fizz { #[key] caller: ContractAddress, @@ -71,7 +71,7 @@ fn test_system() { let mut keys = ArrayTrait::new(); keys.append(0); - let stored = world.entity('Foo', keys.span(), 0, dojo::StorageSize::::unpacked_size()); + let stored = world.entity('Foo', keys.span(), 0, dojo::SerdeLen::::len()); assert(*stored.snapshot.at(0) == 1337, 'data not stored'); } @@ -121,7 +121,7 @@ fn test_set_entity_admin() { data.append(420); data.append(1337); world.execute('bar', data); - let foo = world.entity('Foo', keys.span(), 0, dojo::StorageSize::::unpacked_size()); + let foo = world.entity('Foo', keys.span(), 0, dojo::SerdeLen::::len()); assert(*foo[0] == 420, 'data not stored'); assert(*foo[1] == 1337, 'data not stored'); } @@ -407,8 +407,8 @@ fn test_execute_multiple_worlds() { world.execute('bar', data); another_world.execute('bar', another_data); - let stored = world.entity('Foo', keys.span(), 0, dojo::StorageSize::::unpacked_size()); - let another_stored = another_world.entity('Foo', keys.span(), 0, dojo::StorageSize::::unpacked_size()); + let stored = world.entity('Foo', keys.span(), 0, dojo::SerdeLen::::len()); + let another_stored = another_world.entity('Foo', keys.span(), 0, dojo::SerdeLen::::len()); assert(*stored.snapshot.at(0) == 1337, 'data not stored'); assert(*another_stored.snapshot.at(0) == 7331, 'data not stored'); } diff --git a/crates/dojo-defi/src/market/components.cairo b/crates/dojo-defi/src/market/components.cairo index f0fd6c05d5..0b9cbb7fd6 100644 --- a/crates/dojo-defi/src/market/components.cairo +++ b/crates/dojo-defi/src/market/components.cairo @@ -1,12 +1,14 @@ use starknet::ContractAddress; -use dojo::StorageSize; + +use dojo::serde::SerdeLen; + // Cubit fixed point math library use cubit::f128::types::fixed::Fixed; const SCALING_FACTOR: u128 = 10000; -impl StorageSizeFixed of StorageSize { +impl SerdeLenFixed of SerdeLen { #[inline(always)] fn unpacked_size() -> usize { 1 @@ -18,14 +20,14 @@ impl StorageSizeFixed of StorageSize { } } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Cash { #[key] player: ContractAddress, amount: u128, } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Item { #[key] player: ContractAddress, @@ -34,7 +36,7 @@ struct Item { quantity: u128, } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Liquidity { #[key] player: ContractAddress, @@ -43,7 +45,7 @@ struct Liquidity { shares: Fixed, } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Market { #[key] item_id: u32, diff --git a/crates/dojo-erc/src/erc1155/components.cairo b/crates/dojo-erc/src/erc1155/components.cairo index 0d5d73222a..9f0b7ecd18 100644 --- a/crates/dojo-erc/src/erc1155/components.cairo +++ b/crates/dojo-erc/src/erc1155/components.cairo @@ -12,7 +12,7 @@ use dojo_erc::erc_common::components::{operator_approval, OperatorApproval, Oper // Uri TODO: use BaseURI from erc_common // -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Uri { #[key] token: ContractAddress, @@ -23,7 +23,7 @@ struct Uri { // ERC1155Balance // -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct ERC1155Balance { #[key] token: ContractAddress, diff --git a/crates/dojo-erc/src/erc20/components.cairo b/crates/dojo-erc/src/erc20/components.cairo index 4266db3562..0a0b52ce51 100644 --- a/crates/dojo-erc/src/erc20/components.cairo +++ b/crates/dojo-erc/src/erc20/components.cairo @@ -1,6 +1,6 @@ use starknet::ContractAddress; -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Allowance { #[key] token: ContractAddress, @@ -11,7 +11,7 @@ struct Allowance { amount: felt252, } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Balance { #[key] token: ContractAddress, @@ -20,7 +20,7 @@ struct Balance { amount: felt252, } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Supply { #[key] token: ContractAddress, diff --git a/crates/dojo-erc/src/erc721/components.cairo b/crates/dojo-erc/src/erc721/components.cairo index 25d011fbd5..bb4284ceb0 100644 --- a/crates/dojo-erc/src/erc721/components.cairo +++ b/crates/dojo-erc/src/erc721/components.cairo @@ -14,7 +14,7 @@ use dojo_erc::erc_common::components::{ // ERC721Owner // -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct ERC721Owner { #[key] token: ContractAddress, @@ -56,7 +56,7 @@ impl ERC721OwnerImpl of ERC721OwnerTrait { // ERC721Balance // -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct ERC721Balance { #[key] token: ContractAddress, @@ -133,7 +133,7 @@ impl ERC721BalanceImpl of ERC721BalanceTrait { // ERC721TokenApproval // -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct ERC721TokenApproval { #[key] token: ContractAddress, diff --git a/crates/dojo-erc/src/erc_common/components/base_uri_component.cairo b/crates/dojo-erc/src/erc_common/components/base_uri_component.cairo index 7c6a5fa876..39c0daafe8 100644 --- a/crates/dojo-erc/src/erc_common/components/base_uri_component.cairo +++ b/crates/dojo-erc/src/erc_common/components/base_uri_component.cairo @@ -1,7 +1,7 @@ use starknet::ContractAddress; use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct BaseUri { #[key] token: ContractAddress, diff --git a/crates/dojo-erc/src/erc_common/components/operator_approval_component.cairo b/crates/dojo-erc/src/erc_common/components/operator_approval_component.cairo index b8dd10f302..db792bbfb3 100644 --- a/crates/dojo-erc/src/erc_common/components/operator_approval_component.cairo +++ b/crates/dojo-erc/src/erc_common/components/operator_approval_component.cairo @@ -1,7 +1,7 @@ use starknet::ContractAddress; use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct OperatorApproval { #[key] token: ContractAddress, diff --git a/crates/dojo-lang/src/component.rs b/crates/dojo-lang/src/component.rs index 79a6a092c0..4328edbbdb 100644 --- a/crates/dojo-lang/src/component.rs +++ b/crates/dojo-lang/src/component.rs @@ -101,7 +101,7 @@ pub fn handle_component_struct( $members$ } - impl $type_name$Component of dojo::component::Component<$type_name$> { + impl $type_name$Component of dojo::traits::Component<$type_name$> { #[inline(always)] fn name(self: @$type_name$) -> felt252 { '$type_name$' @@ -122,18 +122,6 @@ pub fn handle_component_struct( } } - impl $type_name$StorageSize of dojo::StorageSize<$type_name$> { - #[inline(always)] - fn unpacked_size() -> usize { - $unpacked_size$ - } - - #[inline(always)] - fn packed_size() -> usize { - $packed_size$ - } - } - #[cfg(test)] impl $type_name$PrintImpl of debug::PrintTrait<$type_name$> { fn print(self: $type_name$) { @@ -160,7 +148,7 @@ pub fn handle_component_struct( #[external(v0)] fn size(self: @ContractState) -> usize { - dojo::StorageSize::<$type_name$>::unpacked_size() + dojo::SerdeLen::<$type_name$>::len() } #[external(v0)] @@ -193,46 +181,6 @@ pub fn handle_component_struct( ("serialized_values".to_string(), RewriteNode::new_modified(serialized_values)), ("schema".to_string(), RewriteNode::new_modified(schema)), ("print".to_string(), RewriteNode::Text(prints.join("\n"))), - ( - "unpacked_size".to_string(), - RewriteNode::Text( - struct_ast - .members(db) - .elements(db) - .iter() - .filter_map(|member| { - if member.has_attr(db, "key") { - return None; - } - - Some(format!( - "dojo::StorageSize::<{}>::unpacked_size()", - member.type_clause(db).ty(db).as_syntax_node().get_text(db), - )) - }) - .join(" + "), - ), - ), - ( - "packed_size".to_string(), - RewriteNode::Text( - struct_ast - .members(db) - .elements(db) - .iter() - .filter_map(|member| { - if member.has_attr(db, "key") { - return None; - } - - Some(format!( - "dojo::StorageSize::<{}>::packed_size()", - member.type_clause(db).ty(db).as_syntax_node().get_text(db), - )) - }) - .join(" + "), - ), - ), ]), ), diagnostics, diff --git a/crates/dojo-lang/src/inline_macros/get.rs b/crates/dojo-lang/src/inline_macros/get.rs index b593352b48..445f14a012 100644 --- a/crates/dojo-lang/src/inline_macros/get.rs +++ b/crates/dojo-lang/src/inline_macros/get.rs @@ -86,7 +86,7 @@ impl InlineMacroExprPlugin for GetMacro { builder.add_str(&format!( "\n let __{component}_values__ = {}.entity('{component}', \ - __get_macro_keys__, 0_u8, dojo::StorageSize::<{component}>::unpacked_size()); + __get_macro_keys__, 0_u8, dojo::SerdeLen::<{component}>::len()); let mut __{component}_component__ = array::ArrayTrait::new(); array::serialize_array_helper(__get_macro_keys__, ref __{component}_component__); array::serialize_array_helper(__{component}_values__, ref \ diff --git a/crates/dojo-lang/src/inline_macros/set.rs b/crates/dojo-lang/src/inline_macros/set.rs index 08d476d4b5..7a58c7b3b3 100644 --- a/crates/dojo-lang/src/inline_macros/set.rs +++ b/crates/dojo-lang/src/inline_macros/set.rs @@ -77,9 +77,9 @@ impl InlineMacroExprPlugin for SetMacro { for entity in bundle { builder.add_str(&format!( "\n let __set_macro_value__ = {}; - {}.set_entity(dojo::component::Component::name(@__set_macro_value__), \ - dojo::component::Component::keys(@__set_macro_value__), 0_u8, \ - dojo::component::Component::values(@__set_macro_value__));", + {}.set_entity(dojo::traits::Component::name(@__set_macro_value__), \ + dojo::traits::Component::keys(@__set_macro_value__), 0_u8, \ + dojo::traits::Component::values(@__set_macro_value__));", entity, world.as_syntax_node().get_text(db), )); diff --git a/crates/dojo-lang/src/lib.rs b/crates/dojo-lang/src/lib.rs index 1e2f7ef81c..7ac2ee7600 100644 --- a/crates/dojo-lang/src/lib.rs +++ b/crates/dojo-lang/src/lib.rs @@ -8,5 +8,6 @@ pub mod component; pub mod inline_macros; mod manifest; pub mod plugin; +mod serde; pub mod system; pub(crate) mod version; diff --git a/crates/dojo-lang/src/plugin.rs b/crates/dojo-lang/src/plugin.rs index b779561d42..85b0cedca8 100644 --- a/crates/dojo-lang/src/plugin.rs +++ b/crates/dojo-lang/src/plugin.rs @@ -25,6 +25,7 @@ use crate::component::handle_component_struct; use crate::inline_macros::emit::EmitMacro; use crate::inline_macros::get::GetMacro; use crate::inline_macros::set::SetMacro; +use crate::serde::handle_serde_len_struct; use crate::system::System; const SYSTEM_ATTR: &str = "system"; @@ -158,6 +159,9 @@ impl MacroPlugin for DojoPlugin { rewrite_nodes.push(component_rewrite_nodes); diagnostics.extend(component_diagnostics); } + "SerdeLen" => { + rewrite_nodes.push(handle_serde_len_struct(db, struct_ast.clone())); + } _ => continue, } } diff --git a/crates/dojo-lang/src/plugin_test_data/component b/crates/dojo-lang/src/plugin_test_data/component index 2f7f610300..0a04308700 100644 --- a/crates/dojo-lang/src/plugin_test_data/component +++ b/crates/dojo-lang/src/plugin_test_data/component @@ -6,7 +6,7 @@ test_expand_plugin //! > cairo_code use serde::Serde; -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Position { #[key] id: felt252, @@ -38,9 +38,16 @@ struct Roles { role_ids: Array } +impl RolesSerdeLen of dojo::SerdeLen { + #[inline(always)] + fn len() -> usize { + 5 + } +} + use starknet::ContractAddress; -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Player { #[key] game: felt252, @@ -60,7 +67,7 @@ struct Position { y: felt252 } -impl PositionComponent of dojo::component::Component { +impl PositionComponent of dojo::traits::Component { #[inline(always)] fn name(self: @Position) -> felt252 { 'Position' @@ -84,19 +91,6 @@ impl PositionComponent of dojo::component::Component { } } -impl PositionStorageSize of dojo::StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - dojo::StorageSize::::unpacked_size() - + dojo::StorageSize::::unpacked_size() - } - - #[inline(always)] - fn packed_size() -> usize { - dojo::StorageSize::::packed_size() + dojo::StorageSize::::packed_size() - } -} - #[cfg(test)] impl PositionPrintImpl of debug::PrintTrait { fn print(self: Position) { @@ -128,7 +122,7 @@ mod position { #[external(v0)] fn size(self: @ContractState) -> usize { - dojo::StorageSize::::unpacked_size() + dojo::SerdeLen::::len() } #[external(v0)] @@ -141,6 +135,13 @@ mod position { } } +impl SerdeLenPosition of dojo::SerdeLen { + #[inline(always)] + fn len() -> usize { + dojo::SerdeLen::::len() + dojo::SerdeLen::::len() + } +} + trait PositionTrait { @@ -166,7 +167,7 @@ struct Roles { role_ids: Array } -impl RolesComponent of dojo::component::Component { +impl RolesComponent of dojo::traits::Component { #[inline(always)] fn name(self: @Roles) -> felt252 { 'Roles' @@ -187,18 +188,6 @@ impl RolesComponent of dojo::component::Component { } } -impl RolesStorageSize of dojo::StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - dojo::StorageSize::>::unpacked_size() - } - - #[inline(always)] - fn packed_size() -> usize { - dojo::StorageSize::>::packed_size() - } -} - #[cfg(test)] impl RolesPrintImpl of debug::PrintTrait { fn print(self: Roles) { @@ -226,7 +215,7 @@ mod roles { #[external(v0)] fn size(self: @ContractState) -> usize { - dojo::StorageSize::::unpacked_size() + dojo::SerdeLen::::len() } #[external(v0)] @@ -239,6 +228,14 @@ mod roles { +impl RolesSerdeLen of dojo::SerdeLen { + #[inline(always)] + fn len() -> usize { + 5 + } +} + + use starknet::ContractAddress; struct Player { @@ -249,7 +246,7 @@ struct Player { name: felt252, } -impl PlayerComponent of dojo::component::Component { +impl PlayerComponent of dojo::traits::Component { #[inline(always)] fn name(self: @Player) -> felt252 { 'Player' @@ -272,18 +269,6 @@ impl PlayerComponent of dojo::component::Component { } } -impl PlayerStorageSize of dojo::StorageSize { - #[inline(always)] - fn unpacked_size() -> usize { - dojo::StorageSize::::unpacked_size() - } - - #[inline(always)] - fn packed_size() -> usize { - dojo::StorageSize::::packed_size() - } -} - #[cfg(test)] impl PlayerPrintImpl of debug::PrintTrait { fn print(self: Player) { @@ -315,7 +300,7 @@ mod player { #[external(v0)] fn size(self: @ContractState) -> usize { - dojo::StorageSize::::unpacked_size() + dojo::SerdeLen::::len() } #[external(v0)] @@ -328,6 +313,13 @@ mod player { } } +impl SerdeLenPlayer of dojo::SerdeLen { + #[inline(always)] + fn len() -> usize { + dojo::SerdeLen::::len() + } +} + //! > expected_diagnostics error: Component must define atleast one #[key] attribute --> dummy_file.cairo:31:8 diff --git a/crates/dojo-lang/src/serde.rs b/crates/dojo-lang/src/serde.rs new file mode 100644 index 0000000000..6cb1eb14d7 --- /dev/null +++ b/crates/dojo-lang/src/serde.rs @@ -0,0 +1,50 @@ +use cairo_lang_defs::patcher::RewriteNode; +use cairo_lang_syntax::node::ast::ItemStruct; +use cairo_lang_syntax::node::db::SyntaxGroup; +use cairo_lang_syntax::node::helpers::QueryAttrs; +use cairo_lang_syntax::node::TypedSyntaxNode; +use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; +use itertools::Itertools; + +/// A handler for Dojo code derives SerdeLen for a struct +/// Parameters: +/// * db: The semantic database. +/// * struct_ast: The AST of the struct. +/// Returns: +/// * A RewriteNode containing the generated code. +pub fn handle_serde_len_struct(db: &dyn SyntaxGroup, struct_ast: ItemStruct) -> RewriteNode { + RewriteNode::interpolate_patched( + " + impl SerdeLen$name$ of dojo::SerdeLen<$type$> { + #[inline(always)] + fn len() -> usize { + $len$ + } + } + ", + UnorderedHashMap::from([ + ("name".to_string(), RewriteNode::new_trimmed(struct_ast.name(db).as_syntax_node())), + ("type".to_string(), RewriteNode::new_trimmed(struct_ast.name(db).as_syntax_node())), + ( + "len".to_string(), + RewriteNode::Text( + struct_ast + .members(db) + .elements(db) + .iter() + .filter_map(|member| { + if member.has_attr(db, "key") { + return None; + } + + Some(format!( + "dojo::SerdeLen::<{}>::len()", + member.type_clause(db).ty(db).as_syntax_node().get_text(db), + )) + }) + .join(" + "), + ), + ), + ]), + ) +} diff --git a/examples/ecs/src/components.cairo b/examples/ecs/src/components.cairo index 398db8b7c1..f392d60765 100644 --- a/examples/ecs/src/components.cairo +++ b/examples/ecs/src/components.cairo @@ -11,16 +11,11 @@ enum Direction { Down: (), } -impl DirectionStorageSizeImpl of dojo::StorageSize { +impl SerdeLenDirection of dojo::SerdeLen { #[inline(always)] - fn unpacked_size() -> usize { + fn len() -> usize { 1 } - - #[inline(always)] - fn packed_size() -> usize { - 2 - } } impl DirectionPrintImpl of PrintTrait { @@ -47,7 +42,7 @@ impl DirectionIntoFelt252 of Into { } } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Moves { #[key] player: ContractAddress, @@ -55,7 +50,7 @@ struct Moves { last_direction: Direction } -#[derive(Component, Copy, Drop, Serde)] +#[derive(Component, Copy, Drop, Serde, SerdeLen)] struct Position { #[key] player: ContractAddress, diff --git a/examples/ecs/src/systems.cairo b/examples/ecs/src/systems.cairo index 46fda78a45..f234d2a93f 100644 --- a/examples/ecs/src/systems.cairo +++ b/examples/ecs/src/systems.cairo @@ -125,11 +125,11 @@ mod tests { let mut keys = array::ArrayTrait::new(); keys.append(caller.into()); - let moves = world.entity('Moves', keys.span(), 0, dojo::StorageSize::::unpacked_size()); + let moves = world.entity('Moves', keys.span(), 0, dojo::SerdeLen::::len()); assert(*moves[0] == 9, 'moves is wrong'); assert(*moves[1] == move::Direction::Right(()).into(), 'last direction is wrong'); let new_position = world - .entity('Position', keys.span(), 0, dojo::StorageSize::::unpacked_size()); + .entity('Position', keys.span(), 0, dojo::SerdeLen::::len()); assert(*new_position[0] == 11, 'position x is wrong'); assert(*new_position[1] == 10, 'position y is wrong'); }