diff --git a/BECNHMARKS.md b/BENCHMARKS.md similarity index 100% rename from BECNHMARKS.md rename to BENCHMARKS.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 74e6855..d8fee5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.3.0] + +### Fixed + +* Fix recursive types. + +### Added + +* Custom attribute to derive `Formula`, `Serialize` and `Deserialize` + when additional data is needed for deriving. + +### Changed + +* Derive macros do not accept attributes anymore. + Use custom attribute instead. +* Support manual generic bounds for formula deriving macro. +* Move packet writing into separate trait. + +## [0.2.0] * Reimplement with no unsafe code. * Fuse deserialization with cheap direct access and lazy deserialization diff --git a/Cargo.toml b/Cargo.toml index 99da303..2dcf88b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alkahest" -version = "0.2.0-rc.9" +version = "0.3.0" edition = "2021" authors = ["Zakarum "] license = "MIT OR Apache-2.0" @@ -14,6 +14,7 @@ description = "Fantastic serialization library with zero-overhead serialization alloc = [] # enables impls for types from `alloc` crate. std = ["alloc"] derive = ["alkahest-proc"] +inline-more = [] ## TODO: Control on value or type level? ## Keep features for defaults? @@ -21,12 +22,12 @@ fixed8 = [] # sets size of `FixedUsize` and `FixedIsize` to 8 bits. fixed16 = [] # sets size of `FixedUsize` and `FixedIsize` to 16 bits. fixed32 = [] # sets size of `FixedUsize` and `FixedIsize` to 32 bits. Default. fixed64 = [] # sets size of `FixedUsize` and `FixedIsize` to 64 bits. -default = ["alloc", "fixed32"] +default = ["alloc", "fixed32", "inline-more"] bincoded = ["bincode", "serde", "std"] [dependencies] -alkahest-proc = { version = "=0.2.0-rc.9", path = "proc", optional = true } +alkahest-proc = { version = "=0.3.0", path = "proc", optional = true } bincode = { version = "1.3", optional = true } serde = { version = "1.0", optional = true } @@ -37,5 +38,9 @@ rand = { version = "0.8", features = ["small_rng"] } name = "test" required-features = ["derive", "alloc"] +[[example]] +name = "profile" +required-features = ["derive", "alloc"] + [workspace] members = ["proc", "benchmark"] diff --git a/README.md b/README.md index a05da09..6816163 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,14 @@ This benchmark that mimics some game networking protocol. | | `alkahest` | `bincode` | `rkyv` | `speedy` | |:----------------|:-------------------------|:--------------------------------|:--------------------------------|:-------------------------------- | -| **`serialize`** | `10.75 us` (✅ **1.00x**) | `10.95 us` (✅ **1.02x slower**) | `12.24 us` (❌ *1.14x slower*) | `11.03 us` (✅ **1.03x slower**) | -| **`read`** | `1.43 us` (✅ **1.00x**) | `9.27 us` (❌ *6.47x slower*) | `2.13 us` (❌ *1.49x slower*) | `1.54 us` (✅ **1.07x slower**) | +| **`serialize`** | `10.69 us` (✅ **1.00x**) | `11.08 us` (✅ **1.04x slower**) | `12.43 us` (❌ *1.16x slower*) | `11.13 us` (✅ **1.04x slower**) | +| **`read`** | `1.19 us` (✅ **1.00x**) | `9.19 us` (❌ *7.74x slower*) | `2.10 us` (❌ *1.77x slower*) | `1.54 us` (❌ *1.30x slower*) | +--- +Made with [criterion-table](https://github.com/nu11ptr/criterion-table) -See also [benchmark results](./benches/BENCHMARKS.md) from (in draft until 0.2 release). + +See also [benchmark results](./BENCHMARKS.md) from (in draft until 0.2 release). ## Features @@ -122,7 +125,7 @@ The crate works using three fundamental traits. `Formula`, `Serialize` and `Deserialize`. There's also supporting trait - `BareFormula`. -*Alkahest* provides derive macros for `Formula`, `Serialize` and `Deserialize`. +*Alkahest* provides proc-macro `alkahest` for deriving `Formula`, `Serialize` and `Deserialize`. ### Formula @@ -262,10 +265,11 @@ formula, naturally it will be serialized using `bincode` crate. // This requires two default features - "alloc" and "derive". #[cfg(all(feature = "derive", feature = "alloc"))] fn main() { - use alkahest::{Formula, Serialize, Deserialize, serialize_to_vec, deserialize}; + use alkahest::{alkahest, serialize_to_vec, deserialize}; // Define simple formula. Make it self-serializable. - #[derive(Clone, Debug, PartialEq, Eq, Formula, Serialize, Deserialize)] + #[derive(Clone, Debug, PartialEq, Eq)] + #[alkahest(Formula, SerializeRef, Deserialize)] struct MyDataType { a: u32, b: Vec, @@ -284,10 +288,9 @@ fn main() { // This is default behavior for `Serialized` derive macro. // Some types required ownership transfer for serialization. // Notable example is iterators. - let size = serialize_to_vec::(&value, &mut data); + let (size, _) = serialize_to_vec::(&value, &mut data); - let (de, de_size) = deserialize::(&data).unwrap(); - assert_eq!(de_size, size); + let de = deserialize::(&data[..size]).unwrap(); assert_eq!(de, value); } diff --git a/benchmark/bench.json b/benchmark/bench.json new file mode 100644 index 0000000..89bf1bf --- /dev/null +++ b/benchmark/bench.json @@ -0,0 +1,12 @@ +{"reason":"benchmark-complete","id":"net-packet/alkahest/serialize","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_alkahest/serialize","iteration_count":[93,186,279,372,465,558,651,744,837,930,1023,1116,1209,1302,1395,1488,1581,1674,1767,1860,1953,2046,2139,2232,2325,2418,2511,2604,2697,2790,2883,2976,3069,3162,3255,3348,3441,3534,3627,3720,3813,3906,3999,4092,4185,4278,4371,4464,4557,4650,4743,4836,4929,5022,5115,5208,5301,5394,5487,5580,5673,5766,5859,5952,6045,6138,6231,6324,6417,6510,6603,6696,6789,6882,6975,7068,7161,7254,7347,7440,7533,7626,7719,7812,7905,7998,8091,8184,8277,8370,8463,8556,8649,8742,8835,8928,9021,9114,9207,9300],"measured_values":[1010875.0,1997458.0,2986792.0,3982292.0,4982959.0,5972167.0,6986292.0,8001583.0,8970042.0,9989542.0,11026958.0,11968791.0,12948333.0,13969334.0,15048708.0,15977375.0,16934417.0,17927125.0,18936750.0,19892541.0,21085291.0,21945708.0,22934375.0,23942625.0,25424292.0,25900250.0,26922959.0,27877458.0,28918000.0,29917208.0,30870500.0,31882875.0,32887291.0,33934291.0,34840458.0,35798584.0,36797333.0,37810416.0,38808458.0,39756292.0,40822000.0,41888708.0,42881084.0,43896667.0,44880458.0,45871416.0,46860666.0,47865750.0,48858708.0,49864958.0,51267125.0,51890792.0,52842542.0,53814583.0,54784750.0,55852375.0,56833916.0,57804791.0,58704083.0,59704792.0,60699584.0,61571750.0,62556792.0,63526375.0,64541083.0,65548209.0,66423375.0,67405458.0,68400875.0,69287583.0,70372500.0,71321458.0,72355666.0,73409583.0,74545166.0,75488125.0,76440542.0,77425083.0,78403625.0,79365042.0,80355625.0,82079250.0,82608000.0,83699833.0,84615000.0,85624750.0,86655333.0,87584000.0,88596416.0,89554000.0,90573375.0,91446917.0,92939625.0,93310583.0,94167292.0,95214542.0,96236666.0,97222500.0,98349542.0,99137792.0],"unit":"ns","throughput":[],"typical":{"estimate":10691.696496279383,"lower_bound":10684.863394197173,"upper_bound":10699.22257615322,"unit":"ns"},"mean":{"estimate":10709.577465396782,"lower_bound":10701.833588082418,"upper_bound":10718.185761918936,"unit":"ns"},"median":{"estimate":10706.887727045845,"lower_bound":10702.277561148529,"upper_bound":10715.767224213461,"unit":"ns"},"median_abs_dev":{"estimate":25.76944104260991,"lower_bound":18.048223902027644,"upper_bound":35.01036302771098,"unit":"ns"},"slope":{"estimate":10691.696496279383,"lower_bound":10684.863394197173,"upper_bound":10699.22257615322,"unit":"ns"},"change":{"mean":{"estimate":-0.020326151049254504,"lower_bound":-0.02135903930122774,"upper_bound":-0.019366935530664597,"unit":"%"},"median":{"estimate":-0.020230866658418334,"lower_bound":-0.02086056548582338,"upper_bound":-0.01923983269511842,"unit":"%"},"change":"Improved"}} +{"reason":"benchmark-complete","id":"net-packet/alkahest/read","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_alkahest/read","iteration_count":[832,1664,2496,3328,4160,4992,5824,6656,7488,8320,9152,9984,10816,11648,12480,13312,14144,14976,15808,16640,17472,18304,19136,19968,20800,21632,22464,23296,24128,24960,25792,26624,27456,28288,29120,29952,30784,31616,32448,33280,34112,34944,35776,36608,37440,38272,39104,39936,40768,41600,42432,43264,44096,44928,45760,46592,47424,48256,49088,49920,50752,51584,52416,53248,54080,54912,55744,56576,57408,58240,59072,59904,60736,61568,62400,63232,64064,64896,65728,66560,67392,68224,69056,69888,70720,71552,72384,73216,74048,74880,75712,76544,77376,78208,79040,79872,80704,81536,82368,83200],"measured_values":[999875.0,1985709.0,2977958.0,3960916.0,4953958.0,5927000.0,6954458.0,7920584.0,8914625.0,9884333.0,10860709.0,11870542.0,12842000.0,13865459.0,14920125.0,15798875.0,16783833.0,17812209.0,18774542.0,19748792.0,20780333.0,21720125.0,22709542.0,23884959.0,24698292.0,25690875.0,26673375.0,27652250.0,28679584.0,29630625.0,30644584.0,31621208.0,32630291.0,33607958.0,34978000.0,35567416.0,36549166.0,37530583.0,38535250.0,39665208.0,40771791.0,41711541.0,42861791.0,43861000.0,44665292.0,45619458.0,46605500.0,47478458.0,48409541.0,49397166.0,50488208.0,51385209.0,52251042.0,53281417.0,54268708.0,55242416.0,56619375.0,57165708.0,58226958.0,59257292.0,60428125.0,61153333.0,62185959.0,63332167.0,64238458.0,65328125.0,66098750.0,67037709.0,68058958.0,69174000.0,70419500.0,71402750.0,72375916.0,73275291.0,74397958.0,75007167.0,76005667.0,77347834.0,78276083.0,79319125.0,80306458.0,81264750.0,82142000.0,82792292.0,84593334.0,84786917.0,85779084.0,86814792.0,87758292.0,88736542.0,89767333.0,90717292.0,91994959.0,93099458.0,94172708.0,94647667.0,96034167.0,96668416.0,97806250.0,98728167.0],"unit":"ns","throughput":[],"typical":{"estimate":1188.418849404207,"lower_bound":1187.654150762463,"upper_bound":1189.2333913620787,"unit":"ns"},"mean":{"estimate":1189.155979066048,"lower_bound":1188.4717374581276,"upper_bound":1189.8837461030826,"unit":"ns"},"median":{"estimate":1187.9310512204142,"lower_bound":1187.431875,"upper_bound":1189.3811410757212,"unit":"ns"},"median_abs_dev":{"estimate":3.240545220602199,"lower_bound":2.380010126102089,"upper_bound":4.00377555460591,"unit":"ns"},"slope":{"estimate":1188.418849404207,"lower_bound":1187.654150762463,"upper_bound":1189.2333913620787,"unit":"ns"},"change":{"mean":{"estimate":-0.008565756491871412,"lower_bound":-0.009971114341065582,"upper_bound":-0.0072045342731808905,"unit":"%"},"median":{"estimate":-0.009107414268741842,"lower_bound":-0.01086219503578209,"upper_bound":-0.006679953330653374,"unit":"%"},"change":"NoChange"}} +{"reason":"group-complete","group_name":"net-packet/alkahest","benchmarks":["net-packet/alkahest/serialize","net-packet/alkahest/read"],"report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_alkahest"} +{"reason":"benchmark-complete","id":"net-packet/bincode/serialize","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_bincode/serialize","iteration_count":[90,180,270,360,450,540,630,720,810,900,990,1080,1170,1260,1350,1440,1530,1620,1710,1800,1890,1980,2070,2160,2250,2340,2430,2520,2610,2700,2790,2880,2970,3060,3150,3240,3330,3420,3510,3600,3690,3780,3870,3960,4050,4140,4230,4320,4410,4500,4590,4680,4770,4860,4950,5040,5130,5220,5310,5400,5490,5580,5670,5760,5850,5940,6030,6120,6210,6300,6390,6480,6570,6660,6750,6840,6930,7020,7110,7200,7290,7380,7470,7560,7650,7740,7830,7920,8010,8100,8190,8280,8370,8460,8550,8640,8730,8820,8910,9000],"measured_values":[1005792.0,1990750.0,2982416.0,3928166.0,4898541.0,5944000.0,6900167.0,7987958.0,8974250.0,9997417.0,10986500.0,11950125.0,12945791.0,13950750.0,15032291.0,15938416.0,16933917.0,18098917.0,18857709.0,19937292.0,20941125.0,22000709.0,22901458.0,23912416.0,24954625.0,25943500.0,26977459.0,27906167.0,28960292.0,29951583.0,31105959.0,32128792.0,33121333.0,34127708.0,35135042.0,36110084.0,37082333.0,38058125.0,38966708.0,39904333.0,40881167.0,41930750.0,42800625.0,43725042.0,44784500.0,45566459.0,46751583.0,48117666.0,48620500.0,49603917.0,50557125.0,51683375.0,52717542.0,53712583.0,54643666.0,55566125.0,56597125.0,57654292.0,58812875.0,59632333.0,60516375.0,61544083.0,62704459.0,63667708.0,64713500.0,65957458.0,66582500.0,67505833.0,68544833.0,69573917.0,70680167.0,71749542.0,72825625.0,73849083.0,74957084.0,75991333.0,76996958.0,77992750.0,79196541.0,80111916.0,80873708.0,81445834.0,82519917.0,83544417.0,84590875.0,85809834.0,86822708.0,88010541.0,89051333.0,90051209.0,91170125.0,92257667.0,92961334.0,93726208.0,94667333.0,95363709.0,96414042.0,97299584.0,98646042.0,99449583.0],"unit":"ns","throughput":[],"typical":{"estimate":11076.13470433312,"lower_bound":11067.042654685814,"upper_bound":11085.364418406543,"unit":"ns"},"mean":{"estimate":11074.582741258035,"lower_bound":11064.976716464726,"upper_bound":11083.834749883183,"unit":"ns"},"median":{"estimate":11072.331437621833,"lower_bound":11062.136752136752,"upper_bound":11085.53940245478,"unit":"ns"},"median_abs_dev":{"estimate":43.4413513697872,"lower_bound":33.39445874620996,"upper_bound":52.41389950425247,"unit":"ns"},"slope":{"estimate":11076.13470433312,"lower_bound":11067.042654685814,"upper_bound":11085.364418406543,"unit":"ns"},"change":{"mean":{"estimate":-0.00917408294936628,"lower_bound":-0.011454178421561934,"upper_bound":-0.006959720463482139,"unit":"%"},"median":{"estimate":-0.01232904838988802,"lower_bound":-0.015847775274933196,"upper_bound":-0.00191395976562217,"unit":"%"},"change":"NoChange"}} +{"reason":"benchmark-complete","id":"net-packet/bincode/read","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_bincode/read","iteration_count":[107,214,321,428,535,642,749,856,963,1070,1177,1284,1391,1498,1605,1712,1819,1926,2033,2140,2247,2354,2461,2568,2675,2782,2889,2996,3103,3210,3317,3424,3531,3638,3745,3852,3959,4066,4173,4280,4387,4494,4601,4708,4815,4922,5029,5136,5243,5350,5457,5564,5671,5778,5885,5992,6099,6206,6313,6420,6527,6634,6741,6848,6955,7062,7169,7276,7383,7490,7597,7704,7811,7918,8025,8132,8239,8346,8453,8560,8667,8774,8881,8988,9095,9202,9309,9416,9523,9630,9737,9844,9951,10058,10165,10272,10379,10486,10593,10700],"measured_values":[1002792.0,1981834.0,2965750.0,3954000.0,5003916.0,5930416.0,6909958.0,7939834.0,8886209.0,9914583.0,10882625.0,11933834.0,12855833.0,13879166.0,14908041.0,15780750.0,16810833.0,17762041.0,18751750.0,19756542.0,20678292.0,21646417.0,22670458.0,23735791.0,24616500.0,25614500.0,26595750.0,27599208.0,28573958.0,29515167.0,30507375.0,31492916.0,32496250.0,33438167.0,34487417.0,35521417.0,36429959.0,37451667.0,38349750.0,39395709.0,40330792.0,41344042.0,42356917.0,43305958.0,44298250.0,45391458.0,46412875.0,47390250.0,48336084.0,49272542.0,50256375.0,51293583.0,52272084.0,53194042.0,54167583.0,55268625.0,56226708.0,57613125.0,58131083.0,59156375.0,60115167.0,61101125.0,62104833.0,63126709.0,64134542.0,65086875.0,66045167.0,67072209.0,68009542.0,69075917.0,70042292.0,70914292.0,72165708.0,72716666.0,73806166.0,74849917.0,75831625.0,76846834.0,77672625.0,78463167.0,79633750.0,80617583.0,81497875.0,82332541.0,83325291.0,84601166.0,85330833.0,86411792.0,87331500.0,88332041.0,89308208.0,90259041.0,91347750.0,92178417.0,93113834.0,94065292.0,95390292.0,96041417.0,97200333.0,98336583.0],"unit":"ns","throughput":[],"typical":{"estimate":9192.50679761183,"lower_bound":9186.816850039082,"upper_bound":9198.942108272162,"unit":"ns"},"mean":{"estimate":9212.345943293376,"lower_bound":9205.700522180072,"upper_bound":9219.497983501988,"unit":"ns"},"median":{"estimate":9208.708728879887,"lower_bound":9204.36756025578,"upper_bound":9212.605244804017,"unit":"ns"},"median_abs_dev":{"estimate":21.409572270003032,"lower_bound":16.37198708667274,"upper_bound":28.35368610819962,"unit":"ns"},"slope":{"estimate":9192.50679761183,"lower_bound":9186.816850039082,"upper_bound":9198.942108272162,"unit":"ns"},"change":{"mean":{"estimate":-0.016520737373101446,"lower_bound":-0.01890284819238658,"upper_bound":-0.014266273704381492,"unit":"%"},"median":{"estimate":-0.01396602236018707,"lower_bound":-0.014903890929811201,"upper_bound":-0.01319720983135253,"unit":"%"},"change":"Improved"}} +{"reason":"group-complete","group_name":"net-packet/bincode","benchmarks":["net-packet/bincode/serialize","net-packet/bincode/read"],"report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_bincode"} +{"reason":"benchmark-complete","id":"net-packet/rkyv/serialize","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_rkyv/serialize","iteration_count":[81,162,243,324,405,486,567,648,729,810,891,972,1053,1134,1215,1296,1377,1458,1539,1620,1701,1782,1863,1944,2025,2106,2187,2268,2349,2430,2511,2592,2673,2754,2835,2916,2997,3078,3159,3240,3321,3402,3483,3564,3645,3726,3807,3888,3969,4050,4131,4212,4293,4374,4455,4536,4617,4698,4779,4860,4941,5022,5103,5184,5265,5346,5427,5508,5589,5670,5751,5832,5913,5994,6075,6156,6237,6318,6399,6480,6561,6642,6723,6804,6885,6966,7047,7128,7209,7290,7371,7452,7533,7614,7695,7776,7857,7938,8019,8100],"measured_values":[1012917.0,2008083.0,3005708.0,4007417.0,4990500.0,6018125.0,7029916.0,8059500.0,9014417.0,9888625.0,10981542.0,11932083.0,13016125.0,13966833.0,15004292.0,16023541.0,16950750.0,18057959.0,19140042.0,21718834.0,20804667.0,22021208.0,23138000.0,24235459.0,25159125.0,26476625.0,27267042.0,28227166.0,29162041.0,30220625.0,31257667.0,32357042.0,33370709.0,34145000.0,35067541.0,36289625.0,37132542.0,38149334.0,39036542.0,40803375.0,41400583.0,42360625.0,42864417.0,44220709.0,44781500.0,45381583.0,46692875.0,48075542.0,49354333.0,49905500.0,51112083.0,52898958.0,52637958.0,54082833.0,57293750.0,56679625.0,57712792.0,59507792.0,60642125.0,60574792.0,62075333.0,62913792.0,63516334.0,64929916.0,65480250.0,66931750.0,67346958.0,68798084.0,69549084.0,70542917.0,71363875.0,73112916.0,73455875.0,75636625.0,76093875.0,76723833.0,78206083.0,77623167.0,78948292.0,79622083.0,80736208.0,82648250.0,83709833.0,83951375.0,84489541.0,85621666.0,88223708.0,88733375.0,89755792.0,90179875.0,90433875.0,91472875.0,93119625.0,95155417.0,95472084.0,96265292.0,98307209.0,98983625.0,101371583.0,100524041.0],"unit":"ns","throughput":[],"typical":{"estimate":12432.854746728404,"lower_bound":12405.33973044752,"upper_bound":12461.02938205142,"unit":"ns"},"mean":{"estimate":12429.46710570337,"lower_bound":12403.157850178277,"upper_bound":12460.041884946046,"unit":"ns"},"median":{"estimate":12417.207535121328,"lower_bound":12392.73898898899,"upper_bound":12443.277627220717,"unit":"ns"},"median_abs_dev":{"estimate":85.8739849181085,"lower_bound":68.41524437900598,"upper_bound":122.0986870942322,"unit":"ns"},"slope":{"estimate":12432.854746728404,"lower_bound":12405.33973044752,"upper_bound":12461.02938205142,"unit":"ns"},"change":{"mean":{"estimate":-0.0025453528891822286,"lower_bound":-0.005951860232446584,"upper_bound":0.0011949924574439271,"unit":"%"},"median":{"estimate":-0.0008277910063320126,"lower_bound":-0.0033247002342529086,"upper_bound":0.0023014107638310577,"unit":"%"},"change":"NoChange"}} +{"reason":"benchmark-complete","id":"net-packet/rkyv/read","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_rkyv/read","iteration_count":[467,934,1401,1868,2335,2802,3269,3736,4203,4670,5137,5604,6071,6538,7005,7472,7939,8406,8873,9340,9807,10274,10741,11208,11675,12142,12609,13076,13543,14010,14477,14944,15411,15878,16345,16812,17279,17746,18213,18680,19147,19614,20081,20548,21015,21482,21949,22416,22883,23350,23817,24284,24751,25218,25685,26152,26619,27086,27553,28020,28487,28954,29421,29888,30355,30822,31289,31756,32223,32690,33157,33624,34091,34558,35025,35492,35959,36426,36893,37360,37827,38294,38761,39228,39695,40162,40629,41096,41563,42030,42497,42964,43431,43898,44365,44832,45299,45766,46233,46700],"measured_values":[1004042.0,1987167.0,2983166.0,3981750.0,5004792.0,6014792.0,6989833.0,8007500.0,8879209.0,9838833.0,10818916.0,11797625.0,12767042.0,14017417.0,14874667.0,15893209.0,16766500.0,17824125.0,19021083.0,19816834.0,20582292.0,21660750.0,22810416.0,23811125.0,25040167.0,25978417.0,26360667.0,27819875.0,29249000.0,29878541.0,30723250.0,31707459.0,32569584.0,33635750.0,34749542.0,35778417.0,36382875.0,37425166.0,38511166.0,39665708.0,41124584.0,41725208.0,42533500.0,43747958.0,45028416.0,45528708.0,46547792.0,47160250.0,48039625.0,49355042.0,50073292.0,51629417.0,52361000.0,53832708.0,54812500.0,55572583.0,56368917.0,57106917.0,58377917.0,58969417.0,59279000.0,61057625.0,61402333.0,62817541.0,63354291.0,64427333.0,65080584.0,67640542.0,67959250.0,69362667.0,70509750.0,70893792.0,71933459.0,72584917.0,73159917.0,74131292.0,75032542.0,76028792.0,77577375.0,78950041.0,79803250.0,80678958.0,81315375.0,82236667.0,83298458.0,84678083.0,85693625.0,86398208.0,87439875.0,88579625.0,89605958.0,90343625.0,91139083.0,92200750.0,93120875.0,93476875.0,94803375.0,95393875.0,96588041.0,97834375.0],"unit":"ns","throughput":[],"typical":{"estimate":2104.164955956748,"lower_bound":2101.158554311094,"upper_bound":2107.522852519286,"unit":"ns"},"mean":{"estimate":2113.826567526718,"lower_bound":2110.4140810421754,"upper_bound":2117.3335105311176,"unit":"ns"},"median":{"estimate":2110.979181153556,"lower_bound":2108.355497304881,"upper_bound":2118.423322408652,"unit":"ns"},"median_abs_dev":{"estimate":18.46521355258715,"lower_bound":14.533989714599867,"upper_bound":22.448288714931348,"unit":"ns"},"slope":{"estimate":2104.164955956748,"lower_bound":2101.158554311094,"upper_bound":2107.522852519286,"unit":"ns"},"change":{"mean":{"estimate":0.00828935421265009,"lower_bound":0.006184187069442844,"upper_bound":0.010330524494234744,"unit":"%"},"median":{"estimate":0.007889749310224348,"lower_bound":0.00611870897770328,"upper_bound":0.011298687356757542,"unit":"%"},"change":"NoChange"}} +{"reason":"group-complete","group_name":"net-packet/rkyv","benchmarks":["net-packet/rkyv/serialize","net-packet/rkyv/read"],"report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_rkyv"} +{"reason":"benchmark-complete","id":"net-packet/speedy/serialize","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_speedy/serialize","iteration_count":[90,180,270,360,450,540,630,720,810,900,990,1080,1170,1260,1350,1440,1530,1620,1710,1800,1890,1980,2070,2160,2250,2340,2430,2520,2610,2700,2790,2880,2970,3060,3150,3240,3330,3420,3510,3600,3690,3780,3870,3960,4050,4140,4230,4320,4410,4500,4590,4680,4770,4860,4950,5040,5130,5220,5310,5400,5490,5580,5670,5760,5850,5940,6030,6120,6210,6300,6390,6480,6570,6660,6750,6840,6930,7020,7110,7200,7290,7380,7470,7560,7650,7740,7830,7920,8010,8100,8190,8280,8370,8460,8550,8640,8730,8820,8910,9000],"measured_values":[1015208.0,2022875.0,3003417.0,4051417.0,5037959.0,6017000.0,7014917.0,8069542.0,9032417.0,10056917.0,11067125.0,12073542.0,13053208.0,14049208.0,15465958.0,16064292.0,17089459.0,18064833.0,19047375.0,20048833.0,21037791.0,22115500.0,23138583.0,24102250.0,25046958.0,26096541.0,27058041.0,28088875.0,29066125.0,30087375.0,30993541.0,31961666.0,32894791.0,33905917.0,34882750.0,36037167.0,37147500.0,38124833.0,39116042.0,40140792.0,41149375.0,42159541.0,43118083.0,43974709.0,44972667.0,45974958.0,47240666.0,47899166.0,49093333.0,50061959.0,51082833.0,51972375.0,53026708.0,54065666.0,55089541.0,55996958.0,56968667.0,57980750.0,58882583.0,59807917.0,60960750.0,62129041.0,63092708.0,64112709.0,65329875.0,66012209.0,66799083.0,67635958.0,68737042.0,69705750.0,70522708.0,71546875.0,72766416.0,73800458.0,74990917.0,76563500.0,77377375.0,78209458.0,79358500.0,80094083.0,80840292.0,81907834.0,82809792.0,83763333.0,84736291.0,86354500.0,87417584.0,88272792.0,89278000.0,90216042.0,91396542.0,92431375.0,93825667.0,94911375.0,95927167.0,96847167.0,97715292.0,98582084.0,99135583.0,100069791.0],"unit":"ns","throughput":[],"typical":{"estimate":11133.048605257542,"lower_bound":11120.080744998346,"upper_bound":11145.562934663263,"unit":"ns"},"mean":{"estimate":11139.27111476003,"lower_bound":11129.041075264397,"upper_bound":11150.462029732667,"unit":"ns"},"median":{"estimate":11137.11466155811,"lower_bound":11128.330965207631,"upper_bound":11145.891749137802,"unit":"ns"},"median_abs_dev":{"estimate":41.266527759375265,"lower_bound":28.62794674656248,"upper_bound":50.242292715230235,"unit":"ns"},"slope":{"estimate":11133.048605257542,"lower_bound":11120.080744998346,"upper_bound":11145.562934663263,"unit":"ns"},"change":{"mean":{"estimate":0.021143112377389528,"lower_bound":0.019413767440181806,"upper_bound":0.022813759621199376,"unit":"%"},"median":{"estimate":0.022674194105019252,"lower_bound":0.02050960474465824,"upper_bound":0.02428094622489141,"unit":"%"},"change":"Regressed"}} +{"reason":"benchmark-complete","id":"net-packet/speedy/read","report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_speedy/read","iteration_count":[639,1278,1917,2556,3195,3834,4473,5112,5751,6390,7029,7668,8307,8946,9585,10224,10863,11502,12141,12780,13419,14058,14697,15336,15975,16614,17253,17892,18531,19170,19809,20448,21087,21726,22365,23004,23643,24282,24921,25560,26199,26838,27477,28116,28755,29394,30033,30672,31311,31950,32589,33228,33867,34506,35145,35784,36423,37062,37701,38340,38979,39618,40257,40896,41535,42174,42813,43452,44091,44730,45369,46008,46647,47286,47925,48564,49203,49842,50481,51120,51759,52398,53037,53676,54315,54954,55593,56232,56871,57510,58149,58788,59427,60066,60705,61344,61983,62622,63261,63900],"measured_values":[1006625.0,2008834.0,2955375.0,3937084.0,4951792.0,5917458.0,6897791.0,7944791.0,8882416.0,9928417.0,10838584.0,11874958.0,12810416.0,13796958.0,14927500.0,15878959.0,16814167.0,18189125.0,18727125.0,19733083.0,20694709.0,21685500.0,22741417.0,23716083.0,24651500.0,25647500.0,26622500.0,27596875.0,28574667.0,29612542.0,30577333.0,31566916.0,32541916.0,33555625.0,34496875.0,35493167.0,36503125.0,37465500.0,38434417.0,39462041.0,40418083.0,41426000.0,42411208.0,43378625.0,44348917.0,45354292.0,46348167.0,47263042.0,48596583.0,49226416.0,50255625.0,51179167.0,52186333.0,53167250.0,54192375.0,55170292.0,56334959.0,57296500.0,58339042.0,59138834.0,60155000.0,61287125.0,62255500.0,63273875.0,64000709.0,65536125.0,66207875.0,67018541.0,67972416.0,68936375.0,69909375.0,70876959.0,71844542.0,72890375.0,73863584.0,74820583.0,75793625.0,76783958.0,77950833.0,79249000.0,79735458.0,80776834.0,81736500.0,82746959.0,83712542.0,84760167.0,85868292.0,86689167.0,87627167.0,88656292.0,89602292.0,90955250.0,91564042.0,92940833.0,93580500.0,94523291.0,95588500.0,96555083.0,97520541.0,98656458.0],"unit":"ns","throughput":[],"typical":{"estimate":1542.9193852010806,"lower_bound":1542.2795273981053,"upper_bound":1543.6544559671734,"unit":"ns"},"mean":{"estimate":1544.7035118565555,"lower_bound":1543.5392399153927,"upper_bound":1546.097306120943,"unit":"ns"},"median":{"estimate":1542.5287783942713,"lower_bound":1542.1945748565468,"upper_bound":1543.244293936051,"unit":"ns"},"median_abs_dev":{"estimate":2.0693141546164315,"lower_bound":1.5555190553261924,"upper_bound":2.98739129787119,"unit":"ns"},"slope":{"estimate":1542.9193852010806,"lower_bound":1542.2795273981053,"upper_bound":1543.6544559671734,"unit":"ns"},"change":{"mean":{"estimate":-0.016821656593056455,"lower_bound":-0.018140957261407315,"upper_bound":-0.015367874155973422,"unit":"%"},"median":{"estimate":-0.018522723829514032,"lower_bound":-0.018994328924142212,"upper_bound":-0.01791084239936569,"unit":"%"},"change":"Improved"}} +{"reason":"group-complete","group_name":"net-packet/speedy","benchmarks":["net-packet/speedy/serialize","net-packet/speedy/read"],"report_directory":"/Users/romanbarykin/code/alkahest/target/criterion/reports/net-packet_speedy"} diff --git a/benchmark/benches/benchmark.rs b/benchmark/benches/benchmark.rs index a5630e1..de38151 100644 --- a/benchmark/benches/benchmark.rs +++ b/benchmark/benches/benchmark.rs @@ -11,7 +11,7 @@ extern crate rkyv; #[cfg(feature = "speedy")] extern crate speedy; -use alkahest::{Deserialize, Formula, Lazy, Ref, SerIter, Serialize}; +use alkahest::{alkahest, Deserialize, Formula, Lazy, Ref, SerIter, Serialize}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; #[cfg(feature = "rkyv")] @@ -32,9 +32,9 @@ pub enum GameMessage { Server(ServerMessage), } -#[derive(Debug, Deserialize)] +#[derive(Debug)] #[cfg_attr(feature = "speedy", derive(speedy::Readable))] -#[alkahest(GameMessage)] +#[alkahest(Deserialize<'de, GameMessage>)] pub enum GameMessageRead<'de> { Client(ClientMessageRead<'de>), Server(ServerMessageRead<'de>), @@ -50,9 +50,9 @@ pub enum ClientMessage { Chat(String), } -#[derive(Debug, Deserialize)] +#[derive(Debug)] #[cfg_attr(feature = "speedy", derive(speedy::Readable))] -#[alkahest(ClientMessage)] +#[alkahest(Deserialize<'de, ClientMessage>)] pub enum ClientMessageRead<'de> { ClientData { nickname: &'de str, clan: &'de str }, Chat(&'de str), @@ -68,9 +68,9 @@ pub enum ServerMessage { ClientChat { client_id: u64, message: String }, } -#[derive(Debug, Deserialize)] +#[derive(Debug)] #[cfg_attr(feature = "speedy", derive(speedy::Readable))] -#[alkahest(ServerMessage)] +#[alkahest(Deserialize<'de, ServerMessage>)] pub enum ServerMessageRead<'de> { ServerData(u64), ClientChat { client_id: u64, message: &'de str }, @@ -85,14 +85,14 @@ pub struct NetPacket { pub game_messages: Vec, } -#[derive(Debug, Serialize)] -#[alkahest(owned(for NetPacket where G: Serialize<[X]>))] +#[derive(Debug)] +#[alkahest(for Serialize> where G: Serialize<[X]>)] pub struct NetPacketWrite { pub game_messages: G, } -#[derive(Debug, Deserialize)] -#[alkahest(NetPacket where G: Formula)] +#[derive(Debug)] +#[alkahest(Deserialize<'de, NetPacket> where G: Formula)] pub struct NetPacketRead<'de, G> { pub game_messages: Lazy<'de, [G]>, } @@ -126,26 +126,28 @@ pub fn criterion_benchmark(c: &mut Criterion) { let mut rng = SmallRng::seed_from_u64(42); const LEN: usize = 200; + let mut size = 0; { let mut group = c.benchmark_group("net-packet/alkahest"); group.bench_function("serialize", |b| { b.iter(|| { - alkahest::serialize_to_vec::, _>( + size = alkahest::serialize_to_vec::, _>( NetPacketWrite { game_messages: SerIter(messages(rng.clone(), black_box(LEN))), }, &mut buffer, - ); + ) + .0; }) }); group.bench_function("read", |b| { b.iter(|| { - let (packet, _) = alkahest::deserialize::< + let packet = alkahest::deserialize::< NetPacket, NetPacketRead, - >(&buffer[..]) + >(&buffer[..size]) .unwrap(); for message in packet.game_messages.iter::() { diff --git a/examples/profile.rs b/examples/profile.rs new file mode 100644 index 0000000..bba18cd --- /dev/null +++ b/examples/profile.rs @@ -0,0 +1,85 @@ +use std::{hint::black_box, mem::size_of}; + +use alkahest::*; +use rand::{distributions::Standard, prelude::Distribution, Rng}; + +#[derive(Clone, Copy)] +#[alkahest(Formula, Serialize, SerializeRef, Deserialize)] +pub struct Vector3 { + pub x: f32, + pub y: f32, + pub z: f32, +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Vector3 { + Vector3 { + x: rng.gen(), + y: rng.gen(), + z: rng.gen(), + } + } +} + +#[derive(Clone, Copy)] +#[alkahest(Formula, Serialize, SerializeRef, Deserialize)] +pub struct Triangle { + pub v0: Vector3, + pub v1: Vector3, + pub v2: Vector3, + pub normal: Vector3, +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Triangle { + let v0 = rng.gen(); + let v1 = rng.gen(); + let v2 = rng.gen(); + let normal = rng.gen(); + + Triangle { v0, v1, v2, normal } + } +} + +#[alkahest(Formula)] +pub struct MeshFormula { + pub triangles: [Triangle], +} + +#[derive(Clone)] +#[alkahest(Serialize, SerializeRef, Deserialize<'_, MeshFormula>)] +pub struct Mesh { + pub triangles: Vec, +} + +#[alkahest(Deserialize<'a, MeshFormula>)] +pub struct LazyMesh<'a> { + pub triangles: Lazy<'a, [Triangle]>, +} + +#[inline(always)] +fn do_serialize(mesh: &Mesh, buffer: &mut [u8]) -> usize { + alkahest::write_packet_unchecked::(&mesh, buffer) +} + +fn main() { + const TRIG_COUNT: usize = 100_000; + + let mesh = Mesh { + triangles: rand::thread_rng() + .sample_iter(Standard) + .take(TRIG_COUNT) + .collect(), + }; + + let mut mesh = black_box(mesh); + + let mut buffer = Vec::new(); + buffer.resize(TRIG_COUNT * size_of::() + 32, 0); + + for _ in 0..10_000 { + let size = do_serialize(&mesh, &mut buffer); + black_box(&buffer[..size]); + mesh = black_box(mesh); + } +} diff --git a/examples/test.rs b/examples/test.rs index f8d800c..acdaed9 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -2,50 +2,53 @@ use alkahest::*; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Formula, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[alkahest(Formula, Serialize, SerializeRef, Deserialize)] struct X; -#[derive(Debug, Formula)] +#[derive(Debug)] +#[alkahest(Formula)] struct Test { a: u32, b: X, c: T, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -#[alkahest(serialize(for Test where U: Formula, for<'ser> &'ser T: Serialize))] -#[alkahest(serialize(owned(for Test where U: Formula, T: Serialize)))] -#[alkahest(deserialize(for<'de, U: ?Sized> Test where U: Formula, T: Deserialize<'de, U>))] +#[derive(Clone, Copy, Debug)] +#[alkahest(for SerializeRef> where U: Formula, for<'ser> &'ser T: Serialize)] +#[alkahest(for Serialize> where U: Formula, T: Serialize)] +#[alkahest(for<'de, U: ?Sized> Deserialize<'de, Test> where U: Formula, T: Deserialize<'de, U>)] struct TestS { a: u32, b: X, c: T, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Formula, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[alkahest(Formula, Serialize, Deserialize)] enum Test2 { Unit, Tuple(u32, u64), Struct { a: u32, b: u64 }, } -#[derive(Clone, Copy, PartialEq, Eq, Serialize)] -#[alkahest(Test2, @Unit)] +#[derive(Clone, Copy, PartialEq, Eq)] +#[alkahest(Serialize)] struct Test2U; -#[derive(Clone, Copy, PartialEq, Eq, Serialize)] -#[alkahest(Test2, @Tuple)] +#[derive(Clone, Copy, PartialEq, Eq)] +#[alkahest(Serialize)] struct Test2T(u32, u64); -#[derive(Clone, Copy, PartialEq, Eq, Serialize)] -#[alkahest(Test2, @Struct)] +#[derive(Clone, Copy, PartialEq, Eq)] +#[alkahest(Serialize)] struct Test2S { a: u32, b: u64, } -#[derive(Clone, Copy, PartialEq, Eq, Serialize)] -#[alkahest(Test2)] +#[derive(Clone, Copy, PartialEq, Eq)] +#[alkahest(Serialize)] enum Test2E { Unit, // Tuple(u32, u64), // variants may be omitted for `Serialize` @@ -58,12 +61,12 @@ fn main() { let value = ("qwe", "rty"); let size = serialized_size::<[Foo], _>([value]); - let mut buffer = vec![0u8; size]; + let mut buffer = vec![0u8; size.0]; let size = serialize::<[Foo], _>([value], &mut buffer).unwrap(); - assert_eq!(size, buffer.len()); + assert_eq!(size.0, buffer.len()); - let foo = deserialize::<[Foo], Vec<(&str, &str)>>(&buffer).unwrap().0; + let foo = deserialize::<[Foo], Vec<(&str, &str)>>(&buffer).unwrap(); assert_eq!(foo, vec![("qwe", "rty")]); type MyFormula = Test>>; @@ -75,11 +78,10 @@ fn main() { }; let size = serialized_size::(value.clone()); - let mut buffer = vec![0; size]; + let mut buffer = vec![0; size.0]; let size = serialize::(value.clone(), &mut buffer).unwrap(); - assert_eq!(size, buffer.len()); - let (value, size) = deserialize::>>>(&buffer).unwrap(); - assert_eq!(size, buffer.len()); + assert_eq!(size.0, buffer.len()); + let value = deserialize::>>>(&buffer).unwrap(); assert_eq!(value.a, 1); assert_eq!(value.b, X); @@ -87,46 +89,41 @@ fn main() { let value = Test2U; let size = serialized_size::(value); - let mut buffer = vec![0; size]; + let mut buffer = vec![0; size.0]; let size = serialize::(value, &mut buffer).unwrap(); - assert_eq!(size, buffer.len()); - let (unit, size) = deserialize::(&buffer).unwrap(); - assert_eq!(size, buffer.len()); + assert_eq!(size.0, buffer.len()); + let unit = deserialize::(&buffer).unwrap(); assert_eq!(unit, Test2::Unit); let value = Test2T(1, 2); let size = serialized_size::(value); - let mut buffer = vec![0; size]; + let mut buffer = vec![0; size.0]; let size = serialize::(value, &mut buffer).unwrap(); - assert_eq!(size, buffer.len()); - let (structure, size) = deserialize::(&buffer).unwrap(); - assert_eq!(size, buffer.len()); + assert_eq!(size.0, buffer.len()); + let structure = deserialize::(&buffer).unwrap(); assert_eq!(structure, Test2::Tuple(1, 2)); let value = Test2S { a: 1, b: 2 }; let size = serialized_size::(value); - let mut buffer = vec![0; size]; + let mut buffer = vec![0; size.0]; let size = serialize::(value, &mut buffer).unwrap(); - assert_eq!(size, buffer.len()); - let (structure, size) = deserialize::(&buffer).unwrap(); - assert_eq!(size, buffer.len()); + assert_eq!(size.0, buffer.len()); + let structure = deserialize::(&buffer).unwrap(); assert_eq!(structure, Test2::Struct { a: 1, b: 2 }); let value = Test2E::Unit; let size = serialized_size::(value); - let mut buffer = vec![0; size]; + let mut buffer = vec![0; size.0]; let size = serialize::(value, &mut buffer).unwrap(); - assert_eq!(size, buffer.len()); - let (unit, size) = deserialize::(&buffer).unwrap(); - assert_eq!(size, buffer.len()); + assert_eq!(size.0, buffer.len()); + let unit = deserialize::(&buffer).unwrap(); assert_eq!(unit, Test2::Unit); let value = Test2E::Struct { a: 1, b: 2 }; let size = serialized_size::(value); - let mut buffer = vec![0; size]; + let mut buffer = vec![0; size.0]; let size = serialize::(value, &mut buffer).unwrap(); - assert_eq!(size, buffer.len()); - let (structure, size) = deserialize::(&buffer).unwrap(); - assert_eq!(size, buffer.len()); + assert_eq!(size.0, buffer.len()); + let structure = deserialize::(&buffer).unwrap(); assert_eq!(structure, Test2::Struct { a: 1, b: 2 }); } diff --git a/proc/Cargo.toml b/proc/Cargo.toml index 1cdca52..b44efa4 100644 --- a/proc/Cargo.toml +++ b/proc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alkahest-proc" -version = "0.2.0-rc.9" +version = "0.3.0" authors = ["Zakarum "] edition = "2018" license = "MIT OR Apache-2.0" diff --git a/proc/src/attrs.rs b/proc/src/attrs.rs index e3b68aa..5c159e5 100644 --- a/proc/src/attrs.rs +++ b/proc/src/attrs.rs @@ -1,223 +1,212 @@ -use proc_easy::{EasyArgument, EasyAttributes, EasyPeek, EasyToken}; -use proc_macro2::{Span, TokenStream}; -use quote::ToTokens; -use syn::{ - parse::{Lookahead1, Parse, ParseStream}, - spanned::Spanned, -}; - -proc_easy::easy_token!(owned); -proc_easy::easy_token!(serialize); -proc_easy::easy_token!(deserialize); -// proc_easy::easy_token!(non_exhaustive); +proc_easy::easy_token!(Formula); +proc_easy::easy_token!(Serialize); +proc_easy::easy_token!(SerializeRef); +proc_easy::easy_token!(Deserialize); proc_easy::easy_parse! { - struct FormulaParams { + struct Params { token: syn::Token![for], generics: syn::Generics, } } -struct FormulaRef { - params: Option, - path: syn::Path, - where_clause: Option, +proc_easy::easy_parse! { + struct Variant { + at: syn::Token![@], + ident: syn::Ident, + } } -impl From for Formula { - fn from(formula: FormulaRef) -> Self { - let mut generics = formula - .params - .map(|params| params.generics) - .unwrap_or_default(); - - if let Some(where_clause) = formula.where_clause { - generics.make_where_clause().predicates = where_clause.predicates; - } - - Formula { - path: path_make_expr_style(formula.path), - generics, - } +proc_easy::easy_parse! { + struct SerializeParams { + lt_token: syn::Token![<], + formula: syn::Path, + variant: proc_easy::EasyMaybe, + gt_token: syn::Token![>], } } -impl EasyToken for FormulaRef { - fn display() -> &'static str { - "Formula type" +proc_easy::easy_parse! { + struct DeserializeParams { + lt_token: syn::Token![<], + lifetime: syn::Lifetime, + comma_token: syn::Token![,], + formula: syn::Path, + gt_token: syn::Token![>], } } -impl EasyPeek for FormulaRef { - fn peek_stream(stream: ParseStream) -> bool { - stream.peek(syn::Token![for]) || stream.peek(syn::Token![<]) || stream.peek(syn::Ident) +proc_easy::easy_parse! { + enum ImplTrait { + Formula(Formula), + Serialize(Serialize, proc_easy::EasyMaybe), + SerializeRef(SerializeRef, proc_easy::EasyMaybe), + Deserialize(Deserialize, proc_easy::EasyMaybe), } +} - fn peek(lookahead1: &Lookahead1) -> bool { - lookahead1.peek(syn::Token![for]) - || lookahead1.peek(syn::Token![<]) - || lookahead1.peek(syn::Ident) +proc_easy::easy_parse! { + struct ImplBlock { + params: proc_easy::EasyMaybe, + impl_trait: ImplTrait, + where_clause: Option, } } -impl Parse for FormulaRef { - fn parse(input: ParseStream) -> syn::Result { - let params = if input.peek(syn::Token![for]) { - Some(input.parse()?) - } else { - None - }; - - let path = input.parse()?; - - let where_clause = if input.peek(syn::Token![where]) { - Some(input.parse()?) - } else { - None - }; +struct ImplBlocks { + blocks: syn::punctuated::Punctuated, +} - Ok(FormulaRef { - params, - path, - where_clause, +impl syn::parse::Parse for ImplBlocks { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + Ok(ImplBlocks { + blocks: syn::punctuated::Punctuated::parse_separated_nonempty(input)?, }) } } -impl ToTokens for FormulaRef { - fn to_tokens(&self, tokens: &mut TokenStream) { - if let Some(params) = &self.params { - params.token.to_tokens(tokens); - params.generics.to_tokens(tokens); - } - self.path.to_tokens(tokens); - if let Some(where_clause) = &self.where_clause { - where_clause.to_tokens(tokens); +impl ImplBlock { + fn split(self) -> (ImplTrait, Option) { + let mut generics = match self.params { + proc_easy::EasyMaybe::Nothing => None, + proc_easy::EasyMaybe::Just(params) => Some(params.generics), + }; + + if let Some(where_clause) = self.where_clause { + generics + .get_or_insert_with(syn::Generics::default) + .make_where_clause() + .predicates + .extend(where_clause.predicates); } - } -} -proc_easy::easy_argument! { - struct Variant { - token: syn::Token![@], - variant: syn::Ident, + (self.impl_trait, generics) } } -proc_easy::easy_argument_tuple! { - struct NoReferenceRef { - token: owned, - formula: Option, - } +pub struct FormulaArgs { + pub generics: Option, } -proc_easy::easy_argument_tuple! { - struct SerializeArg { - token: serialize, - owned: Option, - formula: Option, +impl FormulaArgs { + pub fn empty() -> Self { + FormulaArgs { generics: None } } } -proc_easy::easy_argument_tuple! { - struct DeserializeArg { - token: deserialize, - formula: Option, - // non_exhaustive: Option, - } +pub struct SerializeArgs { + pub formula: Option, + pub generics: Option, + pub variant: Option, } -proc_easy::easy_attributes! { - @(alkahest) - struct Attrs { - // non_exhaustive: Option, - owned: Option, - serialize: Vec, - deserialize: Vec, - variant: Option, - formula: Option, +impl SerializeArgs { + pub fn empty() -> Self { + SerializeArgs { + formula: None, + generics: None, + variant: None, + } } } -#[derive(Clone)] -pub struct Formula { - pub path: syn::Path, - pub generics: syn::Generics, +pub struct DeserializeArgs { + pub formula: Option, + pub generics: Option, + pub lifetime: Option, } -pub struct Args { - // pub non_exhaustive: Option, - #[allow(clippy::option_option)] - pub owned: Option>, - pub common: Option, - pub serialize: Option, - pub deserialize: Option, - pub variant: Option, -} - -pub fn parse_attributes(attrs: &[syn::Attribute]) -> syn::Result { - let attrs = Attrs::parse(attrs, Span::call_site())?; - - let mut serialize_opt = None; - let mut deserialize_opt = None; - let common_opt = attrs.formula.map(Formula::from); - // let mut non_exhaustive_opt = attrs.non_exhaustive; - let mut owned_opt = attrs.owned; - - for serialize in attrs.serialize { - if let Some(formula) = serialize.formula { - if common_opt.is_some() { - return Err(syn::Error::new( - formula.span(), - "Common formula reference already specified", - )); - } - serialize_opt = Some(Formula::from(formula)); - } - - if let Some(owned) = serialize.owned { - if owned_opt.is_some() { - return Err(syn::Error::new( - owned.name_span(), - "Reference already specified", - )); - } - - owned_opt = Some(owned); +impl DeserializeArgs { + pub fn empty() -> Self { + DeserializeArgs { + formula: None, + generics: None, + lifetime: None, } } +} - for deserialize in attrs.deserialize { - if let Some(formula) = deserialize.formula { - if common_opt.is_some() { - return Err(syn::Error::new( - formula.span(), - "Common formula reference already specified", - )); +pub struct Args { + pub formula: Option, + pub serialize: Option, + pub serialize_ref: Option, + pub deserialize: Option, +} + +impl Args { + pub fn parse_attributes(attrs: proc_macro2::TokenStream) -> syn::Result { + let blocks: ImplBlocks = syn::parse2(attrs)?; + + let mut formula: Option = None; + let mut serialize: Option = None; + let mut serialize_ref: Option = None; + let mut deserialize: Option = None; + + for block in blocks.blocks { + let (impl_trait, generics) = block.split(); + match impl_trait { + ImplTrait::Formula(_) => formula = Some(FormulaArgs { generics }), + ImplTrait::Serialize(_, params) => { + let (formula, variant) = match params { + proc_easy::EasyMaybe::Just(params) => ( + Some(path_make_expr_style(params.formula)), + match params.variant { + proc_easy::EasyMaybe::Just(variant) => Some(variant.ident), + proc_easy::EasyMaybe::Nothing => None, + }, + ), + proc_easy::EasyMaybe::Nothing => (None, None), + }; + + serialize = Some(SerializeArgs { + formula, + generics, + variant, + }); + } + ImplTrait::SerializeRef(_, params) => { + let (formula, variant) = match params { + proc_easy::EasyMaybe::Just(params) => ( + Some(path_make_expr_style(params.formula)), + match params.variant { + proc_easy::EasyMaybe::Just(variant) => Some(variant.ident), + proc_easy::EasyMaybe::Nothing => None, + }, + ), + proc_easy::EasyMaybe::Nothing => (None, None), + }; + + serialize_ref = Some(SerializeArgs { + formula, + generics, + variant, + }); + } + ImplTrait::Deserialize(_, params) => { + let (formula, lifetime) = match params { + proc_easy::EasyMaybe::Just(params) => ( + Some(path_make_expr_style(params.formula)), + Some(params.lifetime), + ), + proc_easy::EasyMaybe::Nothing => (None, None), + }; + + deserialize = Some(DeserializeArgs { + formula, + generics, + lifetime, + }); + } } - deserialize_opt = Some(Formula::from(formula)); } - // if let Some(non_exhaustive) = deserialize.non_exhaustive { - // if non_exhaustive_opt.is_some() { - // return Err(syn::Error::new( - // non_exhaustive.span(), - // "Non-exhaustive already specified", - // )); - // } - - // non_exhaustive_opt = Some(non_exhaustive); - // } + Ok(Args { + formula, + serialize, + serialize_ref, + deserialize, + }) } - - Ok(Args { - common: common_opt, - serialize: serialize_opt, - deserialize: deserialize_opt, - // non_exhaustive: non_exhaustive_opt, - owned: owned_opt.map(|owned| owned.formula.map(Formula::from)), - variant: attrs.variant.map(|v| v.variant), - }) } pub fn path_make_expr_style(mut path: syn::Path) -> syn::Path { diff --git a/proc/src/deserialize.rs b/proc/src/deserialize.rs index 9c868fe..029b58a 100644 --- a/proc/src/deserialize.rs +++ b/proc/src/deserialize.rs @@ -1,16 +1,22 @@ +use std::collections::HashSet; + use proc_macro2::TokenStream; use crate::{ - attrs::{parse_attributes, Args, Formula}, - enum_field_order_checks, struct_field_order_checks, + attrs::DeserializeArgs, enum_field_order_checks, filter_type_param, is_generic_ty, + struct_field_order_checks, }; fn default_de_lifetime() -> syn::Lifetime { syn::Lifetime::new("'__de", proc_macro2::Span::call_site()) } -fn de_lifetime(formula: &mut Formula, generics: &syn::Generics) -> syn::Lifetime { - match formula.generics.lifetimes().next() { +fn de_lifetime( + lifetime: Option, + formula_generics: &mut syn::Generics, + generics: &syn::Generics, +) -> syn::Lifetime { + match lifetime { None => { let lifetime = default_de_lifetime(); let bounds: syn::punctuated::Punctuated<_, syn::Token![+]> = @@ -21,15 +27,18 @@ fn de_lifetime(formula: &mut Formula, generics: &syn::Generics) -> syn::Lifetime colon_token: (!bounds.is_empty()).then(Default::default), bounds, }; - formula.generics.params.push(de.into()); + formula_generics + .params + .push(syn::GenericParam::Lifetime(de)); lifetime } - Some(first) => first.lifetime.clone(), + Some(lifetime) => lifetime, } } struct Config { - formula: Formula, + formula: syn::Path, + generics: syn::Generics, /// Signals if fields should be checked to match on formula. /// `false` if `formula` is inferred to `Self`. @@ -43,100 +52,106 @@ struct Config { } impl Config { - fn for_struct(args: Args, data: &syn::DataStruct, generics: &syn::Generics) -> Self { - // let non_exhaustive = args.non_exhaustive.is_some(); - match args.deserialize.or(args.common) { - None => { - let mut formula = Formula { - path: syn::parse_quote!(Self), - generics: syn::Generics { - lt_token: Some(::default()), - params: syn::punctuated::Punctuated::default(), - gt_token: Some(]>::default()), - where_clause: None, - }, + fn for_type(args: DeserializeArgs, data: &syn::Data, generics: &syn::Generics) -> Self { + match (args.formula, args.generics) { + (None, None) => { + let mut formula_generics = syn::Generics { + lt_token: Some(::default()), + params: syn::punctuated::Punctuated::default(), + gt_token: Some(]>::default()), + where_clause: None, }; - let de = de_lifetime(&mut formula, generics); + let de = de_lifetime(args.lifetime, &mut formula_generics, generics); // Add predicates that fields implement // `Formula + Deserialize<'__de, #field_type>` // Except that last one if `non_exhaustive` is not set. - let predicates = data.fields.iter().map(|field| -> syn::WherePredicate { - let ty = &field.ty; - syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Deserialize<#de, #ty> } - }); + match data { + syn::Data::Union(_) => unreachable!(), + syn::Data::Struct(data) => { + let mut all_generic_field_types: HashSet<_> = + data.fields.iter().map(|f| &f.ty).collect(); + all_generic_field_types.retain(|ty| { + is_generic_ty(ty, &filter_type_param(generics.params.iter())) + }); + + if !all_generic_field_types.is_empty() { + let predicates = all_generic_field_types.iter().map(|&ty| -> syn::WherePredicate { + syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Deserialize<#de, #ty> } + }); - formula - .generics - .make_where_clause() - .predicates - .extend(predicates); + formula_generics + .make_where_clause() + .predicates + .extend(predicates); + } + } + syn::Data::Enum(data) => { + let all_fields = data.variants.iter().flat_map(|v| v.fields.iter()); + + let mut all_generic_field_types: HashSet<_> = + all_fields.map(|f| &f.ty).collect(); + all_generic_field_types.retain(|ty| { + is_generic_ty(ty, &filter_type_param(generics.params.iter())) + }); + + if !all_generic_field_types.is_empty() { + let predicates = all_generic_field_types.iter().map(|&ty| -> syn::WherePredicate { + syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Deserialize<#de, #ty> } + }); + + formula_generics + .make_where_clause() + .predicates + .extend(predicates); + } + } + } Config { - formula, + formula: syn::parse_quote! { Self }, + generics: formula_generics, check_fields: false, // non_exhaustive, de, } } - Some(mut formula) => { - let de = de_lifetime(&mut formula, generics); + (None, Some(mut formula_generics)) => { + let de = de_lifetime(args.lifetime, &mut formula_generics, generics); Config { - formula, - check_fields: true, + formula: syn::parse_quote! { Self }, + generics: formula_generics, + check_fields: false, // non_exhaustive, de, } } - } - } - - fn for_enum(args: Args, data: &syn::DataEnum, generics: &syn::Generics) -> Self { - // let non_exhaustive = args.non_exhaustive.is_some(); - match args.deserialize.or(args.common) { - None => { - let mut formula = Formula { - path: syn::parse_quote!(Self), - generics: syn::Generics { - lt_token: Some(::default()), - params: syn::punctuated::Punctuated::default(), - gt_token: Some(]>::default()), - where_clause: None, - }, + (Some(formula), None) => { + let mut formula_generics = syn::Generics { + lt_token: Some(::default()), + params: syn::punctuated::Punctuated::default(), + gt_token: Some(]>::default()), + where_clause: None, }; - let de = de_lifetime(&mut formula, generics); - - // Add predicates that fields implement - // `Formula + Deserialize<'__de, #field_type>` - // Except that last one if `non_exhaustive` is not set. - let predicates = data.variants.iter().flat_map(|v| v.fields.iter().map(|field| -> syn::WherePredicate { - let ty = &field.ty; - syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Deserialize<#de, #ty> } - })); - - formula - .generics - .make_where_clause() - .predicates - .extend(predicates); + let de = de_lifetime(args.lifetime, &mut formula_generics, generics); Config { formula, + generics: formula_generics, check_fields: false, - // non_exhaustive, de, } } - Some(mut formula) => { - let de = de_lifetime(&mut formula, generics); + (Some(formula), Some(mut formula_generics)) => { + let de = de_lifetime(args.lifetime, &mut formula_generics, generics); Config { formula, + generics: formula_generics, check_fields: true, - // non_exhaustive, de, } } @@ -145,43 +160,36 @@ impl Config { } #[allow(clippy::too_many_lines)] -pub fn derive(input: proc_macro::TokenStream) -> syn::Result { - let input = syn::parse::(input)?; - let args = parse_attributes(&input.attrs)?; - +pub fn derive(args: DeserializeArgs, input: &syn::DeriveInput) -> syn::Result { let ident = &input.ident; - match input.data { + let cfg = Config::for_type(args, &input.data, &input.generics); + + match &input.data { syn::Data::Union(_) => Err(syn::Error::new_spanned( input, "Deserialize cannot be derived for unions", )), syn::Data::Struct(data) => { - let cfg = Config::for_struct(args, &data, &input.generics); - let field_checks = if cfg.check_fields { - struct_field_order_checks(&data, None, &input.ident, &cfg.formula.path) + struct_field_order_checks(&data, None, &input.ident, &cfg.formula) } else { TokenStream::new() }; - let formula_path = &cfg.formula.path; + let formula_path = &cfg.formula; let de = cfg.de; let mut deserialize_generics = input.generics.clone(); - deserialize_generics.lt_token = deserialize_generics - .lt_token - .or(cfg.formula.generics.lt_token); - deserialize_generics.gt_token = deserialize_generics - .gt_token - .or(cfg.formula.generics.gt_token); + deserialize_generics.lt_token = deserialize_generics.lt_token.or(cfg.generics.lt_token); + deserialize_generics.gt_token = deserialize_generics.gt_token.or(cfg.generics.gt_token); deserialize_generics .params - .extend(cfg.formula.generics.params.into_iter()); + .extend(cfg.generics.params.into_iter()); - if let Some(where_clause) = cfg.formula.generics.where_clause { + if let Some(where_clause) = cfg.generics.where_clause { deserialize_generics .make_where_clause() .predicates @@ -321,31 +329,25 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { }) } syn::Data::Enum(data) => { - let cfg = Config::for_enum(args, &data, &input.generics); - let field_checks = if cfg.check_fields { - enum_field_order_checks(&data, &input.ident, &cfg.formula.path) + enum_field_order_checks(&data, &input.ident, &cfg.formula) } else { TokenStream::new() }; - let formula_path = &cfg.formula.path; + let formula_path = &cfg.formula; let de = cfg.de; let mut deserialize_generics = input.generics.clone(); - deserialize_generics.lt_token = deserialize_generics - .lt_token - .or(cfg.formula.generics.lt_token); - deserialize_generics.gt_token = deserialize_generics - .gt_token - .or(cfg.formula.generics.gt_token); + deserialize_generics.lt_token = deserialize_generics.lt_token.or(cfg.generics.lt_token); + deserialize_generics.gt_token = deserialize_generics.gt_token.or(cfg.generics.gt_token); deserialize_generics .params - .extend(cfg.formula.generics.params.into_iter()); + .extend(cfg.generics.params.into_iter()); - if let Some(where_clause) = cfg.formula.generics.where_clause { + if let Some(where_clause) = cfg.generics.where_clause { deserialize_generics .make_where_clause() .predicates diff --git a/proc/src/formula.rs b/proc/src/formula.rs index 6d976bc..8e1ca35 100644 --- a/proc/src/formula.rs +++ b/proc/src/formula.rs @@ -3,34 +3,69 @@ use std::collections::HashSet; use proc_macro2::TokenStream; use syn::spanned::Spanned; -use crate::{attrs::parse_attributes, filter_type_param, is_generic_ty}; +use crate::{attrs::FormulaArgs, filter_type_param, is_generic_ty}; -#[allow(clippy::too_many_lines)] -pub fn derive(input: proc_macro::TokenStream) -> syn::Result { - let input = syn::parse::(input)?; - let ident = &input.ident; +struct Config { + formula_generics: syn::Generics, +} - let args = parse_attributes(&input.attrs)?; - // let non_exhaustive = args.non_exhaustive.is_some(); - - if let Some(formula) = args - .serialize - .or(args.deserialize) - .or(args.common) - .or(args.owned.flatten()) - { - return Err(syn::Error::new_spanned( - formula.path, - "Formula type should not be specified for `Serialize` and `Deserialize` when type is also `Formula`", - )); - } +impl Config { + pub fn from_args(args: FormulaArgs, generics: &syn::Generics, data: &syn::Data) -> Self { + let formula_generics = match args.generics { + None => { + let all_field_types: Vec<_> = match data { + syn::Data::Struct(data) => data.fields.iter().map(|field| &field.ty).collect(), + syn::Data::Enum(data) => data + .variants + .iter() + .flat_map(|variant| variant.fields.iter().map(|field| &field.ty)) + .collect(), + syn::Data::Union(_) => { + panic!("Alkahest does not support unions"); + } + }; + + let mut all_generic_field_types: HashSet<_> = + all_field_types.iter().copied().collect(); + + all_generic_field_types + .retain(|ty| is_generic_ty(ty, &filter_type_param(generics.params.iter()))); - if args.variant.is_some() { - return Err(syn::Error::new_spanned( - input, - "Variant should not be specified for `Serialize` when type is also `Formula`", - )); + let mut formula_generics = generics.clone(); + if !all_generic_field_types.is_empty() { + let predicates = all_generic_field_types + .iter() + .map(|ty| -> syn::WherePredicate { + syn::parse_quote_spanned! { ty.span() => #ty: ::alkahest::private::Formula } + }); + let where_clause = formula_generics.make_where_clause(); + where_clause.predicates.extend(predicates); + }; + + formula_generics + } + Some(args_generics) => { + let mut formula_generics = generics.clone(); + formula_generics.params.extend(args_generics.params); + if let Some(where_clause) = args_generics.where_clause { + formula_generics + .make_where_clause() + .predicates + .extend(where_clause.predicates); + } + formula_generics + } + }; + + Config { formula_generics } } +} + +#[allow(clippy::too_many_lines)] +pub fn derive(args: FormulaArgs, input: &syn::DeriveInput) -> syn::Result { + let ident = &input.ident; + + let config = Config::from_args(args, &input.generics, &input.data); match &input.data { syn::Data::Union(data) => Err(syn::Error::new_spanned( @@ -40,20 +75,6 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { syn::Data::Struct(data) => { let all_field_types: Vec<_> = data.fields.iter().map(|field| &field.ty).collect(); let last_field_type = all_field_types.last().copied().into_iter(); - let mut all_generic_field_types: HashSet<_> = all_field_types.iter().copied().collect(); - all_generic_field_types - .retain(|ty| is_generic_ty(ty, &filter_type_param(input.generics.params.iter()))); - - let mut formula_generics = input.generics.clone(); - if !all_generic_field_types.is_empty() { - let predicates = all_generic_field_types - .iter() - .map(|ty| -> syn::WherePredicate { - syn::parse_quote_spanned! { ty.span() => #ty: ::alkahest::private::Formula } - }); - let where_clause = formula_generics.make_where_clause(); - where_clause.predicates.extend(predicates); - } let field_names_order = match &data.fields { syn::Fields::Named(fields) => fields @@ -72,7 +93,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { let field_ids: Vec<_> = (0..data.fields.len()).collect(); let (formula_impl_generics, formula_type_generics, formula_where_clause) = - formula_generics.split_for_impl(); + config.formula_generics.split_for_impl(); let touch_fields = match &data.fields { syn::Fields::Unit => quote::quote! {}, @@ -142,29 +163,6 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { .map(|variants| variants.last().copied().into_iter().collect()) .collect(); - let all_field_types_flat: Vec<&syn::Type> = data - .variants - .iter() - .flat_map(|variant| variant.fields.iter().map(|field| &field.ty)) - .collect(); - - let mut all_generic_field_types: HashSet<_> = - all_field_types_flat.iter().copied().collect(); - all_generic_field_types - .retain(|ty| is_generic_ty(ty, &filter_type_param(input.generics.params.iter()))); - - let mut formula_generics = input.generics.clone(); - - if !all_generic_field_types.is_empty() { - let predicates = all_generic_field_types - .iter() - .map(|ty| -> syn::WherePredicate { - syn::parse_quote_spanned! { ty.span() => #ty: ::alkahest::private::Formula } - }); - let where_clause = formula_generics.make_where_clause(); - where_clause.predicates.extend(predicates); - } - let field_names_order: Vec> = data .variants .iter() @@ -202,7 +200,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); let (formula_impl_generics, formula_type_generics, formula_where_clause) = - formula_generics.split_for_impl(); + config.formula_generics.split_for_impl(); // let expand_size = if non_exhaustive { // quote::quote! { diff --git a/proc/src/lib.rs b/proc/src/lib.rs index 0b5b993..252e1ad 100644 --- a/proc/src/lib.rs +++ b/proc/src/lib.rs @@ -5,15 +5,51 @@ mod deserialize; mod formula; mod serialize; +use attrs::{DeserializeArgs, FormulaArgs, SerializeArgs}; use proc_macro::TokenStream; +#[proc_macro_attribute] +pub fn alkahest(attr: TokenStream, item: TokenStream) -> TokenStream { + let mut output = item.clone(); + let input = syn::parse_macro_input!(item as syn::DeriveInput); + + match alkahest_impl(attr, input) { + Ok(tokens) => output.extend(TokenStream::from(tokens)), + Err(err) => output.extend(TokenStream::from(err.to_compile_error())), + } + output +} + +fn alkahest_impl( + attr: TokenStream, + input: syn::DeriveInput, +) -> syn::Result { + let mut output = proc_macro2::TokenStream::new(); + let attr = proc_macro2::TokenStream::from(attr); + let args = attrs::Args::parse_attributes(attr)?; + if let Some(args) = args.formula { + output.extend(formula::derive(args, &input)?); + } + if let Some(args) = args.serialize { + output.extend(serialize::derive(args, &input, false)?); + } + if let Some(args) = args.serialize_ref { + output.extend(serialize::derive(args, &input, true)?); + } + if let Some(args) = args.deserialize { + output.extend(deserialize::derive(args, &input)?); + } + Ok(output) +} + /// Proc-macro to derive `Formula` trait for user-defined type. /// /// This macro requires that type is either `struct` or `enum`. /// All fields must implement `Formula`. -#[proc_macro_derive(Formula, attributes(alkahest))] +#[proc_macro_derive(Formula)] pub fn derive_formula(input: TokenStream) -> TokenStream { - match formula::derive(input) { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + match formula::derive(FormulaArgs::empty(), &input) { Ok(tokens) => tokens.into(), Err(err) => err.to_compile_error().into(), } @@ -23,9 +59,23 @@ pub fn derive_formula(input: TokenStream) -> TokenStream { /// /// This macro requires that type is either `struct` or `enum`. /// All fields must implement `Serialize`. -#[proc_macro_derive(Serialize, attributes(alkahest))] +#[proc_macro_derive(Serialize)] pub fn derive_serialize(input: TokenStream) -> TokenStream { - match serialize::derive(input) { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + match serialize::derive(SerializeArgs::empty(), &input, false) { + Ok(tokens) => tokens.into(), + Err(err) => err.to_compile_error().into(), + } +} + +/// Proc-macro to derive `SerializeRef` trait for user-defined type. +/// +/// This macro requires that type is either `struct` or `enum`. +/// All fields must implement `Serialize`. +#[proc_macro_derive(SerializeRef)] +pub fn derive_serialize_ref(input: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + match serialize::derive(SerializeArgs::empty(), &input, true) { Ok(tokens) => tokens.into(), Err(err) => err.to_compile_error().into(), } @@ -35,9 +85,10 @@ pub fn derive_serialize(input: TokenStream) -> TokenStream { /// /// This macro requires that type is either `struct` or `enum`. /// All fields must implement `Deserialize`. -#[proc_macro_derive(Deserialize, attributes(alkahest))] +#[proc_macro_derive(Deserialize)] pub fn derive_deserialize(input: TokenStream) -> TokenStream { - match deserialize::derive(input) { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + match deserialize::derive(DeserializeArgs::empty(), &input) { Ok(tokens) => tokens.into(), Err(err) => err.to_compile_error().into(), } diff --git a/proc/src/serialize.rs b/proc/src/serialize.rs index 6f6a6a8..e9d9296 100644 --- a/proc/src/serialize.rs +++ b/proc/src/serialize.rs @@ -3,13 +3,13 @@ use std::collections::HashSet; use proc_macro2::TokenStream; use crate::{ - attrs::{parse_attributes, path_make_expr_style, Args, Formula}, - enum_field_order_checks, filter_type_param, is_generic_ty, struct_field_order_checks, + attrs::SerializeArgs, enum_field_order_checks, filter_type_param, is_generic_ty, + struct_field_order_checks, }; struct Config { - reference: Option, - owned: Formula, + formula: syn::Path, + generics: syn::Generics, variant: Option, @@ -20,77 +20,22 @@ struct Config { impl Config { #[allow(clippy::too_many_lines)] - fn for_struct( - args: Args, - data: &syn::DataStruct, - ident: &syn::Ident, + fn for_type( + args: SerializeArgs, + data: &syn::Data, generics: &syn::Generics, + by_ref: bool, ) -> Self { - let (_, type_generics, _) = generics.split_for_impl(); let params = &generics.params; - match (args.serialize.or(args.common), args.owned) { - (None, Some(None)) if params.is_empty() => Config { - reference: None, - owned: Formula { - path: syn::parse_quote!(#ident), - generics: syn::Generics::default(), - }, - variant: None, - check_fields: false, - }, + match (args.formula, args.generics) { (None, None) if params.is_empty() => Config { - reference: Some(Formula { - path: syn::parse_quote!(#ident), - generics: syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }, - }), - owned: Formula { - path: syn::parse_quote!(#ident), - generics: syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }, - }, + formula: syn::parse_quote! { Self }, + generics: syn::Generics::default(), variant: None, check_fields: false, }, (None, None) => { - // Add predicates that fields implement - // `T: Formula + Serialize` - // for fields where generics are involved. - let mut generics = syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }; - - let mut all_generic_field_types: HashSet<_> = - data.fields.iter().map(|f| &f.ty).collect(); - all_generic_field_types - .retain(|ty| is_generic_ty(ty, &filter_type_param(params.iter()))); - - if !all_generic_field_types.is_empty() { - let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula } - }).chain(all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { for<'ser> &'ser #ty: ::alkahest::private::Serialize<#ty> } - })); - generics.make_where_clause().predicates.extend(predicates); - } - - let reference = Formula { - path: path_make_expr_style(syn::parse_quote!(#ident #type_generics)), - generics, - }; - let mut generics = syn::Generics { lt_token: None, params: syn::punctuated::Punctuated::default(), @@ -98,234 +43,83 @@ impl Config { where_clause: None, }; - if !all_generic_field_types.is_empty() { - let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula } - }).chain(all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Serialize<#ty> } - })); - generics.make_where_clause().predicates.extend(predicates); - } - - let owned = Formula { - path: syn::parse_quote!(Self), - generics, - }; - - Config { - reference: Some(reference), - owned, - variant: args.variant, - check_fields: false, - } - } - (None, Some(None)) => { - // Add predicates that fields implement - // `T: Formula` and `T: Serialize` - // for fields where generics are involved. - let mut generics = syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }; - - let mut all_generic_field_types: HashSet<_> = - data.fields.iter().map(|f| &f.ty).collect(); - all_generic_field_types - .retain(|ty| is_generic_ty(ty, &filter_type_param(generics.params.iter()))); - - if !all_generic_field_types.is_empty() { - let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula } - }).chain(all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Serialize<#ty> } - })); - generics.make_where_clause().predicates.extend(predicates); - } - - Config { - reference: None, - owned: Formula { - path: syn::parse_quote!(Self), - generics, - }, - variant: None, - check_fields: true, - } - } - (None, Some(Some(owned))) => Config { - owned, - reference: None, - variant: args.variant, - check_fields: true, - }, - (Some(reference), None | Some(None)) => Config { - reference: Some(reference.clone()), - owned: reference, - variant: args.variant, - check_fields: true, - }, - (Some(reference), Some(Some(owned))) => Config { - reference: Some(reference), - owned, - variant: args.variant, - check_fields: true, - }, - } - } - - #[allow(clippy::too_many_lines)] - fn for_enum( - args: Args, - data: &syn::DataEnum, - ident: &syn::Ident, - generics: &syn::Generics, - ) -> Self { - let (_, type_generics, _) = generics.split_for_impl(); - - let all_fields = data.variants.iter().flat_map(|v| v.fields.iter()); - - match (args.serialize.or(args.common), args.owned) { - (None, Some(None)) if generics.params.is_empty() => Config { - reference: None, - owned: Formula { - path: syn::parse_quote!(Self), - generics: syn::Generics::default(), - }, - variant: None, - check_fields: false, - }, - (None, None) if generics.params.is_empty() => Config { - reference: Some(Formula { - path: syn::parse_quote!(#ident), - generics: syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }, - }), - owned: Formula { - path: syn::parse_quote!(Self), - generics: syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }, - }, - variant: None, - check_fields: false, - }, - (None, None) => { // Add predicates that fields implement // `T: Formula + Serialize` // for fields where generics are involved. - let mut generics = syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }; - - let mut all_generic_field_types: HashSet<_> = all_fields.map(|f| &f.ty).collect(); - all_generic_field_types - .retain(|ty| is_generic_ty(ty, &filter_type_param(generics.params.iter()))); - - if !all_generic_field_types.is_empty() { - let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula } - }).chain(all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { for<'ser> &'ser #ty: ::alkahest::private::Serialize<#ty> } - })); - generics.make_where_clause().predicates.extend(predicates); - } - - let reference = Formula { - path: path_make_expr_style(syn::parse_quote!(#ident #type_generics)), - generics, - }; - - let mut generics = syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: None, - }; - - if !all_generic_field_types.is_empty() { - let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula } - }).chain(all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { - syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Serialize<#ty> } - })); - generics.make_where_clause().predicates.extend(predicates); + match data { + syn::Data::Union(_) => unreachable!(), + syn::Data::Struct(data) => { + let mut all_generic_field_types: HashSet<_> = + data.fields.iter().map(|f| &f.ty).collect(); + all_generic_field_types + .retain(|ty| is_generic_ty(ty, &filter_type_param(params.iter()))); + + if !all_generic_field_types.is_empty() { + if by_ref { + let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { + syn::parse_quote! { #ty: ::alkahest::private::Formula } + }).chain(all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { + syn::parse_quote! { for<'ser> &'ser #ty: ::alkahest::private::Serialize<#ty> } + })); + generics.make_where_clause().predicates.extend(predicates); + } else { + let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { + syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Serialize<#ty> } + }); + generics.make_where_clause().predicates.extend(predicates); + } + } + } + syn::Data::Enum(data) => { + let all_fields = data.variants.iter().flat_map(|v| v.fields.iter()); + + let mut all_generic_field_types: HashSet<_> = + all_fields.map(|f| &f.ty).collect(); + all_generic_field_types.retain(|ty| { + is_generic_ty(ty, &filter_type_param(generics.params.iter())) + }); + + if !all_generic_field_types.is_empty() { + if by_ref { + let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { + syn::parse_quote! { #ty: ::alkahest::private::Formula } + }).chain(all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { + syn::parse_quote! { for<'ser> &'ser #ty: ::alkahest::private::Serialize<#ty> } + })); + generics.make_where_clause().predicates.extend(predicates); + } else { + let predicates = all_generic_field_types.iter().map(|ty| -> syn::WherePredicate { + syn::parse_quote! { #ty: ::alkahest::private::Formula + ::alkahest::private::Serialize<#ty> } + }); + generics.make_where_clause().predicates.extend(predicates); + } + } + } } - let owned = Formula { - path: syn::parse_quote!(Self), - generics, - }; - Config { - reference: Some(reference), - owned, + formula: syn::parse_quote! { Self }, + generics, variant: args.variant, check_fields: false, } } - (None, Some(None)) => { - // Add predicates that fields implement - // `T: Formula` and `T: Serialize` - let predicates = all_fields - .clone() - .map(|field| -> syn::WherePredicate { - let ty = &field.ty; - syn::parse_quote! { #ty: ::alkahest::private::Formula } - }) - .chain(all_fields.clone().map(|field| -> syn::WherePredicate { - let ty = &field.ty; - syn::parse_quote! { #ty: ::alkahest::private::Serialize<#ty> } - })) - .collect(); - - let generics = syn::Generics { - lt_token: None, - params: syn::punctuated::Punctuated::default(), - gt_token: None, - where_clause: Some(syn::WhereClause { - where_token: ::default(), - predicates, - }), - }; - - Config { - reference: None, - owned: Formula { - path: syn::parse_quote!(Self), - generics, - }, - variant: None, - check_fields: true, - } - } - (None, Some(Some(owned))) => Config { - owned, - reference: None, + (None, Some(generics)) => Config { + formula: syn::parse_quote! { Self }, + generics, variant: args.variant, check_fields: true, }, - (Some(reference), None | Some(None)) => Config { - reference: Some(reference.clone()), - owned: reference, + (Some(formula), None) => Config { + formula, + generics: syn::Generics::default(), variant: args.variant, - check_fields: true, + check_fields: false, }, - (Some(reference), Some(Some(owned))) => Config { - reference: Some(reference), - owned, + (Some(formula), Some(generics)) => Config { + formula, + generics, variant: args.variant, check_fields: true, }, @@ -334,29 +128,25 @@ impl Config { } #[allow(clippy::too_many_lines)] -pub fn derive(input: proc_macro::TokenStream) -> syn::Result { - let input = syn::parse::(input)?; - let args = parse_attributes(&input.attrs)?; - +pub fn derive( + args: SerializeArgs, + input: &syn::DeriveInput, + by_ref: bool, +) -> syn::Result { let ident = &input.ident; let generics = &input.generics; let (_impl_generics, type_generics, _where_clause) = generics.split_for_impl(); - match input.data { + let cfg = Config::for_type(args, &input.data, generics, by_ref); + + match &input.data { syn::Data::Union(_) => Err(syn::Error::new_spanned( input, "Serialize cannot be derived for unions", )), syn::Data::Struct(data) => { - let cfg = Config::for_struct(args, &data, ident, generics); - let field_checks = if cfg.check_fields { - struct_field_order_checks( - &data, - cfg.variant.as_ref(), - &input.ident, - &cfg.owned.path, - ) + struct_field_order_checks(&data, cfg.variant.as_ref(), &input.ident, &cfg.formula) } else { TokenStream::new() }; @@ -435,47 +225,44 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { Some(_) => quote::quote! { ::alkahest::private::VARIANT_SIZE }, }; - let mut tokens = TokenStream::new(); - { - let formula_path = &cfg.owned.path; + let formula_path = &cfg.formula; - let write_variant = match &cfg.variant { - None => quote::quote! {}, - Some(v) => { - let variant_name_idx = - quote::format_ident!("__ALKAHEST_FORMULA_VARIANT_{}_IDX", v); - quote::quote! { ::alkahest::private::write_exact_size_field::(#formula_path::#variant_name_idx, __sizes, __buffer.reborrow())?; } - } - }; + let write_variant = match &cfg.variant { + None => quote::quote! {}, + Some(v) => { + let variant_name_idx = + quote::format_ident!("__ALKAHEST_FORMULA_VARIANT_{}_IDX", v); + quote::quote! { ::alkahest::private::write_exact_size_field::(#formula_path::#variant_name_idx, __sizes, __buffer.reborrow())?; } + } + }; + + let mut generics = input.generics.clone(); - let mut generics = input.generics.clone(); + generics.lt_token = generics.lt_token.or(cfg.generics.lt_token); + generics.gt_token = generics.gt_token.or(cfg.generics.gt_token); + generics.params.extend(cfg.generics.params.into_iter()); - generics.lt_token = generics.lt_token.or(cfg.owned.generics.lt_token); - generics.gt_token = generics.gt_token.or(cfg.owned.generics.gt_token); + if let Some(where_clause) = cfg.generics.where_clause { generics - .params - .extend(cfg.owned.generics.params.into_iter()); - - if let Some(where_clause) = cfg.owned.generics.where_clause { - generics - .make_where_clause() - .predicates - .extend(where_clause.predicates); - } + .make_where_clause() + .predicates + .extend(where_clause.predicates); + } - let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); - tokens.extend(quote::quote! { - impl #impl_generics ::alkahest::private::Serialize<#formula_path> for #ident #type_generics #where_clause { + let tokens = if by_ref { + quote::quote! { + impl #impl_generics ::alkahest::private::SerializeRef<#formula_path> for #ident #type_generics #where_clause { #[inline(always)] - fn serialize(self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: B) -> ::alkahest::private::Result<(), B::Error> + fn serialize<__alkahest_Buffer>(&self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: __alkahest_Buffer) -> ::alkahest::private::Result<(), __alkahest_Buffer::Error> where - B: ::alkahest::private::Buffer, + __alkahest_Buffer: ::alkahest::private::Buffer, { #![allow(unused_mut)] #field_checks - let #ident #bind_names = self; + let #ident #bind_ref_names = *self; #write_variant #( let with_formula = ::alkahest::private::with_formula(|s: &#formula_path| match *s { @@ -501,53 +288,24 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { #formula_path #with_variant #bind_ref_names => #bound_names, _ => unreachable!(), }); - __total += with_formula.size_hint(#bound_names, #field_count == 1 + #field_ids)?; + __total += with_formula.size_hint(&#bound_names, #field_count == 1 + #field_ids)?; )* Some(__total) } } - }); - } - - if let Some(reference) = cfg.reference { - let formula_path = &reference.path; - let mut generics = input.generics.clone(); - - let write_variant = match &cfg.variant { - None => quote::quote! {}, - Some(v) => { - let variant_name_idx = - quote::format_ident!("__ALKAHEST_FORMULA_VARIANT_{}_IDX", v); - quote::quote! { ::alkahest::private::write_exact_size_field::(#formula_path::#variant_name_idx, __sizes, __buffer.reborrow())?; } - } - }; - - generics.lt_token = generics.lt_token.or(reference.generics.lt_token); - generics.gt_token = generics.gt_token.or(reference.generics.gt_token); - generics - .params - .extend(reference.generics.params.into_iter()); - - if let Some(where_clause) = reference.generics.where_clause { - generics - .make_where_clause() - .predicates - .extend(where_clause.predicates); } - - let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); - - tokens.extend(quote::quote! { - impl #impl_generics ::alkahest::private::Serialize<#formula_path> for &#ident #type_generics #where_clause { + } else { + quote::quote! { + impl #impl_generics ::alkahest::private::Serialize<#formula_path> for #ident #type_generics #where_clause { #[inline(always)] - fn serialize(self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: B) -> ::alkahest::private::Result<(), B::Error> + fn serialize<__alkahest_Buffer>(self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: __alkahest_Buffer) -> ::alkahest::private::Result<(), __alkahest_Buffer::Error> where - B: ::alkahest::private::Buffer, + __alkahest_Buffer: ::alkahest::private::Buffer, { #![allow(unused_mut)] #field_checks - let #ident #bind_ref_names = *self; + let #ident #bind_names = self; #write_variant #( let with_formula = ::alkahest::private::with_formula(|s: &#formula_path| match *s { @@ -566,28 +324,26 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { if let ::alkahest::private::Option::Some(sizes) = ::alkahest::private::formula_fast_sizes::<#formula_path>() { return Some(sizes); } - let #ident #bind_ref_names = **self; + let #ident #bind_ref_names = *self; let mut __total = ::alkahest::private::Sizes::with_stack(#start_stack_size); #( let with_formula = ::alkahest::private::with_formula(|s: &#formula_path| match *s { #formula_path #with_variant #bind_ref_names => #bound_names, _ => unreachable!(), }); - __total += with_formula.size_hint(&#bound_names, #field_count == 1 + #field_ids)?; + __total += with_formula.size_hint(#bound_names, #field_count == 1 + #field_ids)?; )* Some(__total) } } - }); - } + } + }; Ok(tokens) } syn::Data::Enum(data) => { - let cfg = Config::for_enum(args, &data, ident, generics); - let field_checks = if cfg.check_fields { - enum_field_order_checks(&data, &input.ident, &cfg.owned.path) + enum_field_order_checks(&data, &input.ident, &cfg.formula) } else { TokenStream::new() }; @@ -690,39 +446,36 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { let field_counts: Vec<_> = data.variants.iter().map(|v| v.fields.len()).collect(); - let mut tokens = TokenStream::new(); - { - let formula_path = &cfg.owned.path; + let formula_path = &cfg.formula; + + let mut generics = input.generics.clone(); - let mut generics = input.generics.clone(); + generics.lt_token = generics.lt_token.or(cfg.generics.lt_token); + generics.gt_token = generics.gt_token.or(cfg.generics.gt_token); + generics.params.extend(cfg.generics.params.into_iter()); - generics.lt_token = generics.lt_token.or(cfg.owned.generics.lt_token); - generics.gt_token = generics.gt_token.or(cfg.owned.generics.gt_token); + if let Some(where_clause) = cfg.generics.where_clause { generics - .params - .extend(cfg.owned.generics.params.into_iter()); - - if let Some(where_clause) = cfg.owned.generics.where_clause { - generics - .make_where_clause() - .predicates - .extend(where_clause.predicates); - } + .make_where_clause() + .predicates + .extend(where_clause.predicates); + } - let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); - tokens.extend(quote::quote! { - impl #impl_generics ::alkahest::private::Serialize<#formula_path> for #ident #type_generics #where_clause { + let tokens = if by_ref { + quote::quote! { + impl #impl_generics ::alkahest::private::SerializeRef<#formula_path> for #ident #type_generics #where_clause { #[inline(always)] - fn serialize(self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: B) -> ::alkahest::private::Result<(), B::Error> + fn serialize<__alkahest_Buffer>(&self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: __alkahest_Buffer) -> ::alkahest::private::Result<(), __alkahest_Buffer::Error> where - B: ::alkahest::private::Buffer, + __alkahest_Buffer: ::alkahest::private::Buffer, { #![allow(unused_mut, unused_variables)] #field_checks - match self { + match *self { #( - #ident::#variant_names #bind_names => { + #ident::#variant_names #bind_ref_names => { ::alkahest::private::write_exact_size_field::(#formula_path::#variant_name_ids, __sizes, __buffer.reborrow())?; #( let with_formula = ::alkahest::private::with_formula(|s: &#formula_path| match *s { @@ -753,7 +506,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { #formula_path::#variant_names #bind_ref_names => #bound_names, _ => unreachable!(), }); - __total += with_formula.size_hint(#bound_names, #field_counts == 1 + #field_ids)?; + __total += with_formula.size_hint(&#bound_names, #field_counts == 1 + #field_ids)?; )* Some(__total) } @@ -761,40 +514,20 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { } } } - }); - } - - if let Some(reference) = cfg.reference { - let formula_path = &reference.path; - let mut generics = input.generics.clone(); - - generics.lt_token = generics.lt_token.or(reference.generics.lt_token); - generics.gt_token = generics.gt_token.or(reference.generics.gt_token); - generics - .params - .extend(reference.generics.params.into_iter()); - - if let Some(where_clause) = reference.generics.where_clause { - generics - .make_where_clause() - .predicates - .extend(where_clause.predicates); } - - let (impl_generics, _type_generics, where_clause) = generics.split_for_impl(); - - tokens.extend(quote::quote! { - impl #impl_generics ::alkahest::private::Serialize<#formula_path> for &#ident #type_generics #where_clause { + } else { + quote::quote! { + impl #impl_generics ::alkahest::private::Serialize<#formula_path> for #ident #type_generics #where_clause { #[inline(always)] - fn serialize(self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: B) -> ::alkahest::private::Result<(), B::Error> + fn serialize<__alkahest_Buffer>(self, __sizes: &mut ::alkahest::private::Sizes, mut __buffer: __alkahest_Buffer) -> ::alkahest::private::Result<(), __alkahest_Buffer::Error> where - B: ::alkahest::private::Buffer, + __alkahest_Buffer: ::alkahest::private::Buffer, { #![allow(unused_mut, unused_variables)] #field_checks - match *self { + match self { #( - #ident::#variant_names #bind_ref_names => { + #ident::#variant_names #bind_names => { ::alkahest::private::write_exact_size_field::(#formula_path::#variant_name_ids, __sizes, __buffer.reborrow())?; #( let with_formula = ::alkahest::private::with_formula(|s: &#formula_path| match *s { @@ -816,7 +549,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { if let ::alkahest::private::Option::Some(size) = ::alkahest::private::formula_fast_sizes::<#formula_path>() { return Some(size); } - match **self { + match *self { #( #ident::#variant_names #bind_ref_names => { let mut __total = ::alkahest::private::Sizes::with_stack(::alkahest::private::VARIANT_SIZE); @@ -825,7 +558,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { #formula_path::#variant_names #bind_ref_names => #bound_names, _ => unreachable!(), }); - __total += with_formula.size_hint(&#bound_names, #field_counts == 1 + #field_ids)?; + __total += with_formula.size_hint(#bound_names, #field_counts == 1 + #field_ids)?; )* Some(__total) } @@ -833,8 +566,8 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result { } } } - }); - } + } + }; Ok(tokens) } diff --git a/src/array.rs b/src/array.rs index c2df17d..b48f73d 100644 --- a/src/array.rs +++ b/src/array.rs @@ -3,7 +3,7 @@ use crate::{ deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{repeat_size, BareFormula, Formula}, iter::{owned_iter_fast_sizes, ref_iter_fast_sizes}, - serialize::{write_array, write_slice, Serialize, Sizes}, + serialize::{write_array, write_slice, Serialize, SerializeRef, Sizes}, }; impl Formula for [F; N] @@ -11,7 +11,7 @@ where F: Formula, { const MAX_STACK_SIZE: Option = repeat_size(F::MAX_STACK_SIZE, N); - const EXACT_SIZE: bool = true; // All elements are padded. + const EXACT_SIZE: bool = F::EXACT_SIZE; const HEAPLESS: bool = F::HEAPLESS; } @@ -36,13 +36,13 @@ where } } -impl<'ser, F, T, const N: usize> Serialize<[F; N]> for &'ser [T; N] +impl SerializeRef<[F; N]> for [T; N] where F: Formula, - &'ser T: Serialize, + for<'ser> &'ser T: Serialize, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { diff --git a/src/buffer.rs b/src/buffer.rs index 0496db3..22d42e0 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -38,9 +38,10 @@ pub trait Buffer { /// Reserves heap space and returns a buffer over it. /// Returned buffer is always of `FixedBuffer` type. /// - /// If buffer cannot reserve heap space, it should return `Ok(None)`. - /// In this case serializing code should fallback - /// to using `write_stack` and `move_to_heap`. + /// If buffer cannot reserve heap space, it may return either + /// `Err` or `Ok([])`. + /// If `Ok([])` is returned serializer should skip writing this + /// part of the data and continue writing the rest. /// /// # Errors /// diff --git a/src/bytes.rs b/src/bytes.rs index eeec6c9..cc1dd71 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -2,7 +2,7 @@ use crate::{ buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{BareFormula, Formula}, - serialize::{write_bytes, Serialize, Sizes}, + serialize::{write_bytes, SerializeRef, Sizes}, }; /// A formula for a raw byte slices. @@ -11,15 +11,15 @@ pub struct Bytes; impl Formula for Bytes { const MAX_STACK_SIZE: Option = None; - const EXACT_SIZE: bool = true; + const EXACT_SIZE: bool = false; const HEAPLESS: bool = true; } impl BareFormula for Bytes {} -impl Serialize for &[u8] { +impl SerializeRef for [u8] { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { diff --git a/src/deserialize.rs b/src/deserialize.rs index 0bff154..ab87d32 100644 --- a/src/deserialize.rs +++ b/src/deserialize.rs @@ -655,70 +655,57 @@ where { } -/// Reads size of the value from the input. -/// Returns `None` if the input is too short to determine the size. +/// Deserializes value from the input. +/// The value must occupy the whole input slice. +/// The value must be either sized or heap-less. +/// Returns deserialized value. /// -/// # Panics +/// # Errors /// -/// This function may panic if the value size is too big to fit `usize`. -#[must_use] +/// Returns `DeserializeError` if deserialization fails. #[inline(always)] -pub fn value_size(input: &[u8]) -> Option +pub fn deserialize<'de, F, T>(input: &'de [u8]) -> Result where F: Formula + ?Sized, + T: Deserialize<'de, F>, { - match F::MAX_STACK_SIZE { - Some(0) => Some(0), - _ => { - if input.len() < SIZE_STACK { - None - } else { - let mut bytes = [0u8; SIZE_STACK]; - bytes.copy_from_slice(&input[..SIZE_STACK]); - let address = - FixedUsize::from_le_bytes(bytes).expect("Value size can't fit `usize`"); - Some(address.into()) - } - } - } + let stack = match F::MAX_STACK_SIZE { + None => input.len(), + Some(max_stack) => max_stack.min(input.len()), + }; + + let de = Deserializer::new_unchecked(stack, input); + let value = >::deserialize(de)?; + + Ok(value) } /// Deserializes value from the input. -/// Returns deserialized value and number of bytes consumed. +/// The value must occupy the whole input slice. +/// Returns deserialized value. /// /// # Errors /// /// Returns `DeserializeError` if deserialization fails. #[inline(always)] -pub fn deserialize<'de, F, T>(input: &'de [u8]) -> Result<(T, usize), DeserializeError> +pub fn deserialize_with_size<'de, F, T>( + input: &'de [u8], + stack: usize, +) -> Result where F: Formula + ?Sized, T: Deserialize<'de, F>, { - let reference_size = reference_size::(); - - if input.len() < reference_size { - return Err(DeserializeError::OutOfBounds); - } - - let (address, size) = read_reference::(input, input.len() - reference_size); - - if size > address { - return Err(DeserializeError::WrongAddress); - } - - if address > input.len() { - return Err(DeserializeError::OutOfBounds); - } - - let de = Deserializer::new_unchecked(size, &input[..address]); + let de = Deserializer::new_unchecked(stack, input); let value = >::deserialize(de)?; - Ok((value, address)) + Ok(value) } -/// Deserializes value from the input into specified place. -/// Returns number of bytes consumed. +/// Deserializes value from the input. +/// The value must occupy the whole input slice. +/// The value must be either sized or heap-less. +/// Updates value in-place. /// /// # Errors /// @@ -727,55 +714,59 @@ where pub fn deserialize_in_place<'de, F, T>( place: &mut T, input: &'de [u8], -) -> Result +) -> Result<(), DeserializeError> where F: Formula + ?Sized, T: Deserialize<'de, F> + ?Sized, { - let reference_size = reference_size::(); - - if input.len() < reference_size { - return Err(DeserializeError::OutOfBounds); - } - - let (address, size) = read_reference::(input, input.len() - reference_size); - - if size > address { - return Err(DeserializeError::WrongAddress); - } + let stack = match F::MAX_STACK_SIZE { + None => input.len(), + Some(max_stack) => max_stack.min(input.len()), + }; + let de = Deserializer::new_unchecked(stack, input); + >::deserialize_in_place(place, de)?; - if address > input.len() { - return Err(DeserializeError::OutOfBounds); - } + Ok(()) +} - let de = Deserializer::new_unchecked(size, &input[..address]); +/// Deserializes value from the input. +/// The value must occupy the whole input slice. +/// Updates value in-place. +/// +/// # Errors +/// +/// Returns `DeserializeError` if deserialization fails. +#[inline(always)] +pub fn deserialize_in_place_with_size<'de, F, T>( + place: &mut T, + input: &'de [u8], + stack: usize, +) -> Result<(), DeserializeError> +where + F: Formula + ?Sized, + T: Deserialize<'de, F> + ?Sized, +{ + let de = Deserializer::new_unchecked(stack, input); >::deserialize_in_place(place, de)?; - Ok(address) + Ok(()) } #[inline(always)] -fn read_reference(input: &[u8], len: usize) -> (usize, usize) +pub fn read_reference(input: &[u8], len: usize) -> (usize, usize) where F: Formula + ?Sized, { let reference_size = reference_size::(); debug_assert!(reference_size <= input.len()); - match (F::MAX_STACK_SIZE, F::EXACT_SIZE) { - (Some(0), _) => { - // do nothing - (0, 0) - } - (Some(max_stack), true) => { - let mut de = Deserializer::new(reference_size, &input[..reference_size]).unwrap(); - let Ok(address) = de.read_value::(true) else { unreachable!(); }; - (address, max_stack.min(len)) - } - _ => { - let mut de = Deserializer::new(reference_size, &input[..reference_size]).unwrap(); - let Ok([size, address]) = de.read_value::<[FixedUsize; 2], [usize; 2]>(true) else { unreachable!(); }; - (address, size) - } + if F::EXACT_SIZE { + let mut de = Deserializer::new(reference_size, &input[..reference_size]).unwrap(); + let Ok(address) = de.read_value::(true) else { unreachable!(); }; + (address, unwrap_size(F::MAX_STACK_SIZE).min(len)) + } else { + let mut de = Deserializer::new(reference_size, &input[..reference_size]).unwrap(); + let Ok([size, address]) = de.read_value::<[FixedUsize; 2], [usize; 2]>(true) else { unreachable!(); }; + (address, size) } } diff --git a/src/formula.rs b/src/formula.rs index 2628a66..afcd5e7 100644 --- a/src/formula.rs +++ b/src/formula.rs @@ -59,26 +59,26 @@ use crate::size::SIZE_STACK; feature = "derive", doc = r#" -When "derive" feature is enabled, `derive(Formula)` is also available. +When "derive" feature is enabled, proc-macro `alkahest(Formula)` is also available. ``` # use alkahest::*; /// Formula for serializing unit structures. -#[derive(Formula)] +#[alkahest(Formula)] struct UnitFormula; # #[cfg(feature = "alloc")] /// Formula for serializing tuple structures with fields /// that are serializable with `u8` and `String` formulas. -#[derive(Formula)] +#[alkahest(Formula)] struct TupleFormula(u8, String); # #[cfg(feature = "alloc")] /// Formula for serializing structures with fields /// that are serializable with `TupleFormula` and `Vec` formulas. -#[derive(Formula)] +#[alkahest(Formula)] struct StructFormula { a: TupleFormula, b: Vec, @@ -87,7 +87,7 @@ struct StructFormula { # #[cfg(feature = "alloc")] /// Formula for serializing enums. -#[derive(Formula)] +#[alkahest(Formula)] enum EnumFormula { A, B(StructFormula), @@ -95,7 +95,7 @@ enum EnumFormula { } ``` -Names of the formula variants and fields are important for `Serialize` and `Deserialize` derive macros. +Names of the formula variants and fields are important for `Serialize` and `Deserialize` proc-macros. "# )] pub trait Formula { @@ -177,9 +177,9 @@ pub const fn reference_size() -> usize where F: Formula + ?Sized, { - match (F::MAX_STACK_SIZE, F::EXACT_SIZE) { - (Some(0), _) => 0, - (Some(_), true) => SIZE_STACK, - _ => SIZE_STACK * 2, + if F::EXACT_SIZE { + SIZE_STACK + } else { + SIZE_STACK * 2 } } diff --git a/src/lazy.rs b/src/lazy.rs index 9515653..56de58f 100644 --- a/src/lazy.rs +++ b/src/lazy.rs @@ -97,8 +97,8 @@ where /// # use alkahest::*; /// let mut buffer = [0u8; 1024]; /// - /// serialize::<[u32], _>([1u8, 2, 3], &mut buffer).unwrap(); - /// let (lazy, _) = deserialize::<[u32], Lazy<[u32]>>(&buffer).unwrap(); + /// let (size, root) = serialize::<[u32], _>([1u8, 2, 3], &mut buffer).unwrap(); + /// let lazy = deserialize_with_size::<[u32], Lazy<[u32]>>(&buffer[..size], root).unwrap(); /// let mut iter = lazy.sized_iter::(); /// assert_eq!(iter.next().unwrap().unwrap(), 1); /// assert_eq!(iter.next().unwrap().unwrap(), 2); @@ -136,8 +136,8 @@ where /// # use alkahest::*; /// let mut buffer = [0u8; 1024]; /// - /// serialize::<[u32], _>([1u8, 2, 3], &mut buffer).unwrap(); - /// let (lazy, _) = deserialize::<[u32], Lazy<[u32]>>(&buffer).unwrap(); + /// let (size, root) = serialize::<[u32], _>([1u8, 2, 3], &mut buffer).unwrap(); + /// let lazy = deserialize_with_size::<[u32], Lazy<[u32]>>(&buffer[..size], root).unwrap(); /// let mut iter = lazy.iter::(); /// assert_eq!(iter.next().unwrap().unwrap(), 1); /// assert_eq!(iter.next().unwrap().unwrap(), 2); @@ -151,8 +151,8 @@ where /// # use alkahest::*; /// let mut buffer = [0u8; 1024]; /// - /// serialize::<[As], _>(["qwe", "rty"], &mut buffer).unwrap(); - /// let (seq, _) = deserialize::<[As], Lazy<[As]>>(&buffer).unwrap(); + /// let (size, root) = serialize::<[As], _>(["qwe", "rty"], &mut buffer).unwrap(); + /// let seq = deserialize_with_size::<[As], Lazy<[As]>>(&buffer[..size], root).unwrap(); /// let mut iter = seq.iter::<&str>(); /// assert_eq!(iter.next().unwrap().unwrap(), "qwe"); /// assert_eq!(iter.next().unwrap().unwrap(), "rty"); diff --git a/src/lib.rs b/src/lib.rs index 7df7730..3cceac0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ mod formula; mod iter; mod lazy; mod option; +mod packet; mod primitive; mod reference; mod serialize; @@ -53,16 +54,21 @@ pub use crate::{ buffer::BufferExhausted, bytes::Bytes, deserialize::{ - deserialize, deserialize_in_place, value_size, DeIter, Deserialize, DeserializeError, + deserialize, deserialize_in_place, deserialize_in_place_with_size, deserialize_with_size, + DeIter, Deserialize, DeserializeError, }, formula::Formula, iter::SerIter, lazy::Lazy, + packet::{ + packet_size, read_packet, read_packet_in_place, read_packet_size, write_packet, + write_packet_into, write_packet_unchecked, + }, r#as::As, reference::Ref, serialize::{ serialize, serialize_or_size, serialize_unchecked, serialized_size, BufferSizeRequired, - Serialize, + Serialize, SerializeRef, }, size::{FixedIsize, FixedUsize}, skip::Skip, @@ -70,10 +76,10 @@ pub use crate::{ }; #[cfg(feature = "alloc")] -pub use crate::serialize::serialize_to_vec; +pub use crate::{packet::write_packet_to_vec, serialize::serialize_to_vec}; #[cfg(feature = "derive")] -pub use alkahest_proc::{Deserialize, Formula, Serialize}; +pub use alkahest_proc::{alkahest, Deserialize, Formula, Serialize, SerializeRef}; #[cfg(feature = "bincoded")] pub use bincoded::{Bincode, Bincoded}; @@ -112,7 +118,9 @@ pub mod private { buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{max_size, sum_size, BareFormula, Formula}, - serialize::{formula_fast_sizes, write_exact_size_field, write_field, Serialize, Sizes}, + serialize::{ + formula_fast_sizes, write_exact_size_field, write_field, Serialize, SerializeRef, Sizes, + }, }; use core::marker::PhantomData; diff --git a/src/option.rs b/src/option.rs index 69d6b8c..b95b01f 100644 --- a/src/option.rs +++ b/src/option.rs @@ -2,7 +2,7 @@ use crate::{ buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{sum_size, BareFormula, Formula}, - serialize::{field_size_hint, write_bytes, write_field, Serialize, Sizes}, + serialize::{field_size_hint, write_bytes, write_field, Serialize, SerializeRef, Sizes}, }; impl Formula for Option @@ -51,13 +51,13 @@ where } } -impl<'ser, F, T> Serialize> for &'ser Option +impl SerializeRef> for Option where F: Formula, - &'ser T: Serialize, + for<'ser> &'ser T: Serialize, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> where B: Buffer, { @@ -72,7 +72,7 @@ where #[inline(always)] fn size_hint(&self) -> Option { - match *self { + match self { None => { let stack = >::MAX_STACK_SIZE?; Some(Sizes::with_stack(stack)) diff --git a/src/packet.rs b/src/packet.rs new file mode 100644 index 0000000..c057d8a --- /dev/null +++ b/src/packet.rs @@ -0,0 +1,212 @@ +use crate::{ + buffer::{Buffer, BufferExhausted, CheckedFixedBuffer, DryBuffer, VecBuffer}, + deserialize::{read_reference, Deserialize, DeserializeError, Deserializer}, + formula::{reference_size, Formula}, + serialize::{write_ref, write_reference, Serialize, Sizes}, + size::{FixedUsize, SIZE_STACK}, +}; + +/// Returns the number of bytes required to write packet with the value. +/// Note that value is consumed. +/// +/// Use when value is `Copy` or can be cheaply replicated to allocate +/// the buffer for serialization in advance. +/// Or to find out required size after [`write_packet`] fails. +#[inline(always)] +pub fn packet_size(value: T) -> usize +where + F: Formula + ?Sized, + T: Serialize, +{ + match write_packet_into(value, DryBuffer) { + Ok(size) => size, + Err(never) => match never {}, + } +} + +/// Writes packet with the value into buffer. +/// The buffer type controls bytes writing and failing strategy. +#[inline(always)] +pub fn write_packet_into(value: T, mut buffer: B) -> Result +where + F: Formula + ?Sized, + T: Serialize, + B: Buffer, +{ + let reference_size = reference_size::(); + buffer.reserve_heap(0, 0, reference_size)?; + + let mut sizes = Sizes { + heap: reference_size, + stack: 0, + }; + + let size = write_ref(value, &mut sizes, buffer.reborrow())?; + + match buffer.reserve_heap(0, 0, reference_size)? { + [] => {} + reserved => { + write_reference::(size, sizes.heap, 0, 0, reserved).unwrap(); + } + } + + Ok(sizes.heap) +} + +/// Writes packet with the value into bytes slice. +/// Returns the number of bytes written. +/// Fails if the buffer is too small. +/// +/// To retrieve the number of bytes required to serialize the value, +/// use [`serialized_size`] or [`serialize_or_size`]. +/// +/// # Errors +/// +/// Returns [`BufferExhausted`] if the buffer is too small. +#[inline(always)] +pub fn write_packet(value: T, output: &mut [u8]) -> Result +where + F: Formula + ?Sized, + T: Serialize, +{ + write_packet_into::(value, CheckedFixedBuffer::new(output)) +} + +/// Writes packet with the value into bytes slice. +/// Slightly faster version of [`write_packet`]. +/// Panics if the buffer is too small instead of returning an error. +/// +/// Use instead of using [`write_packet`] with immediate [`unwrap`](Result::unwrap). +#[inline(always)] +pub fn write_packet_unchecked(value: T, output: &mut [u8]) -> usize +where + F: Formula + ?Sized, + T: Serialize, +{ + match write_packet_into::(value, output) { + Ok(size) => size, + Err(never) => match never {}, + } +} + +/// Writes packet with the value into byte vector. +/// Returns the number of bytes written. +/// +/// Grows the vector if needed. +/// Infallible except for allocation errors. +/// +/// Use pre-allocated vector when possible to avoid reallocations. +#[cfg(feature = "alloc")] +#[inline(always)] +pub fn write_packet_to_vec(value: T, output: &mut alloc::vec::Vec) -> usize +where + F: Formula + ?Sized, + T: Serialize, +{ + match write_packet_into::(value, VecBuffer::new(output)) { + Ok(size) => size, + Err(never) => match never {}, + } +} + +/// Reads size of the packet with value from the input. +/// Returns `None` if the input is too short to determine the size. +/// +/// # Panics +/// +/// This function may panic if the value size is too big to fit `usize`. +#[must_use] +#[inline(always)] +pub fn read_packet_size(input: &[u8]) -> Option +where + F: Formula + ?Sized, +{ + match F::MAX_STACK_SIZE { + Some(0) => Some(0), + _ => { + if input.len() < SIZE_STACK { + None + } else { + let mut bytes = [0u8; SIZE_STACK]; + bytes.copy_from_slice(&input[..SIZE_STACK]); + let address = + FixedUsize::from_le_bytes(bytes).expect("Value size can't fit `usize`"); + Some(address.into()) + } + } + } +} + +/// Reads packet with value from the input. +/// Returns deserialized value and number of bytes consumed. +/// +/// # Errors +/// +/// Returns `DeserializeError` if deserialization fails. +#[must_use] +#[inline(always)] +pub fn read_packet<'de, F, T>(input: &'de [u8]) -> Result<(T, usize), DeserializeError> +where + F: Formula + ?Sized, + T: Deserialize<'de, F>, +{ + let reference_size = reference_size::(); + + if input.len() < reference_size { + return Err(DeserializeError::OutOfBounds); + } + + let (address, size) = read_reference::(input, input.len() - reference_size); + + if size > address { + return Err(DeserializeError::WrongAddress); + } + + if address > input.len() { + return Err(DeserializeError::OutOfBounds); + } + + let de = Deserializer::new_unchecked(size, &input[..address]); + let value = >::deserialize(de)?; + + Ok((value, address)) +} + +/// Reads packet with value from the input. +/// Updates the value in-place. +/// Returns number of bytes consumed. +/// +/// # Errors +/// +/// Returns `DeserializeError` if deserialization fails. +#[must_use] +#[inline(always)] +pub fn read_packet_in_place<'de, F, T>( + place: &mut T, + input: &'de [u8], +) -> Result +where + F: Formula + ?Sized, + T: Deserialize<'de, F> + ?Sized, +{ + let reference_size = reference_size::(); + + if input.len() < reference_size { + return Err(DeserializeError::OutOfBounds); + } + + let (address, size) = read_reference::(input, input.len() - reference_size); + + if size > address { + return Err(DeserializeError::WrongAddress); + } + + if address > input.len() { + return Err(DeserializeError::OutOfBounds); + } + + let de = Deserializer::new_unchecked(size, &input[..address]); + >::deserialize_in_place(place, de)?; + + Ok(address) +} diff --git a/src/primitive.rs b/src/primitive.rs index 5e26da9..3bbf0d5 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -4,7 +4,7 @@ use crate::{ buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{BareFormula, Formula}, - serialize::{write_bytes, Serialize, Sizes}, + serialize::{write_bytes, Serialize, SerializeRef, Sizes}, }; macro_rules! impl_primitive { @@ -62,9 +62,9 @@ macro_rules! impl_primitive { } )* - impl Serialize<$ty> for &$ty { + impl SerializeRef<$ty> for $ty { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { @@ -78,9 +78,9 @@ macro_rules! impl_primitive { } $( - impl Serialize<$ty> for &$from { + impl SerializeRef<$ty> for $from { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { diff --git a/src/reference.rs b/src/reference.rs index d91166e..7ebfbcc 100644 --- a/src/reference.rs +++ b/src/reference.rs @@ -8,7 +8,7 @@ use crate::{ buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{reference_size, BareFormula, Formula}, - serialize::{field_size_hint, write_ref, Serialize, Sizes}, + serialize::{field_size_hint, write_ref, write_reference, Serialize, Sizes}, }; /// `Ref` is a formula wrapper. @@ -37,11 +37,14 @@ where T: Serialize, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> where B: Buffer, { - write_ref::(self, sizes, buffer) + let size = write_ref::(self, sizes, buffer.reborrow())?; + write_reference::(size, sizes.heap, sizes.heap, sizes.stack, buffer)?; + sizes.stack += reference_size::(); + Ok(()) } #[inline(always)] diff --git a/src/serialize.rs b/src/serialize.rs index a2aa6be..5e79a47 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,8 +1,8 @@ -use core::{any::type_name, fmt, marker::PhantomData, ops}; +use core::{fmt, marker::PhantomData, ops}; use crate::{ buffer::{Buffer, BufferExhausted, CheckedFixedBuffer, DryBuffer, MaybeFixedBuffer}, - formula::{reference_size, unwrap_size, BareFormula, Formula}, + formula::{unwrap_size, BareFormula, Formula}, size::{FixedUsize, SIZE_STACK}, }; @@ -57,6 +57,12 @@ impl Sizes { self.stack = until; len } + + /// Returns total size. + #[inline(always)] + pub fn total(&self) -> usize { + self.heap + self.stack + } } impl ops::Add for Sizes { @@ -127,12 +133,11 @@ When "derive" feature is enabled, `derive(Serialize)` is also available. /// Self-serializable empty formula. -#[derive(Formula, Serialize)] +#[alkahest(Formula, Serialize)] struct EmptyFormula {} /// Another type serializable with `EmptyFormula`. -#[derive(Serialize)] -#[alkahest(EmptyFormula)] +#[alkahest(Serialize)] struct EmptySerialize; @@ -140,18 +145,17 @@ struct EmptySerialize; /// that are serializable with `u8` and `[u16]` formulas. /// Slice formulas are serialized from some `IntoIterator`s and `SerIter` wrapper over any `Iterator` /// with serializable item type. -#[derive(Formula)] +#[alkahest(Formula)] struct TupleFormula(u8, [u16]); -#[derive(Serialize)] -#[alkahest(owned(TupleFormula))] // `owned()` because iterators cannot be serialized by reference. +#[alkahest(Serialize)] struct TupleSerialize(u8, std::iter::Once); /// Formula for serializing structures with fields /// that are serializable with `u8` and `str` formulas. -#[derive(Formula)] +#[alkahest(Formula)] struct StructFormula { a: u8, b: str, @@ -159,8 +163,7 @@ struct StructFormula { # #[cfg(feature = "alloc")] /// `String` can be serialized with `str` formula. -#[derive(Serialize)] -#[alkahest(StructFormula)] +#[alkahest(Serialize)] struct StructSerialize { a: u8, b: String, @@ -168,7 +171,7 @@ struct StructSerialize { # #[cfg(feature = "alloc")] /// Formula for serializing enums. -#[derive(Formula, Serialize)] +#[alkahest(Formula, Serialize)] enum EnumFormula { A, B(u8), @@ -177,8 +180,7 @@ enum EnumFormula { # #[cfg(feature = "alloc")] /// `&str` can be serialized with `String` formula. -#[derive(Serialize)] -#[alkahest(EnumFormula)] +#[alkahest(Serialize)] # // While `Formula` derive macro makes all variants and fields used, # // this is not the case for `Serialize` derive macro. # #[allow(dead_code)] @@ -190,8 +192,7 @@ enum EnumSerialize<'a> { # #[cfg(feature = "alloc")] /// `&str` can be serialized with `String` formula. -#[derive(Serialize)] -#[alkahest(EnumFormula, @C)] +#[alkahest(Serialize)] struct CVariantSerialize { y: String, } @@ -222,10 +223,73 @@ pub trait Serialize { fn size_hint(&self) -> Option; } -impl<'ser, F, T: ?Sized> Serialize for &&'ser T +// impl<'ser, F, T: ?Sized> Serialize for &&'ser T +// where +// F: BareFormula + ?Sized, +// &'ser T: Serialize, +// { +// #[inline(always)] +// fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> +// where +// Self: Sized, +// B: Buffer, +// { +// <&'ser T as Serialize>::serialize(self, sizes, buffer) +// } + +// #[inline(always)] +// fn size_hint(&self) -> Option { +// <&'ser T as Serialize>::size_hint(self) +// } +// } + +/// `Serialize` but for references. +pub trait SerializeRef { + /// Serializes `self` into the given buffer. + /// `heap` specifies the size of the buffer's heap occupied prior to this call. + /// + /// # Errors + /// + /// Returns error if buffer write fails. + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + where + B: Buffer; + + /// Returns heap and stack sizes required to serialize `self`. + /// If some sizes are returned they must be exact. + /// + /// This function may return none conservatively. + /// + /// Returning incorrect sizes may cause panics during implementation + /// or broken data. + fn size_hint(&self) -> Option; +} + +impl SerializeRef for &T +where + F: Formula + ?Sized, + T: ?Sized, + for<'a> &'a T: Serialize, +{ + #[inline(always)] + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + where + Self: Sized, + B: Buffer, + { + <&T as Serialize>::serialize(self, sizes, buffer) + } + + #[inline(always)] + fn size_hint(&self) -> Option { + <&T as Serialize>::size_hint(self) + } +} + +impl Serialize for &T where F: BareFormula + ?Sized, - &'ser T: Serialize, + T: SerializeRef + ?Sized, { #[inline(always)] fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> @@ -233,90 +297,28 @@ where Self: Sized, B: Buffer, { - <&'ser T as Serialize>::serialize(self, sizes, buffer) + >::serialize(self, sizes, buffer) } #[inline(always)] fn size_hint(&self) -> Option { - <&'ser T as Serialize>::size_hint(self) + >::size_hint(self) } } /// Serialize value into buffer. +/// Returns total number of bytes written and size of the root value. /// The buffer type controls bytes writing and failing strategy. #[inline(always)] -pub fn serialize_into(value: T, mut buffer: B) -> Result +pub fn serialize_into(value: T, buffer: B) -> Result<(usize, usize), B::Error> where F: Formula + ?Sized, T: Serialize, B: Buffer, { - let reference_size = reference_size::(); - buffer.reserve_heap(0, 0, reference_size)?; - - // Can we get promised sizes? - let promised = >::size_hint(&value); - - let mut sizes = Sizes { - heap: reference_size, - stack: 0, - }; - - match promised { - None => { - >::serialize(value, &mut sizes, buffer.reborrow())?; - buffer.move_to_heap(sizes.heap, sizes.stack, sizes.stack); - } - Some(promised) => { - match buffer.reserve_heap(reference_size, 0, promised.heap + promised.stack)? { - [] => { - sizes += serialized_sizes(value); - } - reserved => { - >::serialize(value, &mut sizes, reserved).unwrap(); - debug_assert_eq!(sizes.heap, promised.heap + reference_size); - debug_assert_eq!(sizes.stack, promised.stack); - } - } - debug_assert_eq!( - reference_size + promised.heap, - sizes.heap, - "<{} as Serialize<{}>>::size_hint() result is incorrect", - type_name::(), - type_name::() - ); - debug_assert_eq!( - promised.stack, - sizes.stack, - "<{} as Serialize<{}>>::size_hint() result is incorrect", - type_name::(), - type_name::() - ); - } - }; - - #[cfg(debug_assertions)] - { - if let Some(max_size) = F::MAX_STACK_SIZE { - assert!( - sizes.stack <= max_size, - "Incorrect `<{} as Serialize<{}>>` implementation. `stack` size is `{}` but must be at most `{}`", - type_name::(), - type_name::(), - sizes.stack, - max_size, - ); - }; - } - - match buffer.reserve_heap(0, 0, reference_size)? { - [] => {} - reserved => { - write_reference::(sizes.stack, sizes.heap + sizes.stack, 0, 0, reserved).unwrap(); - } - } - - Ok(sizes.heap + sizes.stack) + let mut sizes = Sizes { heap: 0, stack: 0 }; + let size = write_ref(value, &mut sizes, buffer)?; + Ok((sizes.heap, size)) } /// Serialize value into bytes slice. @@ -330,7 +332,7 @@ where /// /// Returns [`BufferExhausted`] if the buffer is too small. #[inline(always)] -pub fn serialize(value: T, output: &mut [u8]) -> Result +pub fn serialize(value: T, output: &mut [u8]) -> Result<(usize, usize), BufferExhausted> where F: Formula + ?Sized, T: Serialize, @@ -343,13 +345,13 @@ where /// /// Use instead of using [`serialize`] with immediate [`unwrap`](Result::unwrap). #[inline(always)] -pub fn serialize_unchecked(value: T, output: &mut [u8]) -> usize +pub fn serialize_unchecked(value: T, output: &mut [u8]) -> (usize, usize) where F: Formula + ?Sized, T: Serialize, { match serialize_into::(value, output) { - Ok(size) => size, + Ok(sizes) => sizes, Err(never) => match never {}, } } @@ -385,21 +387,24 @@ impl fmt::Display for BufferSizeRequired { /// Returns [`BufferSizeRequired`] error if the buffer is too small. /// Error contains the exact number of bytes required. #[inline(always)] -pub fn serialize_or_size(value: T, output: &mut [u8]) -> Result +pub fn serialize_or_size( + value: T, + output: &mut [u8], +) -> Result<(usize, usize), BufferSizeRequired> where F: Formula + ?Sized, T: Serialize, { let mut exhausted = false; let result = serialize_into::(value, MaybeFixedBuffer::new(output, &mut exhausted)); - let size = match result { - Ok(size) => size, + let sizes = match result { + Ok(sizes) => sizes, Err(never) => match never {}, }; if exhausted { - Err(BufferSizeRequired { required: size }) + Err(BufferSizeRequired { required: sizes.0 }) } else { - Ok(size) + Ok(sizes) } } @@ -412,26 +417,13 @@ where /// Use pre-allocated vector when possible to avoid reallocations. #[cfg(feature = "alloc")] #[inline(always)] -pub fn serialize_to_vec(value: T, output: &mut alloc::vec::Vec) -> usize +pub fn serialize_to_vec(value: T, output: &mut alloc::vec::Vec) -> (usize, usize) where F: Formula + ?Sized, T: Serialize, { match serialize_into::(value, VecBuffer::new(output)) { - Ok(size) => size, - Err(never) => match never {}, - } -} - -#[inline(always)] -fn serialized_sizes(value: T) -> Sizes -where - F: Formula + ?Sized, - T: Serialize, -{ - let mut sizes = Sizes::ZERO; - match Serialize::::serialize(value, &mut sizes, DryBuffer) { - Ok(()) => sizes, + Ok(sizes) => sizes, Err(never) => match never {}, } } @@ -443,14 +435,16 @@ where /// the buffer for serialization in advance. /// Or to find out required size after [`serialize`] fails. #[inline(always)] -pub fn serialized_size(value: T) -> usize +pub fn serialized_size(value: T) -> (usize, usize) where F: Formula + ?Sized, T: Serialize, { - let reference_size = reference_size::(); - let sizes = serialized_sizes::(value); - sizes.heap + sizes.stack + reference_size + let mut sizes = Sizes::ZERO; + match Serialize::::serialize(value, &mut sizes, DryBuffer) { + Ok(()) => (sizes.total(), sizes.stack), + Err(never) => match never {}, + } } /// Size hint for serializing a field. @@ -501,17 +495,11 @@ where let address = FixedUsize::truncate_unchecked(address); let size = FixedUsize::truncate_unchecked(size); - match (F::MAX_STACK_SIZE, F::EXACT_SIZE) { - (Some(0), _) => { - // do nothing - } - (Some(_), true) => { - buffer.write_stack(heap, stack, &address.to_le_bytes())?; - } - _ => { - buffer.write_stack(heap, stack, &size.to_le_bytes())?; - buffer.write_stack(heap, stack + SIZE_STACK, &address.to_le_bytes())?; - } + if F::EXACT_SIZE { + buffer.write_stack(heap, stack, &address.to_le_bytes())?; + } else { + buffer.write_stack(heap, stack, &size.to_le_bytes())?; + buffer.write_stack(heap, stack + SIZE_STACK, &address.to_le_bytes())?; } Ok(()) } @@ -614,6 +602,7 @@ where } #[cold] +#[inline(always)] fn write_ref_slow(value: T, sizes: &mut Sizes, mut buffer: B) -> Result where F: Formula + ?Sized, @@ -635,49 +624,49 @@ where /// # Errors /// /// Returns error if buffer write fails. +#[must_use] #[inline(always)] -pub fn write_ref(value: T, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> +pub fn write_ref(value: T, sizes: &mut Sizes, mut buffer: B) -> Result where F: Formula + ?Sized, T: Serialize, B: Buffer, { - let reference_size = reference_size::(); - // Can we get promised sizes? let promised = >::size_hint(&value); let stack = match promised { None => write_ref_slow(value, sizes, buffer.reborrow())?, - Some(promised) => { - match buffer.reserve_heap(sizes.heap, sizes.stack, promised.heap + promised.stack)? { - [] => write_ref_slow(value, sizes, buffer.reborrow())?, - reserved => { - let mut reserved_sizes = Sizes { - heap: sizes.heap, - stack: 0, - }; - >::serialize(value, &mut reserved_sizes, reserved) - .expect("Reserved enough space"); - - debug_assert_eq!(reserved_sizes.heap, sizes.heap + promised.heap); - debug_assert_eq!(reserved_sizes.stack, promised.stack); - - sizes.heap = reserved_sizes.heap + reserved_sizes.stack; - reserved_sizes.stack - } + Some(promised) => match buffer.reserve_heap(sizes.heap, sizes.stack, promised.total())? { + [] => match write_ref_slow(value, sizes, DryBuffer) { + Ok(stack) => stack, + Err(never) => match never {}, + }, + reserved => { + let mut reserved_sizes = Sizes { + heap: sizes.heap, + stack: 0, + }; + >::serialize(value, &mut reserved_sizes, reserved) + .expect("Reserved enough space"); + + debug_assert_eq!(reserved_sizes.heap, sizes.heap + promised.heap); + debug_assert_eq!(reserved_sizes.stack, promised.stack); + + sizes.heap = reserved_sizes.total(); + reserved_sizes.stack } - } + }, }; - write_reference::(stack, sizes.heap, sizes.heap, sizes.stack, buffer)?; - sizes.stack += reference_size; - Ok(()) + + Ok(stack) } /// Writes elements of a slice one by one into associated buffer. /// /// Use in [`Serialize::serialize`](Serialize::serialize) implementation /// for slice formulas. +#[must_use] pub struct SliceWriter<'a, F: Formula + ?Sized, B: Buffer + ?Sized> { buffer: &'a mut B, sizes: &'a mut Sizes, @@ -754,7 +743,7 @@ where /// # Errors /// /// Returns error if buffer write fails. -#[inline] +#[inline(always)] pub fn write_slice( mut iter: impl Iterator, sizes: &mut Sizes, @@ -769,7 +758,8 @@ where debug_assert!(::HEAPLESS); let count = if cfg!(debug_assertions) { iter.fold(0, |acc, item| { - debug_assert!(serialize::(item, &mut []).is_ok()); + let r = serialize::(item, &mut []); + assert!(r.is_ok()); acc + 1 }) } else { @@ -777,8 +767,9 @@ where }; write_field::(count, sizes, buffer, true) } else { - iter.try_for_each(|elem| write_field::(elem, sizes, buffer.reborrow(), false))?; - Ok(()) + iter.try_fold((), |(), elem| { + write_field::(elem, sizes, buffer.reborrow(), false) + }) } } @@ -792,7 +783,7 @@ where /// # Errors /// /// Returns error if buffer write fails. -#[inline] +#[inline(always)] pub fn write_array( mut iter: impl Iterator, sizes: &mut Sizes, @@ -803,7 +794,9 @@ where T: Serialize, B: Buffer, { - iter.try_for_each(|elem| write_field::(elem, sizes, buffer.reborrow(), false)) + iter.try_fold((), |(), elem| { + write_field::(elem, sizes, buffer.reborrow(), false) + }) } /// Returns size hint for the formula if it is known at compile time. diff --git a/src/slice.rs b/src/slice.rs index 1b9449b..3841b2c 100644 --- a/src/slice.rs +++ b/src/slice.rs @@ -14,7 +14,7 @@ where Some(0) => Some(SIZE_STACK), _ => None, }; - const EXACT_SIZE: bool = true; // All elements are padded. + const EXACT_SIZE: bool = false; const HEAPLESS: bool = F::HEAPLESS; } diff --git a/src/str.rs b/src/str.rs index fab7dd9..0a182ce 100644 --- a/src/str.rs +++ b/src/str.rs @@ -2,20 +2,20 @@ use crate::{ buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{BareFormula, Formula}, - serialize::{write_bytes, Serialize, Sizes}, + serialize::{write_bytes, SerializeRef, Sizes}, }; impl Formula for str { const MAX_STACK_SIZE: Option = None; - const EXACT_SIZE: bool = true; + const EXACT_SIZE: bool = false; const HEAPLESS: bool = true; } impl BareFormula for str {} -impl Serialize for &str { +impl SerializeRef for str { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { diff --git a/src/string.rs b/src/string.rs index bffa9d5..c1a9033 100644 --- a/src/string.rs +++ b/src/string.rs @@ -5,7 +5,7 @@ use crate::{ deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{reference_size, Formula}, reference::Ref, - serialize::{write_bytes, write_ref, Serialize, Sizes}, + serialize::{write_bytes, write_ref, write_reference, Serialize, Sizes}, }; impl Formula for String { @@ -19,11 +19,14 @@ where T: Serialize, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> where B: Buffer, { - write_ref::(self, sizes, buffer) + let size = write_ref::(self, sizes, buffer.reborrow())?; + write_reference::(size, sizes.heap, sizes.heap, sizes.stack, buffer)?; + sizes.stack += reference_size::(); + Ok(()) } #[inline(always)] diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 8605dff..2d2a24f 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -4,14 +4,13 @@ mod net; #[cfg(feature = "alloc")] use alloc::{collections::VecDeque, vec, vec::Vec}; -#[cfg(all(feature = "derive", feature = "alloc"))] -use alkahest_proc::{Deserialize, Formula, Serialize}; - use crate::{ buffer::BufferExhausted, bytes::Bytes, - deserialize::{deserialize, deserialize_in_place, value_size, Deserialize}, - formula::{reference_size, Formula}, + deserialize::{ + deserialize, deserialize_in_place_with_size, deserialize_with_size, Deserialize, + }, + formula::Formula, lazy::Lazy, r#as::As, reference::Ref, @@ -28,43 +27,44 @@ where { let size = serialized_size::(value); - if size * 2 > buffer.len() { + if (size.0) * 2 > buffer.len() { panic!("Test data is too large"); } - let header_size = reference_size::(); - assert!(header_size <= size); - - match (F::HEAPLESS, F::EXACT_SIZE, F::MAX_STACK_SIZE) { - (true, true, Some(max_stack)) => assert_eq!(header_size + max_stack, size), - (true, false, Some(max_stack)) => assert!(header_size + max_stack >= size), + match (F::EXACT_SIZE, F::MAX_STACK_SIZE) { + (true, Some(max_stack)) => assert_eq!(max_stack, size.1), + (false, Some(max_stack)) => assert!(max_stack >= size.1), _ => {} } - // match serialize_or_size::(value, &mut []) { - // Ok(_) => assert_eq!(size, 0), - // Err(err) => assert_eq!(err.required, size), - // } + if F::HEAPLESS { + assert_eq!(size.0, size.1); + } - // if size > 0 { - // match serialize_or_size::(value, &mut buffer[..size - 1]) { - // Ok(_) => panic!("expected error"), - // Err(err) => assert_eq!(err.required, size), - // } - // } + match serialize_or_size::(value, &mut []) { + Ok(_) => assert_eq!(size.0, 0), + Err(err) => assert_eq!(err.required, size.0), + } + + if size.0 > 0 { + match serialize_or_size::(value, &mut buffer[..size.0 - 1]) { + Ok(_) => panic!("expected error"), + Err(err) => assert_eq!(err.required, size.0), + } + } let size1 = serialize_or_size::(value, buffer).expect("expected success"); assert_eq!(size, size1); - assert_eq!(size, value_size::(&buffer).expect("expected success")); - let buffer2 = &mut buffer[size..]; + + let buffer2 = &mut buffer[size.0..]; match serialize::(value, &mut []) { - Ok(_) => assert_eq!(size, 0), + Ok(_) => assert_eq!(size.0, 0), Err(BufferExhausted) => {} } - if size > 0 { - match serialize::(value, &mut buffer2[..size - 1]) { + if size.0 > 0 { + match serialize::(value, &mut buffer2[..size.0 - 1]) { Ok(_) => panic!("expected error"), Err(BufferExhausted) => {} } @@ -72,25 +72,24 @@ where let size2 = serialize::(value, buffer2).expect("expected success"); assert_eq!(size, size2); - assert_eq!(size, value_size::(&buffer2).expect("expected success")); let buffer = &buffer[..]; - let buffer2 = &buffer[size..]; + let buffer2 = &buffer[size.0..]; - let (mut deval, desize) = deserialize::(buffer).expect("expected success"); - assert_eq!(size, desize); + let mut deval = + deserialize_with_size::(&buffer[..size.0], size.1).expect("expected success"); assert!(eq(value, &deval)); - let desize = deserialize_in_place::(&mut deval, buffer).expect("expected success"); - assert_eq!(size, desize); + deserialize_in_place_with_size::(&mut deval, &buffer[..size.0], size.1) + .expect("expected success"); assert!(eq(value, &deval)); - let (mut deval, desize) = deserialize::(buffer2).expect("expected success"); - assert_eq!(size, desize); + let mut deval = + deserialize_with_size::(&buffer2[..size.0], size.1).expect("expected success"); assert!(eq(value, &deval)); - let desize = deserialize_in_place::(&mut deval, buffer2).expect("expected success"); - assert_eq!(size, desize); + deserialize_in_place_with_size::(&mut deval, &buffer2[..size.0], size.1) + .expect("expected success"); assert!(eq(value, &deval)); } @@ -165,8 +164,8 @@ fn test_slice() { #[test] fn test_ref() { let mut buffer = [0u8; 256]; - test_type::, (), ()>(&(), &mut buffer, |x, y| x == y); - test_type::, u32, u32>(&1, &mut buffer, |x, y| x == y); + // test_type::, (), ()>(&(), &mut buffer, |x, y| x == y); + // test_type::, u32, u32>(&1, &mut buffer, |x, y| x == y); test_type::, str, &str>("qwe", &mut buffer, |x, y| x == *y); } @@ -201,24 +200,23 @@ fn test_vec() { #[cfg(all(feature = "alloc", feature = "derive"))] #[test] fn test_enums() { - use crate::{Deserialize, Formula, Serialize}; + use alkahest_proc::alkahest; use alloc::vec::Vec; - #[derive(Formula)] + #[alkahest(Formula)] enum TestFormula { Foo { a: Ref }, Bar { c: Vec, d: Vec> }, } - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - #[alkahest(TestFormula)] + #[derive(Debug, PartialEq, Eq)] + #[alkahest(Serialize, for<'a> Deserialize<'a, TestFormula>)] enum TestData { Foo { a: u32 }, Bar { c: Vec, d: Vec> }, } - #[derive(Deserialize)] - #[alkahest(TestFormula)] + #[alkahest(Deserialize<'a, TestFormula>)] enum TestDataLazy<'a> { Foo { a: u32, @@ -232,50 +230,11 @@ fn test_enums() { let data = TestData::Foo { a: 1 }; let mut bytes = [0u8; 1024]; - alkahest::serialize::(data, &mut bytes).unwrap(); - let (data, _) = alkahest::deserialize::(&bytes).unwrap(); + let size = alkahest::serialize::(data, &mut bytes).unwrap(); + let data = alkahest::deserialize::(&bytes[..size.0]).unwrap(); assert_eq!(data, TestData::Foo { a: 1 }); } -#[cfg(all(feature = "alloc", feature = "derive"))] -#[test] -fn test_bench() { - use crate::{Deserialize, Formula, Serialize}; - use alloc::vec::Vec; - - #[derive(Formula)] - enum TestFormula { - Foo { a: Ref, b: Ref }, - Bar { c: Vec, d: Vec> }, - } - - #[derive(Serialize, Deserialize)] - #[alkahest(TestFormula)] - enum TestData { - Foo { a: u32, b: u32 }, - Bar { c: Vec, d: Vec> }, - } - - #[derive(Deserialize)] - #[alkahest(TestFormula)] - enum TestDataLazy<'a> { - Foo { - a: u32, - b: u32, - }, - Bar { - c: Lazy<'a, [u32]>, - d: Lazy<'a, [Vec]>, - }, - } - - let data = TestData::Foo { a: 1, b: 2 }; - let mut bytes = [0u8; 1024]; - - alkahest::serialize::(data, &mut bytes).unwrap(); - let (_data, _) = alkahest::deserialize::(&bytes).unwrap(); -} - #[cfg(feature = "alloc")] #[test] fn test_slice_of_slice() { @@ -320,9 +279,11 @@ fn test_size() { #[cfg(all(feature = "derive", feature = "alloc"))] #[test] fn test_packet() { + use alkahest_proc::alkahest; use alloc::{string::String, vec, vec::Vec}; - #[derive(Debug, Clone, Formula, Serialize, Deserialize)] + #[derive(Debug, Clone)] + #[alkahest(Formula, Serialize, Deserialize)] pub enum GameMessage { Client(ClientMessage), Server(ServerMessage), @@ -334,7 +295,8 @@ fn test_packet() { "Enum with non-EXACT_SIZE variants are not EXACT_SIZE" ); - #[derive(Debug, Clone, Formula, Serialize, Deserialize)] + #[derive(Debug, Clone)] + #[alkahest(Formula, Serialize, Deserialize)] pub enum ClientMessage { ClientData { nickname: String, clan: String }, Chat(String), @@ -346,7 +308,8 @@ fn test_packet() { "Enums with differently sized variants are not EXACT_SIZE" ); - #[derive(Debug, Clone, Formula, Serialize, Deserialize)] + #[derive(Debug, Clone)] + #[alkahest(Formula, Serialize, Deserialize)] pub enum ServerMessage { ServerData, ClientChat { client_id: u64, message: String }, @@ -358,7 +321,8 @@ fn test_packet() { "Enums with differently sized variants are not EXACT_SIZE" ); - #[derive(Debug, Formula, Serialize, Deserialize)] + #[derive(Debug)] + #[alkahest(Formula, Serialize, Deserialize)] pub struct NetPacket { pub game_messages: Vec, } @@ -402,9 +366,10 @@ fn test_ref_in_enum() { vec::Vec, }; - use alkahest_proc::{Deserialize, Formula, Serialize}; + use alkahest_proc::alkahest; - #[derive(Debug, PartialEq, Eq, Formula, Serialize, Deserialize)] + #[derive(Debug, PartialEq, Eq)] + #[alkahest(Formula, Serialize, SerializeRef, Deserialize)] enum Test { B([u64; 16]), A(String), @@ -414,7 +379,7 @@ fn test_ref_in_enum() { let mut buffer = [0u8; 256]; let size = serialize::<[Test], _>([&value], &mut buffer).unwrap(); - let (data, _) = deserialize::<[Test], Vec>(&buffer[..size]).unwrap(); + let data = deserialize::<[Test], Vec>(&buffer[..size.0]).unwrap(); assert_eq!(data, [value]); } @@ -456,17 +421,17 @@ fn test_vlq() { for i in u8s { let size = serialize::(i, &mut buffer).unwrap(); - let (de, _) = deserialize::(&buffer[..size]).unwrap(); + let de = deserialize::(&buffer[..size.0]).unwrap(); assert_eq!(de, u32::from(i)); } for i in u32s { let size = serialize::(i, &mut buffer).unwrap(); - let (de, _) = deserialize::(&buffer[..size]).unwrap(); + let de = deserialize::(&buffer[..size.0]).unwrap(); assert_eq!(de, u64::from(i)); } for i in u128s { let size = serialize::(i, &mut buffer).unwrap(); - let (de, _) = deserialize::(&buffer[..size]).unwrap(); + let de = deserialize::(&buffer[..size.0]).unwrap(); assert_eq!(de, i); } } @@ -510,8 +475,8 @@ fn test_zero_sized_arrays() { serialize::<[u8; 0], [u8; 0]>([], &mut []).unwrap(); serialize::<[(); 1], [(); 1]>([()], &mut []).unwrap(); - let ([], _) = deserialize::<[u8; 0], [u8; 0]>(&[]).unwrap(); - let ([()], _) = deserialize::<[(); 1], [(); 1]>(&[]).unwrap(); + let [] = deserialize::<[u8; 0], [u8; 0]>(&[]).unwrap(); + let [()] = deserialize::<[(); 1], [(); 1]>(&[]).unwrap(); #[cfg(feature = "alloc")] { @@ -519,3 +484,78 @@ fn test_zero_sized_arrays() { deserialize::<[u8; 0], VecDeque>(&[]).unwrap(); } } + +#[cfg(feature = "derive")] +#[test] +fn test_recursive_types() { + use alkahest_proc::alkahest; + + let mut buffer = [0; 1024]; + + #[derive(Clone, Debug, PartialEq, Eq)] + #[alkahest(Formula, SerializeRef, Deserialize)] + struct Node { + value: u32, + children: Vec, + } + + let node = Node { + value: 1, + children: vec![ + Node { + value: 2, + children: vec![Node { + value: 3, + children: vec![], + }], + }, + Node { + value: 4, + children: vec![], + }, + ], + }; + + let (size, root) = crate::serialize_unchecked::(&node, &mut buffer); + let de = crate::deserialize_with_size::(&buffer[..size], root).unwrap(); + + assert_eq!(de, node); + + #[alkahest(Formula where T: Formula)] + struct A { + a: T, + b: Vec>, + } + + #[derive(Debug)] + #[alkahest(for SerializeRef> where for<'a> &'a T: Serialize)] + struct B { + a: T, + b: Vec>, + } + + #[derive(Debug)] + #[alkahest(for<'de, U: Formula> Deserialize<'de, A> where T: Deserialize<'de, U>)] + struct C { + a: T, + b: Vec>, + } + + impl PartialEq> for B + where + T: PartialEq, + { + fn eq(&self, other: &C) -> bool { + self.a == other.a && self.b == other.b + } + } + + let b = B { + a: 1, + b: vec![B { a: 2, b: vec![] }], + }; + + let (size, root) = crate::serialize_unchecked::, &B>(&b, &mut buffer); + let c = crate::deserialize_with_size::, C>(&buffer[..size], root).unwrap(); + assert_eq!(b, c); +} diff --git a/src/tests/net.rs b/src/tests/net.rs index b24c6b5..fd0cf3d 100644 --- a/src/tests/net.rs +++ b/src/tests/net.rs @@ -5,60 +5,67 @@ use rand::{ Rng, SeedableRng, }; -use crate::{deserialize, serialize_to_vec, Deserialize, Formula, Lazy, SerIter, Serialize}; +use crate::{ + alkahest, read_packet, write_packet_to_vec, Formula, Lazy, SerIter, Serialize, SerializeRef, +}; -#[derive(Debug, Clone, PartialEq, Eq, Formula, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[alkahest(Formula, Serialize, Deserialize)] pub enum GameMessage { Client(ClientMessage), Server(ServerMessage), } -#[derive(Debug, Deserialize)] -#[alkahest(GameMessage)] +#[derive(Debug)] +#[alkahest(Deserialize<'de, GameMessage>)] pub enum GameMessageRead<'de> { Client(ClientMessageRead<'de>), Server(ServerMessageRead<'de>), } -#[derive(Debug, PartialEq, Eq, Clone, Formula, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone)] +#[alkahest(Formula, Serialize, Deserialize)] pub enum ClientMessage { ClientData { nickname: String, clan: String }, Chat(String), } -#[derive(Debug, Deserialize)] -#[alkahest(ClientMessage)] +#[derive(Debug)] +#[alkahest(Deserialize<'de, ClientMessage>)] pub enum ClientMessageRead<'de> { ClientData { nickname: &'de str, clan: &'de str }, Chat(&'de str), } -#[derive(Debug, PartialEq, Eq, Clone, Formula, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone)] +#[alkahest(Formula, Serialize, Deserialize)] pub enum ServerMessage { ServerData(u64), ClientChat { client_id: u64, message: String }, } -#[derive(Debug, Deserialize)] -#[alkahest(ServerMessage)] +#[derive(Debug)] +#[alkahest(Deserialize<'de, ServerMessage>)] pub enum ServerMessageRead<'de> { ServerData(u64), ClientChat { client_id: u64, message: &'de str }, } -#[derive(Debug, Formula, Serialize, Deserialize)] +#[derive(Debug)] +#[alkahest(Formula, Serialize, Deserialize)] pub struct NetPacket { pub game_messages: Vec, } -#[derive(Debug, Serialize)] -#[alkahest(owned(for NetPacket where G: Serialize<[X]>))] +#[derive(Debug)] +#[alkahest(for Serialize> where G: Serialize<[X]>)] +#[alkahest(for SerializeRef> where G: SerializeRef<[X]>)] pub struct NetPacketWrite { pub game_messages: G, } -#[derive(Debug, Deserialize)] -#[alkahest(NetPacket where G: Formula)] +#[derive(Debug)] +#[alkahest(Deserialize<'de, NetPacket::> where G: Formula)] pub struct NetPacketRead<'de, G> { pub game_messages: Lazy<'de, [G]>, } @@ -84,6 +91,7 @@ fn messages<'a>(mut rng: impl Rng + 'a, len: usize) -> impl Iterator, _>( + let size = write_packet_to_vec::, _>( NetPacketWrite { game_messages: SerIter(messages(rng.clone(), LEN)), }, @@ -103,7 +111,7 @@ fn test_net_packet() { ); let mut buffer2 = Vec::new(); - let size2 = serialize_to_vec::, _>( + let size2 = write_packet_to_vec::, _>( NetPacket { game_messages: messages(rng, LEN).collect::>(), }, @@ -114,7 +122,7 @@ fn test_net_packet() { assert_eq!(buffer[..size], buffer2[..size]); let (packet, _) = - deserialize::, NetPacketRead>(&buffer[..]).unwrap(); + read_packet::, NetPacketRead>(&buffer[..]).unwrap(); for message in packet.game_messages.iter::() { match message.unwrap() { diff --git a/src/tuple.rs b/src/tuple.rs index 4a88527..53670ef 100644 --- a/src/tuple.rs +++ b/src/tuple.rs @@ -2,7 +2,7 @@ use crate::{ buffer::Buffer, deserialize::{Deserialize, DeserializeError, Deserializer}, formula::{sum_size, BareFormula, Formula}, - serialize::{field_size_hint, write_field, Serialize, Sizes}, + serialize::{field_size_hint, write_field, Serialize, SerializeRef, Sizes}, size::SIZE_STACK, }; @@ -29,9 +29,9 @@ impl Serialize<()> for () { } } -impl Serialize<()> for &'_ () { +impl SerializeRef<()> for () { #[inline(always)] - fn serialize(self, _sizes: &mut Sizes, _buffer: B) -> Result<(), B::Error> + fn serialize(&self, _sizes: &mut Sizes, _buffer: B) -> Result<(), B::Error> where B: Buffer, { @@ -88,7 +88,7 @@ macro_rules! formula_serialize { size }; - const EXACT_SIZE: bool = <$at as Formula>::EXACT_SIZE; + const EXACT_SIZE: bool = $(<$a as Formula>::EXACT_SIZE &&)* <$at as Formula>::EXACT_SIZE; const HEAPLESS: bool = $(<$a as Formula>::HEAPLESS &&)* <$at as Formula>::HEAPLESS; } @@ -99,7 +99,6 @@ macro_rules! formula_serialize { { } - impl<$($a,)* $at, $($b,)* $bt> Serialize<($($a,)* $at,)> for ($($b,)* $bt,) where $( @@ -139,18 +138,18 @@ macro_rules! formula_serialize { } } - impl<'ser, $($a,)* $at, $($b,)* $bt,> Serialize<($($a,)* $at,)> for &'ser ($($b,)* $bt,) + impl<$($a,)* $at, $($b,)* $bt,> SerializeRef<($($a,)* $at,)> for ($($b,)* $bt,) where $( $a: Formula, - &'ser $b: Serialize<$a>, + for<'ser> &'ser $b: Serialize<$a>, )* $at: Formula + ?Sized, - &'ser $bt: Serialize<$at>, + for<'ser> &'ser $bt: Serialize<$at>, $bt: ?Sized, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> where B: Buffer, { diff --git a/src/vec.rs b/src/vec.rs index a74f28c..4c5ac6b 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -7,7 +7,7 @@ use crate::{ formula::{reference_size, Formula}, iter::{deserialize_extend_iter, owned_iter_fast_sizes, ref_iter_fast_sizes}, reference::Ref, - serialize::{write_bytes, write_ref, write_slice, Serialize, Sizes}, + serialize::{write_bytes, write_ref, write_reference, write_slice, Serialize, Sizes}, }; impl Formula for Vec @@ -25,11 +25,14 @@ where T: Serialize<[F]>, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> where B: Buffer, { - write_ref::<[F], T, B>(self, sizes, buffer) + let size = write_ref::<[F], T, _>(self, sizes, buffer.reborrow())?; + write_reference::<[F], B>(size, sizes.heap, sizes.heap, sizes.stack, buffer)?; + sizes.stack += reference_size::<[F]>(); + Ok(()) } #[inline(always)] diff --git a/src/vec_deque.rs b/src/vec_deque.rs index 4089ce5..0e3ab3a 100644 --- a/src/vec_deque.rs +++ b/src/vec_deque.rs @@ -7,7 +7,9 @@ use crate::{ formula::{reference_size, Formula}, iter::{deserialize_extend_iter, owned_iter_fast_sizes, ref_iter_fast_sizes}, reference::Ref, - serialize::{write_bytes, write_ref, write_slice, Serialize, Sizes}, + serialize::{ + write_bytes, write_ref, write_reference, write_slice, Serialize, SerializeRef, Sizes, + }, }; impl Formula for VecDeque @@ -25,11 +27,14 @@ where T: Serialize<[F]>, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(self, sizes: &mut Sizes, mut buffer: B) -> Result<(), B::Error> where B: Buffer, { - write_ref::<[F], T, B>(self, sizes, buffer) + let size = write_ref::<[F], T, _>(self, sizes, buffer.reborrow())?; + write_reference::<[F], B>(size, sizes.heap, sizes.heap, sizes.stack, buffer)?; + sizes.stack += reference_size::<[F]>(); + Ok(()) } #[inline(always)] @@ -78,13 +83,13 @@ where } } -impl<'ser, F, T> Serialize<[F]> for &'ser VecDeque +impl SerializeRef<[F]> for VecDeque where F: Formula, - &'ser T: Serialize, + for<'ser> &'ser T: Serialize, { #[inline(always)] - fn serialize(self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> + fn serialize(&self, sizes: &mut Sizes, buffer: B) -> Result<(), B::Error> where B: Buffer, { diff --git a/src/vlq.rs b/src/vlq.rs index 502f905..6759e46 100644 --- a/src/vlq.rs +++ b/src/vlq.rs @@ -24,8 +24,8 @@ use crate::{ /// # use alkahest::*; /// /// let mut buffer = [0u8; 1024]; -/// let size = serialize::(0, &mut buffer).unwrap(); -/// let value = deserialize::(&buffer[..size]).unwrap().0; +/// let (size, root) = serialize::(0, &mut buffer).unwrap(); +/// let value = deserialize_with_size::(&buffer[..size], root).unwrap(); /// assert_eq!(0, value); /// ``` /// @@ -38,8 +38,8 @@ use crate::{ /// /// let mut buffer = [0u8; 1024]; /// -/// let size = serialize::(8573, &mut buffer).unwrap(); -/// let value = deserialize::(&buffer[..size]).unwrap().0; +/// let (size, root) = serialize::(8573, &mut buffer).unwrap(); +/// let value = deserialize_with_size::(&buffer[..size], root).unwrap(); /// assert_eq!(8573, value); /// ``` /// @@ -50,15 +50,15 @@ use crate::{ /// /// let mut buffer = [0u8; 1024]; /// -/// let size = serialize::(70000, &mut buffer).unwrap(); -/// let err = deserialize::(&buffer[..size]).unwrap_err(); +/// let (size, root) = serialize::(70000, &mut buffer).unwrap(); +/// let err = deserialize_with_size::(&buffer[..size], root).unwrap_err(); /// assert!(matches!(err, DeserializeError::IntegerOverflow)); /// ``` pub struct Vlq; impl Formula for Vlq { const MAX_STACK_SIZE: Option = None; - const EXACT_SIZE: bool = true; + const EXACT_SIZE: bool = false; const HEAPLESS: bool = true; }