borsh-1.5.7/.cargo_vcs_info.json0000644000000001430000000000100121610ustar { "git": { "sha1": "abb9582c70b2afd54eef302c23b6e6d3a0b2c1c4" }, "path_in_vcs": "borsh" }borsh-1.5.7/Cargo.lock0000644000000465250000000000100101520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom 0.2.15", "once_cell", "version_check", "zerocopy 0.7.35", ] [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "ascii" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", "tap", "wyz", ] [[package]] name = "borsh" version = "1.5.7" dependencies = [ "ascii", "borsh-derive", "bson", "bytes", "cfg_aliases", "hashbrown", "indexmap", "insta", "serde_json", ] [[package]] name = "borsh-derive" version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", "syn", ] [[package]] name = "bson" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af8113ff51309e2779e8785a246c10fb783e8c2452f134d6257fd71cc03ccd6c" dependencies = [ "ahash", "base64", "bitvec", "getrandom 0.2.15", "getrandom 0.3.2", "hex", "indexmap", "js-sys", "once_cell", "rand", "serde", "serde_bytes", "serde_json", "time", "uuid", ] [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "console" version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", "libc", "once_cell", "windows-sys", ] [[package]] name = "deranged" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" dependencies = [ "powerfmt", ] [[package]] name = "encode_unicode" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "indexmap" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "insta" version = "1.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084" dependencies = [ "console", "linked-hash-map", "once_cell", "pin-project", "similar", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "once_cell" version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "pin-project" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy 0.8.24", ] [[package]] name = "proc-macro-crate" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha", "rand_core", "zerocopy 0.8.24", ] [[package]] name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.2", ] [[package]] name = "rustversion" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "indexmap", "itoa", "memchr", "ryu", "serde", ] [[package]] name = "similar" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "syn" version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "time" version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "toml_datetime", "winnow", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "uuid" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ "getrandom 0.3.2", "js-sys", "serde", "wasm-bindgen", ] [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "wyz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive 0.7.35", ] [[package]] name = "zerocopy" version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ "zerocopy-derive 0.8.24", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerocopy-derive" version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", "syn", ] borsh-1.5.7/Cargo.toml0000644000000042650000000000100101700ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.67.0" name = "borsh" version = "1.5.7" authors = ["Near Inc "] build = "build.rs" exclude = ["*.snap"] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Binary Object Representation Serializer for Hashing """ homepage = "https://borsh.io" readme = "README.md" categories = [ "encoding", "network-programming", ] license = "MIT OR Apache-2.0" repository = "https://github.com/near/borsh-rs" [package.metadata.docs.rs] features = [ "derive", "unstable__schema", "rc", ] targets = ["x86_64-unknown-linux-gnu"] [features] de_strict_order = [] default = ["std"] derive = ["borsh-derive"] rc = [] std = [] unstable__schema = [ "derive", "borsh-derive/schema", ] [lib] name = "borsh" path = "src/lib.rs" [[bin]] name = "generate_schema_schema" path = "src/generate_schema_schema.rs" required-features = [ "std", "unstable__schema", ] [[example]] name = "serde_json_value" path = "examples/serde_json_value.rs" required-features = [ "std", "derive", ] [[test]] name = "common_macro" path = "tests/common_macro.rs" [[test]] name = "smoke" path = "tests/smoke.rs" [[test]] name = "tests" path = "tests/tests.rs" [dependencies.ascii] version = "1.1" optional = true [dependencies.borsh-derive] version = "~1.5.7" optional = true [dependencies.bson] version = "2" optional = true [dependencies.bytes] version = "1" optional = true [dependencies.hashbrown] version = ">=0.11,<0.16.0" optional = true [dependencies.indexmap] version = "2" optional = true [dev-dependencies.insta] version = "1.29.0" [dev-dependencies.serde_json] version = "1" [build-dependencies.cfg_aliases] version = "0.2.1" borsh-1.5.7/Cargo.toml.orig000064400000000000000000000034601046102023000136450ustar 00000000000000[package] name = "borsh" version.workspace = true rust-version.workspace = true authors = ["Near Inc "] edition = "2018" license = "MIT OR Apache-2.0" readme = "README.md" categories = ["encoding", "network-programming"] repository = "https://github.com/near/borsh-rs" homepage = "https://borsh.io" description = """ Binary Object Representation Serializer for Hashing """ exclude = ["*.snap"] [lib] name = "borsh" path = "src/lib.rs" [[example]] name = "serde_json_value" required-features = ["std", "derive"] [[bin]] name = "generate_schema_schema" path = "src/generate_schema_schema.rs" required-features = ["std", "unstable__schema"] [build-dependencies] cfg_aliases = "0.2.1" [dependencies] ascii = { version = "1.1", optional = true } borsh-derive = { path = "../borsh-derive", version = "~1.5.7", optional = true } # hashbrown can be used in no-std context. # NOTE: There is no reason to restrict use of older versions, but we don't want to get # sudden breaking changes with an open range of versions, so we limit the range by not yet released 0.16.0 version: hashbrown = { version = ">=0.11,<0.16.0", optional = true } bytes = { version = "1", optional = true } indexmap = { version = "2", optional = true } bson = { version = "2", optional = true } [dev-dependencies] insta = "1.29.0" serde_json = { version = "1" } [package.metadata.docs.rs] features = ["derive", "unstable__schema", "rc"] targets = ["x86_64-unknown-linux-gnu"] [features] default = ["std"] derive = ["borsh-derive"] unstable__schema = ["derive", "borsh-derive/schema"] std = [] # Opt into impls for Rc and Arc. Serializing and deserializing these types # does not preserve identity and may result in multiple copies of the same data. # Be sure that this is what you want before enabling this feature. rc = [] de_strict_order = [] borsh-1.5.7/README.md000064400000000000000000000060231046102023000122330ustar 00000000000000# Borsh in Rust   [![Latest Version]][crates.io] [![borsh: rustc 1.67+]][Rust 1.67] [![License Apache-2.0 badge]][License Apache-2.0] [![License MIT badge]][License MIT] [Borsh]: https://borsh.io [Latest Version]: https://img.shields.io/crates/v/borsh.svg [crates.io]: https://crates.io/crates/borsh [borsh: rustc 1.67+]: https://img.shields.io/badge/rustc-1.67+-lightgray.svg [Rust 1.67]: https://blog.rust-lang.org/2023/01/26/Rust-1.67.0.html [License Apache-2.0 badge]: https://img.shields.io/badge/license-Apache2.0-blue.svg [License Apache-2.0]: https://opensource.org/licenses/Apache-2.0 [License MIT badge]: https://img.shields.io/badge/license-MIT-blue.svg [License MIT]: https://opensource.org/licenses/MIT **borsh-rs** is Rust implementation of the [Borsh] binary serialization format. Borsh stands for _Binary Object Representation Serializer for Hashing_. It is meant to be used in security-critical projects as it prioritizes [consistency, safety, speed][Borsh], and comes with a strict [specification](https://github.com/near/borsh#specification). ## Example ```rust use borsh::{BorshSerialize, BorshDeserialize, from_slice, to_vec}; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct A { x: u64, y: String, } #[test] fn test_simple_struct() { let a = A { x: 3301, y: "liber primus".to_string(), }; let encoded_a = to_vec(&a).unwrap(); let decoded_a = from_slice::(&encoded_a).unwrap(); assert_eq!(a, decoded_a); } ``` ## Docs shortcuts Following pages are highlighted here just to give reader a chance at learning that they exist. - [Derive Macro `BorshSerialize`](./borsh/docs/rustdoc_include/borsh_serialize.md) - [Derive Macro `BorshDeserialize`](./borsh/docs/rustdoc_include/borsh_deserialize.md) - [Derive Macro `BorshSchema`](./borsh/docs/rustdoc_include/borsh_schema.md) ## Advanced examples Some of the less trivial examples are present in [examples](./borsh/examples) folder: - [implementing `BorshSerialize`/`BorshDeserialize` for third-party `serde_json::Value`](./borsh/examples/serde_json_value.rs) ## Testing Integration tests should generally be preferred to unit ones. Root module of integration tests of `borsh` crate is [linked](./borsh/tests/tests.rs) here. ## Releasing The versions of all public crates in this repository are collectively managed by a single version in the [workspace manifest](https://github.com/near/borsh-rs/blob/master/Cargo.toml). So, to publish a new version of all the crates, you can do so by simply bumping that to the next "patch" version and submit a PR. We have CI Infrastructure put in place to automate the process of publishing all crates once a version change has merged into master. However, before you release, make sure the [CHANGELOG](CHANGELOG.md) is up to date and that the `[Unreleased]` section is present but empty. ## License This repository is distributed under the terms of both the MIT license and the Apache License (Version 2.0). See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE) for details. borsh-1.5.7/build.rs000064400000000000000000000002211046102023000124130ustar 00000000000000use cfg_aliases::cfg_aliases; fn main() { cfg_aliases! { hash_collections: { any(feature = "hashbrown", feature = "std") }, } } borsh-1.5.7/docs/migration_guides/v0.10.2_to_v1.0.0_nearcore.md000064400000000000000000000405611046102023000217520ustar 00000000000000# `0.10.2` -> `1.0.0` `nearcore` upgrade *migration guide* The link to `nearcore` pr is [chore: update borsh dependency](https://github.com/near/nearcore/pull/9432) Steps: ## 1. update dependencies in `nearcore` workspace `Cargo.toml` First we update to `1.0.0-alpha.5` version, which contains [deprecation](https://github.com/near/borsh-rs/pull/206) of `BorshSerialize::try_to_vec` method. ```diff diff --git a/Cargo.toml b/Cargo.toml index f38e88411..1587f4131 100644 --- a/Cargo.toml +++ b/Cargo.toml -borsh = { version = "0.10.2", features = ["rc"] } +borsh = { version = "=1.0.0-alpha.5", features = ["derive", "rc"] } ``` ## 2. We receive a great number of deprecation warnings of `borsh::BorshSerialize::try_to_vec` method (`near-primitives-core` and other packages): ```bash warning: use of deprecated method `borsh::BorshSerialize::try_to_vec`: use `borsh::to_vec(&object)` instead --> core/primitives-core/src/account.rs:246:25 | 246 | let bytes = acc.try_to_vec().unwrap(); | ^^^^^^^^^^ | = note: `#[warn(deprecated)]` on by default ``` We choose to fix it at once, as this method is [removed](https://github.com/near/borsh-rs/pull/221) in `1.0.0-alpha.5` -> `1.0.0` transition completely with following diff: ```diff diff --git a/core/primitives-core/src/account.rs b/core/primitives-core/src/account.rs index 50a3a340d..380bf4494 100644 --- a/core/primitives-core/src/account.rs +++ b/core/primitives-core/src/account.rs @@ -236,3 +236,2 @@ pub struct FunctionCallPermission { mod tests { - use borsh::BorshSerialize; @@ -245,3 +244,3 @@ mod tests { let acc = Account::new(1_000_000, 1_000_000, CryptoHash::default(), 100); - let bytes = acc.try_to_vec().unwrap(); + let bytes = borsh::to_vec(&acc).unwrap(); assert_eq!(hash(&bytes).to_string(), "EVk5UaxBe8LQ8r8iD5EAxVBs6TJcMDKqyH7PBuho6bBJ"); @@ -257,3 +256,3 @@ mod tests { }; - let mut old_bytes = &old_account.try_to_vec().unwrap()[..]; + let mut old_bytes = &borsh::to_vec(&old_account).unwrap()[..]; let new_account = ::deserialize(&mut old_bytes).unwrap(); @@ -264,3 +263,3 @@ mod tests { assert_eq!(new_account.version, AccountVersion::V1); - let mut new_bytes = &new_account.try_to_vec().unwrap()[..]; + let mut new_bytes = &borsh::to_vec(&new_account).unwrap()[..]; let deserialized_account = ... ... ... ``` As `nearcore` has a considerable number of occurencies of usage of `borsh::BorshSerialize::try_to_vec` method, which are tedious to replace manually, [ast-grep](https://github.com/ast-grep/ast-grep) tool may help to do the refactoring automatically: ```bash sg -p '$A.try_to_vec()' -l rs -r 'borsh::to_vec(&$A)' # preview changes sg -p '$A.try_to_vec()' -l rs -r 'borsh::to_vec(&$A)' -A # apply changes without preview ``` ## 3. next we encounter error in `near-primitives` package: ```bash 1 error[E0433]: failed to resolve: could not find `maybestd` in `borsh` ▐ --> core/primitives/src/receipt.rs:1:19 ▐ | ▐ 1 | use crate::borsh::maybestd::collections::HashMap; ▐ | ^^^^^^^^ could not find `maybestd` in `borsh` ▐ ``` `maybestd` [has moved](https://github.com/near/borsh-rs/pull/171) to a `__private` package in `borsh`, and is not supposed to be accessed directly now besides from within code, derived in `borsh` traits implementations. As `near-primitives` crate is not supposed to be used in `no_std` context, we can replace import with standard collections `HashMap`: ```diff diff --git a/core/primitives/src/receipt.rs b/core/primitives/src/receipt.rs index 30af36fb9..d5a6632ed 100644 --- a/core/primitives/src/receipt.rs +++ b/core/primitives/src/receipt.rs @@ -1,11 +1,11 @@ -use crate::borsh::maybestd::collections::HashMap; +use std::collections::HashMap; ``` Otherwise, we would've imported from `hashbrown`: ```diff -use crate::borsh::maybestd::collections::HashMap; +#[cfg(feature = "std")] +use std::collections::HashMap; +#[cfg(not(feature = "std"))] +use hashbrown::HashMap; ``` ## 4. next we encounter a bunch of similar errors in `near-primitives` with `#[borsh_init(...)]`: ```bash 1 error: cannot find attribute `borsh_init` in this scope ▐ --> core/primitives/src/block_header.rs:267:3 ▐ | ▐ 267 | #[borsh_init(init)] ▐ | ^^^^^^^^^^ help: a derive helper attribute with a similar name exists: `borsh_skip`▐ ``` The syntax of this attribute [has changed](https://github.com/near/borsh-rs/pull/187). We change all of these occurencies according to `#[borsh(init=)]` syntax. The following diff is shortened to first and last occurencies: ```diff diff --git a/core/primitives/src/block_header.rs b/core/primitives/src/block_header.rs index 38491b52c..84ab48238 100644 --- a/core/primitives/src/block_header.rs +++ b/core/primitives/src/block_header.rs @@ -266,3 +266,3 @@ impl ApprovalMessage { #[derive(BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, Eq, PartialEq)] -#[borsh_init(init)] +#[borsh(init=init)] pub struct BlockHeaderV1 { ... diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs index 912120b56..2de7a1d52 100644 --- a/core/primitives/src/transaction.rs +++ b/core/primitives/src/transaction.rs @@ -58,3 +58,3 @@ impl Transaction { )] -#[borsh_init(init)] +#[borsh(init=init)] pub struct SignedTransaction { ``` ## 5. next we encounter a large number of similar syntax errors in `near-primitives` package ```bash 1 error: cannot find attribute `borsh_skip` in this scope ▐ --> core/primitives/src/transaction.rs:196:7 ▐ | ▐ 196 | #[borsh_skip] ▐ | ^^^^^^^^^^ ▐ ▐ ``` We change all of these occurencies according to [new](https://github.com/near/borsh-rs/pull/192) `#[borsh(skip)]` syntax. The following diff is shortened to first and last occurencies: ```diff diff --git a/core/primitives/src/block_header.rs b/core/primitives/src/block_header.rs index 84ab48238..6514f8222 100644 --- a/core/primitives/src/block_header.rs +++ b/core/primitives/src/block_header.rs @@ -279,3 +279,3 @@ pub struct BlockHeaderV1 { /// Cached value of hash for this block. - #[borsh_skip] + #[borsh(skip)] pub hash: CryptoHash, ... diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs index 2de7a1d52..f3ac54ba8 100644 --- a/core/primitives/src/transaction.rs +++ b/core/primitives/src/transaction.rs @@ -62,5 +62,5 @@ pub struct SignedTransaction { pub signature: Signature, - #[borsh_skip] + #[borsh(skip)] hash: CryptoHash, - #[borsh_skip] + #[borsh(skip)] size: u64, ... ``` ## 6. next we encounter 2 errors in `near-primitives` package similar to those in point 3.: ```bash 1 error[E0433]: failed to resolve: could not find `maybestd` in `borsh` --> core/primitives/src/action/delegate.rs:119:41 | 119 | fn deserialize_reader( | ^^^^^^^^ could not find `maybestd` in `borsh` 2 error[E0433]: failed to resolve: could not find `maybestd` in `borsh` --> core/primitives/src/action/delegate.rs:121:50 | 121 | ) -> ::core::result::Result { | ^^^^^^^^ could not find `maybestd` i n `borsh` ``` As `near-primitives` crate is not supposed to be used in `no_std` context, we can replace import with `std::io`: ```diff diff --git a/core/primitives/src/action/delegate.rs b/core/primitives/src/action/delegate.rs index 25db73022..ebd009a44 100644 --- a/core/primitives/src/action/delegate.rs +++ b/core/primitives/src/action/delegate.rs @@ -14,3 +14,3 @@ use near_primitives_core::types::{AccountId, Nonce}; use serde::{Deserialize, Serialize}; -use std::io::{Error, ErrorKind}; +use std::io::{Error, ErrorKind, Read}; @@ -118,5 +118,5 @@ mod private_non_delegate_action { impl borsh::de::BorshDeserialize for NonDelegateAction { - fn deserialize_reader( + fn deserialize_reader( rd: &mut R, - ) -> ::core::result::Result { + ) -> ::core::result::Result { match u8::deserialize_reader(rd)? { ``` Otherwise, if we intended to support [both `std` and `no_std`](https://github.com/near/borsh-rs/pull/212), we would've imported from `borsh::io`: ```diff +use borsh::io::{Error, ErrorKind, Read}; ``` ## 7. next we encounter an error with `BorshDeserialize` trait derivation: ```bash 1 error[E0277]: the trait bound `&T: borsh::BorshDeserialize` is not satisfied --> core/primitives/src/signable_message.rs:58:26 | 58 | #[derive(BorshSerialize, BorshDeserialize)] | ^^^^^^^^^^^^^^^^ the trait `borsh::BorshDeserialize` is not implemented for `&T` ``` on ```rust /// A wrapper around a message that should be signed using this scheme. /// /// Only used for constructing a signature, not used to transmit messages. The /// discriminant prefix is implicit and should be known by the receiver based on /// the context in which the message is received. #[derive(BorshSerialize, BorshDeserialize)] pub struct SignableMessage<'a, T> { pub discriminant: MessageDiscriminant, pub msg: &'a T, } ``` On version change `0.10.3` -> `1.0.0-alpha.5` bounds derivation in `borsh` [has changed](https://github.com/near/borsh-rs/pull/178): From bounds on the types of the fields: ```rust impl<'a, T> borsh::de::BorshDeserialize for SignableMessage<'a, T> where MessageDiscriminant: borsh::de::BorshDeserialize, &'a T: borsh::de::BorshDeserialize, ``` to bounds on type parameters, encountered in fields: ```rust impl<'a, T> borsh::de::BorshDeserialize for SignableMessage<'a, T> where T: borsh::de::BorshDeserialize, ``` We could potentially [patch the bounds](https://github.com/near/borsh-rs/pull/180) on struct to make it compile: ```rust #[derive(BorshSerialize, BorshDeserialize)] pub struct SignableMessage<'a, T> { pub discriminant: MessageDiscriminant, #[borsh(bound(deserialize="&'a T: borsh::de::BorshDeserialize"))] pub msg: &'a T, } ``` which would transform into following bound on trait's implementation: ```rust where &'a T: borsh::de::BorshDeserialize, ``` But the real issue here is that `borsh` doesn't have a generic implementation of `BorshDeserialize` for `&'a T`, where `T: borsh::de::BorshDeserialize` (nor did it have it in 0.10.2 version), and that the derived `BorshDeserialize` wasn't used (and it couldn't be for such a field's type). So the right change is to remove `BorshDeserialize` derive from the struct: ```diff diff --git a/core/primitives/src/signable_message.rs b/core/primitives/src/signable_message.rs index efdd489ac..db97eb1fd 100644 --- a/core/primitives/src/signable_message.rs +++ b/core/primitives/src/signable_message.rs @@ -57,3 +57,3 @@ pub struct MessageDiscriminant { /// the context in which the message is received. -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(BorshSerialize)] pub struct SignableMessage<'a, T> { ``` ## 8. next we encounter an error in `near-network` package: ```rust 1 error: You have to specify `#[borsh(use_discriminant=true)]` or `#[borsh(use_discriminant=false)]` for all enums with explicit discriminant ▐ --> chain/network/src/types.rs:56:10 ▐ | ▐ 56 | pub enum ReasonForBan { ▐ | ^^^^^^^^^^^^ ▐ ``` ```rust #[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq, Copy)] pub enum ReasonForBan { None = 0, BadBlock = 1, BadBlockHeader = 2, HeightFraud = 3, BadHandshake = 4, BadBlockApproval = 5, Abusive = 6, InvalidSignature = 7, InvalidPeerId = 8, InvalidHash = 9, InvalidEdge = 10, InvalidDistanceVector = 11, Blacklisted = 14, } ``` We fix it with `#[borsh(use_discriminant=false)]` to preserve the behaviour of borsh before 1.0 release which serialized `ReasonForBan::Blacklisted` as 12 instead of 14 (borsh 0.10 and older [ignored explicit discriminant values in enum definitions](https://github.com/near/borsh-rs/issues/137)): ```diff diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs index b2dd97c32..ea2d67f2d 100644 --- a/chain/network/src/types.rs +++ b/chain/network/src/types.rs @@ -55,2 +55,3 @@ pub struct KnownProducer { #[derive(borsh::BorshSerialize, borsh::BorshDeserialize, Debug, Clone, PartialEq, Eq, Copy)] +#[borsh(use_discriminant=false)] pub enum ReasonForBan { ``` ## 9. change in behaviour, unit test error in ci in `near-primitives` package Assertion fails: ```rust #[test] fn test_delegate_action_deserialization() { // Expected an error. Buffer is empty assert_eq!( NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()), Err(ErrorKind::InvalidInput) ); ``` ```bash --- STDERR: near-primitives action::delegate::tests::test_delegate_action_deserialization --- thread 'action::delegate::tests::test_delegate_action_deserialization' panicked at 'assertion failed: `(left == right)` left: `Err(InvalidData)`, right: `Err(InvalidInput)`', core/primitives/src/action/delegate.rs:172:9 ``` The `ErrorKind` in error in `borsh` [has changed](https://github.com/near/borsh-rs/pull/170), so we apply the following diff: ```diff diff --git a/core/primitives/src/action/delegate.rs b/core/primitives/src/action/delegate.rs index ebd009a44..80a0475b6 100644 --- a/core/primitives/src/action/delegate.rs +++ b/core/primitives/src/action/delegate.rs @@ -173,3 +173,3 @@ mod tests { NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()), - Err(ErrorKind::InvalidInput) + Err(ErrorKind::InvalidData) ); ``` And there's also a similar error in ```bash --- STDERR: near-store tests::test_save_to_file --- thread 'tests::test_save_to_file' panicked at 'assertion failed: `(left == right)` left: `InvalidInput`, right: `InvalidData`', core/store/src/lib.rs:1096:9 ``` with similar fix. ## 10. errors similar to previous ones, in `near-store`, `near-network` and `near-state-viewer` packages There was a bunch of `borsh::maybestd` imports, which got replaced by their direct from-`std` counterparts. ## 11. finally, we update `borsh` version to `1.0.0`: ```diff diff --git a/Cargo.toml b/Cargo.toml index f38e88411..1587f4131 100644 --- a/Cargo.toml +++ b/Cargo.toml -borsh = { version = "=1.0.0-alpha.5", features = ["derive", "rc"] } +borsh = { version = "1.0.0", features = ["derive", "rc"] } ``` borsh-1.5.7/docs/migration_guides/v0.9_to_v1.0.0_near_sdk_rs.md000064400000000000000000001020271046102023000222320ustar 00000000000000# `v0.9` -> `v1.0.0` `near-sdk-rs` upgrade *migration guide* The link to `near-sdk-rs` pr is [chore: borsh version update](https://github.com/near/near-sdk-rs/pull/1075) Steps: ## 1. update dependencies in `near-sdk/Cargo.toml`. First we update to `1.0.0-alpha.5` version, which contains [deprecation](https://github.com/near/borsh-rs/pull/206) of `BorshSerialize::try_to_vec` method. We enable `derive` feature by default, and make `unstable__schema` feature optional, enabled depending on whether `abi` feature of `near-sdk` package is enabled or not. ```diff diff --git a/near-sdk/Cargo.toml b/near-sdk/Cargo.toml index a015a64..e6099d4 100644 --- a/near-sdk/Cargo.toml +++ b/near-sdk/Cargo.toml @@ -26,3 +26,3 @@ near-sys = { path = "../near-sys", version = "0.2" } base64 = "0.13" -borsh = { version = "0.9", features = ["const-generics"] } +borsh = { version = "=1.0.0-alpha.5", features = ["derive"] } bs58 = "0.4" @@ -35,3 +35,4 @@ once_cell = { version = "1.17", default-features = false } @@ -58,3 +59,3 @@ unstable = [] legacy = [] -abi = ["near-abi", "schemars", "near-sdk-macros/abi"] +abi = ["borsh/unstable__schema", "near-abi", "schemars", "near-sdk-macros/abi"] unit-testing = ["near-vm-logic", "near-primitives-core", "near-primitives", "near-crypto"] ``` ## 2. We receive a great number of deprecation warnings of `borsh::BorshSerialize::try_to_vec` method (`near-sdk` package): ```bash 2 warning: use of deprecated method `borsh::BorshSerialize::try_to_vec`: use `borsh::to_vec(&object)` instead --> near-sdk/src/store/lazy/mod.rs:43:28 | 43 | let serialized = value.try_to_vec().unwrap_or_else(|_| env::panic_str(ERR_VALUE_SERIALIZATION)); | ^^^^^^^^^^ | = note: `#[warn(deprecated)]` on by default ``` We choose to fix it at once, as this method is [removed](https://github.com/near/borsh-rs/pull/221) in `1.0.0-alpha.5` -> `1.0.0` transition completely with following diff: ```diff diff --git a/near-sdk/src/store/lazy/mod.rs b/near-sdk/src/store/lazy/mod.rs index 7df7ee4..42112ea 100644 --- a/near-sdk/src/store/lazy/mod.rs +++ b/near-sdk/src/store/lazy/mod.rs @@ -8,3 +8,3 @@ mod impls; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::{BorshDeserialize, BorshSerialize, to_vec}; use once_cell::unsync::OnceCell; @@ -42,3 +42,3 @@ where { - let serialized = value.try_to_vec().unwrap_or_else(|_| env::panic_str(ERR_VALUE_SERIALIZATION)); + let serialized = to_vec(value).unwrap_or_else(|_| env::panic_str(ERR_VALUE_SERIALIZATION)); env::storage_write(key, &serialized); ... ... ``` where `value` is `&T`, where `T: BorshSerialize`. ## 3. We replace the usage of `BorshSchema::schema_container` method (`near-sdk-macros` package) To prevent compilation errors in the future, we grep for `schema_container` string. `schema_container` was changed from being a `BorshSchema` trait method to being a function, external to the trait in [chore!: make BorshSchema::{add_definition,schema_container} free-standing funcs](https://github.com/near/borsh-rs/pull/204) We fix code, generated with `near_bindgen` procedural macro, with following diff: ```diff diff --git a/near-sdk-macros/src/core_impl/abi/abi_generator.rs b/near-sdk-macros/src/core_impl/abi/abi_generator.rs index cbe659a..994e63c 100644 --- a/near-sdk-macros/src/core_impl/abi/abi_generator.rs +++ b/near-sdk-macros/src/core_impl/abi/abi_generator.rs @@ -239,21 +239,21 @@ impl ImplItemMethodInfo { } } } fn generate_schema(ty: &Type, serializer_type: &SerializerType) -> TokenStream2 { match serializer_type { SerializerType::JSON => quote! { gen.subschema_for::<#ty>() }, SerializerType::Borsh => quote! { - <#ty as ::near_sdk::borsh::BorshSchema>::schema_container() + ::near_sdk::borsh::schema_container_of::<#ty>() }, } } fn generate_abi_type(ty: &Type, serializer_type: &SerializerType) -> TokenStream2 { let schema = generate_schema(ty, serializer_type); match serializer_type { SerializerType::JSON => quote! { ::near_sdk::__private::AbiType::Json { type_schema: #schema, ``` ## 4. next we encounter error with `#[borsh(use_discriminant=)]` (`near-sdk` package): ```bash 1 error: You have to specify `#[borsh(use_discriminant=true)]` or `#[borsh(use_discriminant=false)]` for all enums with explicit discriminant --> near-sdk/src/types/public_key.rs:8:10 | 8 | pub enum CurveType { | ^^^^^^^^^ ``` on ```rust /// PublicKey curve #[derive(Debug, Clone, Copy, PartialOrd, Ord, Eq, PartialEq, BorshDeserialize, BorshSerialize)] #[repr(u8)] pub enum CurveType { ED25519 = 0, SECP256K1 = 1, } ``` We fix it with `#[borsh(use_discriminant=true)]`, which will behave the same as `#[borsh(use_discriminant=false)]` in this particular case, where `false` preserves the behaviour of borsh before 1.0 release (borsh 0.10 and older [ignored explicit discriminant values in enum definitions](https://github.com/near/borsh-rs/issues/137)): ```diff diff --git a/near-sdk/src/types/public_key.rs b/near-sdk/src/types/public_key.rs index 30ebd43..b539ddd 100644 --- a/near-sdk/src/types/public_key.rs +++ b/near-sdk/src/types/public_key.rs @@ -7,2 +7,3 @@ use std::convert::TryFrom; #[repr(u8)] +#[borsh(use_discriminant=true)] pub enum CurveType { @@ -144,4 +145,4 @@ impl serde::Serialize for PublicKey { ``` ## 5. next we encounter errors with `borsh::maybestd` imports (`near-sdk` package): ```bash 1 error[E0432]: unresolved import `borsh::maybestd` --> near-sdk/src/types/public_key.rs:1:13 | 1 | use borsh::{maybestd::io, BorshDeserialize, BorshSerialize}; | ^^^^^^^^ could not find `maybestd` in `borsh` 2 error[E0432]: unresolved import `borsh::maybestd` --> near-sdk/src/types/account_id.rs:1:13 | 1 | use borsh::{maybestd::io, BorshDeserialize, BorshSchema, BorshSerialize}; | ^^^^^^^^ could not find `maybestd` in `borsh` ``` ```rust // near-sdk/src/types/public_key.rs impl BorshDeserialize for PublicKey { fn deserialize(buf: &mut &[u8]) -> io::Result { as BorshDeserialize>::deserialize(buf).and_then(|s| { Self::try_from(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) }) } } ``` `maybestd` [has moved](https://github.com/near/borsh-rs/pull/171) to a `__private` package in `borsh`, and is not supposed to be accessed directly now besides from within code, derived in `borsh` traits implementations. As `near-sdk` crate is not supposed to be used in `no_std` context, we can replace imports with `std::io`: ```diff diff --git a/near-sdk/src/types/account_id.rs b/near-sdk/src/types/account_id.rs index a338b5c..7876d77 100644 --- a/near-sdk/src/types/account_id.rs +++ b/near-sdk/src/types/account_id.rs @@ -1,5 +1,5 @@ -use borsh::{maybestd::io, BorshDeserialize, BorshSchema, BorshSerialize}; +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use serde::{de, Deserialize, Serialize}; use std::convert::TryFrom; -use std::fmt; +use std::{fmt, io}; diff --git a/near-sdk/src/types/public_key.rs b/near-sdk/src/types/public_key.rs index 10175a0..4280f70 100644 --- a/near-sdk/src/types/public_key.rs +++ b/near-sdk/src/types/public_key.rs @@ -1,4 +1,4 @@ -use borsh::{maybestd::io, BorshDeserialize, BorshSerialize}; +use borsh::{BorshDeserialize, BorshSerialize}; use bs58::decode::Error as B58Error; -use std::convert::TryFrom; +use std::{convert::TryFrom, io}; ``` Otherwise, if we intended to support [both `std` and `no_std`](https://github.com/near/borsh-rs/pull/212), we would've imported from `borsh::io`: ```diff -use borsh::{maybestd::io, BorshDeserialize, BorshSerialize}; +use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::io; ``` ## 6. next we encounter a large number of similar syntax errors with `borsh_skip` (`near-sdk` package): ```bash 1 error: cannot find attribute `borsh_skip` in this scope --> near-sdk/src/store/lookup_map/mod.rs:89:7 | 89 | #[borsh_skip] | ^^^^^^^^^^ ``` We change all of these occurencies according to [new](https://github.com/near/borsh-rs/pull/192) `#[borsh(skip)]` syntax. The following diff is shortened to first and last occurencies: ```diff diff --git a/near-sdk/src/collections/lazy_option.rs b/near-sdk/src/collections/lazy_option.rs index 04e79fb..f4ea0dc 100644 --- a/near-sdk/src/collections/lazy_option.rs +++ b/near-sdk/src/collections/lazy_option.rs @@ -19,3 +19,3 @@ pub struct LazyOption { storage_key: Vec, - #[borsh_skip] + #[borsh(skip)] el: PhantomData, ... diff --git a/near-sdk/src/store/lookup_set/mod.rs b/near-sdk/src/store/lookup_set/mod.rs index 762956a..b2d1ac0 100644 --- a/near-sdk/src/store/lookup_set/mod.rs +++ b/near-sdk/src/store/lookup_set/mod.rs @@ -54,3 +54,3 @@ where - #[borsh_skip] + #[borsh(skip)] hasher: PhantomData (T, H)>, ``` ## 7. next there's a bunch of similar errors with `borsh::maybestd::io` imports (`near-sdk` package): They're fixed in a similar way as in 5. ## 8. next there's a bunch of similar errors due to `BorshDeserialize` trait signature change (`near-sdk` package): ```bash 1 error[E0046]: not all trait items implemented, missing: `deserialize_reader` --> near-sdk/src/store/vec/mod.rs:138:1 | 138 | impl BorshDeserialize for Vector | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `deserialize_reader` in implementation = help: implement the missing item: `fn deserialize_reader(_: &mut R) -> std::result::Result where R: std::io::Read { todo!() } ``` The signature of trait has changed on 0.9.3 -> 0.10.0 transition in [implement deserialize_reader](https://github.com/near/borsh-rs/pull/116). We fix it the following way: ```diff diff --git a/near-sdk/src/store/free_list/mod.rs b/near-sdk/src/store/free_list/mod.rs index 43d8908..20a1cc7 100644 --- a/near-sdk/src/store/free_list/mod.rs +++ b/near-sdk/src/store/free_list/mod.rs @@ -47,7 +47,7 @@ where { - fn deserialize(buf: &mut &[u8]) -> Result { + fn deserialize_reader(reader: &mut R) -> Result { Ok(Self { - first_free: BorshDeserialize::deserialize(buf)?, - occupied_count: BorshDeserialize::deserialize(buf)?, - elements: BorshDeserialize::deserialize(buf)?, + first_free: BorshDeserialize::deserialize_reader(reader)?, + occupied_count: BorshDeserialize::deserialize_reader(reader)?, + elements: BorshDeserialize::deserialize_reader(reader)?, }) diff --git a/near-sdk/src/store/unordered_map/mod.rs b/near-sdk/src/store/unordered_map/mod.rs index 5decc60..d82a8aa 100644 --- a/near-sdk/src/store/unordered_map/mod.rs +++ b/near-sdk/src/store/unordered_map/mod.rs @@ -117,6 +117,6 @@ where { - fn deserialize(buf: &mut &[u8]) -> Result { + fn deserialize_reader(reader: &mut R) -> Result { Ok(Self { - keys: BorshDeserialize::deserialize(buf)?, - values: BorshDeserialize::deserialize(buf)?, + keys: BorshDeserialize::deserialize_reader(reader)?, + values: BorshDeserialize::deserialize_reader(reader)?, }) diff --git a/near-sdk/src/store/vec/mod.rs b/near-sdk/src/store/vec/mod.rs index 9d19614..94127ba 100644 --- a/near-sdk/src/store/vec/mod.rs +++ b/near-sdk/src/store/vec/mod.rs @@ -141,6 +141,6 @@ where { - fn deserialize(buf: &mut &[u8]) -> Result { + fn deserialize_reader(reader: &mut R) -> Result { Ok(Self { - len: BorshDeserialize::deserialize(buf)?, - values: BorshDeserialize::deserialize(buf)?, + len: BorshDeserialize::deserialize_reader(reader)?, + values: BorshDeserialize::deserialize_reader(reader)?, }) diff --git a/near-sdk/src/types/account_id.rs b/near-sdk/src/types/account_id.rs index 7876d77..3da417a 100644 --- a/near-sdk/src/types/account_id.rs +++ b/near-sdk/src/types/account_id.rs @@ -88,4 +88,4 @@ impl<'de> Deserialize<'de> for AccountId { impl BorshDeserialize for AccountId { - fn deserialize(buf: &mut &[u8]) -> io::Result { - ::deserialize(buf).and_then(|s| { + fn deserialize_reader(reader: &mut R) -> io::Result { + ::deserialize_reader(reader).and_then(|s| { Self::try_from(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) diff --git a/near-sdk/src/types/public_key.rs b/near-sdk/src/types/public_key.rs index 4280f70..b539ddd 100644 --- a/near-sdk/src/types/public_key.rs +++ b/near-sdk/src/types/public_key.rs @@ -145,4 +145,4 @@ impl serde::Serialize for PublicKey { impl BorshDeserialize for PublicKey { - fn deserialize(buf: &mut &[u8]) -> io::Result { - as BorshDeserialize>::deserialize(buf).and_then(|s| { + fn deserialize_reader(reader: &mut R) -> io::Result { + as BorshDeserialize>::deserialize_reader(reader).and_then(|s| { Self::try_from(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) diff --git a/near-sdk/src/collections/unordered_map/mod.rs b/near-sdk/src/collections/unordered_map/mod.rs index d3ba8d5..aab31a4 100644 --- a/near-sdk/src/collections/unordered_map/mod.rs +++ b/near-sdk/src/collections/unordered_map/mod.rs @@ -512,5 +512,5 @@ mod tests { impl BorshDeserialize for DeserializeCounter { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { + fn deserialize_reader(reader: &mut R) -> std::io::Result { DES_COUNT.fetch_add(1, Ordering::SeqCst); - u64::deserialize(buf).map(DeserializeCounter) + u64::deserialize_reader(reader).map(DeserializeCounter) } ``` ## 9. next we encounter an error with `BorshDeserialize` trait derivation (`near-sdk` package): ```bash 6 error[E0277]: the trait bound `T: Default` is not satisfied --> near-sdk/src/store/vec/mod.rs:145:21 | 145 | values: BorshDeserialize::deserialize_reader(reader)?, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `T` | note: required for `IndexMap` to implement `BorshDeserialize` --> near-sdk/src/store/index_map.rs:12:26 | 12 | #[derive(BorshSerialize, BorshDeserialize)] | ^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro 13 | pub(crate) struct IndexMap | ^^^^^^^^^^^ = note: this error originates in the derive macro `BorshDeserialize` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | 140 | T: BorshSerialize + std::default::Default, | +++++++++++++++++++++++ ``` where `IndexMap` looks like the following: ```rust #[derive(BorshSerialize, BorshDeserialize)] pub(crate) struct IndexMap where T: BorshSerialize, { pub(crate) prefix: Box<[u8]>, /// Cache for loads and intermediate changes to the underlying index map. /// The cached entries are wrapped in a [`Box`] to avoid existing pointers from being /// invalidated. /// /// Note: u32 indices are used over usize to have consistent functionality across architectures. /// Some functionality would be different from tests to Wasm if exceeding 32-bit length. #[borsh(skip)] pub(crate) cache: StableMap>>, } ``` On version change `v0.9` -> `v1.0.0-alpha.5` bounds derivation in `borsh` [has changed](https://github.com/near/borsh-rs/pull/178): From bounds on the types of the fields: ```rust // cd near-sdk; cargo expand ::store::index_map impl borsh::de::BorshDeserialize for IndexMap where T: BorshSerialize, Box<[u8]>: borsh::BorshDeserialize, { fn deserialize( buf: &mut &[u8], ) -> ::core::result::Result { Ok(Self { prefix: borsh::BorshDeserialize::deserialize(buf)?, cache: Default::default(), }) } } ``` to bounds on type parameters, encountered in fields. `borsh::de::BorshDeserialize` bound for parameters in non-skipped fields, `core::default::Default` bound - otherwise: ```rust impl borsh::de::BorshDeserialize for IndexMap where T: BorshSerialize, T: core::default::Default, { fn deserialize_reader( reader: &mut R, ) -> ::core::result::Result { Ok(Self { prefix: borsh::BorshDeserialize::deserialize_reader(reader)?, cache: core::default::Default::default(), }) } } ``` We can instruct `borsh` to [replace automatically derived bound](https://github.com/near/borsh-rs/pull/180) with nothing, as `StableMap` has a `impl Default for StableMap` implementation of its own, as it will be used when deserializing skipped field, irrelevant of bounds on `V`: ```diff diff --git a/near-sdk/src/store/index_map.rs b/near-sdk/src/store/index_map.rs index 834fc98..7d1df75 100644 --- a/near-sdk/src/store/index_map.rs +++ b/near-sdk/src/store/index_map.rs @@ -23,3 +23,3 @@ where /// Some functionality would be different from tests to Wasm if exceeding 32-bit length. - #[borsh(skip)] + #[borsh(skip, bound(deserialize = ""))] pub(crate) cache: StableMap>>, ``` which would transform into following bound on trait's implementation: ```rust // line with `T: core::default::Default,` disappeared impl borsh::de::BorshDeserialize for IndexMap where T: BorshSerialize, { ... ``` Similar diffs were also applied here: ```diff diff --git a/near-sdk/src/store/lookup_map/mod.rs b/near-sdk/src/store/lookup_map/mod.rs index 0b20345..927b2d6 100644 --- a/near-sdk/src/store/lookup_map/mod.rs +++ b/near-sdk/src/store/lookup_map/mod.rs @@ -88,3 +88,3 @@ where /// invalidated. - #[borsh(skip)] + #[borsh(skip, bound(deserialize = ""))] cache: StableMap>, ``` ```diff diff --git a/near-sdk/src/store/unordered_set/mod.rs b/near-sdk/src/store/unordered_set/mod.rs index 4504580..77621b9 100644 --- a/near-sdk/src/store/unordered_set/mod.rs +++ b/near-sdk/src/store/unordered_set/mod.rs @@ -83,9 +83,11 @@ pub struct UnorderedSet where T: BorshSerialize + Ord, H: ToKey, { + #[borsh(bound(serialize = "", deserialize = ""))] elements: FreeList, + #[borsh(bound(serialize = "", deserialize = ""))] index: LookupMap, } ``` ## 10. next we encounter an error with `BorshSchema` trait derivation (`near-sdk` package): ```bash 4 error[E0053]: method `add_definitions_recursively` has an incompatible type for trait --> near-sdk/src/promise.rs:232:22 | 232 | definitions: &mut HashMap, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | | expected `BTreeMap`, found `HashMap` | help: change the parameter type to match the trait: `&mut BTreeMap` | = note: expected signature `fn(&mut BTreeMap)` found signature `fn(&mut HashMap)` ``` Signature in trait's method [has changed](https://github.com/near/borsh-rs/pull/165/): ```diff diff --git a/near-sdk/src/promise.rs b/near-sdk/src/promise.rs index f8afe56..a430568 100644 --- a/near-sdk/src/promise.rs +++ b/near-sdk/src/promise.rs @@ -2,3 +2,3 @@ use borsh::BorshSchema; use std::cell::RefCell; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::io::{Error, Write}; @@ -231,3 +231,3 @@ impl BorshSchema for Promise { fn add_definitions_recursively( - definitions: &mut HashMap, + definitions: &mut BTreeMap, ) { @@ -576,3 +576,3 @@ where fn add_definitions_recursively( - definitions: &mut HashMap, + definitions: &mut BTreeMap, ) { ``` ## 11. next we encounter an error with both `BorshSerialize` and `BorshDeserialize` traits' derivation (`near-contract-standards` package): ```bash 1 error: proc-macro derive panicked --> near-contract-standards/src/fungible_token/core_impl.rs:27:10 | 27 | #[derive(BorshDeserialize, BorshSerialize)] | ^^^^^^^^^^^^^^^^ | = help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/home/user/Documents/code/near-sdk-rs/near-contract-standards/Cargo.toml" } 2 error: proc-macro derive panicked --> near-contract-standards/src/fungible_token/core_impl.rs:27:28 | 27 | #[derive(BorshDeserialize, BorshSerialize)] | ^^^^^^^^^^^^^^ | = help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/home/user/Documents/code/near-sdk-rs/near-contract-standards/Cargo.toml" } ``` Thing is, `borsh` [has started getting into a `panic`](https://github.com/near/borsh-rs/pull/149) when using [proc-macro-crate](https://crates.io/crates/proc-macro-crate) dependency for derives, in the cases when `borsh` is not imported as direct dependency in the crate, which attempts to use its derive macros. `near-contract-standards` wasn't importing `borsh` directly, just using `near-sdk`'s reexports. We may instruct `BorshSerialize` and `BorshDeserialize` derives to skip this check of direct import and to [use a reexported version](https://github.com/near/borsh-rs/pull/210) of `borsh` via following diff: ```diff diff --git a/near-contract-standards/src/fungible_token/core_impl.rs b/near-contract-standards/src/fungible_token/core_impl.rs index d61ee8e..cae776c 100644 --- a/near-contract-standards/src/fungible_token/core_impl.rs +++ b/near-contract-standards/src/fungible_token/core_impl.rs @@ -27,2 +27,3 @@ const ERR_TOTAL_SUPPLY_OVERFLOW: &str = "Total supply overflow"; #[derive(BorshDeserialize, BorshSerialize)] +#[borsh(crate = "::near_sdk::borsh")] pub struct FungibleToken { ``` ## 12. finally, we update `borsh` version to `1.0.0`: ```diff diff --git a/near-sdk/Cargo.toml b/near-sdk/Cargo.toml index a015a64..e6099d4 100644 --- a/near-sdk/Cargo.toml +++ b/near-sdk/Cargo.toml @@ -26,3 +26,3 @@ near-sys = { path = "../near-sys", version = "0.2" } base64 = "0.13" -borsh = { version = "=1.0.0-alpha.5", features = ["derive"] } +borsh = { version = "1.0.0", features = ["derive"] } bs58 = "0.4" ``` borsh-1.5.7/docs/rustdoc_include/borsh_crate_top_level.md000064400000000000000000000065741046102023000217730ustar 00000000000000# Crate features ### Ecosystem features * **std** - When enabled, `borsh` uses the standard library. Disabling this feature will result in building the crate in `no_std` environment. To carter such builds, Borsh offers [`io`] module which includes a items which are used in [`BorshSerialize`] and [`BorshDeserialize`] traits. Most notably `io::Read`, `io::Write` and `io::Result`. When **std** feature is enabled, those items are re-exports of corresponding `std::io` items. Otherwise they are borsh-specific types which mimic behaviour of corresponding standard types. ### Default features * **std** - enabled by default. ### Other features * **derive** - Gates derive macros of [BorshSerialize] and [BorshDeserialize] traits. * **unstable__schema** - Gates [BorshSchema] trait and its derive macro. Gates [schema] module. This feature requires **derive** to be enabled too. * **rc** - Gates implementation of [BorshSerialize] and [BorshDeserialize] for [`Rc`](std::rc::Rc)/[`Arc`](std::sync::Arc) respectively. In `no_std` setting `Rc`/`Arc` are pulled from `alloc` crate. Serializing and deserializing these types does not preserve identity and may result in multiple copies of the same data. Be sure that this is what you want before enabling this feature. * **hashbrown** - Pulls in [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet) when no `std` is available. This feature is set to be mutually exclusive with **std** feature. * **bytes** - Gates implementation of [BorshSerialize] and [BorshDeserialize] for [Bytes](https://docs.rs/bytes/1.5.0/bytes/struct.Bytes.html) and [BytesMut](https://docs.rs/bytes/1.5.0/bytes/struct.BytesMut.html). * **bson** - Gates implementation of [BorshSerialize] and [BorshDeserialize] for [ObjectId](https://docs.rs/bson/2.9.0/bson/oid/struct.ObjectId.html). * **indexmap** - Gates implementation of [BorshSerialize] and [BorshDeserialize] for [indexmap::IndexMap](https://docs.rs/indexmap/2.8.0/indexmap/map/struct.IndexMap.html) and [IndexSet](https://docs.rs/indexmap/2.8.0/indexmap/set/struct.IndexSet.html) * **ascii** - Gates implementation of [BorshSerialize], [BorshDeserialize], [BorshSchema] for types from [ascii](https://docs.rs/ascii/1.1.0/ascii/) crate. * **de_strict_order** - Enables check that keys, parsed during deserialization of [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet) and [BTreeSet](std::collections::BTreeSet)/[BTreeMap](std::collections::BTreeMap) are encountered in ascending order with respect to [PartialOrd] for hash collections, and [Ord] for btree ones. Deserialization emits error otherwise. If this feature is not enabled, it is possible that two different byte slices could deserialize into the same `HashMap`/`HashSet` object. ### Config aliases * **hash_collections** - This is a feature alias, set up in `build.rs` to be equivalent to (**std** OR **hashbrown**). Gates implementation of [BorshSerialize], [BorshDeserialize] and [BorshSchema] for [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet). # Shortcuts Following pages are highlighted here just to give reader a chance at learning that they exist. - [Derive Macro `BorshSerialize`](macro@crate::BorshSerialize) - [Derive Macro `BorshDeserialize`](macro@crate::BorshDeserialize) - [Derive Macro `BorshSchema`](macro@crate::BorshSchema) borsh-1.5.7/docs/rustdoc_include/borsh_deserialize.md000064400000000000000000000236511046102023000211170ustar 00000000000000Derive macro available if borsh is built with `features = ["derive"]`. # derive proc-macro for [`BorshDeserialize`] trait ## Bounds Generally, `BorshDeserialize` adds `borsh::de::BorshDeserialize` bound to any type parameter found in item's fields and `core::default::Default` bound to any type parameter found in item's skipped fields. ```rust use borsh::BorshDeserialize; /// impl borsh::de::BorshDeserialize for A /// where /// U: borsh::de::BorshDeserialize, /// V: borsh::de::BorshDeserialize, #[derive(BorshDeserialize)] struct A { x: U, y: V, } ``` ```rust use borsh::BorshDeserialize; /// impl borsh::de::BorshDeserialize for A /// where /// U: borsh::de::BorshDeserialize, /// V: core::default::Default, #[derive(BorshDeserialize)] struct A { x: U, #[borsh(skip)] y: V, } ``` ## Attributes ### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute) ###### syntax Attribute takes literal string value, which is the syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html) to `borsh` crate used. ###### usage Attribute is optional. 1. If the attribute is not provided, [crate_name](https://docs.rs/proc-macro-crate/3.2.0/proc_macro_crate/fn.crate_name.html) is used to find a version of `borsh` in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised: ```bash 1 error: proc-macro derive panicked --> path/to/file.rs:27:10 | 27 | #[derive(BorshDeserialize, BorshSerialize)] | ^^^^^^^^^^^^^^^^ | = help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/path/to/Cargo.toml" } ``` 2. If the attribute is provided, the check for `borsh` in `[dependencies]` of the relevant `Cargo.toml` is skipped. Examples of usage: (example is not tested, as there's usually no `reexporter` crate during doc build) ```rust,ignore use reexporter::borsh::BorshDeserialize; // specifying the attribute removes need for a direct import of `borsh` into `[dependencies]` #[derive(BorshDeserialize)] #[borsh(crate = "reexporter::borsh")] struct B { x: u64, y: i32, c: String, } ``` ```rust,ignore use reexporter::borsh::{self, BorshDeserialize}; // specifying the attribute removes need for a direct import of `borsh` into `[dependencies]` #[derive(BorshDeserialize)] #[borsh(crate = "borsh")] struct B { x: u64, y: i32, c: String, } ``` ### 2. `#[borsh(init=...)]` (item level attribute) ###### syntax Attribute's value is syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html)-s, passed to borsh top level meta attribute as value of `init` argument. ###### usage `#[borsh(init=...)]` allows to automatically run an initialization function right after deserialization. This adds a lot of convenience for objects that are architectured to be used as strictly immutable. ```rust type CryptoHash = String; use borsh::BorshDeserialize; #[derive(BorshDeserialize)] #[borsh(init=init)] struct Message { message: String, timestamp: u64, hash: CryptoHash, } impl Message { pub fn init(&mut self) { self.hash = { let mut hash = CryptoHash::new(); hash.push_str(&self.message); hash.push_str(&format!("{}", self.timestamp)); hash }; } } ``` ### 3. `borsh(use_discriminant=)` (item level attribute) This attribute is only applicable to enums. `use_discriminant` allows to override the default behavior of serialization of enums with explicit discriminant. `use_discriminant` is `false` behaves like version of borsh of 0.10.3. It's useful for backward compatibility and you can set this value to `false` to deserialise data serialised by older version of `borsh`. You must specify `use_discriminant` for all enums with explicit discriminants in your project. This is equivalent of borsh version 0.10.3 (explicit discriminant is ignored and this enum is equivalent to `A` without explicit discriminant): ```rust use borsh::BorshDeserialize; #[derive(BorshDeserialize)] #[borsh(use_discriminant = false)] enum A { A, B = 10, } ``` To have explicit discriminant value serialized as is, you must specify `borsh(use_discriminant=true)` for enum. ```rust use borsh::BorshDeserialize; #[derive(BorshDeserialize)] #[borsh(use_discriminant = true)] enum B { A, B = 10, } ``` ###### borsh, expressions, evaluating to `isize`, as discriminant This case is not supported: ```rust,compile_fail use borsh::BorshDeserialize; const fn discrim() -> isize { 0x14 } #[derive(BorshDeserialize)] #[borsh(use_discriminant = true)] enum X { A, B = discrim(), // expressions, evaluating to `isize`, which are allowed outside of `borsh` context C, D, E = 10, F, } ``` ###### borsh explicit discriminant does not support literal values outside of u8 range. This is not supported: ```rust,compile_fail #[derive(BorshDeserialize)] #[borsh(use_discriminant = true)] enum X { A, B = 0x100, // literal values outside of `u8` range C, D, E = 10, F, } ``` ### 4. `#[borsh(skip)]` (field level attribute) `#[borsh(skip)]` makes derive skip deserializing annotated field. `#[borsh(skip)]` makes derive skip adding any type parameters, present in the field, to parameters bound by `borsh::de::BorshDeserialize`. It adds `core::default::Default` bound to any parameters encountered in annotated field. ```rust use borsh::BorshDeserialize; #[derive(BorshDeserialize)] struct A { x: u64, #[borsh(skip)] y: f32, } ``` ### 5. `#[borsh(bound(deserialize = ...))]` (field level attribute) ###### syntax Attribute takes literal string value, which is a comma-separated list of syn's [WherePredicate](https://docs.rs/syn/latest/syn/enum.WherePredicate.html)-s, which may be empty. ###### usage Attribute adds possibility to override bounds for `BorshDeserialize` in order to enable: 1. removal of bounds on type parameters from struct/enum definition itself and moving them to the trait's implementation block. 2. fixing complex cases, when derive hasn't figured out the right bounds on type parameters automatically. ```rust use borsh::BorshDeserialize; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(feature = "std")] use std::collections::HashMap; use core::hash::Hash; /// additional bounds `T: Ord + Hash + Eq` (required by `HashMap`) are injected into /// derived trait implementation via attribute to avoid adding the bounds on the struct itself #[cfg(any(feature = "hashbrown", feature = "std"))] #[derive(BorshDeserialize)] struct A { a: String, #[borsh(bound( deserialize = "T: Ord + Hash + Eq + borsh::de::BorshDeserialize, U: borsh::de::BorshDeserialize" ))] b: HashMap, } ``` ```rust use borsh::BorshDeserialize; trait TraitName { type Associated; fn method(&self); } // derive here figures the bound erroneously as `T: borsh::de::BorshDeserialize,` #[derive(BorshDeserialize)] struct A where T: TraitName, { #[borsh(bound(deserialize = "::Associated: borsh::de::BorshDeserialize"))] field: ::Associated, another: V, } ``` ###### interaction with `#[borsh(skip)]` `#[borsh(bound(deserialize = ...))]` replaces bounds, which are derived automatically, irrelevant of whether `#[borsh(skip)]` attribute is present. ```rust use borsh::BorshDeserialize; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(feature = "std")] use std::collections::HashMap; /// implicit derived `core::default::Default` bounds on `K` and `V` type parameters are removed by /// empty bound specified, as `HashMap` has its own `Default` implementation #[cfg(any(feature = "hashbrown", feature = "std"))] #[derive(BorshDeserialize)] struct A( #[borsh(skip, bound(deserialize = ""))] HashMap, U, ); ``` ### 6. `#[borsh(deserialize_with = ...)]` (field level attribute) ###### syntax Attribute takes literal string value, which is a syn's [ExprPath](https://docs.rs/syn/latest/syn/struct.ExprPath.html). ###### usage Attribute adds possibility to specify full path of function, optionally qualified with generics, with which to deserialize the annotated field. It may be used when `BorshDeserialize` cannot be implemented for field's type, if it's from foreign crate. It may be used to override the implementation of deserialization for some other reason. ```rust use borsh::BorshDeserialize; use indexmap::IndexMap; use core::hash::Hash; /// this a stub module, representing a 3rd party crate `indexmap` mod indexmap { /// this a stub struct, representing a 3rd party `indexmap::IndexMap` /// or some local type we want to override trait implementation for pub struct IndexMap { pub(crate) tuples: Vec<(K, V)>, } } mod index_map_impl { use super::IndexMap; use core::hash::Hash; pub fn deserialize_index_map< R: borsh::io::Read, K: borsh::de::BorshDeserialize + Hash + Eq, V: borsh::de::BorshDeserialize, >( reader: &mut R, ) -> ::core::result::Result, borsh::io::Error> { let vec: Vec<(K, V)> = borsh::BorshDeserialize::deserialize_reader(reader)?; // the line of implementation for type from real `indexmap` crate // let result: IndexMap = vec.into_iter().collect(); let result = IndexMap { tuples: vec, }; Ok(result) } } #[derive(BorshDeserialize)] struct B { #[borsh( deserialize_with = "index_map_impl::deserialize_index_map", )] x: IndexMap, y: String, } # fn main() { # } ``` ###### usage (comprehensive example) [borsh/examples/serde_json_value.rs](https://github.com/near/borsh-rs/blob/master/borsh/examples/serde_json_value.rs) is a more complex example of how the attribute may be used. ###### interaction with `#[borsh(skip)]` `#[borsh(deserialize_with = ...)]` is not allowed to be used simultaneously with `#[borsh(skip)]`. borsh-1.5.7/docs/rustdoc_include/borsh_schema.md000064400000000000000000000223411046102023000200520ustar 00000000000000Derive macro available if borsh is built with `features = ["unstable__schema"]`. # derive proc-macro for [`BorshSchema`] trait ## Bounds Generally, `BorshSchema` adds `borsh::BorshSchema` bound to any type parameter found in item's fields. ```rust use borsh::BorshSchema; /// impl borsh::BorshSchema for A /// where /// U: borsh::BorshSchema, /// V: borsh::BorshSchema, #[derive(BorshSchema)] struct A { x: U, y: V, } ``` ```rust use borsh::BorshSchema; /// impl borsh::BorshSchema for A /// where /// U: borsh::BorshSchema, #[derive(BorshSchema)] struct A { x: U, #[borsh(skip)] y: V, } ``` ## Attributes ### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute) ###### syntax Attribute takes literal string value, which is the syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html) to `borsh` crate used. ###### usage Attribute is optional. 1. If the attribute is not provided, [crate_name](https://docs.rs/proc-macro-crate/3.2.0/proc_macro_crate/fn.crate_name.html) is used to find a version of `borsh` in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised: ```bash 1 error: proc-macro derive panicked --> path/to/file.rs:27:10 | 27 | #[derive(BorshSchema, BorshSerialize)] | ^^^^^^^^^^^ | = help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/path/to/Cargo.toml" } ``` 2. If the attribute is provided, the check for `borsh` in `[dependencies]` of the relevant `Cargo.toml` is skipped. Examples of usage: (example is not tested, as there's usually no `reexporter` crate during doc build) ```rust,ignore use reexporter::borsh::BorshSchema; // specifying the attribute removes need for a direct import of `borsh` into `[dependencies]` #[derive(BorshSchema)] #[borsh(crate = "reexporter::borsh")] struct B { x: u64, y: i32, c: String, } ``` ```rust,ignore use reexporter::borsh::{self, BorshSchema}; // specifying the attribute removes need for a direct import of `borsh` into `[dependencies]` #[derive(BorshSchema)] #[borsh(crate = "borsh")] struct B { x: u64, y: i32, c: String, } ``` ### 2. `borsh(use_discriminant=)` (item level attribute) This attribute is only applicable to enums. `use_discriminant` allows to override the default behavior of serialization of enums with explicit discriminant. `use_discriminant` is `false` behaves like version of borsh of 0.10.3. You must specify `use_discriminant` for all enums with explicit discriminants in your project. This is equivalent of borsh version 0.10.3 (explicit discriminant is ignored and this enum is equivalent to `A` without explicit discriminant): ```rust use borsh::BorshSchema; #[derive(BorshSchema)] #[borsh(use_discriminant = false)] enum A { A, B = 10, } ``` To have explicit discriminant value serialized as is, you must specify `borsh(use_discriminant=true)` for enum. ```rust use borsh::BorshSchema; #[derive(BorshSchema)] #[borsh(use_discriminant = true)] enum B { A, B = 10, } ``` ###### borsh, expressions, evaluating to `isize`, as discriminant This case is not supported: ```rust,compile_fail const fn discrim() -> isize { 0x14 } #[derive(BorshSchema)] #[borsh(use_discriminant = true)] enum X { A, B = discrim(), // expressions, evaluating to `isize`, which are allowed outside of `borsh` context C, D, E = 10, F, } ``` ###### borsh explicit discriminant does not support literal values outside of u8 range This is not supported: ```rust,compile_fail #[derive(BorshSchema)] #[borsh(use_discriminant = true)] enum X { A, B = 0x100, // literal values outside of `u8` range C, D, E = 10, F, } ``` ### 3. `#[borsh(skip)]` (field level attribute) `#[borsh(skip)]` makes derive skip including schema from annotated field into schema's implementation. `#[borsh(skip)]` makes derive skip adding any type parameters, present in the field, to parameters bound by `borsh::BorshSchema`. ```rust use borsh::BorshSchema; #[derive(BorshSchema)] struct A { x: u64, #[borsh(skip)] y: f32, } ``` ### 4. `#[borsh(schema(params = ...))]` (field level attribute) ###### syntax Attribute takes literal string value, which is a comma-separated list of `ParameterOverride`-s, which may be empty. ###### usage It may be used in order to: 1. fix complex cases, when derive hasn't figured out the right bounds on type parameters and declaration parameters automatically. 2. remove parameters, which do not take part in serialization/deserialization, from bounded ones and from declaration parameters. `ParameterOverride` describes an entry like `order_param => override_type`, e.g. `K => ::Associated`. Such an entry instructs `BorshSchema` derive to: 1. add `override_type` to types, bounded by `borsh::BorshSchema` in implementation block. 2. add `::declaration()` to parameters vector in `fn declaration()` method of `BorshSchema` trait that is being derived. 3. the `order_param` is required to establish the same order in parameters vector (2.) as that of type parameters in generics of type, that `BorshSchema` is derived for. 4. entries, specified for a field, together replace whatever would've been derived automatically for 1. and 2. . ```rust use borsh::BorshSchema; trait TraitName { type Associated; fn method(&self); } // derive here figures the bound erroneously as `T: borsh::BorshSchema` . // attribute replaces it with ::Associated: borsh::BorshSchema` #[derive(BorshSchema)] struct A where T: TraitName, { #[borsh(schema(params = "T => ::Associated"))] field: ::Associated, another: V, } ``` ```rust use borsh::BorshSchema; use core::marker::PhantomData; trait EntityRef { fn key_property(&self) -> u64; } // K in PrimaryMap isn't stored during serialization / read during deserialization. // thus, it's not a parameter, relevant for `BorshSchema` // ... // impl borsh::BorshSchema for A // where // V: borsh::BorshSchema, #[derive(BorshSchema)] struct A { #[borsh( schema( params = "V => V" ) )] x: PrimaryMap, y: String, } #[derive(BorshSchema)] pub struct PrimaryMap where K: EntityRef, { elems: Vec, unused: PhantomData, } ``` ###### interaction with `#[borsh(skip)]` `#[borsh(schema(params = ...))]` is not allowed to be used simultaneously with `#[borsh(skip)]`. ### 5. `#[borsh(schema(with_funcs(declaration = ..., definitions = ...)))]` (field level attribute) ###### syntax Each of `declaration` and `definitions` nested sub-attributes takes literal string value, which is a syn's [ExprPath](https://docs.rs/syn/latest/syn/struct.ExprPath.html). Currently both `declaration` and `definitions` are required to be specified at the same time. ###### usage Attribute adds possibility to specify full path of 2 functions, optionally qualified with generics, with which to generate borsh schema for annotated field. It may be used when `BorshSchema` cannot be implemented for field's type, if it's from foreign crate. It may be used to override the implementation of schema for some other reason. ```rust use borsh::BorshSchema; use indexmap::IndexMap; /// this a stub module, representing a 3rd party crate `indexmap` mod indexmap { /// this a stub struct, representing a 3rd party `indexmap::IndexMap` /// or some local type we want to override trait implementation for pub struct IndexMap { pub(crate) tuples: Vec<(K, V)>, } } mod index_map_impl { pub mod schema { use std::collections::BTreeMap; use borsh::{ schema::{Declaration, Definition, self}, BorshSchema, }; pub fn declaration() -> Declaration { let params = vec![::declaration(), ::declaration()]; format!(r#"{}<{}>"#, "IndexMap", params.join(", ")) } pub fn add_definitions_recursively( definitions: &mut BTreeMap, ) { let definition = Definition::Sequence { elements: <(K, V)>::declaration(), length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, }; let no_recursion_flag = definitions.get(&declaration::()).is_none(); schema::add_definition(declaration::(), definition, definitions); if no_recursion_flag { <(K, V)>::add_definitions_recursively(definitions); } } } } #[derive(BorshSchema)] struct B { #[borsh( schema( with_funcs( declaration = "index_map_impl::schema::declaration::", definitions = "index_map_impl::schema::add_definitions_recursively::" ), ) )] x: IndexMap, y: String, } # fn main() { # } ``` ###### interaction with `#[borsh(skip)]` `#[borsh(schema(with_funcs(declaration = ..., definitions = ...)))]` is not allowed to be used simultaneously with `#[borsh(skip)]`. borsh-1.5.7/docs/rustdoc_include/borsh_serialize.md000064400000000000000000000177431046102023000206130ustar 00000000000000Derive macro available if borsh is built with `features = ["derive"]`. # derive proc-macro for [`BorshSerialize`] trait ## Bounds Generally, `BorshSerialize` adds `borsh::ser::BorshSerialize` bound to any type parameter found in item's fields. ```rust use borsh::BorshSerialize; /// impl borsh::ser::BorshSerialize for A /// where /// U: borsh::ser::BorshSerialize, /// V: borsh::ser::BorshSerialize, #[derive(BorshSerialize)] struct A { x: U, y: V, } ``` ```rust use borsh::BorshSerialize; /// impl borsh::ser::BorshSerialize for A /// where /// U: borsh::ser::BorshSerialize, #[derive(BorshSerialize)] struct A { x: U, #[borsh(skip)] y: V, } ``` ## Attributes ### 1. `#[borsh(crate = "path::to::borsh")]` (item level attribute) ###### syntax Attribute takes literal string value, which is the syn's [Path](https://docs.rs/syn/2.0.92/syn/struct.Path.html) to `borsh` crate used. ###### usage Attribute is optional. 1. If the attribute is not provided, [crate_name](https://docs.rs/proc-macro-crate/3.2.0/proc_macro_crate/fn.crate_name.html) is used to find a version of `borsh` in `[dependencies]` of the relevant `Cargo.toml`. If there is no match, a compilation error, similar to the following, is raised: ```bash 1 error: proc-macro derive panicked --> path/to/file.rs:27:10 | 27 | #[derive(BorshSerialize, BorshDeserialize)] | ^^^^^^^^^^^^^^ | = help: message: called `Result::unwrap()` on an `Err` value: CrateNotFound { crate_name: "borsh", path: "/path/to/Cargo.toml" } ``` 2. If the attribute is provided, the check for `borsh` in `[dependencies]` of the relevant `Cargo.toml` is skipped. Examples of usage: (example is not tested, as there's usually no `reexporter` crate during doc build) ```rust,ignore use reexporter::borsh::BorshSerialize; // specifying the attribute removes need for a direct import of `borsh` into `[dependencies]` #[derive(BorshSerialize)] #[borsh(crate = "reexporter::borsh")] struct B { x: u64, y: i32, c: String, } ``` ```rust,ignore use reexporter::borsh::{self, BorshSerialize}; // specifying the attribute removes need for a direct import of `borsh` into `[dependencies]` #[derive(BorshSerialize)] #[borsh(crate = "borsh")] struct B { x: u64, y: i32, c: String, } ``` ### 2. `borsh(use_discriminant=)` (item level attribute) This attribute is only applicable to enums. `use_discriminant` allows to override the default behavior of serialization of enums with explicit discriminant. `use_discriminant` is `false` behaves like version of borsh of 0.10.3. You must specify `use_discriminant` for all enums with explicit discriminants in your project. This is equivalent of borsh version 0.10.3 (explicit discriminant is ignored and this enum is equivalent to `A` without explicit discriminant): ```rust use borsh::BorshSerialize; #[derive(BorshSerialize)] #[borsh(use_discriminant = false)] enum A { A, B = 10, } ``` To have explicit discriminant value serialized as is, you must specify `borsh(use_discriminant=true)` for enum. ```rust use borsh::BorshSerialize; #[derive(BorshSerialize)] #[borsh(use_discriminant = true)] enum B { A, B = 10, } ``` ###### borsh, expressions, evaluating to `isize`, as discriminant This case is not supported: ```rust,compile_fail const fn discrim() -> isize { 0x14 } #[derive(BorshSerialize)] #[borsh(use_discriminant = true)] enum X { A, B = discrim(), // expressions, evaluating to `isize`, which are allowed outside of `borsh` context C, D, E = 10, F, } ``` ###### borsh explicit discriminant does not support literal values outside of u8 range This is not supported: ```rust,compile_fail #[derive(BorshSerialize)] #[borsh(use_discriminant = true)] enum X { A, B = 0x100, // literal values outside of `u8` range C, D, E = 10, F, } ``` ### 3. `#[borsh(skip)]` (field level attribute) `#[borsh(skip)]` makes derive skip serializing annotated field. `#[borsh(skip)]` makes derive skip adding any type parameters, present in the field, to parameters bound by `borsh::ser::BorshSerialize`. ```rust use borsh::BorshSerialize; #[derive(BorshSerialize)] struct A { x: u64, #[borsh(skip)] y: f32, } ``` ### 4. `#[borsh(bound(serialize = ...))]` (field level attribute) ###### syntax Attribute takes literal string value, which is a comma-separated list of syn's [WherePredicate](https://docs.rs/syn/latest/syn/enum.WherePredicate.html)-s, which may be empty. ###### usage Attribute adds possibility to override bounds for `BorshSerialize` in order to enable: 1. removal of bounds on type parameters from struct/enum definition itself and moving them to the trait's implementation block. 2. fixing complex cases, when derive hasn't figured out the right bounds on type parameters automatically. ```rust use borsh::BorshSerialize; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(feature = "std")] use std::collections::HashMap; /// additional bound `T: Ord` (required by `HashMap`) is injected into /// derived trait implementation via attribute to avoid adding the bounds on the struct itself #[cfg(any(feature = "hashbrown", feature = "std"))] #[derive(BorshSerialize)] struct A { a: String, #[borsh(bound(serialize = "T: borsh::ser::BorshSerialize + Ord, U: borsh::ser::BorshSerialize"))] b: HashMap, } ``` ```rust use borsh::BorshSerialize; trait TraitName { type Associated; fn method(&self); } /// derive here figures the bound erroneously as `T: borsh::ser::BorshSerialize` #[derive(BorshSerialize)] struct A where T: TraitName, { #[borsh(bound(serialize = "::Associated: borsh::ser::BorshSerialize"))] field: ::Associated, another: V, } ``` ###### interaction with `#[borsh(skip)]` `#[borsh(bound(serialize = ...))]` replaces bounds, which are derived automatically, irrelevant of whether `#[borsh(skip)]` attribute is present. ### 5. `#[borsh(serialize_with = ...)]` (field level attribute) ###### syntax Attribute takes literal string value, which is a syn's [ExprPath](https://docs.rs/syn/latest/syn/struct.ExprPath.html). ###### usage Attribute adds possibility to specify full path of function, optionally qualified with generics, with which to serialize the annotated field. It may be used when `BorshSerialize` cannot be implemented for field's type, if it's from foreign crate. It may be used to override the implementation of serialization for some other reason. ```rust use borsh::BorshSerialize; use indexmap::IndexMap; /// this a stub module, representing a 3rd party crate `indexmap` mod indexmap { /// this a stub struct, representing a 3rd party `indexmap::IndexMap` /// or some local type we want to override trait implementation for pub struct IndexMap { pub(crate) tuples: Vec<(K, V)>, } } mod index_map_impl { use super::indexmap::IndexMap; pub fn serialize_index_map< K: borsh::ser::BorshSerialize, V: borsh::ser::BorshSerialize, W: borsh::io::Write, >( obj: &IndexMap, writer: &mut W, ) -> ::core::result::Result<(), borsh::io::Error> { // the line of implementation for type from real `indexmap` crate // let key_value_tuples = obj.iter().collect::>(); let key_value_tuples = obj.tuples.iter().collect::>(); borsh::BorshSerialize::serialize(&key_value_tuples, writer)?; Ok(()) } } #[derive(BorshSerialize)] struct B { #[borsh( serialize_with = "index_map_impl::serialize_index_map", )] x: IndexMap, y: String, } # fn main() { # } ``` ###### usage (comprehensive example) [borsh/examples/serde_json_value.rs](https://github.com/near/borsh-rs/blob/master/borsh/examples/serde_json_value.rs) is a more complex example of how the attribute may be used. ###### interaction with `#[borsh(skip)]` `#[borsh(serialize_with = ...)]` is not allowed to be used simultaneously with `#[borsh(skip)]`. borsh-1.5.7/examples/serde_json_value.rs000064400000000000000000000266321046102023000164770ustar 00000000000000use std::collections::HashMap; use borsh::{BorshDeserialize, BorshSerialize}; mod serde_json_value { pub use de::deserialize_value; pub use ser::serialize_value; mod ser { use borsh::{ io::{ErrorKind, Result, Write}, BorshSerialize, }; use core::convert::TryFrom; /// this is mutually recursive with `serialize_array` and `serialize_map` pub fn serialize_value(value: &serde_json::Value, writer: &mut W) -> Result<()> { match value { serde_json::Value::Null => 0_u8.serialize(writer), serde_json::Value::Bool(b) => { 1_u8.serialize(writer)?; b.serialize(writer) } serde_json::Value::Number(n) => { 2_u8.serialize(writer)?; serialize_number(n, writer) } serde_json::Value::String(s) => { 3_u8.serialize(writer)?; s.serialize(writer) } serde_json::Value::Array(a) => { 4_u8.serialize(writer)?; serialize_array(a, writer) } serde_json::Value::Object(o) => { 5_u8.serialize(writer)?; serialize_map(o, writer) } } } fn serialize_number(number: &serde_json::Number, writer: &mut W) -> Result<()> { // A JSON number can either be a non-negative integer (represented in // serde_json by a u64), a negative integer (by an i64), or a non-integer // (by an f64). // We identify these cases with the following single-byte discriminants: // 0 - u64 // 1 - i64 // 2 - f64 if let Some(u) = number.as_u64() { 0_u8.serialize(writer)?; return u.serialize(writer); } if let Some(i) = number.as_i64() { 1_u8.serialize(writer)?; return i.serialize(writer); } if let Some(f) = number.as_f64() { 2_u8.serialize(writer)?; return f.serialize(writer); } // technically, it should not be unreachable, but an error instead, // as assumption about unreachable depends on private implementation detail // but it's fine to leave it be unreachable! for an example unreachable!("number is neither a u64, i64, nor f64"); } /// this is mutually recursive with `serialize_value` fn serialize_array(array: &Vec, writer: &mut W) -> Result<()> { // The implementation here is very similar to that of Vec. writer.write_all( &(u32::try_from(array.len()).map_err(|_| ErrorKind::InvalidData)?).to_le_bytes(), )?; for item in array { serialize_value(&item, writer)?; } Ok(()) } /// this is mutually recursive with `serialize_value` fn serialize_map( map: &serde_json::Map, writer: &mut W, ) -> Result<()> { // The implementation here is very similar to that of BTreeMap. u32::try_from(map.len()) .map_err(|_| ErrorKind::InvalidData)? .serialize(writer)?; for (key, value) in map { key.serialize(writer)?; serialize_value(&value, writer)?; } Ok(()) } } mod de { use borsh::{ io::{Error, ErrorKind, Read, Result}, BorshDeserialize, }; /// this is copy-paste of https://github.com/near/borsh-rs/blob/master/borsh/src/de/hint.rs#L2-L5 fn hint_cautious(hint: u32) -> usize { let el_size = core::mem::size_of::() as u32; core::cmp::max(core::cmp::min(hint, 4096 / el_size), 1) as usize } /// this is mutually recursive with `deserialize_array`, `deserialize_map` pub fn deserialize_value(reader: &mut R) -> Result { let flag: u8 = BorshDeserialize::deserialize_reader(reader)?; match flag { 0 => Ok(serde_json::Value::Null), 1 => { let b: bool = BorshDeserialize::deserialize_reader(reader)?; Ok(serde_json::Value::Bool(b)) } 2 => { let n: serde_json::Number = deserialize_number(reader)?; Ok(serde_json::Value::Number(n)) } 3 => { let s: String = BorshDeserialize::deserialize_reader(reader)?; Ok(serde_json::Value::String(s)) } 4 => { let a: Vec = deserialize_array(reader)?; Ok(serde_json::Value::Array(a)) } 5 => { let o: serde_json::Map<_, _> = deserialize_map(reader)?; Ok(serde_json::Value::Object(o)) } _ => { let msg = format!( "Invalid JSON value representation: {}. The first byte must be 0-5", flag ); Err(Error::new(ErrorKind::InvalidData, msg)) } } } fn deserialize_number(reader: &mut R) -> Result { let flag: u8 = BorshDeserialize::deserialize_reader(reader)?; match flag { 0 => { let u: u64 = BorshDeserialize::deserialize_reader(reader)?; Ok(u.into()) } 1 => { let i: i64 = BorshDeserialize::deserialize_reader(reader)?; Ok(i.into()) } 2 => { let f: f64 = BorshDeserialize::deserialize_reader(reader)?; // This returns None if the number is a NaN or +/-Infinity, // which are not valid JSON numbers. serde_json::Number::from_f64(f).ok_or_else(|| { let msg = format!("Invalid JSON number: {}", f); Error::new(ErrorKind::InvalidData, msg) }) } _ => { let msg = format!( "Invalid JSON number representation: {}. The first byte must be 0-2", flag ); Err(Error::new(ErrorKind::InvalidData, msg)) } } } /// this is mutually recursive with `deserialize_value` fn deserialize_array(reader: &mut R) -> Result> { // The implementation here is very similar to that of Vec. let len = u32::deserialize_reader(reader)?; let mut result = Vec::with_capacity(hint_cautious::<(String, serde_json::Value)>(len)); for _ in 0..len { let value = deserialize_value(reader)?; result.push(value); } Ok(result) } /// this is mutually recursive with `deserialize_value` fn deserialize_map( reader: &mut R, ) -> Result> { // The implementation here is very similar to that of BTreeMap. let vec: Vec<(String, serde_json::Value)> = { // The implementation here is very similar to that of Vec<(K, V)>. let len = u32::deserialize_reader(reader)?; let mut result = Vec::with_capacity(hint_cautious::<(String, serde_json::Value)>(len)); for _ in 0..len { let pair = { let key = String::deserialize_reader(reader)?; let value = deserialize_value(reader)?; (key, value) }; result.push(pair); } result }; Ok(vec.into_iter().collect()) } } } mod borsh_wrapper { use borsh::{BorshDeserialize, BorshSerialize}; #[derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct SerdeJsonBorshWrapper( #[borsh( serialize_with = "super::serde_json_value::serialize_value", deserialize_with = "super::serde_json_value::deserialize_value" )] pub serde_json::Value, ); impl From for SerdeJsonBorshWrapper { fn from(value: serde_json::Value) -> Self { Self(value) } } impl From for serde_json::Value { fn from(value: SerdeJsonBorshWrapper) -> Self { value.0 } } } use borsh_wrapper::SerdeJsonBorshWrapper; #[derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)] struct SerdeJsonAsField { pub examples: HashMap, } fn main() { // original code is from https://github.com/near/borsh-rs/pull/312 let original = serde_json::json!({ "null": null, "true": true, "false": false, "zero": 0, "positive_integer": 12345, "negative_integer": -88888, "positive_float": 123.45, "negative_float": -888.88, "positive_max": 1.7976931348623157e+308, "negative_max": -1.7976931348623157e+308, "string": "Larry", "array_of_nulls": [null, null, null], "array_of_numbers": [0, -1, 1, 1.1, -1.1, 34798324], "array_of_strings": ["Larry", "Jake", "Pumpkin"], "array_of_arrays": [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ], "array_of_objects": [ { "name": "Larry", "age": 30 }, { "name": "Jake", "age": 7 }, { "name": "Pumpkin", "age": 8 } ], "object": { "name": "Larry", "age": 30, "pets": [ { "name": "Jake", "age": 7 }, { "name": "Pumpkin", "age": 8 } ] } }); let mut examples = HashMap::new(); examples.insert("Larry Jake Pumpkin".into(), original.clone().into()); let complex_struct = SerdeJsonAsField { examples }; let serialized = borsh::to_vec(&complex_struct).unwrap(); let mut deserialized: SerdeJsonAsField = borsh::from_slice(&serialized).unwrap(); assert_eq!(complex_struct, deserialized); let deserialized_value: serde_json::Value = deserialized .examples .remove("Larry Jake Pumpkin") .expect("key present") .into(); assert_eq!(original, deserialized_value); let number = deserialized_value .get("array_of_numbers") .expect("has key") .as_array() .expect("is array") .get(5) .expect("has index") .as_i64() .expect("is i64"); assert_eq!(number, 34798324); } borsh-1.5.7/src/de/hint.rs000064400000000000000000000004731046102023000134460ustar 00000000000000#[inline] pub fn cautious(hint: u32) -> usize { let el_size = core::mem::size_of::() as u32; core::cmp::max(core::cmp::min(hint, 4096 / el_size), 1) as usize } #[cfg(test)] mod tests { use super::*; #[test] pub fn test_cautious_u8() { assert_eq!(cautious::(10), 10); } } borsh-1.5.7/src/de/mod.rs000064400000000000000000001112051046102023000132570ustar 00000000000000use core::marker::PhantomData; use core::mem::MaybeUninit; use core::{ convert::{TryFrom, TryInto}, mem::size_of, }; #[cfg(feature = "bytes")] use bytes::{BufMut, BytesMut}; use crate::__private::maybestd::{ borrow::{Borrow, Cow, ToOwned}, boxed::Box, collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, format, string::{String, ToString}, vec, vec::Vec, }; use crate::io::{Error, ErrorKind, Read, Result}; use crate::error::check_zst; mod hint; const ERROR_NOT_ALL_BYTES_READ: &str = "Not all bytes read"; const ERROR_UNEXPECTED_LENGTH_OF_INPUT: &str = "Unexpected length of input"; const ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_ISIZE: &str = "Overflow on machine with 32 bit isize"; const ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_USIZE: &str = "Overflow on machine with 32 bit usize"; const ERROR_INVALID_ZERO_VALUE: &str = "Expected a non-zero value"; #[cfg(feature = "de_strict_order")] const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order"; /// A data-structure that can be de-serialized from binary format by NBOR. pub trait BorshDeserialize: Sized { /// Deserializes this instance from a given slice of bytes. /// Updates the buffer to point at the remaining bytes. fn deserialize(buf: &mut &[u8]) -> Result { Self::deserialize_reader(&mut *buf) } fn deserialize_reader(reader: &mut R) -> Result; /// Deserialize this instance from a slice of bytes. fn try_from_slice(v: &[u8]) -> Result { let mut v_mut = v; let result = Self::deserialize(&mut v_mut)?; if !v_mut.is_empty() { return Err(Error::new(ErrorKind::InvalidData, ERROR_NOT_ALL_BYTES_READ)); } Ok(result) } fn try_from_reader(reader: &mut R) -> Result { let result = Self::deserialize_reader(reader)?; let mut buf = [0u8; 1]; match reader.read_exact(&mut buf) { Err(f) if f.kind() == ErrorKind::UnexpectedEof => Ok(result), _ => Err(Error::new(ErrorKind::InvalidData, ERROR_NOT_ALL_BYTES_READ)), } } #[inline] #[doc(hidden)] fn vec_from_reader(len: u32, reader: &mut R) -> Result>> { let _ = len; let _ = reader; Ok(None) } #[inline] #[doc(hidden)] fn array_from_reader(reader: &mut R) -> Result> { let _ = reader; Ok(None) } } /// Additional methods offered on enums which is used by `[derive(BorshDeserialize)]`. pub trait EnumExt: BorshDeserialize { /// Deserialises given variant of an enum from the reader. /// /// This may be used to perform validation or filtering based on what /// variant is being deserialised. /// /// ``` /// use borsh::BorshDeserialize; /// use borsh::de::EnumExt as _; /// /// /// derive is only available if borsh is built with `features = ["derive"]` /// # #[cfg(feature = "derive")] /// #[derive(Debug, PartialEq, Eq, BorshDeserialize)] /// enum MyEnum { /// Zero, /// One(u8), /// Many(Vec) /// } /// /// # #[cfg(feature = "derive")] /// #[derive(Debug, PartialEq, Eq)] /// struct OneOrZero(MyEnum); /// /// # #[cfg(feature = "derive")] /// impl borsh::de::BorshDeserialize for OneOrZero { /// fn deserialize_reader( /// reader: &mut R, /// ) -> borsh::io::Result { /// use borsh::de::EnumExt; /// let tag = u8::deserialize_reader(reader)?; /// if tag == 2 { /// Err(borsh::io::Error::new( /// borsh::io::ErrorKind::InvalidData, /// "MyEnum::Many not allowed here", /// )) /// } else { /// MyEnum::deserialize_variant(reader, tag).map(Self) /// } /// } /// } /// /// use borsh::from_slice; /// let data = b"\0"; /// # #[cfg(feature = "derive")] /// assert_eq!(MyEnum::Zero, from_slice::(&data[..]).unwrap()); /// # #[cfg(feature = "derive")] /// assert_eq!(MyEnum::Zero, from_slice::(&data[..]).unwrap().0); /// /// let data = b"\x02\0\0\0\0"; /// # #[cfg(feature = "derive")] /// assert_eq!(MyEnum::Many(Vec::new()), from_slice::(&data[..]).unwrap()); /// # #[cfg(feature = "derive")] /// assert!(from_slice::(&data[..]).is_err()); /// ``` fn deserialize_variant(reader: &mut R, tag: u8) -> Result; } fn unexpected_eof_to_unexpected_length_of_input(e: Error) -> Error { if e.kind() == ErrorKind::UnexpectedEof { Error::new(ErrorKind::InvalidData, ERROR_UNEXPECTED_LENGTH_OF_INPUT) } else { e } } impl BorshDeserialize for u8 { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; 1]; reader .read_exact(&mut buf) .map_err(unexpected_eof_to_unexpected_length_of_input)?; Ok(buf[0]) } #[inline] #[doc(hidden)] fn vec_from_reader(len: u32, reader: &mut R) -> Result>> { let len: usize = len.try_into().map_err(|_| ErrorKind::InvalidData)?; // Avoid OOM by limiting the size of allocation. This makes the read // less efficient (since we need to loop and reallocate) but it protects // us from someone sending us [0xff, 0xff, 0xff, 0xff] and forcing us to // allocate 4GiB of memory. let mut vec = vec![0u8; len.min(1024 * 1024)]; let mut pos = 0; while pos < len { if pos == vec.len() { vec.resize(vec.len().saturating_mul(2).min(len), 0) } // TODO(mina86): Convert this to read_buf once that stabilises. match reader.read(&mut vec.as_mut_slice()[pos..])? { 0 => { return Err(Error::new( ErrorKind::InvalidData, ERROR_UNEXPECTED_LENGTH_OF_INPUT, )) } read => { pos += read; } } } Ok(Some(vec)) } #[inline] #[doc(hidden)] fn array_from_reader(reader: &mut R) -> Result> { let mut arr = [0u8; N]; reader .read_exact(&mut arr) .map_err(unexpected_eof_to_unexpected_length_of_input)?; Ok(Some(arr)) } } macro_rules! impl_for_integer { ($type: ident) => { impl BorshDeserialize for $type { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; size_of::<$type>()]; reader .read_exact(&mut buf) .map_err(unexpected_eof_to_unexpected_length_of_input)?; let res = $type::from_le_bytes(buf.try_into().unwrap()); Ok(res) } } }; } impl_for_integer!(i8); impl_for_integer!(i16); impl_for_integer!(i32); impl_for_integer!(i64); impl_for_integer!(i128); impl_for_integer!(u16); impl_for_integer!(u32); impl_for_integer!(u64); impl_for_integer!(u128); macro_rules! impl_for_nonzero_integer { ($type: ty) => { impl BorshDeserialize for $type { #[inline] fn deserialize_reader(reader: &mut R) -> Result { <$type>::new(BorshDeserialize::deserialize_reader(reader)?) .ok_or_else(|| Error::new(ErrorKind::InvalidData, ERROR_INVALID_ZERO_VALUE)) } } }; } impl_for_nonzero_integer!(core::num::NonZeroI8); impl_for_nonzero_integer!(core::num::NonZeroI16); impl_for_nonzero_integer!(core::num::NonZeroI32); impl_for_nonzero_integer!(core::num::NonZeroI64); impl_for_nonzero_integer!(core::num::NonZeroI128); impl_for_nonzero_integer!(core::num::NonZeroU8); impl_for_nonzero_integer!(core::num::NonZeroU16); impl_for_nonzero_integer!(core::num::NonZeroU32); impl_for_nonzero_integer!(core::num::NonZeroU64); impl_for_nonzero_integer!(core::num::NonZeroU128); impl_for_nonzero_integer!(core::num::NonZeroUsize); impl BorshDeserialize for isize { fn deserialize_reader(reader: &mut R) -> Result { let i: i64 = BorshDeserialize::deserialize_reader(reader)?; let i = isize::try_from(i).map_err(|_| { Error::new( ErrorKind::InvalidData, ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_ISIZE, ) })?; Ok(i) } } impl BorshDeserialize for usize { fn deserialize_reader(reader: &mut R) -> Result { let u: u64 = BorshDeserialize::deserialize_reader(reader)?; let u = usize::try_from(u).map_err(|_| { Error::new( ErrorKind::InvalidData, ERROR_OVERFLOW_ON_MACHINE_WITH_32_BIT_USIZE, ) })?; Ok(u) } } // Note NaNs have a portability issue. Specifically, signalling NaNs on MIPS are quiet NaNs on x86, // and vice-versa. We disallow NaNs to avoid this issue. macro_rules! impl_for_float { ($type: ident, $int_type: ident) => { impl BorshDeserialize for $type { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; size_of::<$type>()]; reader .read_exact(&mut buf) .map_err(unexpected_eof_to_unexpected_length_of_input)?; let res = $type::from_bits($int_type::from_le_bytes(buf.try_into().unwrap())); if res.is_nan() { return Err(Error::new( ErrorKind::InvalidData, "For portability reasons we do not allow to deserialize NaNs.", )); } Ok(res) } } }; } impl_for_float!(f32, u32); impl_for_float!(f64, u64); impl BorshDeserialize for bool { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let b: u8 = BorshDeserialize::deserialize_reader(reader)?; if b == 0 { Ok(false) } else if b == 1 { Ok(true) } else { let msg = format!("Invalid bool representation: {}", b); Err(Error::new(ErrorKind::InvalidData, msg)) } } } impl BorshDeserialize for Option where T: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let flag: u8 = BorshDeserialize::deserialize_reader(reader)?; if flag == 0 { Ok(None) } else if flag == 1 { Ok(Some(T::deserialize_reader(reader)?)) } else { let msg = format!( "Invalid Option representation: {}. The first byte must be 0 or 1", flag ); Err(Error::new(ErrorKind::InvalidData, msg)) } } } impl BorshDeserialize for core::result::Result where T: BorshDeserialize, E: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let flag: u8 = BorshDeserialize::deserialize_reader(reader)?; if flag == 0 { Ok(Err(E::deserialize_reader(reader)?)) } else if flag == 1 { Ok(Ok(T::deserialize_reader(reader)?)) } else { let msg = format!( "Invalid Result representation: {}. The first byte must be 0 or 1", flag ); Err(Error::new(ErrorKind::InvalidData, msg)) } } } impl BorshDeserialize for String { #[inline] fn deserialize_reader(reader: &mut R) -> Result { String::from_utf8(Vec::::deserialize_reader(reader)?).map_err(|err| { let msg = err.to_string(); Error::new(ErrorKind::InvalidData, msg) }) } } /// Module is available if borsh is built with `features = ["ascii"]`. #[cfg(feature = "ascii")] pub mod ascii { //! //! Module defines [BorshDeserialize] implementation for //! some types from [ascii](::ascii) crate. use crate::BorshDeserialize; use crate::__private::maybestd::{string::ToString, vec::Vec}; use crate::io::{Error, ErrorKind, Read, Result}; impl BorshDeserialize for ascii::AsciiString { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let bytes = Vec::::deserialize_reader(reader)?; ascii::AsciiString::from_ascii(bytes) .map_err(|err| Error::new(ErrorKind::InvalidData, err.to_string())) } } impl BorshDeserialize for ascii::AsciiChar { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let byte = u8::deserialize_reader(reader)?; ascii::AsciiChar::from_ascii(byte) .map_err(|err| Error::new(ErrorKind::InvalidData, err.to_string())) } } } impl BorshDeserialize for Vec where T: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; let len = u32::deserialize_reader(reader)?; if len == 0 { Ok(Vec::new()) } else if let Some(vec_bytes) = T::vec_from_reader(len, reader)? { Ok(vec_bytes) } else { // TODO(16): return capacity allocation when we can safely do that. let mut result = Vec::with_capacity(hint::cautious::(len)); for _ in 0..len { result.push(T::deserialize_reader(reader)?); } Ok(result) } } } #[cfg(feature = "bytes")] impl BorshDeserialize for bytes::Bytes { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let vec = >::deserialize_reader(reader)?; Ok(vec.into()) } } #[cfg(feature = "bytes")] impl BorshDeserialize for bytes::BytesMut { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let len = u32::deserialize_reader(reader)?; let mut out = BytesMut::with_capacity(hint::cautious::(len)); for _ in 0..len { out.put_u8(u8::deserialize_reader(reader)?); } Ok(out) } } #[cfg(feature = "bson")] impl BorshDeserialize for bson::oid::ObjectId { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; 12]; reader.read_exact(&mut buf)?; Ok(bson::oid::ObjectId::from_bytes(buf)) } } #[cfg(feature = "indexmap")] // Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L39 // license: MIT OR Apache-2.0 impl BorshDeserialize for indexmap::IndexMap where K: BorshDeserialize + Eq + core::hash::Hash, V: BorshDeserialize, S: core::hash::BuildHasher + Default, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; let vec = >::deserialize_reader(reader)?; Ok(vec.into_iter().collect::>()) } } #[cfg(feature = "indexmap")] // Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L75 // license: MIT OR Apache-2.0 impl BorshDeserialize for indexmap::IndexSet where T: BorshDeserialize + Eq + core::hash::Hash, S: core::hash::BuildHasher + Default, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; let vec = >::deserialize_reader(reader)?; Ok(vec.into_iter().collect::>()) } } impl BorshDeserialize for Cow<'_, T> where T: ToOwned + ?Sized, T::Owned: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { Ok(Cow::Owned(BorshDeserialize::deserialize_reader(reader)?)) } } impl BorshDeserialize for VecDeque where T: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let vec = >::deserialize_reader(reader)?; Ok(vec.into()) } } impl BorshDeserialize for LinkedList where T: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let vec = >::deserialize_reader(reader)?; Ok(vec.into_iter().collect::>()) } } /// Module is available if borsh is built with `features = ["std"]` or `features = ["hashbrown"]`. /// /// Module defines [BorshDeserialize] implementation for /// [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet). #[cfg(hash_collections)] pub mod hashes { use core::hash::{BuildHasher, Hash}; use crate::BorshDeserialize; use crate::__private::maybestd::collections::{HashMap, HashSet}; use crate::__private::maybestd::vec::Vec; use crate::io::{Read, Result}; #[cfg(feature = "de_strict_order")] const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order"; use crate::error::check_zst; #[cfg(feature = "de_strict_order")] use crate::io::{Error, ErrorKind}; impl BorshDeserialize for HashSet where T: BorshDeserialize + Eq + Hash + Ord, H: BuildHasher + Default, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the elements // which may fail `cmp()` checks // NOTE: deserialize first to `Vec` is faster let vec = >::deserialize_reader(reader)?; #[cfg(feature = "de_strict_order")] // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 for pair in vec.windows(2) { let [a, b] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); }; let cmp_result = a.cmp(b).is_lt(); if !cmp_result { return Err(Error::new( ErrorKind::InvalidData, ERROR_WRONG_ORDER_OF_KEYS, )); } } Ok(vec.into_iter().collect::>()) } } impl BorshDeserialize for HashMap where K: BorshDeserialize + Eq + Hash + Ord, V: BorshDeserialize, H: BuildHasher + Default, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the entries // which may fail `cmp()` checks // NOTE: deserialize first to `Vec<(K, V)>` is faster let vec = >::deserialize_reader(reader)?; #[cfg(feature = "de_strict_order")] // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 for pair in vec.windows(2) { let [(a_k, _a_v), (b_k, _b_v)] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); }; let cmp_result = a_k.cmp(b_k).is_lt(); if !cmp_result { return Err(Error::new( ErrorKind::InvalidData, ERROR_WRONG_ORDER_OF_KEYS, )); } } Ok(vec.into_iter().collect::>()) } } } impl BorshDeserialize for BTreeSet where T: BorshDeserialize + Ord, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the elements // which may fail `cmp()` checks // NOTE: deserialize first to `Vec` is faster let vec = >::deserialize_reader(reader)?; #[cfg(feature = "de_strict_order")] // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 for pair in vec.windows(2) { let [a, b] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); }; let cmp_result = a.cmp(b).is_lt(); if !cmp_result { return Err(Error::new( ErrorKind::InvalidData, ERROR_WRONG_ORDER_OF_KEYS, )); } } // NOTE: BTreeSet has an optimization inside of impl FromIterator for BTreeSet, // based on BTreeMap::bulk_build_from_sorted_iter Ok(vec.into_iter().collect::>()) } } impl BorshDeserialize for BTreeMap where K: BorshDeserialize + Ord, V: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { check_zst::()?; // NOTE: deserialize-as-you-go approach as once was in HashSet is better in the sense // that it allows to fail early, and not allocate memory for all the entries // which may fail `cmp()` checks // NOTE: deserialize first to `Vec<(K, V)>` is faster let vec = >::deserialize_reader(reader)?; #[cfg(feature = "de_strict_order")] // TODO: replace with `is_sorted` api when stabilizes https://github.com/rust-lang/rust/issues/53485 // TODO: first replace with `array_windows` api when stabilizes https://github.com/rust-lang/rust/issues/75027 for pair in vec.windows(2) { let [(a_k, _a_v), (b_k, _b_v)] = pair else { unreachable!("`windows` always return a slice of length 2 or nothing"); }; let cmp_result = a_k.cmp(b_k).is_lt(); if !cmp_result { return Err(Error::new( ErrorKind::InvalidData, ERROR_WRONG_ORDER_OF_KEYS, )); } } // NOTE: BTreeMap has an optimization inside of impl FromIterator<(K, V)> for BTreeMap, // based on BTreeMap::bulk_build_from_sorted_iter Ok(vec.into_iter().collect::>()) } } #[cfg(feature = "std")] impl BorshDeserialize for std::net::SocketAddr { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let kind = u8::deserialize_reader(reader)?; match kind { 0 => std::net::SocketAddrV4::deserialize_reader(reader).map(std::net::SocketAddr::V4), 1 => std::net::SocketAddrV6::deserialize_reader(reader).map(std::net::SocketAddr::V6), value => Err(Error::new( ErrorKind::InvalidData, format!("Invalid SocketAddr variant: {}", value), )), } } } #[cfg(feature = "std")] impl BorshDeserialize for std::net::IpAddr { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let kind = u8::deserialize_reader(reader)?; match kind { 0u8 => { // Deserialize an Ipv4Addr and convert it to IpAddr::V4 let ipv4_addr = std::net::Ipv4Addr::deserialize_reader(reader)?; Ok(std::net::IpAddr::V4(ipv4_addr)) } 1u8 => { // Deserialize an Ipv6Addr and convert it to IpAddr::V6 let ipv6_addr = std::net::Ipv6Addr::deserialize_reader(reader)?; Ok(std::net::IpAddr::V6(ipv6_addr)) } value => Err(Error::new( ErrorKind::InvalidData, format!("Invalid IpAddr variant: {}", value), )), } } } #[cfg(feature = "std")] impl BorshDeserialize for std::net::SocketAddrV4 { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let ip = std::net::Ipv4Addr::deserialize_reader(reader)?; let port = u16::deserialize_reader(reader)?; Ok(std::net::SocketAddrV4::new(ip, port)) } } #[cfg(feature = "std")] impl BorshDeserialize for std::net::SocketAddrV6 { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let ip = std::net::Ipv6Addr::deserialize_reader(reader)?; let port = u16::deserialize_reader(reader)?; Ok(std::net::SocketAddrV6::new(ip, port, 0, 0)) } } #[cfg(feature = "std")] impl BorshDeserialize for std::net::Ipv4Addr { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; 4]; reader .read_exact(&mut buf) .map_err(unexpected_eof_to_unexpected_length_of_input)?; Ok(std::net::Ipv4Addr::from(buf)) } } #[cfg(feature = "std")] impl BorshDeserialize for std::net::Ipv6Addr { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let mut buf = [0u8; 16]; reader .read_exact(&mut buf) .map_err(unexpected_eof_to_unexpected_length_of_input)?; Ok(std::net::Ipv6Addr::from(buf)) } } impl BorshDeserialize for Box where U: Into> + Borrow, T: ToOwned + ?Sized, T::Owned: BorshDeserialize, { fn deserialize_reader(reader: &mut R) -> Result { Ok(T::Owned::deserialize_reader(reader)?.into()) } } impl BorshDeserialize for [T; N] where T: BorshDeserialize, { #[inline] fn deserialize_reader(reader: &mut R) -> Result { struct ArrayDropGuard { buffer: [MaybeUninit; N], init_count: usize, } impl Drop for ArrayDropGuard { fn drop(&mut self) { let init_range = &mut self.buffer[..self.init_count]; // SAFETY: Elements up to self.init_count have been initialized. Assumes this value // is only incremented in `fill_buffer`, which writes the element before // increasing the init_count. unsafe { core::ptr::drop_in_place(init_range as *mut _ as *mut [T]); }; } } impl ArrayDropGuard { unsafe fn transmute_to_array(mut self) -> [T; N] { debug_assert_eq!(self.init_count, N); // Set init_count to 0 so that the values do not get dropped twice. self.init_count = 0; // SAFETY: This cast is required because `mem::transmute` does not work with // const generics https://github.com/rust-lang/rust/issues/61956. This // array is guaranteed to be initialized by this point. core::ptr::read(&self.buffer as *const _ as *const [T; N]) } fn fill_buffer(&mut self, mut f: impl FnMut() -> Result) -> Result<()> { // TODO: replace with `core::array::try_from_fn` when stabilized to avoid manually // dropping uninitialized values through the guard drop. for elem in self.buffer.iter_mut() { elem.write(f()?); self.init_count += 1; } Ok(()) } } if let Some(arr) = T::array_from_reader(reader)? { Ok(arr) } else { let mut result = ArrayDropGuard { buffer: unsafe { MaybeUninit::uninit().assume_init() }, init_count: 0, }; result.fill_buffer(|| T::deserialize_reader(reader))?; // SAFETY: The elements up to `i` have been initialized in `fill_buffer`. Ok(unsafe { result.transmute_to_array() }) } } } #[test] fn array_deserialization_doesnt_leak() { use core::sync::atomic::{AtomicUsize, Ordering}; static DESERIALIZE_COUNT: AtomicUsize = AtomicUsize::new(0); static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); #[allow(unused)] struct MyType(u8); impl BorshDeserialize for MyType { fn deserialize_reader(reader: &mut R) -> Result { let val = u8::deserialize_reader(reader)?; let v = DESERIALIZE_COUNT.fetch_add(1, Ordering::SeqCst); if v >= 7 { panic!("panic in deserialize"); } Ok(MyType(val)) } } impl Drop for MyType { fn drop(&mut self) { DROP_COUNT.fetch_add(1, Ordering::SeqCst); } } assert!(<[MyType; 5] as BorshDeserialize>::deserialize(&mut &[0u8; 3][..]).is_err()); assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 3); assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3); assert!(<[MyType; 2] as BorshDeserialize>::deserialize(&mut &[0u8; 2][..]).is_ok()); assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 5); assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 5); #[cfg(feature = "std")] { // Test that during a panic in deserialize, the values are still dropped. let result = std::panic::catch_unwind(|| { <[MyType; 3] as BorshDeserialize>::deserialize(&mut &[0u8; 3][..]).unwrap(); }); assert!(result.is_err()); assert_eq!(DESERIALIZE_COUNT.load(Ordering::SeqCst), 8); assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 7); // 5 because 6 panicked and was not init } } macro_rules! impl_tuple { (@unit $name:ty) => { impl BorshDeserialize for $name { #[inline] fn deserialize_reader(_reader: &mut R) -> Result { Ok(<$name>::default()) } } }; ($($name:ident)+) => { impl<$($name),+> BorshDeserialize for ($($name,)+) where $($name: BorshDeserialize,)+ { #[inline] fn deserialize_reader(reader: &mut R) -> Result { Ok(($($name::deserialize_reader(reader)?,)+)) } } }; } impl_tuple!(@unit ()); impl_tuple!(@unit core::ops::RangeFull); impl_tuple!(T0); impl_tuple!(T0 T1); impl_tuple!(T0 T1 T2); impl_tuple!(T0 T1 T2 T3); impl_tuple!(T0 T1 T2 T3 T4); impl_tuple!(T0 T1 T2 T3 T4 T5); impl_tuple!(T0 T1 T2 T3 T4 T5 T6); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18); impl_tuple!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19); macro_rules! impl_range { ($type:ident, $make:expr, $($side:ident),*) => { impl BorshDeserialize for core::ops::$type { #[inline] fn deserialize_reader(reader: &mut R) -> Result { let ($($side,)*) = <_>::deserialize_reader(reader)?; Ok($make) } } }; } impl_range!(Range, start..end, start, end); impl_range!(RangeInclusive, start..=end, start, end); impl_range!(RangeFrom, start.., start); impl_range!(RangeTo, ..end, end); impl_range!(RangeToInclusive, ..=end, end); /// Module is available if borsh is built with `features = ["rc"]`. #[cfg(feature = "rc")] pub mod rc { //! //! Module defines [BorshDeserialize] implementation for //! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc). use crate::__private::maybestd::{boxed::Box, rc::Rc, sync::Arc}; use crate::io::{Read, Result}; use crate::BorshDeserialize; /// This impl requires the [`"rc"`] Cargo feature of borsh. /// /// Deserializing a data structure containing `Rc` will not attempt to /// deduplicate `Rc` references to the same data. Every deserialized `Rc` /// will end up with a strong count of 1. impl BorshDeserialize for Rc where Box: BorshDeserialize, { fn deserialize_reader(reader: &mut R) -> Result { Ok(Box::::deserialize_reader(reader)?.into()) } } /// This impl requires the [`"rc"`] Cargo feature of borsh. /// /// Deserializing a data structure containing `Arc` will not attempt to /// deduplicate `Arc` references to the same data. Every deserialized `Arc` /// will end up with a strong count of 1. impl BorshDeserialize for Arc where Box: BorshDeserialize, { fn deserialize_reader(reader: &mut R) -> Result { Ok(Box::::deserialize_reader(reader)?.into()) } } } impl BorshDeserialize for PhantomData { fn deserialize_reader(_: &mut R) -> Result { Ok(PhantomData) } } impl BorshDeserialize for core::cell::Cell where T: BorshDeserialize + Copy, { fn deserialize_reader(reader: &mut R) -> Result { ::deserialize_reader(reader).map(core::cell::Cell::new) } } impl BorshDeserialize for core::cell::RefCell where T: BorshDeserialize, { fn deserialize_reader(reader: &mut R) -> Result { ::deserialize_reader(reader).map(core::cell::RefCell::new) } } /// Deserializes an object from a slice of bytes. /// # Example /// ``` /// use borsh::{BorshDeserialize, BorshSerialize, from_slice, to_vec}; /// /// /// derive is only available if borsh is built with `features = ["derive"]` /// # #[cfg(feature = "derive")] /// #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] /// struct MyStruct { /// a: u64, /// b: Vec, /// } /// /// # #[cfg(feature = "derive")] /// let original = MyStruct { a: 10, b: vec![1, 2, 3] }; /// # #[cfg(feature = "derive")] /// let encoded = to_vec(&original).unwrap(); /// # #[cfg(feature = "derive")] /// let decoded = from_slice::(&encoded).unwrap(); /// # #[cfg(feature = "derive")] /// assert_eq!(original, decoded); /// ``` /// # Panics /// If the data is invalid, this function will panic. /// # Errors /// If the data is invalid, this function will return an error. /// # Note /// This function will return an error if the data is not fully read. pub fn from_slice(v: &[u8]) -> Result { let mut v_mut = v; let object = T::deserialize(&mut v_mut)?; if !v_mut.is_empty() { return Err(Error::new( ErrorKind::InvalidData, crate::de::ERROR_NOT_ALL_BYTES_READ, )); } Ok(object) } /// Deserializes an object from a reader. /// # Example /// ``` /// use borsh::{BorshDeserialize, BorshSerialize, from_reader, to_vec}; /// /// /// derive is only available if borsh is built with `features = ["derive"]` /// # #[cfg(feature = "derive")] /// #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] /// struct MyStruct { /// a: u64, /// b: Vec, /// } /// /// # #[cfg(feature = "derive")] /// let original = MyStruct { a: 10, b: vec![1, 2, 3] }; /// # #[cfg(feature = "derive")] /// let encoded = to_vec(&original).unwrap(); /// # #[cfg(feature = "derive")] /// let decoded = from_reader::<_, MyStruct>(&mut encoded.as_slice()).unwrap(); /// # #[cfg(feature = "derive")] /// assert_eq!(original, decoded); /// ``` pub fn from_reader(reader: &mut R) -> Result { let result = T::deserialize_reader(reader)?; let mut buf = [0u8; 1]; match reader.read_exact(&mut buf) { Err(f) if f.kind() == ErrorKind::UnexpectedEof => Ok(result), _ => Err(Error::new(ErrorKind::InvalidData, ERROR_NOT_ALL_BYTES_READ)), } } borsh-1.5.7/src/error.rs000064400000000000000000000005731046102023000132460ustar 00000000000000use crate::io::{Error, ErrorKind, Result}; use core::mem::size_of; pub const ERROR_ZST_FORBIDDEN: &str = "Collections of zero-sized types are not allowed due to deny-of-service concerns on deserialization."; pub(crate) fn check_zst() -> Result<()> { if size_of::() == 0 { return Err(Error::new(ErrorKind::InvalidData, ERROR_ZST_FORBIDDEN)); } Ok(()) } borsh-1.5.7/src/generate_schema_schema.rs000064400000000000000000000010761046102023000165460ustar 00000000000000//! Generate `BorshSchemaCointainer` for `BorshSchemaContainer` and save it into a file. #![cfg_attr(not(feature = "std"), no_std)] use borsh::schema_container_of; use std::fs::File; use std::io::Write; fn main() { let container = schema_container_of::(); println!("{:#?}", container); let data = borsh::to_vec(&container).expect("Failed to serialize BorshSchemaContainer"); let mut file = File::create("schema_schema.dat").expect("Failed to create file"); file.write_all(&data).expect("Failed to write file"); } borsh-1.5.7/src/lib.rs000064400000000000000000000052761046102023000126700ustar 00000000000000#![cfg_attr(not(feature = "std"), no_std)] #![doc = include_str!("../docs/rustdoc_include/borsh_crate_top_level.md")] #[cfg(not(feature = "std"))] extern crate alloc; #[doc = include_str!("../docs/rustdoc_include/borsh_schema.md")] #[cfg(feature = "unstable__schema")] pub use borsh_derive::BorshSchema; #[doc = include_str!("../docs/rustdoc_include/borsh_deserialize.md")] #[cfg(feature = "derive")] pub use borsh_derive::BorshDeserialize; #[doc = include_str!("../docs/rustdoc_include/borsh_serialize.md")] #[cfg(feature = "derive")] pub use borsh_derive::BorshSerialize; pub mod de; // See `hash_collections` alias definition in build.rs /// Module is available if borsh is built with `features = ["unstable__schema"]`. #[cfg(feature = "unstable__schema")] pub mod schema; #[cfg(feature = "unstable__schema")] pub(crate) mod schema_helpers; pub mod ser; pub use de::BorshDeserialize; pub use de::{from_reader, from_slice}; #[cfg(feature = "unstable__schema")] pub use schema::BorshSchema; #[cfg(feature = "unstable__schema")] pub use schema_helpers::{ max_serialized_size, schema_container_of, try_from_slice_with_schema, try_to_vec_with_schema, }; pub use ser::helpers::{object_length, to_vec, to_writer}; pub use ser::BorshSerialize; pub mod error; #[cfg(all(feature = "std", feature = "hashbrown"))] compile_error!("feature \"std\" and feature \"hashbrown\" don't make sense at the same time"); #[cfg(feature = "std")] use std::io as io_impl; #[cfg(not(feature = "std"))] mod nostd_io; #[cfg(not(feature = "std"))] use nostd_io as io_impl; /// Subset of `std::io` which is used as part of borsh public API. /// /// When crate is built with `std` feature disabled (it’s enabled by default), /// the exported types are custom borsh types which try to mimic behaviour of /// corresponding standard types usually offering subset of features. pub mod io { pub use super::io_impl::{Error, ErrorKind, Read, Result, Write}; } #[doc(hidden)] pub mod __private { /// A facade around all the types we need from the `std`, and `alloc` /// crates. This avoids elaborate import wrangling having to happen in every /// module. #[cfg(feature = "std")] pub mod maybestd { pub use std::{borrow, boxed, collections, format, string, vec}; #[cfg(feature = "rc")] pub use std::{rc, sync}; } #[cfg(not(feature = "std"))] pub mod maybestd { pub use alloc::{borrow, boxed, format, string, vec}; #[cfg(feature = "rc")] pub use alloc::{rc, sync}; pub mod collections { pub use alloc::collections::{btree_map, BTreeMap, BTreeSet, LinkedList, VecDeque}; #[cfg(feature = "hashbrown")] pub use hashbrown::*; } } } borsh-1.5.7/src/nostd_io.rs000064400000000000000000001006471046102023000137360ustar 00000000000000//! Taken from https://github.com/bbqsrc/bare-io (with adjustments) use crate::__private::maybestd::string::String; use core::{convert::From, fmt, result}; /// A specialized [`Result`] type for I/O operations. /// /// This type is broadly used across [`std::io`] for any operation which may /// produce an error. /// /// This typedef is generally used to avoid writing out [`io::Error`] directly and /// is otherwise a direct mapping to [`Result`]. /// /// While usual Rust style is to import types directly, aliases of [`Result`] /// often are not, to make it easier to distinguish between them. [`Result`] is /// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias /// will generally use `io::Result` instead of shadowing the [prelude]'s import /// of [`std::result::Result`][`Result`]. /// /// [`std::io`]: crate::io /// [`io::Error`]: Error /// [`Result`]: crate::result::Result /// [prelude]: crate::prelude /// /// # Examples /// /// A convenience function that bubbles an `io::Result` to its caller: /// /// ``` /// use std::io; /// /// fn get_string() -> io::Result { /// let mut buffer = String::new(); /// /// io::stdin().read_line(&mut buffer)?; /// /// Ok(buffer) /// } /// ``` pub type Result = result::Result; /// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and /// associated traits. /// /// Errors mostly originate from the underlying OS, but custom instances of /// `Error` can be created with crafted error messages and a particular value of /// [`ErrorKind`]. /// /// [`Read`]: crate::io::Read /// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek pub struct Error { repr: Repr, } impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.repr, f) } } enum Repr { Simple(ErrorKind), Custom(Custom), } #[derive(Debug)] struct Custom { kind: ErrorKind, error: String, } /// A list specifying general categories of I/O error. /// /// This list is intended to grow over time and it is not recommended to /// exhaustively match against it. /// /// It is used with the [`io::Error`] type. /// /// [`io::Error`]: Error #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] // #[allow(deprecated)] #[non_exhaustive] pub enum ErrorKind { /// An entity was not found, often a file. NotFound, /// The operation lacked the necessary privileges to complete. PermissionDenied, /// The connection was refused by the remote server. ConnectionRefused, /// The connection was reset by the remote server. ConnectionReset, /// The connection was aborted (terminated) by the remote server. ConnectionAborted, /// The network operation failed because it was not connected yet. NotConnected, /// A socket address could not be bound because the address is already in /// use elsewhere. AddrInUse, /// A nonexistent interface was requested or the requested address was not /// local. AddrNotAvailable, /// The operation failed because a pipe was closed. BrokenPipe, /// An entity already exists, often a file. AlreadyExists, /// The operation needs to block to complete, but the blocking operation was /// requested to not occur. WouldBlock, /// A parameter was incorrect. InvalidInput, /// Data not valid for the operation were encountered. /// /// Unlike [`InvalidInput`], this typically means that the operation /// parameters were valid, however the error was caused by malformed /// input data. /// /// For example, a function that reads a file into a string will error with /// `InvalidData` if the file's contents are not valid UTF-8. /// /// [`InvalidInput`]: ErrorKind::InvalidInput InvalidData, /// The I/O operation's timeout expired, causing it to be canceled. TimedOut, /// An error returned when an operation could not be completed because a /// call to [`write`] returned [`Ok(0)`]. /// /// This typically means that an operation could only succeed if it wrote a /// particular number of bytes but only a smaller number of bytes could be /// written. /// /// [`write`]: crate::io::Write::write /// [`Ok(0)`]: Ok WriteZero, /// This operation was interrupted. /// /// Interrupted operations can typically be retried. Interrupted, /// Any I/O error not part of this list. /// /// Errors that are `Other` now may move to a different or a new /// [`ErrorKind`] variant in the future. It is not recommended to match /// an error against `Other` and to expect any additional characteristics, /// e.g., a specific [`Error::raw_os_error`] return value. Other, /// An error returned when an operation could not be completed because an /// "end of file" was reached prematurely. /// /// This typically means that an operation could only succeed if it read a /// particular number of bytes but only a smaller number of bytes could be /// read. UnexpectedEof, /// An operation could not be completed, because it failed /// to allocate enough memory. OutOfMemory, } impl ErrorKind { pub(crate) fn as_str(&self) -> &'static str { match *self { ErrorKind::NotFound => "entity not found", ErrorKind::PermissionDenied => "permission denied", ErrorKind::ConnectionRefused => "connection refused", ErrorKind::ConnectionReset => "connection reset", ErrorKind::ConnectionAborted => "connection aborted", ErrorKind::NotConnected => "not connected", ErrorKind::AddrInUse => "address in use", ErrorKind::AddrNotAvailable => "address not available", ErrorKind::BrokenPipe => "broken pipe", ErrorKind::AlreadyExists => "entity already exists", ErrorKind::WouldBlock => "operation would block", ErrorKind::InvalidInput => "invalid input parameter", ErrorKind::InvalidData => "invalid data", ErrorKind::TimedOut => "timed out", ErrorKind::WriteZero => "write zero", ErrorKind::Interrupted => "operation interrupted", ErrorKind::Other => "other os error", ErrorKind::UnexpectedEof => "unexpected end of file", ErrorKind::OutOfMemory => "out of memory", } } } /// Intended for use for errors not exposed to the user, where allocating onto /// the heap (for normal construction via Error::new) is too costly. impl From for Error { /// Converts an [`ErrorKind`] into an [`Error`]. /// /// This conversion allocates a new error with a simple representation of error kind. /// /// # Examples /// /// ``` /// use std::io::{Error, ErrorKind}; /// /// let not_found = ErrorKind::NotFound; /// let error = Error::from(not_found); /// assert_eq!("entity not found", format!("{}", error)); /// ``` #[inline] fn from(kind: ErrorKind) -> Error { Error { repr: Repr::Simple(kind), } } } impl Error { /// Creates a new I/O error from a known kind of error as well as an /// arbitrary error payload. /// /// This function is used to generically create I/O errors which do not /// originate from the OS itself. The `error` argument is an arbitrary /// payload which will be contained in this [`Error`]. /// /// # Examples /// /// ``` /// use std::io::{Error, ErrorKind}; /// /// // errors can be created from strings /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); /// /// // errors can also be created from other errors /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); /// ``` pub fn new>(kind: ErrorKind, error: T) -> Error { Self::_new(kind, error.into()) } fn _new(kind: ErrorKind, error: String) -> Error { Error { repr: Repr::Custom(Custom { kind, error }), } } /// Returns a reference to the inner error wrapped by this error (if any). /// /// If this [`Error`] was constructed via [`new`] then this function will /// return [`Some`], otherwise it will return [`None`]. /// /// [`new`]: Error::new /// /// # Examples /// /// ``` /// use std::io::{Error, ErrorKind}; /// /// fn print_error(err: &Error) { /// if let Some(inner_err) = err.get_ref() { /// println!("Inner error: {:?}", inner_err); /// } else { /// println!("No inner error"); /// } /// } /// /// fn main() { /// // Will print "No inner error". /// print_error(&Error::last_os_error()); /// // Will print "Inner error: ...". /// print_error(&Error::new(ErrorKind::Other, "oh no!")); /// } /// ``` pub fn get_ref(&self) -> Option<&str> { match self.repr { Repr::Simple(..) => None, Repr::Custom(ref c) => Some(&c.error), } } /// Consumes the `Error`, returning its inner error (if any). /// /// If this [`Error`] was constructed via [`new`] then this function will /// return [`Some`], otherwise it will return [`None`]. /// /// [`new`]: Error::new /// /// # Examples /// /// ``` /// use std::io::{Error, ErrorKind}; /// /// fn print_error(err: Error) { /// if let Some(inner_err) = err.into_inner() { /// println!("Inner error: {}", inner_err); /// } else { /// println!("No inner error"); /// } /// } /// /// fn main() { /// // Will print "No inner error". /// print_error(Error::last_os_error()); /// // Will print "Inner error: ...". /// print_error(Error::new(ErrorKind::Other, "oh no!")); /// } /// ``` pub fn into_inner(self) -> Option { match self.repr { Repr::Simple(..) => None, Repr::Custom(c) => Some(c.error), } } /// Returns the corresponding [`ErrorKind`] for this error. /// /// # Examples /// /// ``` /// use std::io::{Error, ErrorKind}; /// /// fn print_error(err: Error) { /// println!("{:?}", err.kind()); /// } /// /// fn main() { /// // Will print "Other". /// print_error(Error::last_os_error()); /// // Will print "AddrInUse". /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); /// } /// ``` pub fn kind(&self) -> ErrorKind { match self.repr { Repr::Custom(ref c) => c.kind, Repr::Simple(kind) => kind, } } } impl fmt::Debug for Repr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt), Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), } } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.repr { Repr::Custom(ref c) => c.error.fmt(fmt), Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()), } } } fn _assert_error_is_sync_send() { fn _is_sync_send() {} _is_sync_send::(); } /// A trait for objects which are byte-oriented sinks. /// /// Implementors of the `Write` trait are sometimes called 'writers'. /// /// Writers are defined by two required methods, [`write`] and [`flush`]: /// /// * The [`write`] method will attempt to write some data into the object, /// returning how many bytes were successfully written. /// /// * The [`flush`] method is useful for adaptors and explicit buffers /// themselves for ensuring that all buffered data has been pushed out to the /// 'true sink'. /// /// Writers are intended to be composable with one another. Many implementors /// throughout [`std::io`] take and provide types which implement the `Write` /// trait. /// /// [`write`]: Write::write /// [`flush`]: Write::flush /// [`std::io`]: self /// /// # Examples /// /// ```no_run /// use std::io::prelude::*; /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { /// let data = b"some bytes"; /// /// let mut pos = 0; /// let mut buffer = File::create("foo.txt")?; /// /// while pos < data.len() { /// let bytes_written = buffer.write(&data[pos..])?; /// pos += bytes_written; /// } /// Ok(()) /// } /// ``` /// /// The trait also provides convenience methods like [`write_all`], which calls /// `write` in a loop until its entire input has been written. /// /// [`write_all`]: Write::write_all pub trait Write { /// Write a buffer into this writer, returning how many bytes were written. /// /// This function will attempt to write the entire contents of `buf`, but /// the entire write may not succeed, or the write may also generate an /// error. A call to `write` represents *at most one* attempt to write to /// any wrapped object. /// /// Calls to `write` are not guaranteed to block waiting for data to be /// written, and a write which would otherwise block can be indicated through /// an [`Err`] variant. /// /// If the return value is [`Ok(n)`] then it must be guaranteed that /// `n <= buf.len()`. A return value of `0` typically means that the /// underlying object is no longer able to accept bytes and will likely not /// be able to in the future as well, or that the buffer provided is empty. /// /// # Errors /// /// Each call to `write` may generate an I/O error indicating that the /// operation could not be completed. If an error is returned then no bytes /// in the buffer were written to this writer. /// /// It is **not** considered an error if the entire buffer could not be /// written to this writer. /// /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the /// write operation should be retried if there is nothing else to do. /// /// # Examples /// /// ```no_run /// use std::io::prelude::*; /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { /// let mut buffer = File::create("foo.txt")?; /// /// // Writes some prefix of the byte string, not necessarily all of it. /// buffer.write(b"some bytes")?; /// Ok(()) /// } /// ``` /// /// [`Ok(n)`]: Ok fn write(&mut self, buf: &[u8]) -> Result; /// Flush this output stream, ensuring that all intermediately buffered /// contents reach their destination. /// /// # Errors /// /// It is considered an error if not all bytes could be written due to /// I/O errors or EOF being reached. /// /// # Examples /// /// ```no_run /// use std::io::prelude::*; /// use std::io::BufWriter; /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { /// let mut buffer = BufWriter::new(File::create("foo.txt")?); /// /// buffer.write_all(b"some bytes")?; /// buffer.flush()?; /// Ok(()) /// } /// ``` fn flush(&mut self) -> Result<()>; /// Attempts to write an entire buffer into this writer. /// /// This method will continuously call [`write`] until there is no more data /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is /// returned. This method will not return until the entire buffer has been /// successfully written or such an error occurs. The first error that is /// not of [`ErrorKind::Interrupted`] kind generated from this method will be /// returned. /// /// If the buffer contains no data, this will never call [`write`]. /// /// # Errors /// /// This function will return the first error of /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. /// /// [`write`]: Write::write /// /// # Examples /// /// ```no_run /// use std::io::prelude::*; /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { /// let mut buffer = File::create("foo.txt")?; /// /// buffer.write_all(b"some bytes")?; /// Ok(()) /// } /// ``` fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { while !buf.is_empty() { match self.write(buf) { Ok(0) => { return Err(Error::new( ErrorKind::WriteZero, "failed to write whole buffer", )); } Ok(n) => buf = &buf[n..], Err(ref e) if e.kind() == ErrorKind::Interrupted => {} Err(e) => return Err(e), } } Ok(()) } /// Writes a formatted string into this writer, returning any error /// encountered. /// /// This method is primarily used to interface with the /// [`format_args!()`] macro, but it is rare that this should /// explicitly be called. The [`write!()`] macro should be favored to /// invoke this method instead. /// /// This function internally uses the [`write_all`] method on /// this trait and hence will continuously write data so long as no errors /// are received. This also means that partial writes are not indicated in /// this signature. /// /// [`write_all`]: Write::write_all /// /// # Errors /// /// This function will return any I/O error reported while formatting. /// /// # Examples /// /// ```no_run /// use std::io::prelude::*; /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { /// let mut buffer = File::create("foo.txt")?; /// /// // this call /// write!(buffer, "{:.*}", 2, 1.234567)?; /// // turns into this: /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; /// Ok(()) /// } /// ``` fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { // Create a shim which translates a Write to a fmt::Write and saves // off I/O errors. instead of discarding them struct Adaptor<'a, T: ?Sized + 'a> { inner: &'a mut T, error: Result<()>, } impl fmt::Write for Adaptor<'_, T> { fn write_str(&mut self, s: &str) -> fmt::Result { match self.inner.write_all(s.as_bytes()) { Ok(()) => Ok(()), Err(e) => { self.error = Err(e); Err(fmt::Error) } } } } let mut output = Adaptor { inner: self, error: Ok(()), }; match fmt::write(&mut output, fmt) { Ok(()) => Ok(()), Err(..) => { // check if the error came from the underlying `Write` or not if output.error.is_err() { output.error } else { Err(Error::new(ErrorKind::Other, "formatter error")) } } } } /// Creates a "by reference" adaptor for this instance of `Write`. /// /// The returned adaptor also implements `Write` and will simply borrow this /// current writer. /// /// # Examples /// /// ```no_run /// use std::io::Write; /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { /// let mut buffer = File::create("foo.txt")?; /// /// let reference = buffer.by_ref(); /// /// // we can use reference just like our original buffer /// reference.write_all(b"some bytes")?; /// Ok(()) /// } /// ``` fn by_ref(&mut self) -> &mut Self where Self: Sized, { self } } impl Write for &mut W { #[inline] fn write(&mut self, buf: &[u8]) -> Result { (**self).write(buf) } #[inline] fn flush(&mut self) -> Result<()> { (**self).flush() } #[inline] fn write_all(&mut self, buf: &[u8]) -> Result<()> { (**self).write_all(buf) } #[inline] fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { (**self).write_fmt(fmt) } } /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting /// its data. /// /// Note that writing updates the slice to point to the yet unwritten part. /// The slice will be empty when it has been completely overwritten. impl Write for &mut [u8] { #[inline] fn write(&mut self, data: &[u8]) -> Result { let amt = core::cmp::min(data.len(), self.len()); let (a, b) = core::mem::replace(self, &mut []).split_at_mut(amt); a.copy_from_slice(&data[..amt]); *self = b; Ok(amt) } #[inline] fn write_all(&mut self, data: &[u8]) -> Result<()> { if self.write(data)? == data.len() { Ok(()) } else { Err(Error::new( ErrorKind::WriteZero, "failed to write whole buffer", )) } } #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } } /// Write is implemented for `Vec` by appending to the vector. /// The vector will grow as needed. impl Write for alloc::vec::Vec { #[inline] fn write(&mut self, buf: &[u8]) -> Result { self.extend_from_slice(buf); Ok(buf.len()) } #[inline] fn write_all(&mut self, buf: &[u8]) -> Result<()> { self.extend_from_slice(buf); Ok(()) } #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } } /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. /// /// Readers are defined by one required method, [`read()`]. Each call to [`read()`] /// will attempt to pull bytes from this source into a provided buffer. A /// number of other methods are implemented in terms of [`read()`], giving /// implementors a number of ways to read bytes while only needing to implement /// a single method. /// /// Readers are intended to be composable with one another. Many implementors /// throughout [`std::io`] take and provide types which implement the `Read` /// trait. /// /// Please note that each call to [`read()`] may involve a system call, and /// therefore, using something that implements [`BufRead`], such as /// [`BufReader`], will be more efficient. /// /// # Examples /// /// [`File`]s implement `Read`: /// /// ```no_run /// use std::io; /// use std::io::prelude::*; /// use std::fs::File; /// /// fn main() -> io::Result<()> { /// let mut f = File::open("foo.txt")?; /// let mut buffer = [0; 10]; /// /// // read up to 10 bytes /// f.read(&mut buffer)?; /// /// let mut buffer = Vec::new(); /// // read the whole file /// f.read_to_end(&mut buffer)?; /// /// // read into a String, so that you don't need to do the conversion. /// let mut buffer = String::new(); /// f.read_to_string(&mut buffer)?; /// /// // and more! See the other methods for more details. /// Ok(()) /// } /// ``` /// /// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`: /// /// ```no_run /// # use std::io; /// use std::io::prelude::*; /// /// fn main() -> io::Result<()> { /// let mut b = "This string will be read".as_bytes(); /// let mut buffer = [0; 10]; /// /// // read up to 10 bytes /// b.read(&mut buffer)?; /// /// // etc... it works exactly as a File does! /// Ok(()) /// } /// ``` /// /// [`read()`]: Read::read /// [`&str`]: prim@str /// [`std::io`]: self /// [`File`]: crate::fs::File pub trait Read { /// Pull some bytes from this source into the specified buffer, returning /// how many bytes were read. /// /// This function does not provide any guarantees about whether it blocks /// waiting for data, but if an object needs to block for a read and cannot, /// it will typically signal this via an [`Err`] return value. /// /// If the return value of this method is [`Ok(n)`], then implementations must /// guarantee that `0 <= n <= buf.len()`. A nonzero `n` value indicates /// that the buffer `buf` has been filled in with `n` bytes of data from this /// source. If `n` is `0`, then it can indicate one of two scenarios: /// /// 1. This reader has reached its "end of file" and will likely no longer /// be able to produce bytes. Note that this does not mean that the /// reader will *always* no longer be able to produce bytes. As an example, /// on Linux, this method will call the `recv` syscall for a [`TcpStream`], /// where returning zero indicates the connection was shut down correctly. While /// for [`File`], it is possible to reach the end of file and get zero as result, /// but if more data is appended to the file, future calls to `read` will return /// more data. /// 2. The buffer specified was 0 bytes in length. /// /// It is not an error if the returned value `n` is smaller than the buffer size, /// even when the reader is not at the end of the stream yet. /// This may happen for example because fewer bytes are actually available right now /// (e. g. being close to end-of-file) or because read() was interrupted by a signal. /// /// As this trait is safe to implement, callers cannot rely on `n <= buf.len()` for safety. /// Extra care needs to be taken when `unsafe` functions are used to access the read bytes. /// Callers have to ensure that no unchecked out-of-bounds accesses are possible even if /// `n > buf.len()`. /// /// No guarantees are provided about the contents of `buf` when this /// function is called, implementations cannot rely on any property of the /// contents of `buf` being true. It is recommended that *implementations* /// only write data to `buf` instead of reading its contents. /// /// Correspondingly, however, *callers* of this method must not assume any guarantees /// about how the implementation uses `buf`. The trait is safe to implement, /// so it is possible that the code that's supposed to write to the buffer might also read /// from it. It is your responsibility to make sure that `buf` is initialized /// before calling `read`. Calling `read` with an uninitialized `buf` (of the kind one /// obtains via [`MaybeUninit`]) is not safe, and can lead to undefined behavior. /// /// [`MaybeUninit`]: crate::mem::MaybeUninit /// /// # Errors /// /// If this function encounters any form of I/O or other error, an error /// variant will be returned. If an error is returned then it must be /// guaranteed that no bytes were read. /// /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read /// operation should be retried if there is nothing else to do. /// /// # Examples /// /// [`File`]s implement `Read`: /// /// [`Ok(n)`]: Ok /// [`File`]: crate::fs::File /// [`TcpStream`]: crate::net::TcpStream /// /// ```no_run /// use std::io; /// use std::io::prelude::*; /// use std::fs::File; /// /// fn main() -> io::Result<()> { /// let mut f = File::open("foo.txt")?; /// let mut buffer = [0; 10]; /// /// // read up to 10 bytes /// let n = f.read(&mut buffer[..])?; /// /// println!("The bytes: {:?}", &buffer[..n]); /// Ok(()) /// } /// ``` fn read(&mut self, buf: &mut [u8]) -> Result; /// Read the exact number of bytes required to fill `buf`. /// /// This function reads as many bytes as necessary to completely fill the /// specified buffer `buf`. /// /// No guarantees are provided about the contents of `buf` when this /// function is called, implementations cannot rely on any property of the /// contents of `buf` being true. It is recommended that implementations /// only write data to `buf` instead of reading its contents. The /// documentation on [`read`] has a more detailed explanation on this /// subject. /// /// # Errors /// /// If this function encounters an error of the kind /// [`ErrorKind::Interrupted`] then the error is ignored and the operation /// will continue. /// /// If this function encounters an "end of file" before completely filling /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. /// The contents of `buf` are unspecified in this case. /// /// If any other read error is encountered then this function immediately /// returns. The contents of `buf` are unspecified in this case. /// /// If this function returns an error, it is unspecified how many bytes it /// has read, but it will never read more than would be necessary to /// completely fill the buffer. /// /// # Examples /// /// [`File`]s implement `Read`: /// /// [`read`]: Read::read /// [`File`]: crate::fs::File /// /// ```no_run /// use std::io; /// use std::io::prelude::*; /// use std::fs::File; /// /// fn main() -> io::Result<()> { /// let mut f = File::open("foo.txt")?; /// let mut buffer = [0; 10]; /// /// // read exactly 10 bytes /// f.read_exact(&mut buffer)?; /// Ok(()) /// } /// ``` fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { default_read_exact(self, buf) } /// Creates a "by reference" adaptor for this instance of `Read`. /// /// The returned adapter also implements `Read` and will simply borrow this /// current reader. /// /// # Examples /// /// [`File`]s implement `Read`: /// /// [`File`]: crate::fs::File /// /// ```no_run /// use std::io; /// use std::io::Read; /// use std::fs::File; /// /// fn main() -> io::Result<()> { /// let mut f = File::open("foo.txt")?; /// let mut buffer = Vec::new(); /// let mut other_buffer = Vec::new(); /// /// { /// let reference = f.by_ref(); /// /// // read at most 5 bytes /// reference.take(5).read_to_end(&mut buffer)?; /// /// } // drop our &mut reference so we can use f again /// /// // original file still usable, read the rest /// f.read_to_end(&mut other_buffer)?; /// Ok(()) /// } /// ``` fn by_ref(&mut self) -> &mut Self where Self: Sized, { self } } fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { match this.read(buf) { Ok(0) => break, Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; } Err(ref e) if e.kind() == ErrorKind::Interrupted => {} Err(e) => return Err(e), } } if !buf.is_empty() { Err(Error::new( ErrorKind::UnexpectedEof, "failed to fill whole buffer", )) } else { Ok(()) } } impl Read for &mut R { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result { (**self).read(buf) } #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { (**self).read_exact(buf) } } impl Read for &[u8] { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result { let amt = core::cmp::min(buf.len(), self.len()); let (a, b) = self.split_at(amt); // First check if the amount of bytes we want to read is small: // `copy_from_slice` will generally expand to a call to `memcpy`, and // for a single byte the overhead is significant. if amt == 1 { buf[0] = a[0]; } else { buf[..amt].copy_from_slice(a); } *self = b; Ok(amt) } #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { if buf.len() > self.len() { return Err(Error::new( ErrorKind::UnexpectedEof, "failed to fill whole buffer", )); } let (a, b) = self.split_at(buf.len()); // First check if the amount of bytes we want to read is small: // `copy_from_slice` will generally expand to a call to `memcpy`, and // for a single byte the overhead is significant. if buf.len() == 1 { buf[0] = a[0]; } else { buf.copy_from_slice(a); } *self = b; Ok(()) } } borsh-1.5.7/src/schema/container_ext/max_size.rs000064400000000000000000000241671046102023000200430ustar 00000000000000use super::{BorshSchemaContainer, Declaration, Definition, Fields}; use crate::__private::maybestd::{string::ToString, vec::Vec}; use core::num::NonZeroUsize; /// NonZeroUsize of value one. // TODO: Replace usage by NonZeroUsize::MIN once MSRV is 1.70+. const ONE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(1) }; impl BorshSchemaContainer { /// Returns the largest possible size of a serialised object based solely on its type. /// /// Even when if returned upper bound is correct, the theoretical value may be /// *much* larger than any practical length. For example, maximum encoded /// length of `String` is 4 GiB while in practice one may encounter strings of /// at most dozen of characters. /// /// # Example /// /// ``` /// use borsh::schema::BorshSchemaContainer; /// /// let schema = BorshSchemaContainer::for_type::<()>(); /// assert_eq!(Ok(0), schema.max_serialized_size()); /// /// let schema = BorshSchemaContainer::for_type::(); /// assert_eq!(Ok(8), schema.max_serialized_size()); /// /// // 4 bytes of length and u32::MAX for the longest possible string. /// let schema = BorshSchemaContainer::for_type::(); /// assert_eq!(Ok(4 + 4294967295), schema.max_serialized_size()); /// /// let schema = BorshSchemaContainer::for_type::>(); /// assert_eq!(Err(borsh::schema::SchemaMaxSerializedSizeError::Overflow), /// schema.max_serialized_size()); /// ``` pub fn max_serialized_size(&self) -> Result { let mut stack = Vec::new(); max_serialized_size_impl(ONE, self.declaration(), self, &mut stack) } } /// Possible error when calculating theoretical maximum size of encoded type `T`. #[derive(Clone, PartialEq, Eq, Debug)] pub enum Error { /// The theoretical maximum size of the encoded value overflows `usize`. /// /// This may happen for nested dynamically-sized types such as /// `Vec>` whose maximum size is `4 + u32::MAX * (4 + u32::MAX)`. Overflow, /// The type is recursive and thus theoretical maximum size is infinite. /// /// Simple type in which this triggers is `struct Rec(Option>)`. Recursive, /// Some of the declared types were lacking definition making it impossible /// to calculate the size. MissingDefinition(Declaration), } /// Implementation of [`BorshSchema::max_serialized_size`]. fn max_serialized_size_impl<'a>( count: NonZeroUsize, declaration: &'a str, schema: &'a BorshSchemaContainer, stack: &mut Vec<&'a str>, ) -> Result { use core::convert::TryFrom; /// Maximum number of elements in a vector or length of a string which can /// be serialised. const MAX_LEN: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(u32::MAX as usize) }; fn add(x: usize, y: usize) -> Result { x.checked_add(y).ok_or(Error::Overflow) } fn mul(x: NonZeroUsize, y: usize) -> Result { x.get().checked_mul(y).ok_or(Error::Overflow) } /// Calculates max serialised size of a tuple with given members. fn tuple<'a>( count: NonZeroUsize, elements: impl core::iter::IntoIterator, schema: &'a BorshSchemaContainer, stack: &mut Vec<&'a str>, ) -> Result { let mut sum: usize = 0; for el in elements { sum = add(sum, max_serialized_size_impl(ONE, el, schema, stack)?)?; } mul(count, sum) } if stack.iter().any(|dec| *dec == declaration) { return Err(Error::Recursive); } stack.push(declaration); let res = match schema.get_definition(declaration).ok_or(declaration) { Ok(Definition::Primitive(size)) => match size { 0 => Ok(0), size => { let count_sizes = usize::from(*size).checked_mul(count.get()); count_sizes.ok_or(Error::Overflow) } }, Ok(Definition::Sequence { length_width, length_range, elements, }) => { // Assume sequence has the maximum number of elements. let max_len = *length_range.end(); let sz = match usize::try_from(max_len).map(NonZeroUsize::new) { Ok(Some(max_len)) => max_serialized_size_impl(max_len, elements, schema, stack)?, Ok(None) => 0, Err(_) if is_zero_size_impl(elements, schema, stack)? => 0, Err(_) => return Err(Error::Overflow), }; mul(count, add(sz, usize::from(*length_width))?) } Ok(Definition::Enum { tag_width, variants, }) => { let mut max = 0; for (_, _, variant) in variants { let sz = max_serialized_size_impl(ONE, variant, schema, stack)?; max = max.max(sz); } add(max, usize::from(*tag_width)) } // Tuples and structs sum sizes of all the members. Ok(Definition::Tuple { elements }) => tuple(count, elements, schema, stack), Ok(Definition::Struct { fields }) => match fields { Fields::NamedFields(fields) => { tuple(count, fields.iter().map(|(_, field)| field), schema, stack) } Fields::UnnamedFields(fields) => tuple(count, fields, schema, stack), Fields::Empty => Ok(0), }, Err(declaration) => Err(Error::MissingDefinition(declaration.to_string())), }?; stack.pop(); Ok(res) } /// Checks whether given declaration schema serialises to an empty string. /// /// This is used by [`BorshSchemaContainer::max_serialized_size`] to handle weird types /// such as `[[[(); u32::MAX]; u32::MAX]; u32::MAX]` which serialises to an /// empty string even though its number of elements overflows `usize`. /// /// Error value means that the method has been called recursively. /// A recursive type either has no exit, so it cannot be instantiated /// or it uses `Definiotion::Enum` or `Definition::Sequence` to exit from recursion /// which make it non-zero size pub(super) fn is_zero_size( declaration: &Declaration, schema: &BorshSchemaContainer, ) -> Result { let mut stack = Vec::new(); is_zero_size_impl(declaration, schema, &mut stack) } #[derive(Debug, PartialEq, Eq)] pub(super) enum ZeroSizeError { Recursive, MissingDefinition(Declaration), } impl From for Error { fn from(value: ZeroSizeError) -> Self { match value { ZeroSizeError::Recursive => Self::Recursive, ZeroSizeError::MissingDefinition(declaration) => Self::MissingDefinition(declaration), } } } fn is_zero_size_impl<'a>( declaration: &'a str, schema: &'a BorshSchemaContainer, stack: &mut Vec<&'a str>, ) -> Result { fn all<'a, T: 'a>( iter: impl Iterator, f_key: impl Fn(&T) -> &'a Declaration, schema: &'a BorshSchemaContainer, stack: &mut Vec<&'a str>, ) -> Result { for element in iter { let declaration = f_key(&element); if !is_zero_size_impl(declaration.as_str(), schema, stack)? { return Ok(false); } } Ok(true) } if stack.iter().any(|dec| *dec == declaration) { return Err(ZeroSizeError::Recursive); } stack.push(declaration); let res = match schema.get_definition(declaration).ok_or(declaration) { Ok(Definition::Primitive(size)) => *size == 0, Ok(Definition::Sequence { length_width, length_range, elements, }) => { if *length_width == 0 { // zero-sized array if length_range.clone().count() == 1 && *length_range.start() == 0 { return Ok(true); } if is_zero_size_impl(elements.as_str(), schema, stack)? { return Ok(true); } } false } Ok(Definition::Tuple { elements }) => all(elements.iter(), |key| *key, schema, stack)?, Ok(Definition::Enum { tag_width: 0, variants, }) => all( variants.iter(), |(_variant_discrim, _variant_name, declaration)| declaration, schema, stack, )?, Ok(Definition::Enum { .. }) => false, Ok(Definition::Struct { fields }) => match fields { Fields::NamedFields(fields) => all( fields.iter(), |(_field_name, declaration)| declaration, schema, stack, )?, Fields::UnnamedFields(fields) => { all(fields.iter(), |declaration| declaration, schema, stack)? } Fields::Empty => true, }, Err(declaration) => { return Err(ZeroSizeError::MissingDefinition(declaration.into())); } }; stack.pop(); Ok(res) } #[cfg(test)] mod tests { use super::*; // this is not integration test module, so can use __private for ease of imports; // it cannot be made integration, as it tests `is_zero_size` function, chosen to be non-pub use crate::__private::maybestd::{boxed::Box, string::ToString}; #[test] fn test_is_zero_size_recursive_check_bypassed() { use crate as borsh; #[derive(::borsh_derive::BorshSchema)] struct RecursiveExitSequence(Vec); let schema = BorshSchemaContainer::for_type::(); assert_eq!(Ok(false), is_zero_size(schema.declaration(), &schema)); } #[test] fn test_is_zero_size_recursive_check_err() { use crate as borsh; #[derive(::borsh_derive::BorshSchema)] struct RecursiveNoExitStructUnnamed(Box); let schema = BorshSchemaContainer::for_type::(); assert_eq!( Err(ZeroSizeError::Recursive), is_zero_size(schema.declaration(), &schema) ); } } borsh-1.5.7/src/schema/container_ext/validate.rs000064400000000000000000000141341046102023000200060ustar 00000000000000use super::{is_zero_size, ZeroSizeError}; use super::{BorshSchemaContainer, Declaration, Definition, Fields}; use crate::__private::maybestd::{string::ToString, vec::Vec}; impl BorshSchemaContainer { /// Validates container for violation of any well-known rules with /// respect to `borsh` serialization. /// /// # Example /// /// ``` /// use borsh::schema::BorshSchemaContainer; /// /// let schema = BorshSchemaContainer::for_type::(); /// assert_eq!(Ok(()), schema.validate()); /// ``` pub fn validate(&self) -> core::result::Result<(), Error> { let mut stack = Vec::new(); validate_impl(self.declaration(), self, &mut stack) } } /// Possible error when validating a [`BorshSchemaContainer`], generated for some type `T`, /// for violation of any well-known rules with respect to `borsh` serialization. #[derive(Clone, PartialEq, Eq, Debug)] pub enum Error { /// sequences of zero-sized types of dynamic length are forbidden by definition /// see and related ones ZSTSequence(Declaration), /// Declared tag width is too large. Tags may be at most eight bytes. TagTooWide(Declaration), /// Declared tag width is too small. Tags must be large enough to represent /// possible length of sequence. TagTooNarrow(Declaration), /// only 0, 1, 2, 4 and 8 bytes long sequences' `length_width` are allowed TagNotPowerOfTwo(Declaration), /// Some of the declared types were lacking definition, which is considered /// a container's validation error MissingDefinition(Declaration), /// A Sequence defined with an empty length range. EmptyLengthRange(Declaration), } fn check_length_width(declaration: &Declaration, width: u8, max: u64) -> Result<(), Error> { match width { 0 => Ok(()), 3 | 5 | 6 | 7 => Err(Error::TagNotPowerOfTwo(declaration.clone())), 1..=7 if max < 1 << (width * 8) => Ok(()), 1..=7 => Err(Error::TagTooNarrow(declaration.clone())), 8 => Ok(()), _ => Err(Error::TagTooWide(declaration.clone())), } } const U64_LEN: u8 = 8; fn validate_impl<'a>( declaration: &'a Declaration, schema: &'a BorshSchemaContainer, stack: &mut Vec<&'a Declaration>, ) -> core::result::Result<(), Error> { let definition = match schema.get_definition(declaration) { Some(definition) => definition, None => { return Err(Error::MissingDefinition(declaration.to_string())); } }; if stack.iter().any(|dec| *dec == declaration) { return Ok(()); } stack.push(declaration); match definition { Definition::Primitive(_size) => {} // arrays branch Definition::Sequence { length_width, length_range, elements, } if *length_width == Definition::ARRAY_LENGTH_WIDTH && length_range.clone().count() == 1 => { validate_impl(elements, schema, stack)? } Definition::Sequence { length_width, length_range, elements, } => { if length_range.is_empty() { return Err(Error::EmptyLengthRange(declaration.clone())); } check_length_width(declaration, *length_width, *length_range.end())?; match is_zero_size(elements, schema) { Ok(true) => return Err(Error::ZSTSequence(declaration.clone())), Ok(false) => (), // a recursive type either has no exit, so it cannot be instantiated // or it uses `Definiotion::Enum` or `Definition::Sequence` to exit from recursion // which make it non-zero size Err(ZeroSizeError::Recursive) => (), Err(ZeroSizeError::MissingDefinition(declaration)) => { return Err(Error::MissingDefinition(declaration)); } } validate_impl(elements, schema, stack)?; } Definition::Enum { tag_width, variants, } => { if *tag_width > U64_LEN { return Err(Error::TagTooWide(declaration.to_string())); } for (_, _, variant) in variants { validate_impl(variant, schema, stack)?; } } Definition::Tuple { elements } => { for element_type in elements { validate_impl(element_type, schema, stack)?; } } Definition::Struct { fields } => match fields { Fields::NamedFields(fields) => { for (_field_name, field_type) in fields { validate_impl(field_type, schema, stack)?; } } Fields::UnnamedFields(fields) => { for field_type in fields { validate_impl(field_type, schema, stack)?; } } Fields::Empty => {} }, }; stack.pop(); Ok(()) } #[cfg(test)] mod tests { use super::{check_length_width, Error}; use crate::__private::maybestd::string::ToString; #[test] fn test_check_tag_width() { let narrow_err: Result<(), Error> = Err(Error::TagTooNarrow("test".to_string())); let power_of_two_err: Result<(), Error> = Err(Error::TagNotPowerOfTwo("test".to_string())); for (width, max, want) in [ (0, u64::MAX, Ok(())), (1, u8::MAX as u64, Ok(())), (1, u8::MAX as u64 + 1, narrow_err.clone()), (2, u16::MAX as u64, Ok(())), (2, u16::MAX as u64 + 1, narrow_err.clone()), (3, 100, power_of_two_err.clone()), (4, u32::MAX as u64, Ok(())), (4, u32::MAX as u64 + 1, narrow_err), (5, 100, power_of_two_err.clone()), (6, 100, power_of_two_err.clone()), (7, 100, power_of_two_err), (8, u64::MAX, Ok(())), ] { assert_eq!( want, check_length_width(&"test".into(), width, max), "width={width}; max={max}" ); } } } borsh-1.5.7/src/schema/container_ext.rs000064400000000000000000000004011046102023000162050ustar 00000000000000use super::{BorshSchemaContainer, Declaration, Definition, Fields}; pub use max_size::Error as SchemaMaxSerializedSizeError; use max_size::{is_zero_size, ZeroSizeError}; pub use validate::Error as SchemaContainerValidateError; mod max_size; mod validate; borsh-1.5.7/src/schema.rs000064400000000000000000000763241046102023000133640ustar 00000000000000//! //! Since Borsh is not a self-descriptive format we have a way to describe types serialized with Borsh so that //! we can deserialize serialized blobs without having Rust types available. Additionally, this can be used to //! serialize content provided in a different format, e.g. JSON object `{"user": "alice", "message": "Message"}` //! can be serialized by JS code into Borsh format such that it can be deserialized into `struct UserMessage {user: String, message: String}` //! on Rust side. //! //! The important components are: `BorshSchema` trait, `Definition` and `Declaration` types, and `BorshSchemaContainer` struct. //! * `BorshSchema` trait allows any type that implements it to be self-descriptive, i.e. generate it's own schema; //! * `Declaration` is used to describe the type identifier, e.g. `HashMap`; //! * `Definition` is used to describe the structure of the type; //! * `BorshSchemaContainer` is used to store all declarations and definitions that are needed to work with a single type. #![allow(dead_code)] // Unclear why rust check complains on fields of `Definition` variants. use crate as borsh; // For `#[derive(BorshSerialize, BorshDeserialize)]`. use crate::__private::maybestd::{ borrow, boxed::Box, collections::{btree_map::Entry, BTreeMap, BTreeSet, LinkedList, VecDeque}, format, string::{String, ToString}, vec, vec::Vec, }; use crate::io::{Read, Result as IOResult, Write}; use crate::{BorshDeserialize, BorshSchema as BorshSchemaMacro, BorshSerialize}; use core::borrow::Borrow; use core::cmp::Ord; use core::marker::PhantomData; mod container_ext; pub use container_ext::{SchemaContainerValidateError, SchemaMaxSerializedSizeError}; /// The type that we use to represent the declaration of the Borsh type. pub type Declaration = String; /// The type that we use for the name of the variant. pub type VariantName = String; /// The type that we use for value of discriminant. pub type DiscriminantValue = i64; /// The name of the field in the struct (can be used to convert JSON to Borsh using the schema). pub type FieldName = String; /// The type that we use to represent the definition of the Borsh type. /// /// Description of data encoding on the wire. #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)] pub enum Definition { /// A fixed-size type, which is considered undivisible Primitive(u8), /// A sequence of homogeneous elements. /// /// If `length_width` is non-zero, the sequence is tagged, i.e. prefixed by /// the number of elements in the sequence. In that case, the length is /// encoded as a `length_width`-byte wide little-endian unsigned integer. /// /// If `length_width` is zero, the sequence is untagged. In that case, if /// `length_range` contains a single number, the sequence is fixed-sized /// with the range determining number of elements. Otherwise, knowledge of /// the type is necessary to be able to decode the number of elements. /// /// Prototypical examples of the use of this definitions are: /// * `[T; N]` → `length_width: 0, length_range: N..=N, elements: "T"` and /// * `Vec` → `length_width: 4, length_range: 0..=u32::MAX, /// elements: "T"`. /// /// With `length_width` and `length_range` other custom encoding formats can /// also be expressed. For example: /// * `BoundedVec` → `length_width: 4, length_range: LO..=HI`; /// * `PascalString` → `length_width: 1, length_range: 0..=255`; /// * `Ipv4Packet` → `length_width: 0, length_range: 20..=65536` or /// * `VarInt` → `length_width: 0, length_range: 1..=5`. Sequence { /// How many bytes does the length tag occupy. /// /// Zero if this is fixed-length array or the length must be determined /// by means not specified in the schema. The schema is invalid if the /// value is greater than eight. length_width: u8, /// Bounds on the possible lengths of the sequence. /// /// Note: The schema is invalid if the range is empty or `length_width` /// is non-zero and either bound of the range cannot be represented as /// `length_width`-byte-wide unsigned integer. length_range: core::ops::RangeInclusive, /// Type of each element of the sequence. elements: Declaration, }, /// A fixed-size tuple with the length known at the compile time and the elements of different /// types. Tuple { elements: Vec }, /// A possibly tagged union, a.k.a enum. /// /// Tagged unions are prefixed by a tag identifying encoded variant followed /// by encoding of that variant. The tag is `tag_width`-byte wide /// little-endian number. /// /// Untagged unions don’t have a separate tag which means that knowledge of /// the type is necessary to fully analyse the binary. Variants may still /// be used to list possible values or determine the longest possible /// encoding. Enum { /// Width in bytes of the discriminant tag. /// /// Zero indicates this is an untagged union. In standard borsh /// encoding this is one. Custom encoding formats may use larger width /// if they need to encode more than 256 variants. The schema is /// invalid if the value is greater than eight. tag_width: u8, /// Possible variants of the enumeration. /// `VariantName` is metadata, not present in a type's serialized representation. variants: Vec<(DiscriminantValue, VariantName, Declaration)>, }, /// A structure, structurally similar to a tuple. Struct { fields: Fields }, } impl Definition { /// Array length isn't present in payload, it's determined by type of data /// serialized. pub const ARRAY_LENGTH_WIDTH: u8 = 0; /// Convenience constant representing the length width of a standard borsh /// sequence. /// /// Can be used for `Definition::Sequence::length_width`. pub const DEFAULT_LENGTH_WIDTH: u8 = 4; /// Convenience constant representing the length range of a standard borsh /// sequence. /// /// It equals `0..=u32::MAX`. Can be used with /// `Definition::Sequence::length_range`. pub const DEFAULT_LENGTH_RANGE: core::ops::RangeInclusive = 0..=(u32::MAX as u64); } /// The collection representing the fields of a struct. #[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize, BorshSchemaMacro)] pub enum Fields { /// The struct with named fields, structurally identical to a tuple. /// `FieldName` is metadata, not present in a type's serialized representation. NamedFields(Vec<(FieldName, Declaration)>), /// The struct with unnamed fields, structurally identical to a tuple. UnnamedFields(Vec), /// The struct with no fields, structurally identical to an empty tuple. Empty, } /// All schema information needed to deserialize a single type. #[derive(Clone, PartialEq, Eq, Debug)] pub struct BorshSchemaContainer { /// Declaration of the type. declaration: Declaration, /// All definitions needed to deserialize the given type. definitions: BTreeMap, } impl BorshSchemaContainer { pub fn new(declaration: Declaration, definitions: BTreeMap) -> Self { Self { declaration, definitions, } } /// generate [BorshSchemaContainer] for type `T` pub fn for_type() -> Self { let mut definitions = Default::default(); T::add_definitions_recursively(&mut definitions); Self::new(T::declaration(), definitions) } pub fn declaration(&self) -> &Declaration { &self.declaration } pub fn definitions(&self) -> impl Iterator { self.definitions.iter() } pub fn get_definition(&self, declaration: &Q) -> Option<&Definition> where Declaration: Borrow, Q: Ord + ?Sized, { self.definitions.get(declaration) } pub fn get_mut_definition(&mut self, declaration: &Q) -> Option<&mut Definition> where Declaration: Borrow, Q: Ord + ?Sized, { self.definitions.get_mut(declaration) } pub fn insert_definition( &mut self, declaration: Declaration, definition: Definition, ) -> Option { self.definitions.insert(declaration, definition) } pub fn remove_definition(&mut self, declaration: &Q) -> Option where Declaration: Borrow, Q: Ord + ?Sized, { self.definitions.remove(declaration) } } impl BorshSerialize for BorshSchemaContainer where Declaration: BorshSerialize, BTreeMap: BorshSerialize, { fn serialize(&self, writer: &mut W) -> IOResult<()> { let declaration = self.declaration(); let definitions: BTreeMap<&Declaration, &Definition> = self.definitions().collect(); BorshSerialize::serialize(declaration, writer)?; BorshSerialize::serialize(&definitions, writer)?; Ok(()) } } impl BorshDeserialize for BorshSchemaContainer where Declaration: BorshDeserialize, BTreeMap: BorshDeserialize, { fn deserialize_reader(reader: &mut R) -> IOResult { let declaration: Declaration = BorshDeserialize::deserialize_reader(reader)?; let definitions: BTreeMap = BorshDeserialize::deserialize_reader(reader)?; Ok(Self::new(declaration, definitions)) } } /// Helper method to add a single type definition to the map. pub fn add_definition( declaration: Declaration, definition: Definition, definitions: &mut BTreeMap, ) { match definitions.entry(declaration) { Entry::Occupied(occ) => { let existing_def = occ.get(); assert_eq!( existing_def, &definition, "Redefining type schema for {}. Types with the same names are not supported.", occ.key() ); } Entry::Vacant(vac) => { vac.insert(definition); } } } /// The declaration and the definition of the type that can be used to (de)serialize Borsh without /// the Rust type that produced it. pub trait BorshSchema { /// Recursively, using DFS, add type definitions required for this type. /// Type definition partially explains how to serialize/deserialize a type. fn add_definitions_recursively(definitions: &mut BTreeMap); /// Get the name of the type without brackets. fn declaration() -> Declaration; } impl BorshSchema for BorshSchemaContainer where Declaration: BorshSchema, BTreeMap: BorshSchema, { fn declaration() -> Declaration { "BorshSchemaContainer".to_string() } fn add_definitions_recursively(definitions: &mut BTreeMap) { let fields = Fields::NamedFields(<[_]>::into_vec(Box::new([ ( "declaration".to_string(), ::declaration(), ), ( "definitions".to_string(), as BorshSchema>::declaration(), ), ]))); let definition = Definition::Struct { fields }; add_definition( ::declaration(), definition, definitions, ); ::add_definitions_recursively(definitions); as BorshSchema>::add_definitions_recursively( definitions, ); } } impl BorshSchema for Box where T: BorshSchema + ?Sized, { fn add_definitions_recursively(definitions: &mut BTreeMap) { T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { T::declaration() } } impl BorshSchema for core::cell::Cell where T: BorshSchema + Copy, { fn add_definitions_recursively(definitions: &mut BTreeMap) { T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { T::declaration() } } impl BorshSchema for core::cell::RefCell where T: BorshSchema + Sized, { fn add_definitions_recursively(definitions: &mut BTreeMap) { T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { T::declaration() } } /// Module is available if borsh is built with `features = ["rc"]`. #[cfg(feature = "rc")] pub mod rc { //! //! Module defines [BorshSchema] implementation for //! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc). use crate::BorshSchema; use super::{Declaration, Definition}; use crate::__private::maybestd::collections::BTreeMap; use crate::__private::maybestd::{rc::Rc, sync::Arc}; impl BorshSchema for Rc where T: BorshSchema + ?Sized, { fn add_definitions_recursively(definitions: &mut BTreeMap) { T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { T::declaration() } } impl BorshSchema for Arc where T: BorshSchema + ?Sized, { fn add_definitions_recursively(definitions: &mut BTreeMap) { T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { T::declaration() } } } impl BorshSchema for borrow::Cow<'_, T> where T: borrow::ToOwned + ?Sized, T::Owned: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { ::add_definitions_recursively(definitions); } fn declaration() -> Declaration { ::declaration() } } macro_rules! impl_for_renamed_primitives { ($($ty: ty : $name: ident => $size: expr);+) => { $( impl BorshSchema for $ty { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Primitive($size); add_definition(Self::declaration(), definition, definitions); } #[inline] fn declaration() -> Declaration { stringify!($name).into() } } )+ }; ($($ty: ty : $name: expr, $size: expr);+) => { $( impl BorshSchema for $ty { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Primitive($size); add_definition(Self::declaration(), definition, definitions); } #[inline] fn declaration() -> Declaration { $name.into() } } )+ }; } macro_rules! impl_for_primitives { ($($ty: ident => $size: expr);+) => { impl_for_renamed_primitives!{$($ty : $ty => $size);+} }; } impl_for_primitives!(bool => 1; f32 => 4; f64 => 8; i8 => 1; i16 => 2; i32 => 4; i64 => 8; i128 => 16); impl_for_primitives!(u8 => 1; u16 => 2; u32 => 4; u64 => 8; u128 => 16); impl_for_renamed_primitives!(isize: i64 => 8); impl_for_renamed_primitives!(usize: u64 => 8); impl_for_renamed_primitives!(core::num::NonZeroI8: NonZeroI8 => 1); impl_for_renamed_primitives!(core::num::NonZeroI16: NonZeroI16 => 2); impl_for_renamed_primitives!(core::num::NonZeroI32: NonZeroI32 => 4); impl_for_renamed_primitives!(core::num::NonZeroI64: NonZeroI64 => 8); impl_for_renamed_primitives!(core::num::NonZeroI128: NonZeroI128 => 16); impl_for_renamed_primitives!(core::num::NonZeroU8: NonZeroU8 => 1); impl_for_renamed_primitives!(core::num::NonZeroU16: NonZeroU16 => 2); impl_for_renamed_primitives!(core::num::NonZeroU32: NonZeroU32 => 4); impl_for_renamed_primitives!(core::num::NonZeroU64: NonZeroU64 => 8); impl_for_renamed_primitives!(core::num::NonZeroU128: NonZeroU128 => 16); // see 12 lines above impl_for_renamed_primitives!(core::num::NonZeroUsize: NonZeroUsize => 8); impl_for_renamed_primitives!((): "()", 0); impl BorshSchema for String { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { str::add_definitions_recursively(definitions); } #[inline] fn declaration() -> Declaration { str::declaration() } } impl BorshSchema for str { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: u8::declaration(), }; add_definition(Self::declaration(), definition, definitions); u8::add_definitions_recursively(definitions); } #[inline] fn declaration() -> Declaration { "String".into() } } /// Module is available if borsh is built with `features = ["ascii"]`. #[cfg(feature = "ascii")] pub mod ascii { //! //! Module defines [BorshSchema] implementation for //! some types from [ascii](::ascii) crate. use crate::BorshSchema; use super::{add_definition, Declaration, Definition}; use crate::__private::maybestd::collections::BTreeMap; impl BorshSchema for ascii::AsciiString { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { ascii::AsciiStr::add_definitions_recursively(definitions); } #[inline] fn declaration() -> Declaration { ascii::AsciiStr::declaration() } } impl BorshSchema for ascii::AsciiStr { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: ascii::AsciiChar::declaration(), }; add_definition(Self::declaration(), definition, definitions); ascii::AsciiChar::add_definitions_recursively(definitions); } #[inline] fn declaration() -> Declaration { "AsciiString".into() } } impl BorshSchema for ascii::AsciiChar { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { add_definition(Self::declaration(), Definition::Primitive(1), definitions); } #[inline] fn declaration() -> Declaration { "AsciiChar".into() } } } impl BorshSchema for core::ops::RangeFull { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { let fields = Fields::Empty; let def = Definition::Struct { fields }; add_definition(Self::declaration(), def, definitions); } #[inline] fn declaration() -> Declaration { "RangeFull".into() } } macro_rules! impl_for_range { ($type:ident, $($name:ident),*) => { impl BorshSchema for core::ops::$type { fn add_definitions_recursively(definitions: &mut BTreeMap) { let decl = T::declaration(); let fields = Fields::NamedFields(vec![$( (FieldName::from(stringify!($name)), decl.clone()) ),*]); let def = Definition::Struct { fields }; add_definition(Self::declaration(), def, definitions); T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!("{}<{}>", stringify!($type), T::declaration()) } } }; } impl_for_range!(Range, start, end); impl_for_range!(RangeInclusive, start, end); impl_for_range!(RangeFrom, start); impl_for_range!(RangeTo, end); impl_for_range!(RangeToInclusive, end); impl BorshSchema for [T; N] where T: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { use core::convert::TryFrom; let length = u64::try_from(N).unwrap(); let definition = Definition::Sequence { length_width: Definition::ARRAY_LENGTH_WIDTH, length_range: length..=length, elements: T::declaration(), }; add_definition(Self::declaration(), definition, definitions); T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"[{}; {}]"#, T::declaration(), N) } } impl BorshSchema for Option where T: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Enum { tag_width: 1, variants: vec![ (0u8 as i64, "None".to_string(), <()>::declaration()), (1u8 as i64, "Some".to_string(), T::declaration()), ], }; add_definition(Self::declaration(), definition, definitions); T::add_definitions_recursively(definitions); <()>::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"Option<{}>"#, T::declaration()) } } impl BorshSchema for core::result::Result where T: BorshSchema, E: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Enum { tag_width: 1, variants: vec![ (1u8 as i64, "Ok".to_string(), T::declaration()), (0u8 as i64, "Err".to_string(), E::declaration()), ], }; add_definition(Self::declaration(), definition, definitions); T::add_definitions_recursively(definitions); E::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"Result<{}, {}>"#, T::declaration(), E::declaration()) } } macro_rules! impl_for_vec_like_collection { ($type: ident) => { impl BorshSchema for $type where T: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: T::declaration(), }; add_definition(Self::declaration(), definition, definitions); T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"{}<{}>"#, stringify!($type), T::declaration()) } } }; } impl_for_vec_like_collection!(Vec); impl_for_vec_like_collection!(VecDeque); impl_for_vec_like_collection!(LinkedList); impl BorshSchema for [T] where T: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: T::declaration(), }; add_definition(Self::declaration(), definition, definitions); T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"Vec<{}>"#, T::declaration()) } } /// Module is available if borsh is built with `features = ["std"]` or `features = ["hashbrown"]`. /// /// Module defines [BorshSchema] implementation for /// [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet). #[cfg(hash_collections)] pub mod hashes { use crate::BorshSchema; use super::{add_definition, Declaration, Definition}; use crate::__private::maybestd::collections::BTreeMap; use crate::__private::maybestd::collections::{HashMap, HashSet}; #[cfg(not(feature = "std"))] use alloc::format; // S is not serialized, so we ignore it in schema too // forcing S to be BorshSchema forces to define Definition // which must be empty, but if not - it will fail // so better to ignore it impl BorshSchema for HashMap where K: BorshSchema, V: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: <(K, V)>::declaration(), }; add_definition(Self::declaration(), definition, definitions); <(K, V)>::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"HashMap<{}, {}>"#, K::declaration(), V::declaration()) } } impl BorshSchema for HashSet where T: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: ::declaration(), }; add_definition(Self::declaration(), definition, definitions); ::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"HashSet<{}>"#, T::declaration()) } } } impl BorshSchema for BTreeMap where K: BorshSchema, V: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: <(K, V)>::declaration(), }; add_definition(Self::declaration(), definition, definitions); <(K, V)>::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"BTreeMap<{}, {}>"#, K::declaration(), V::declaration()) } } impl BorshSchema for BTreeSet where T: BorshSchema, { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: ::declaration(), }; add_definition(Self::declaration(), definition, definitions); ::add_definitions_recursively(definitions); } fn declaration() -> Declaration { format!(r#"BTreeSet<{}>"#, T::declaration()) } } // Because it's a zero-sized marker, its type parameter doesn't need to be // included in the schema and so it's not bound to `BorshSchema` impl BorshSchema for PhantomData { fn add_definitions_recursively(definitions: &mut BTreeMap) { <()>::add_definitions_recursively(definitions); } fn declaration() -> Declaration { <()>::declaration() } } macro_rules! impl_tuple { ($($name:ident),+) => { impl<$($name),+> BorshSchema for ($($name,)+) where $($name: BorshSchema),+ { fn add_definitions_recursively(definitions: &mut BTreeMap) { let elements = vec![$($name::declaration()),+]; let definition = Definition::Tuple { elements }; add_definition(Self::declaration(), definition, definitions); $( $name::add_definitions_recursively(definitions); )+ } fn declaration() -> Declaration { let params = vec![$($name::declaration()),+]; if params.len() == 1 { format!(r#"({},)"#, params[0]) } else { format!(r#"({})"#, params.join(", ")) } } } }; } impl_tuple!(T0); impl_tuple!(T0, T1); impl_tuple!(T0, T1, T2); impl_tuple!(T0, T1, T2, T3); impl_tuple!(T0, T1, T2, T3, T4); impl_tuple!(T0, T1, T2, T3, T4, T5); impl_tuple!(T0, T1, T2, T3, T4, T5, T6); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17); impl_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18); impl_tuple!( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 ); impl_tuple!( T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 ); #[cfg(feature = "std")] mod ip_addr_std_derive_impl { use crate::BorshSchema as BorshSchemaMacro; #[derive(BorshSchemaMacro)] #[borsh(crate = "crate")] pub struct Ipv4Addr { octets: [u8; 4], } #[derive(BorshSchemaMacro)] #[borsh(crate = "crate")] pub struct Ipv6Addr { octets: [u8; 16], } #[derive(BorshSchemaMacro)] #[borsh(crate = "crate")] pub enum IpAddr { /// An IPv4 address. V4(std::net::Ipv4Addr), /// An IPv6 address. V6(std::net::Ipv6Addr), } } #[cfg(feature = "std")] impl BorshSchema for std::net::Ipv4Addr { fn add_definitions_recursively(definitions: &mut BTreeMap) { ::add_definitions_recursively(definitions); } fn declaration() -> Declaration { ip_addr_std_derive_impl::Ipv4Addr::declaration() } } #[cfg(feature = "std")] impl BorshSchema for std::net::Ipv6Addr { fn add_definitions_recursively(definitions: &mut BTreeMap) { ::add_definitions_recursively(definitions); } fn declaration() -> Declaration { ip_addr_std_derive_impl::Ipv6Addr::declaration() } } #[cfg(feature = "std")] impl BorshSchema for std::net::IpAddr { fn add_definitions_recursively(definitions: &mut BTreeMap) { ::add_definitions_recursively(definitions); } fn declaration() -> Declaration { ip_addr_std_derive_impl::IpAddr::declaration() } } borsh-1.5.7/src/schema_helpers.rs000064400000000000000000000036121046102023000150740ustar 00000000000000use crate::__private::maybestd::vec::Vec; use crate::from_slice; use crate::io::{Error, ErrorKind, Result}; use crate::schema::{BorshSchemaContainer, SchemaMaxSerializedSizeError}; use crate::{BorshDeserialize, BorshSchema, BorshSerialize}; /// Deserialize this instance from a slice of bytes, but assume that at the beginning we have /// bytes describing the schema of the type. We deserialize this schema and verify that it is /// correct. pub fn try_from_slice_with_schema(v: &[u8]) -> Result { let (schema, object) = from_slice::<(BorshSchemaContainer, T)>(v)?; if schema_container_of::() != schema { return Err(Error::new( ErrorKind::InvalidData, "Borsh schema does not match", )); } Ok(object) } /// Serialize object into a vector of bytes and prefix with the schema serialized as vector of /// bytes in Borsh format. pub fn try_to_vec_with_schema( value: &T, ) -> Result> { let schema = schema_container_of::(); let mut res = crate::to_vec(&schema)?; value.serialize(&mut res)?; Ok(res) } /// generate [BorshSchemaContainer] for type `T` /// /// this is an alias of [BorshSchemaContainer::for_type] pub fn schema_container_of() -> BorshSchemaContainer { BorshSchemaContainer::for_type::() } /// Returns the largest possible size of a serialised object based solely on its type `T`. /// /// this is a shortcut for using [BorshSchemaContainer::max_serialized_size] /// # Example /// /// ``` /// use borsh::schema::BorshSchemaContainer; /// /// assert_eq!(Ok(8), borsh::max_serialized_size::()); /// ``` pub fn max_serialized_size( ) -> core::result::Result { let schema = BorshSchemaContainer::for_type::(); schema.max_serialized_size() } borsh-1.5.7/src/ser/helpers.rs000064400000000000000000000045171046102023000143520ustar 00000000000000use crate::BorshSerialize; use crate::__private::maybestd::vec::Vec; use crate::io::{ErrorKind, Result, Write}; pub(super) const DEFAULT_SERIALIZER_CAPACITY: usize = 1024; /// Serialize an object into a vector of bytes. /// # Example /// /// ``` /// assert_eq!(vec![12, 0, 0, 0, 0, 0, 0, 0], borsh::to_vec(&12u64).unwrap()); /// ``` pub fn to_vec(value: &T) -> Result> where T: BorshSerialize + ?Sized, { let mut result = Vec::with_capacity(DEFAULT_SERIALIZER_CAPACITY); value.serialize(&mut result)?; Ok(result) } /// Serializes an object directly into a `Writer`. /// # Example /// /// ``` /// # #[cfg(feature = "std")] /// let stderr = std::io::stderr(); /// # #[cfg(feature = "std")] /// assert_eq!((), borsh::to_writer(&stderr, "hello_0x0a").unwrap()); /// ``` pub fn to_writer(mut writer: W, value: &T) -> Result<()> where T: BorshSerialize + ?Sized, { value.serialize(&mut writer) } /// Serializes an object without allocation to compute and return its length /// # Example /// /// ``` /// use borsh::BorshSerialize; /// /// /// derive is only available if borsh is built with `features = ["derive"]` /// # #[cfg(feature = "derive")] /// #[derive(BorshSerialize)] /// struct A { /// tag: String, /// value: u64, /// }; /// /// # #[cfg(feature = "derive")] /// let a = A { tag: "hello".to_owned(), value: 42 }; /// /// assert_eq!(8, borsh::object_length(&12u64).unwrap()); /// # #[cfg(feature = "derive")] /// assert_eq!(17, borsh::object_length(&a).unwrap()); /// ``` pub fn object_length(value: &T) -> Result where T: BorshSerialize + ?Sized, { // copy-paste of solution provided by @matklad // in https://github.com/near/borsh-rs/issues/23#issuecomment-816633365 struct LengthWriter { len: usize, } impl Write for LengthWriter { #[inline] fn write(&mut self, buf: &[u8]) -> Result { let res = self.len.checked_add(buf.len()); self.len = match res { Some(res) => res, None => { return Err(ErrorKind::OutOfMemory.into()); } }; Ok(buf.len()) } #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } } let mut w = LengthWriter { len: 0 }; value.serialize(&mut w)?; Ok(w.len) } borsh-1.5.7/src/ser/mod.rs000064400000000000000000000511621046102023000134650ustar 00000000000000use core::convert::TryFrom; use core::marker::PhantomData; use crate::__private::maybestd::{ borrow::{Cow, ToOwned}, boxed::Box, collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, string::String, vec::Vec, }; use crate::error::check_zst; use crate::io::{Error, ErrorKind, Result, Write}; pub(crate) mod helpers; const FLOAT_NAN_ERR: &str = "For portability reasons we do not allow to serialize NaNs."; /// A data-structure that can be serialized into binary format by NBOR. /// /// ``` /// use borsh::BorshSerialize; /// /// /// derive is only available if borsh is built with `features = ["derive"]` /// # #[cfg(feature = "derive")] /// #[derive(BorshSerialize)] /// struct MyBorshSerializableStruct { /// value: String, /// } /// /// /// # #[cfg(feature = "derive")] /// let x = MyBorshSerializableStruct { value: "hello".to_owned() }; /// let mut buffer: Vec = Vec::new(); /// # #[cfg(feature = "derive")] /// x.serialize(&mut buffer).unwrap(); /// # #[cfg(feature = "derive")] /// let single_serialized_buffer_len = buffer.len(); /// /// # #[cfg(feature = "derive")] /// x.serialize(&mut buffer).unwrap(); /// # #[cfg(feature = "derive")] /// assert_eq!(buffer.len(), single_serialized_buffer_len * 2); /// /// # #[cfg(feature = "derive")] /// let mut buffer: Vec = vec![0; 1024 + single_serialized_buffer_len]; /// # #[cfg(feature = "derive")] /// let mut buffer_slice_enough_for_the_data = &mut buffer[1024..1024 + single_serialized_buffer_len]; /// # #[cfg(feature = "derive")] /// x.serialize(&mut buffer_slice_enough_for_the_data).unwrap(); /// ``` pub trait BorshSerialize { fn serialize(&self, writer: &mut W) -> Result<()>; #[inline] #[doc(hidden)] fn u8_slice(slice: &[Self]) -> Option<&[u8]> where Self: Sized, { let _ = slice; None } } impl BorshSerialize for u8 { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { writer.write_all(core::slice::from_ref(self)) } #[inline] fn u8_slice(slice: &[Self]) -> Option<&[u8]> { Some(slice) } } macro_rules! impl_for_integer { ($type: ident) => { impl BorshSerialize for $type { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { let bytes = self.to_le_bytes(); writer.write_all(&bytes) } } }; } impl_for_integer!(i8); impl_for_integer!(i16); impl_for_integer!(i32); impl_for_integer!(i64); impl_for_integer!(i128); impl_for_integer!(u16); impl_for_integer!(u32); impl_for_integer!(u64); impl_for_integer!(u128); macro_rules! impl_for_nonzero_integer { ($type: ty) => { impl BorshSerialize for $type { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { BorshSerialize::serialize(&self.get(), writer) } } }; } impl_for_nonzero_integer!(core::num::NonZeroI8); impl_for_nonzero_integer!(core::num::NonZeroI16); impl_for_nonzero_integer!(core::num::NonZeroI32); impl_for_nonzero_integer!(core::num::NonZeroI64); impl_for_nonzero_integer!(core::num::NonZeroI128); impl_for_nonzero_integer!(core::num::NonZeroU8); impl_for_nonzero_integer!(core::num::NonZeroU16); impl_for_nonzero_integer!(core::num::NonZeroU32); impl_for_nonzero_integer!(core::num::NonZeroU64); impl_for_nonzero_integer!(core::num::NonZeroU128); impl_for_nonzero_integer!(core::num::NonZeroUsize); impl BorshSerialize for isize { fn serialize(&self, writer: &mut W) -> Result<()> { BorshSerialize::serialize(&(*self as i64), writer) } } impl BorshSerialize for usize { fn serialize(&self, writer: &mut W) -> Result<()> { BorshSerialize::serialize(&(*self as u64), writer) } } // Note NaNs have a portability issue. Specifically, signalling NaNs on MIPS are quiet NaNs on x86, // and vice-versa. We disallow NaNs to avoid this issue. macro_rules! impl_for_float { ($type: ident) => { impl BorshSerialize for $type { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { if self.is_nan() { return Err(Error::new(ErrorKind::InvalidData, FLOAT_NAN_ERR)); } writer.write_all(&self.to_bits().to_le_bytes()) } } }; } impl_for_float!(f32); impl_for_float!(f64); impl BorshSerialize for bool { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { (u8::from(*self)).serialize(writer) } } impl BorshSerialize for Option where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { match self { None => 0u8.serialize(writer), Some(value) => { 1u8.serialize(writer)?; value.serialize(writer) } } } } impl BorshSerialize for core::result::Result where T: BorshSerialize, E: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { match self { Err(e) => { 0u8.serialize(writer)?; e.serialize(writer) } Ok(v) => { 1u8.serialize(writer)?; v.serialize(writer) } } } } impl BorshSerialize for str { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_bytes().serialize(writer) } } impl BorshSerialize for String { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_bytes().serialize(writer) } } /// Module is available if borsh is built with `features = ["ascii"]`. #[cfg(feature = "ascii")] pub mod ascii { //! //! Module defines [BorshSerialize] implementation for //! some types from [ascii](::ascii) crate. use super::BorshSerialize; use crate::io::{Result, Write}; impl BorshSerialize for ascii::AsciiChar { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_byte().serialize(writer) } } impl BorshSerialize for ascii::AsciiStr { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_bytes().serialize(writer) } } impl BorshSerialize for ascii::AsciiString { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_bytes().serialize(writer) } } } /// Helper method that is used to serialize a slice of data (without the length marker). #[inline] fn serialize_slice(data: &[T], writer: &mut W) -> Result<()> { if let Some(u8_slice) = T::u8_slice(data) { writer.write_all(u8_slice)?; } else { for item in data { item.serialize(writer)?; } } Ok(()) } impl BorshSerialize for [T] where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { writer.write_all( &(u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?).to_le_bytes(), )?; serialize_slice(self, writer) } } impl BorshSerialize for &T { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { (*self).serialize(writer) } } impl BorshSerialize for Cow<'_, T> where T: BorshSerialize + ToOwned + ?Sized, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_ref().serialize(writer) } } impl BorshSerialize for Vec where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; self.as_slice().serialize(writer) } } #[cfg(feature = "bytes")] impl BorshSerialize for bytes::Bytes { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_ref().serialize(writer) } } #[cfg(feature = "bytes")] impl BorshSerialize for bytes::BytesMut { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.as_ref().serialize(writer) } } #[cfg(feature = "bson")] impl BorshSerialize for bson::oid::ObjectId { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.bytes().serialize(writer) } } #[cfg(feature = "indexmap")] // Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L74C1-L86C2 // license: MIT OR Apache-2.0 impl BorshSerialize for indexmap::IndexSet where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; let iterator = self.iter(); u32::try_from(iterator.len()) .map_err(|_| ErrorKind::InvalidData)? .serialize(writer)?; for item in iterator { item.serialize(writer)?; } Ok(()) } } #[cfg(feature = "indexmap")] // Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L15 // license: MIT OR Apache-2.0 impl BorshSerialize for indexmap::IndexMap where K: BorshSerialize, V: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; let iterator = self.iter(); u32::try_from(iterator.len()) .map_err(|_| ErrorKind::InvalidData)? .serialize(writer)?; for (key, value) in iterator { key.serialize(writer)?; value.serialize(writer)?; } Ok(()) } } impl BorshSerialize for VecDeque where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; writer.write_all( &(u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?).to_le_bytes(), )?; let slices = self.as_slices(); serialize_slice(slices.0, writer)?; serialize_slice(slices.1, writer) } } impl BorshSerialize for LinkedList where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; writer.write_all( &(u32::try_from(self.len()).map_err(|_| ErrorKind::InvalidData)?).to_le_bytes(), )?; for item in self { item.serialize(writer)?; } Ok(()) } } /// Module is available if borsh is built with `features = ["std"]` or `features = ["hashbrown"]`. /// /// Module defines [BorshSerialize] implementation for /// [HashMap](std::collections::HashMap)/[HashSet](std::collections::HashSet). #[cfg(hash_collections)] pub mod hashes { use crate::__private::maybestd::vec::Vec; use crate::error::check_zst; use crate::{ BorshSerialize, __private::maybestd::collections::{HashMap, HashSet}, }; use core::convert::TryFrom; use core::hash::BuildHasher; use crate::io::{ErrorKind, Result, Write}; impl BorshSerialize for HashMap where K: BorshSerialize + Ord, V: BorshSerialize, H: BuildHasher, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; let mut vec = self.iter().collect::>(); vec.sort_by(|(a, _), (b, _)| a.cmp(b)); u32::try_from(vec.len()) .map_err(|_| ErrorKind::InvalidData)? .serialize(writer)?; for kv in vec { kv.serialize(writer)?; } Ok(()) } } impl BorshSerialize for HashSet where T: BorshSerialize + Ord, H: BuildHasher, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; let mut vec = self.iter().collect::>(); vec.sort(); u32::try_from(vec.len()) .map_err(|_| ErrorKind::InvalidData)? .serialize(writer)?; for item in vec { item.serialize(writer)?; } Ok(()) } } } impl BorshSerialize for BTreeMap where K: BorshSerialize, V: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; // NOTE: BTreeMap iterates over the entries that are sorted by key, so the serialization // result will be consistent without a need to sort the entries as we do for HashMap // serialization. u32::try_from(self.len()) .map_err(|_| ErrorKind::InvalidData)? .serialize(writer)?; for (key, value) in self { key.serialize(writer)?; value.serialize(writer)?; } Ok(()) } } impl BorshSerialize for BTreeSet where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { check_zst::()?; // NOTE: BTreeSet iterates over the items that are sorted, so the serialization result will // be consistent without a need to sort the entries as we do for HashSet serialization. u32::try_from(self.len()) .map_err(|_| ErrorKind::InvalidData)? .serialize(writer)?; for item in self { item.serialize(writer)?; } Ok(()) } } #[cfg(feature = "std")] impl BorshSerialize for std::net::SocketAddr { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { match *self { std::net::SocketAddr::V4(ref addr) => { 0u8.serialize(writer)?; addr.serialize(writer) } std::net::SocketAddr::V6(ref addr) => { 1u8.serialize(writer)?; addr.serialize(writer) } } } } #[cfg(feature = "std")] impl BorshSerialize for std::net::SocketAddrV4 { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.ip().serialize(writer)?; self.port().serialize(writer) } } #[cfg(feature = "std")] impl BorshSerialize for std::net::SocketAddrV6 { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { self.ip().serialize(writer)?; self.port().serialize(writer) } } #[cfg(feature = "std")] impl BorshSerialize for std::net::Ipv4Addr { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { writer.write_all(&self.octets()) } } #[cfg(feature = "std")] impl BorshSerialize for std::net::Ipv6Addr { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { writer.write_all(&self.octets()) } } #[cfg(feature = "std")] impl BorshSerialize for std::net::IpAddr { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { match self { std::net::IpAddr::V4(ipv4) => { writer.write_all(&0u8.to_le_bytes())?; ipv4.serialize(writer) } std::net::IpAddr::V6(ipv6) => { writer.write_all(&1u8.to_le_bytes())?; ipv6.serialize(writer) } } } } impl BorshSerialize for Box { fn serialize(&self, writer: &mut W) -> Result<()> { self.as_ref().serialize(writer) } } impl BorshSerialize for [T; N] where T: BorshSerialize, { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { if N == 0 { return Ok(()); } else if let Some(u8_slice) = T::u8_slice(self) { writer.write_all(u8_slice)?; } else { for el in self.iter() { el.serialize(writer)?; } } Ok(()) } } macro_rules! impl_tuple { (@unit $name:ty) => { impl BorshSerialize for $name { #[inline] fn serialize(&self, _writer: &mut W) -> Result<()> { Ok(()) } } }; ($($idx:tt $name:ident)+) => { impl<$($name),+> BorshSerialize for ($($name,)+) where $($name: BorshSerialize,)+ { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { $(self.$idx.serialize(writer)?;)+ Ok(()) } } }; } impl_tuple!(@unit ()); impl_tuple!(@unit core::ops::RangeFull); impl_tuple!(0 T0); impl_tuple!(0 T0 1 T1); impl_tuple!(0 T0 1 T1 2 T2); impl_tuple!(0 T0 1 T1 2 T2 3 T3); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16 17 T17); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16 17 T17 18 T18); impl_tuple!(0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15 16 T16 17 T17 18 T18 19 T19); macro_rules! impl_range { ($type:ident, $this:ident, $($field:expr),*) => { impl BorshSerialize for core::ops::$type { #[inline] fn serialize(&self, writer: &mut W) -> Result<()> { let $this = self; $( $field.serialize(writer)?; )* Ok(()) } } }; } impl_range!(Range, this, &this.start, &this.end); impl_range!(RangeInclusive, this, this.start(), this.end()); impl_range!(RangeFrom, this, &this.start); impl_range!(RangeTo, this, &this.end); impl_range!(RangeToInclusive, this, &this.end); /// Module is available if borsh is built with `features = ["rc"]`. #[cfg(feature = "rc")] pub mod rc { //! //! Module defines [BorshSerialize] implementation for //! [alloc::rc::Rc](std::rc::Rc) and [alloc::sync::Arc](std::sync::Arc). use crate::__private::maybestd::{rc::Rc, sync::Arc}; use crate::io::{Result, Write}; use crate::BorshSerialize; /// This impl requires the [`"rc"`] Cargo feature of borsh. /// /// Serializing a data structure containing `Rc` will serialize a copy of /// the contents of the `Rc` each time the `Rc` is referenced within the /// data structure. Serialization will not attempt to deduplicate these /// repeated data. impl BorshSerialize for Rc { fn serialize(&self, writer: &mut W) -> Result<()> { (**self).serialize(writer) } } /// This impl requires the [`"rc"`] Cargo feature of borsh. /// /// Serializing a data structure containing `Arc` will serialize a copy of /// the contents of the `Arc` each time the `Arc` is referenced within the /// data structure. Serialization will not attempt to deduplicate these /// repeated data. impl BorshSerialize for Arc { fn serialize(&self, writer: &mut W) -> Result<()> { (**self).serialize(writer) } } } impl BorshSerialize for PhantomData { fn serialize(&self, _: &mut W) -> Result<()> { Ok(()) } } impl BorshSerialize for core::cell::Cell where T: BorshSerialize + Copy, { fn serialize(&self, writer: &mut W) -> Result<()> { ::serialize(&self.get(), writer) } } impl BorshSerialize for core::cell::RefCell where T: BorshSerialize + Sized, { fn serialize(&self, writer: &mut W) -> Result<()> { match self.try_borrow() { Ok(ref value) => value.serialize(writer), Err(_) => Err(Error::new(ErrorKind::Other, "already mutably borrowed")), } } } borsh-1.5.7/tests/common_macro.rs000064400000000000000000000075641046102023000151500ustar 00000000000000#[allow(unused)] macro_rules! set_insert_deser_assert_macro [ [$set: ident, $data: ident, $($key: expr),*] => [ $($set.insert($key));* ; let $data = borsh::to_vec(&$set).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!($data); ] ]; #[allow(unused)] macro_rules! map_insert_deser_assert_macro [ [$map: ident, $data: ident, $($key: expr => $value: expr),*] => [ $($map.insert($key, $value));* ; let $data = borsh::to_vec(&$map).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!($data); ] ]; #[allow(unused)] macro_rules! set_wrong_order_test [ [$test_name: ident, $set_type: ty] => [ #[test] fn $test_name() { let mut data = vec![]; let arr_key = ["various".to_string(), "foo".to_string(), "many".to_string()]; let len = arr_key.len() as u32; u32::serialize(&len, &mut data).expect("no error"); for key in &arr_key { key.serialize(&mut data).expect("no error"); } let result = from_slice::<$set_type>(&data); #[cfg(not(feature = "de_strict_order"))] { let result = result.unwrap(); assert_eq!(result.len(), arr_key.len()); for key in &arr_key { assert!(result.contains(key)); } } #[cfg(feature = "de_strict_order")] { assert!(result.is_err()); assert_eq!(result.unwrap_err().to_string(), ERROR_WRONG_ORDER_OF_KEYS); } } ] ]; #[allow(unused)] macro_rules! map_wrong_order_test [ [$test_name: ident, $map_type: ty] => [ #[test] fn $test_name() { let mut data = vec![]; let arr_key = ["various".to_string(), "foo".to_string(), "many".to_string()]; let arr_val = [ "value".to_string(), "different".to_string(), "unexp".to_string(), ]; let len = arr_key.len() as u32; u32::serialize(&len, &mut data).expect("no error"); let entries = IntoIterator::into_iter(arr_key.clone()) .zip(IntoIterator::into_iter(arr_val)) .collect::>(); for (key, value) in entries.clone() { key.serialize(&mut data).expect("no error"); value.serialize(&mut data).expect("no error"); } let result = from_slice::<$map_type>(&data); #[cfg(not(feature = "de_strict_order"))] { let result = result.unwrap(); assert_eq!(result.len(), arr_key.len()); for (key, value) in entries { assert_eq!(result.get(&key), Some(&value)); } } #[cfg(feature = "de_strict_order")] { assert!(result.is_err()); assert_eq!(result.unwrap_err().to_string(), ERROR_WRONG_ORDER_OF_KEYS); } } ] ]; #[allow(unused)] macro_rules! schema_map( () => { BTreeMap::new() }; { $($key:expr => $value:expr),+ } => { { let mut m = BTreeMap::new(); $( m.insert($key.to_string(), $value); )+ m } }; ); #[allow(unused)] #[cfg(feature = "unstable__schema")] pub mod schema_imports { extern crate alloc; pub use alloc::{ boxed::Box, collections::BTreeMap, format, string::{String, ToString}, vec, vec::Vec, }; pub use borsh::schema::{ add_definition, BorshSchemaContainer, Declaration, Definition, Fields, SchemaContainerValidateError, SchemaMaxSerializedSizeError, }; pub use borsh::{schema_container_of, BorshSchema}; } borsh-1.5.7/tests/compile_derives/schema/test_generic_enums.rs000064400000000000000000000013031046102023000227530ustar 00000000000000#![allow(unused)] use crate::common_macro::schema_imports::*; use core::fmt::{Debug, Display}; /// test: Sausage wasn't populated with param Sausage #[derive(borsh::BorshSchema, Debug)] enum AWithSkip { Bacon, Eggs, Salad(u32, C, u32), Sausage { #[borsh(skip)] wrapper: W, filling: u32, }, } /// test: inner structs in BorshSchema derive don't need any bounds, unrelated to BorshSchema // #[derive(borsh::BorshSchema)] // struct SideLeft( // A, // ) // where // A: Display + Debug, // B: Display + Debug; #[derive(borsh::BorshSchema)] enum Side where A: Display + Debug, B: Display + Debug, { Left(A), Right(B), } borsh-1.5.7/tests/compile_derives/test_generic_enums.rs000064400000000000000000000016301046102023000215160ustar 00000000000000use borsh::{BorshDeserialize, BorshSerialize}; #[allow(unused)] use alloc::{string::String, vec::Vec}; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(hash_collections)] use core::{cmp::Eq, hash::Hash}; #[cfg(feature = "std")] use std::collections::HashMap; use alloc::collections::BTreeMap; /// `T: Ord` bound is required for `BorshDeserialize` derive to be successful #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] enum E { X { f: BTreeMap }, Y(W), } #[cfg(hash_collections)] #[derive(BorshSerialize, BorshDeserialize, Debug)] enum I1 { B { #[allow(unused)] #[borsh(skip)] x: HashMap, y: String, }, C(K, Vec), } #[cfg(hash_collections)] #[derive(BorshSerialize, BorshDeserialize, Debug)] enum I2 { B { x: HashMap, y: String }, C(K, #[borsh(skip)] U), } borsh-1.5.7/tests/compile_derives/test_generic_structs.rs000064400000000000000000000046451046102023000221070ustar 00000000000000#[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(hash_collections)] use core::{cmp::Eq, hash::Hash}; #[cfg(feature = "std")] use std::collections::HashMap; use alloc::{ collections::BTreeMap, string::String, }; use borsh::{BorshDeserialize, BorshSerialize}; #[derive(BorshSerialize, BorshDeserialize, Debug)] struct TupleA(W, u32); #[derive(BorshSerialize, BorshDeserialize, Debug)] struct NamedA { a: W, b: u32, } /// `T: PartialOrd` is injected here via field bound to avoid having this restriction on /// the struct itself #[cfg(hash_collections)] #[derive(BorshSerialize)] struct C1 { a: String, #[borsh(bound(serialize = "T: borsh::ser::BorshSerialize + Ord, U: borsh::ser::BorshSerialize"))] b: HashMap, } /// `T: PartialOrd + Hash + Eq` is injected here via field bound to avoid having this restriction on /// the struct itself #[allow(unused)] #[cfg(hash_collections)] #[derive(BorshDeserialize)] struct C2 { a: String, #[borsh(bound(deserialize = "T: Ord + Hash + Eq + borsh::de::BorshDeserialize, U: borsh::de::BorshDeserialize"))] b: HashMap, } /// `T: Ord` bound is required for `BorshDeserialize` derive to be successful #[derive(BorshSerialize, BorshDeserialize)] struct D { a: String, b: BTreeMap, } #[cfg(hash_collections)] #[derive(BorshSerialize)] struct G(#[borsh(skip)] HashMap, U); #[cfg(hash_collections)] #[derive(BorshDeserialize)] struct G1(#[borsh(skip)] HashMap, U); #[cfg(hash_collections)] #[derive(BorshDeserialize)] struct G2(HashMap, #[borsh(skip)] U); /// implicit derived `core::default::Default` bounds on `K` and `V` are dropped by empty bound /// specified, as `HashMap` hash its own `Default` implementation #[cfg(hash_collections)] #[derive(BorshDeserialize)] struct G3(#[borsh(skip, bound(deserialize = ""))] HashMap, U); #[cfg(hash_collections)] #[derive(BorshSerialize, BorshDeserialize)] struct H { x: BTreeMap, #[allow(unused)] #[borsh(skip)] y: U, } trait TraitName { type Associated; fn method(&self); } #[allow(unused)] #[derive(BorshSerialize)] struct ParametrizedWrongDerive where T: TraitName, { #[borsh(bound(serialize = "::Associated: borsh::ser::BorshSerialize"))] field: ::Associated, another: V, } borsh-1.5.7/tests/compile_derives/test_macro_namespace_collisions.rs000064400000000000000000000004311046102023000242440ustar 00000000000000// Borsh macros should not collide with the local modules: // https://github.com/near/borsh-rs/issues/11 mod std {} mod core {} #[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] struct A; #[derive(borsh::BorshSerialize, borsh::BorshDeserialize)] enum B { C, D, } borsh-1.5.7/tests/compile_derives/test_recursive_structs.rs000064400000000000000000000013501046102023000224700ustar 00000000000000use borsh::{BorshDeserialize, BorshSerialize}; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(feature = "std")] use std::collections::HashMap; #[cfg(hash_collections)] use core::{cmp::Eq, hash::Hash}; use alloc::{boxed::Box, string::String}; #[cfg(hash_collections)] #[derive(BorshSerialize, BorshDeserialize)] struct CRec { a: String, b: HashMap>, } // `impl BorshDeserialize for Box` pulls in => `ToOwned` // => pulls in at least `Clone` #[derive(Clone, BorshSerialize, BorshDeserialize)] struct CRecA { a: String, b: Box, } #[cfg(hash_collections)] #[derive(BorshSerialize, BorshDeserialize)] struct CRecC { a: String, b: HashMap, } borsh-1.5.7/tests/custom_reader/test_custom_reader.rs000064400000000000000000000074221046102023000212170ustar 00000000000000use borsh::{from_reader, to_vec, BorshDeserialize, BorshSerialize}; use alloc::{ string::{String, ToString}, vec::Vec, }; const ERROR_NOT_ALL_BYTES_READ: &str = "Not all bytes read"; const ERROR_UNEXPECTED_LENGTH_OF_INPUT: &str = "Unexpected length of input"; #[derive(BorshSerialize, BorshDeserialize, Debug)] struct Serializable { item1: i32, item2: String, item3: f64, } #[test] fn test_custom_reader() { let s = Serializable { item1: 100, item2: "foo".into(), item3: 1.2345, }; let bytes = to_vec(&s).unwrap(); let mut reader = CustomReader { data: bytes, read_index: 0, }; let de: Serializable = BorshDeserialize::deserialize_reader(&mut reader).unwrap(); assert_eq!(de.item1, s.item1); assert_eq!(de.item2, s.item2); assert_eq!(de.item3, s.item3); } #[test] fn test_custom_reader_with_insufficient_data() { let s = Serializable { item1: 100, item2: "foo".into(), item3: 1.2345, }; let mut bytes = to_vec(&s).unwrap(); bytes.pop().unwrap(); let mut reader = CustomReader { data: bytes, read_index: 0, }; assert_eq!( ::deserialize_reader(&mut reader) .unwrap_err() .to_string(), ERROR_UNEXPECTED_LENGTH_OF_INPUT ); } #[test] fn test_custom_reader_with_too_much_data() { let s = Serializable { item1: 100, item2: "foo".into(), item3: 1.2345, }; let mut bytes = to_vec(&s).unwrap(); bytes.push(1); let mut reader = CustomReader { data: bytes, read_index: 0, }; assert_eq!( from_reader::(&mut reader) .unwrap_err() .to_string(), ERROR_NOT_ALL_BYTES_READ ); } struct CustomReader { data: Vec, read_index: usize, } impl borsh::io::Read for CustomReader { fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result { let len = buf.len().min(self.data.len() - self.read_index); buf[0..len].copy_from_slice(&self.data[self.read_index..self.read_index + len]); self.read_index += len; Ok(len) } } #[test] fn test_custom_reader_that_doesnt_fill_slices() { let s = Serializable { item1: 100, item2: "foo".into(), item3: 1.2345, }; let bytes = to_vec(&s).unwrap(); let mut reader = CustomReaderThatDoesntFillSlices { data: bytes, read_index: 0, }; let de: Serializable = BorshDeserialize::deserialize_reader(&mut reader).unwrap(); assert_eq!(de.item1, s.item1); assert_eq!(de.item2, s.item2); assert_eq!(de.item3, s.item3); } struct CustomReaderThatDoesntFillSlices { data: Vec, read_index: usize, } impl borsh::io::Read for CustomReaderThatDoesntFillSlices { fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result { let len = buf.len().min(self.data.len() - self.read_index); let len = if len <= 1 { len } else { len / 2 }; buf[0..len].copy_from_slice(&self.data[self.read_index..self.read_index + len]); self.read_index += len; Ok(len) } } #[test] fn test_custom_reader_that_fails_preserves_error_information() { let mut reader = CustomReaderThatFails; let err = from_reader::(&mut reader).unwrap_err(); assert_eq!(err.to_string(), "I don't like to run"); assert_eq!(err.kind(), borsh::io::ErrorKind::ConnectionAborted); } struct CustomReaderThatFails; impl borsh::io::Read for CustomReaderThatFails { fn read(&mut self, _buf: &mut [u8]) -> borsh::io::Result { Err(borsh::io::Error::new( borsh::io::ErrorKind::ConnectionAborted, "I don't like to run", )) } } borsh-1.5.7/tests/deserialization_errors/test_ascii_strings.rs000064400000000000000000000012631046102023000231470ustar 00000000000000use alloc::string::ToString; use borsh::from_slice; #[test] fn test_non_ascii() { let buf = borsh::to_vec(&[0xbf, 0xf3, 0xb3, 0x77][..]).unwrap(); assert_eq!( from_slice::(&buf) .unwrap_err() .to_string(), "the byte at index 0 is not ASCII" ); let buf = borsh::to_vec("żółw").unwrap(); assert_eq!( from_slice::(&buf) .unwrap_err() .to_string(), "the byte at index 0 is not ASCII" ); assert_eq!( from_slice::(&[0xbf]) .unwrap_err() .to_string(), "not an ASCII character" ); } borsh-1.5.7/tests/deserialization_errors/test_cells.rs000064400000000000000000000004551046102023000214120ustar 00000000000000use alloc::string::ToString; #[test] fn test_ref_cell_try_borrow_error() { let rcell = core::cell::RefCell::new("str"); let _active_borrow = rcell.try_borrow_mut().unwrap(); assert_eq!( borsh::to_vec(&rcell).unwrap_err().to_string(), "already mutably borrowed" ); } borsh-1.5.7/tests/deserialization_errors/test_initial.rs000064400000000000000000000121661046102023000217430ustar 00000000000000use borsh::from_slice; #[cfg(feature = "derive")] use borsh::BorshDeserialize; use alloc::{ format, string::{String, ToString}, vec, vec::Vec, }; #[cfg(feature = "derive")] #[derive(BorshDeserialize, Debug)] #[borsh(use_discriminant = true)] enum A { X, Y, } #[cfg(feature = "derive")] #[derive(BorshDeserialize, Debug)] #[borsh(use_discriminant = false)] enum AWithUseDiscriminantFalse { X, Y, } #[cfg(feature = "derive")] #[derive(BorshDeserialize, Debug)] struct B { #[allow(unused)] x: u64, #[allow(unused)] y: u32, } const ERROR_UNEXPECTED_LENGTH_OF_INPUT: &str = "Unexpected length of input"; const ERROR_INVALID_ZERO_VALUE: &str = "Expected a non-zero value"; #[cfg(feature = "derive")] #[test] fn test_missing_bytes() { let bytes = vec![1, 0]; assert_eq!( from_slice::(&bytes).unwrap_err().to_string(), ERROR_UNEXPECTED_LENGTH_OF_INPUT ); } #[cfg(feature = "derive")] #[test] fn test_invalid_enum_variant() { let bytes = vec![123]; assert_eq!( from_slice::(&bytes).unwrap_err().to_string(), "Unexpected variant tag: 123" ); } #[cfg(feature = "derive")] #[test] fn test_invalid_enum_variant_old() { let bytes = vec![123]; assert_eq!( from_slice::(&bytes) .unwrap_err() .to_string(), "Unexpected variant tag: 123" ); } #[test] fn test_extra_bytes() { let bytes = vec![1, 0, 0, 0, 32, 32]; assert_eq!( from_slice::>(&bytes).unwrap_err().to_string(), "Not all bytes read" ); } #[test] fn test_invalid_bool() { for i in 2u8..=255 { let bytes = [i]; assert_eq!( from_slice::(&bytes).unwrap_err().to_string(), format!("Invalid bool representation: {}", i) ); } } #[test] fn test_invalid_option() { for i in 2u8..=255 { let bytes = [i, 32]; assert_eq!( from_slice::>(&bytes).unwrap_err().to_string(), format!( "Invalid Option representation: {}. The first byte must be 0 or 1", i ) ); } } #[test] fn test_invalid_result() { for i in 2u8..=255 { let bytes = [i, 0]; assert_eq!( from_slice::>(&bytes) .unwrap_err() .to_string(), format!( "Invalid Result representation: {}. The first byte must be 0 or 1", i ) ); } } #[test] fn test_invalid_length() { let bytes = vec![255u8; 4]; assert_eq!( from_slice::>(&bytes).unwrap_err().to_string(), ERROR_UNEXPECTED_LENGTH_OF_INPUT ); } #[test] fn test_invalid_length_string() { let bytes = vec![255u8; 4]; assert_eq!( from_slice::(&bytes).unwrap_err().to_string(), ERROR_UNEXPECTED_LENGTH_OF_INPUT ); } #[test] fn test_non_utf_string() { let bytes = vec![1, 0, 0, 0, 0xC0]; assert_eq!( from_slice::(&bytes).unwrap_err().to_string(), "invalid utf-8 sequence of 1 bytes from index 0" ); } #[test] fn test_nan_float() { let bytes = vec![0, 0, 192, 127]; assert_eq!( from_slice::(&bytes).unwrap_err().to_string(), "For portability reasons we do not allow to deserialize NaNs." ); } #[test] fn test_evil_bytes_vec_with_extra() { // Should fail to allocate given length // test takes a really long time if read() is used instead of read_exact() let bytes = vec![255, 255, 255, 255, 32, 32]; assert_eq!( from_slice::>(&bytes).unwrap_err().to_string(), ERROR_UNEXPECTED_LENGTH_OF_INPUT ); } #[test] fn test_evil_bytes_string_extra() { // Might fail if reading too much let bytes = vec![255, 255, 255, 255, 32, 32]; assert_eq!( from_slice::(&bytes).unwrap_err().to_string(), ERROR_UNEXPECTED_LENGTH_OF_INPUT ); } #[test] fn test_zero_on_nonzero_integer_u8() { let bytes = &[0]; assert_eq!( from_slice::(bytes) .unwrap_err() .to_string(), ERROR_INVALID_ZERO_VALUE ); } #[test] fn test_zero_on_nonzero_integer_u32() { let bytes = &[0; 4]; assert_eq!( from_slice::(bytes) .unwrap_err() .to_string(), ERROR_INVALID_ZERO_VALUE ); } #[test] fn test_zero_on_nonzero_integer_i64() { let bytes = &[0; 8]; assert_eq!( from_slice::(bytes) .unwrap_err() .to_string(), ERROR_INVALID_ZERO_VALUE ); } #[test] fn test_zero_on_nonzero_integer_usize() { let bytes = &[0; 8]; assert_eq!( from_slice::(bytes) .unwrap_err() .to_string(), ERROR_INVALID_ZERO_VALUE ); } #[test] fn test_zero_on_nonzero_integer_missing_byte() { let bytes = &[0; 7]; assert_eq!( from_slice::(bytes) .unwrap_err() .to_string(), ERROR_UNEXPECTED_LENGTH_OF_INPUT ); } borsh-1.5.7/tests/init_in_deserialize/test_init_in_deserialize.rs000064400000000000000000000021641046102023000235470ustar 00000000000000use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init=init)] struct A { lazy: Option, } impl A { pub fn init(&mut self) { if let Some(v) = self.lazy.as_mut() { *v *= 10; } } } #[test] fn test_simple_struct() { let a = A { lazy: Some(5) }; let encoded_a = to_vec(&a).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded_a); let decoded_a = from_slice::(&encoded_a).unwrap(); let expected_a = A { lazy: Some(50) }; assert_eq!(expected_a, decoded_a); } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] #[borsh(init=initialization_method)] enum AEnum { A, B, C, } impl AEnum { pub fn initialization_method(&mut self) { *self = AEnum::C; } } #[test] fn test_simple_enum() { let a = AEnum::B; let encoded_a = to_vec(&a).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded_a); let decoded_a = from_slice::(&encoded_a).unwrap(); assert_eq!(AEnum::C, decoded_a); } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_bson_object_ids.rs000064400000000000000000000011701046102023000257670ustar 00000000000000#![allow(clippy::float_cmp)] use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; use bson::oid::ObjectId; #[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug)] struct StructWithObjectId(i32, ObjectId, u8); #[test] fn test_object_id() { let obj = StructWithObjectId( 123, ObjectId::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), 33, ); let serialized = to_vec(&obj).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(serialized); let deserialized: StructWithObjectId = from_slice(&serialized).unwrap(); assert_eq!(obj, deserialized); } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_enum_discriminants.rs000064400000000000000000000105151046102023000265370ustar 00000000000000use alloc::vec; use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; // sequence, no unit enums #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)] #[borsh(use_discriminant = true)] #[repr(u16)] enum XY { A, B = 20, C, D(u32, u32), E = 10, F(u64), } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)] #[borsh(use_discriminant = false)] #[repr(u16)] enum XYNoDiscriminant { A, B = 20, C, D(u32, u32), E = 10, F(u64), } #[test] fn test_discriminant_serde_no_unit_type() { let values = vec![XY::A, XY::B, XY::C, XY::E, XY::D(12, 14), XY::F(35325423)]; let expected_discriminants = [0u8, 20, 21, 10, 22, 11]; for (ind, value) in values.iter().enumerate() { let data = to_vec(value).unwrap(); assert_eq!(data[0], expected_discriminants[ind]); assert_eq!(from_slice::(&data).unwrap(), values[ind]); } } #[test] fn test_discriminant_serde_no_unit_type_no_use_discriminant() { let values = vec![ XYNoDiscriminant::A, XYNoDiscriminant::B, XYNoDiscriminant::C, XYNoDiscriminant::D(12, 14), XYNoDiscriminant::E, XYNoDiscriminant::F(35325423), ]; let expected_discriminants = [0u8, 1, 2, 3, 4, 5]; for (ind, value) in values.iter().enumerate() { let data = to_vec(value).unwrap(); assert_eq!(data[0], expected_discriminants[ind]); assert_eq!(from_slice::(&data).unwrap(), values[ind]); } } // minimal #[derive(BorshSerialize)] #[borsh(use_discriminant = true)] enum MyDiscriminantEnum { A = 20, } #[derive(BorshSerialize)] #[borsh(use_discriminant = false)] enum MyDiscriminantEnumFalse { A = 20, } #[derive(BorshSerialize)] enum MyEnumNoDiscriminant { A, } #[test] fn test_discriminant_minimal_true() { assert_eq!(MyDiscriminantEnum::A as u8, 20); assert_eq!(to_vec(&MyDiscriminantEnum::A).unwrap(), vec![20]); } #[test] fn test_discriminant_minimal_false() { assert_eq!(MyDiscriminantEnumFalse::A as u8, 20); assert_eq!( to_vec(&MyEnumNoDiscriminant::A).unwrap(), to_vec(&MyDiscriminantEnumFalse::A).unwrap(), ); assert_eq!(to_vec(&MyDiscriminantEnumFalse::A).unwrap(), vec![0]); } // sequence #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)] #[borsh(use_discriminant = false)] enum XNoDiscriminant { A, B = 20, C, D, E = 10, F, } #[test] fn test_discriminant_serde_no_use_discriminant() { let values = vec![ XNoDiscriminant::A, XNoDiscriminant::B, XNoDiscriminant::C, XNoDiscriminant::D, XNoDiscriminant::E, XNoDiscriminant::F, ]; let expected_discriminants = [0u8, 1, 2, 3, 4, 5]; for (index, value) in values.iter().enumerate() { let data = to_vec(value).unwrap(); assert_eq!(data[0], expected_discriminants[index]); assert_eq!(from_slice::(&data).unwrap(), values[index]); } } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct D { x: u64, } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] enum C { C1, C2(u64), C3(u64, u64), C4 { x: u64, y: u64 }, C5(D), } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Clone, Copy, Debug)] #[borsh(use_discriminant = true)] enum X { A, B = 20, C, D, E = 10, F, } #[test] fn test_discriminant_serialization() { let values = vec![X::A, X::B, X::C, X::D, X::E, X::F]; for value in values { assert_eq!(to_vec(&value).unwrap(), [value as u8]); } } #[test] fn test_discriminant_deserialization() { let values = vec![X::A, X::B, X::C, X::D, X::E, X::F]; for value in values { assert_eq!(from_slice::(&[value as u8]).unwrap(), value,); } } #[test] #[should_panic = "Unexpected variant tag: 2"] fn test_deserialize_invalid_discriminant() { from_slice::(&[2]).unwrap(); } #[test] fn test_discriminant_serde() { let values = vec![X::A, X::B, X::C, X::D, X::E, X::F]; let expected_discriminants = [0u8, 20, 21, 22, 10, 11]; for (index, value) in values.iter().enumerate() { let data = to_vec(value).unwrap(); assert_eq!(data[0], expected_discriminants[index]); assert_eq!(from_slice::(&data).unwrap(), values[index]); } } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_generic_enums.rs000064400000000000000000000012661046102023000254720ustar 00000000000000use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; use alloc::{ string::{String, ToString}, vec, vec::Vec, }; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] enum B { X { f: Vec }, Y(G), } #[test] fn test_generic_enum() { let b: B = B::X { f: vec!["one".to_string(), "two".to_string(), "three".to_string()], }; let c: B = B::Y(656556u64); let list = vec![b, c]; let data = to_vec(&list).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_list = from_slice::>>(&data).unwrap(); assert_eq!(list, actual_list); } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_generic_structs.rs000064400000000000000000000053411046102023000260500ustar 00000000000000use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; use core::marker::PhantomData; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(hash_collections)] use core::{cmp::Eq, hash::Hash}; #[cfg(feature = "std")] use std::collections::HashMap; use alloc::{ string::{ToString, String}, vec, vec::Vec, }; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] enum B { X { f: Vec }, Y(G), } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct A { x: Vec, y: String, b: B, pd: PhantomData, c: Result, d: [u64; 5], } #[test] fn test_generic_struct() { let a = A:: { x: vec!["foo".to_string(), "bar".to_string()], pd: Default::default(), y: "world".to_string(), b: B::X { f: vec![1, 2] }, c: Err("error".to_string()), d: [0, 1, 2, 3, 4], }; let data = to_vec(&a).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_a = from_slice::>(&data).unwrap(); assert_eq!(a, actual_a); } trait TraitName { type Associated; #[allow(unused)] fn method(&self); } impl TraitName for u32 { type Associated = String; fn method(&self) {} } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct Parametrized where T: TraitName, { field: T::Associated, another: V, } #[test] fn test_generic_associated_type_field() { let a = Parametrized:: { field: "value".to_string(), another: "field".to_string(), }; let data = to_vec(&a).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_a = from_slice::>(&data).unwrap(); assert_eq!(a, actual_a); } /// `T: PartialOrd` bound is required for `BorshSerialize` derive to be successful /// `T: Hash + Eq` bound is required for `BorshDeserialize` derive to be successful #[cfg(hash_collections)] #[derive(BorshSerialize, BorshDeserialize)] struct C { a: String, b: HashMap, } #[cfg(hash_collections)] #[test] fn test_generic_struct_hashmap() { let mut hashmap = HashMap::new(); hashmap.insert(34, "another".to_string()); hashmap.insert(14, "value".to_string()); let a = C:: { a: "field".to_string(), b: hashmap, }; let data = to_vec(&a).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_a = from_slice::>(&data).unwrap(); assert_eq!(actual_a.b.get(&14), Some("value".to_string()).as_ref()); assert_eq!(actual_a.b.get(&34), Some("another".to_string()).as_ref()); } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_recursive_enums.rs000064400000000000000000000012451046102023000260620ustar 00000000000000use alloc::{ string::{String, ToString}, vec, vec::Vec, }; use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] enum ERecD { B { x: String, y: i32 }, C(u8, Vec), } #[test] fn test_recursive_enum() { let one = ERecD::B { x: "one".to_string(), y: 3213123, }; let two = ERecD::C(10, vec![]); let three = ERecD::C(11, vec![one, two]); let data = to_vec(&three).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_three = from_slice::(&data).unwrap(); assert_eq!(three, actual_three); } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_recursive_structs.rs000064400000000000000000000013371046102023000264440ustar 00000000000000use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; use alloc::{string::{String, ToString}, vec::Vec, vec}; #[derive(Debug, BorshSerialize, BorshDeserialize, PartialEq, Eq)] struct CRecB { a: String, b: Vec, } #[test] fn test_recursive_struct() { let one = CRecB { a: "one".to_string(), b: vec![], }; let two = CRecB { a: "two".to_string(), b: vec![], }; let three = CRecB { a: "three".to_string(), b: vec![one, two], }; let data = to_vec(&three).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_three = from_slice::(&data).unwrap(); assert_eq!(three, actual_three); } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_serde_with_third_party.rs000064400000000000000000000052771046102023000274230ustar 00000000000000use alloc::{ collections::BTreeMap, string::{String, ToString}, }; use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; #[derive(Debug, PartialEq, Eq)] struct ThirdParty(pub BTreeMap); mod third_party_impl { use super::ThirdParty; pub(super) fn serialize_third_party< K: borsh::ser::BorshSerialize, V: borsh::ser::BorshSerialize, W: borsh::io::Write, >( obj: &ThirdParty, writer: &mut W, ) -> ::core::result::Result<(), borsh::io::Error> { borsh::BorshSerialize::serialize(&obj.0, writer)?; Ok(()) } pub(super) fn deserialize_third_party< R: borsh::io::Read, K: borsh::de::BorshDeserialize + Ord, V: borsh::de::BorshDeserialize, >( reader: &mut R, ) -> ::core::result::Result, borsh::io::Error> { Ok(ThirdParty(borsh::BorshDeserialize::deserialize_reader( reader, )?)) } } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug)] struct A { #[borsh( deserialize_with = "third_party_impl::deserialize_third_party", serialize_with = "third_party_impl::serialize_third_party", bound( deserialize = "K: borsh::de::BorshDeserialize + Ord, V: borsh::de::BorshDeserialize", ) )] x: ThirdParty, y: u64, } #[allow(unused)] #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug)] enum C { C3(u64, u64), C4( u64, #[borsh( deserialize_with = "third_party_impl::deserialize_third_party", serialize_with = "third_party_impl::serialize_third_party", bound( deserialize = "K: borsh::de::BorshDeserialize + Ord, V: borsh::de::BorshDeserialize", ) )] ThirdParty, ), } #[test] fn test_overriden_struct() { let mut m = BTreeMap::::new(); m.insert(0, "0th element".to_string()); m.insert(1, "1st element".to_string()); let th_p = ThirdParty(m); let a = A { x: th_p, y: 42 }; let data = to_vec(&a).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_a = from_slice::>(&data).unwrap(); assert_eq!(a, actual_a); } #[test] fn test_overriden_enum() { let mut m = BTreeMap::::new(); m.insert(0, "0th element".to_string()); m.insert(1, "1st element".to_string()); let th_p = ThirdParty(m); let c = C::C4(42, th_p); let data = to_vec(&c).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let actual_c = from_slice::>(&data).unwrap(); assert_eq!(c, actual_c); } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_simple_enums.rs000064400000000000000000000013321046102023000253410ustar 00000000000000use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; use alloc::vec; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] enum MixedWithUnitVariants { A(u16), B, C { x: i32, y: i32 }, D, } #[test] fn test_mixed_enum() { let vars = vec![ MixedWithUnitVariants::A(13), MixedWithUnitVariants::B, MixedWithUnitVariants::C { x: 132, y: -17 }, MixedWithUnitVariants::D, ]; for variant in vars { let encoded = to_vec(&variant).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let decoded = from_slice::(&encoded).unwrap(); assert_eq!(variant, decoded); } } borsh-1.5.7/tests/roundtrip/requires_derive_category/test_ultimate_many_features_combined.rs000064400000000000000000000132641046102023000312560ustar 00000000000000use core::{ops, result::Result}; use alloc::{ borrow, boxed::Box, collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, string::{String, ToString}, vec, vec::Vec, }; use bytes::{Bytes, BytesMut}; use borsh::{from_slice, to_vec, BorshDeserialize, BorshSerialize}; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct A<'a> { x: u64, b: B, y: f32, z: String, t: (String, u64), btree_map_string: BTreeMap, btree_set_u64: BTreeSet, linked_list_string: LinkedList, vec_deque_u64: VecDeque, bytes: Bytes, bytes_mut: BytesMut, v: Vec, w: Box<[u8]>, box_str: Box, i: [u8; 32], u: Result, lazy: Option, c: borrow::Cow<'a, str>, cow_arr: borrow::Cow<'a, [borrow::Cow<'a, str>]>, range_u32: ops::Range, #[borsh(skip)] skipped: Option, } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct B { x: u64, y: i32, c: C, } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] enum C { C1, C2(u64), C3(u64, u64), C4 { x: u64, y: u64 }, C5(D), } #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)] struct D { x: u64, } #[derive(BorshSerialize)] struct E<'a, 'b> { a: &'a A<'b>, } #[derive(BorshSerialize)] struct F1<'a, 'b> { aa: &'a [&'a A<'b>], } #[derive(BorshDeserialize)] struct F2<'b> { aa: Vec>, } #[test] fn test_ultimate_combined_all_features() { let mut map: BTreeMap = BTreeMap::new(); map.insert("test".into(), "test".into()); let mut set: BTreeSet = BTreeSet::new(); set.insert(u64::MAX); let cow_arr = [ borrow::Cow::Borrowed("Hello1"), borrow::Cow::Owned("Hello2".to_string()), ]; let a = A { x: 1, b: B { x: 2, y: 3, c: C::C5(D { x: 1 }), }, y: 4.0, z: "123".to_string(), t: ("Hello".to_string(), 10), btree_map_string: map.clone(), btree_set_u64: set.clone(), linked_list_string: vec!["a".to_string(), "b".to_string()].into_iter().collect(), vec_deque_u64: vec![1, 2, 3].into_iter().collect(), bytes: vec![5, 4, 3, 2, 1].into(), bytes_mut: BytesMut::from(&[1, 2, 3, 4, 5][..]), v: vec!["qwe".to_string(), "zxc".to_string()], w: vec![0].into_boxed_slice(), box_str: Box::from("asd"), i: [4u8; 32], u: Ok("Hello".to_string()), lazy: Some(5), c: borrow::Cow::Borrowed("Hello"), cow_arr: borrow::Cow::Borrowed(&cow_arr), range_u32: 12..71, skipped: Some(6), }; let encoded_a = to_vec(&a).unwrap(); let e = E { a: &a }; let encoded_ref_a = to_vec(&e).unwrap(); assert_eq!(encoded_ref_a, encoded_a); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded_a); let decoded_a = from_slice::(&encoded_a).unwrap(); let expected_a = A { x: 1, b: B { x: 2, y: 3, c: C::C5(D { x: 1 }), }, y: 4.0, z: a.z.clone(), t: ("Hello".to_string(), 10), btree_map_string: map, btree_set_u64: set, linked_list_string: vec!["a".to_string(), "b".to_string()].into_iter().collect(), vec_deque_u64: vec![1, 2, 3].into_iter().collect(), bytes: vec![5, 4, 3, 2, 1].into(), bytes_mut: BytesMut::from(&[1, 2, 3, 4, 5][..]), v: a.v.clone(), w: a.w.clone(), box_str: Box::from("asd"), i: a.i, u: Ok("Hello".to_string()), lazy: Some(5), c: borrow::Cow::Owned("Hello".to_string()), cow_arr: borrow::Cow::Owned(vec![ borrow::Cow::Owned("Hello1".to_string()), borrow::Cow::Owned("Hello2".to_string()), ]), range_u32: 12..71, skipped: None, }; assert_eq!(expected_a, decoded_a); let f1 = F1 { aa: &[&a, &a] }; let encoded_f1 = to_vec(&f1).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded_f1); let decoded_f2 = from_slice::(&encoded_f1).unwrap(); assert_eq!(decoded_f2.aa.len(), 2); assert!(decoded_f2.aa.iter().all(|f2_a| f2_a == &expected_a)); } #[test] fn test_object_length() { let mut map: BTreeMap = BTreeMap::new(); map.insert("test".into(), "test".into()); let mut set: BTreeSet = BTreeSet::new(); set.insert(u64::MAX); set.insert(100); set.insert(103); set.insert(109); let cow_arr = [ borrow::Cow::Borrowed("Hello1"), borrow::Cow::Owned("Hello2".to_string()), ]; let a = A { x: 1, b: B { x: 2, y: 3, c: C::C5(D { x: 1 }), }, y: 4.0, z: "123".to_string(), t: ("Hello".to_string(), 10), btree_map_string: map.clone(), btree_set_u64: set.clone(), linked_list_string: vec!["a".to_string(), "b".to_string()].into_iter().collect(), vec_deque_u64: vec![1, 2, 3].into_iter().collect(), bytes: vec![5, 4, 3, 2, 1].into(), bytes_mut: BytesMut::from(&[1, 2, 3, 4, 5][..]), v: vec!["qwe".to_string(), "zxc".to_string()], w: vec![0].into_boxed_slice(), box_str: Box::from("asd"), i: [4u8; 32], u: Ok("Hello".to_string()), lazy: Some(5), c: borrow::Cow::Borrowed("Hello"), cow_arr: borrow::Cow::Borrowed(&cow_arr), range_u32: 12..71, skipped: Some(6), }; let encoded_a_len = to_vec(&a).unwrap().len(); let len_helper_result = borsh::object_length(&a).unwrap(); assert_eq!(encoded_a_len, len_helper_result); } borsh-1.5.7/tests/roundtrip/test_arrays.rs000064400000000000000000000046121046102023000170540ustar 00000000000000#![allow(clippy::float_cmp)] use borsh::{from_slice, to_vec}; #[cfg(feature = "derive")] use borsh::{BorshDeserialize, BorshSerialize}; use alloc::string::{String, ToString}; macro_rules! test_array { ($v: expr, $t: ty, $len: expr) => { let buf = borsh::to_vec(&$v).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(buf); let actual_v: [$t; $len] = from_slice(&buf).expect("failed to deserialize"); assert_eq!($v.len(), actual_v.len()); #[allow(clippy::reversed_empty_ranges)] for i in 0..$len { assert_eq!($v[i], actual_v[i]); } }; } macro_rules! test_arrays { ($test_name: ident, $el: expr, $t: ty) => { #[test] fn $test_name() { test_array!([$el; 0], $t, 0); test_array!([$el; 1], $t, 1); test_array!([$el; 2], $t, 2); test_array!([$el; 3], $t, 3); test_array!([$el; 4], $t, 4); test_array!([$el; 8], $t, 8); test_array!([$el; 16], $t, 16); test_array!([$el; 32], $t, 32); test_array!([$el; 64], $t, 64); test_array!([$el; 65], $t, 65); } }; } test_arrays!(test_array_u8, 100u8, u8); test_arrays!(test_array_i8, 100i8, i8); test_arrays!(test_array_u32, 1000000000u32, u32); test_arrays!(test_array_u64, 1000000000000000000u64, u64); test_arrays!( test_array_u128, 1000000000000000000000000000000000000u128, u128 ); test_arrays!(test_array_f32, 1000000000.0f32, f32); test_arrays!(test_array_array_u8, [100u8; 32], [u8; 32]); test_arrays!(test_array_zst, (), ()); #[cfg(feature = "derive")] #[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug)] struct CustomStruct(u8); #[cfg(feature = "derive")] #[test] fn test_custom_struct_array() { let arr = [CustomStruct(0), CustomStruct(1), CustomStruct(2)]; let serialized = to_vec(&arr).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(serialized); let deserialized: [CustomStruct; 3] = from_slice(&serialized).unwrap(); assert_eq!(arr, deserialized); } #[test] fn test_string_array() { let arr = ["0".to_string(), "1".to_string(), "2".to_string()]; let serialized = to_vec(&arr).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(serialized); let deserialized: [String; 3] = from_slice(&serialized).unwrap(); assert_eq!(arr, deserialized); } borsh-1.5.7/tests/roundtrip/test_ascii_strings.rs000064400000000000000000000032741046102023000204170ustar 00000000000000use borsh::{from_slice, to_vec}; use alloc::{ string::String, vec::Vec, }; /// Verifies serialisation and deserialisation of an ASCII string `value`. fn check_ascii(value: &str) -> Vec { // Caller promises value is ASCII. let ascii_str = ascii::AsciiStr::from_ascii(&value).unwrap(); let buf = to_vec(ascii_str).unwrap(); // AsciiStr and AsciiString serialise the same way String does. assert_eq!(buf, to_vec(&ascii::AsciiString::from(ascii_str)).unwrap()); // Check round trip. let got = from_slice::(&buf).unwrap(); assert_eq!(ascii_str, got); buf } macro_rules! test_ascii_string { ($test_name: ident, $str: expr, $snap:expr) => { #[test] fn $test_name() { let value = String::from($str); let _buf = check_ascii(&value); #[cfg(feature = "std")] if $snap { insta::assert_debug_snapshot!(_buf); } } }; } test_ascii_string!(test_empty_string, "", true); test_ascii_string!(test_a, "a", true); test_ascii_string!(test_hello_world, "hello world", true); test_ascii_string!(test_x_1024, "x".repeat(1024), true); test_ascii_string!(test_x_4096, "x".repeat(4096), false); test_ascii_string!(test_x_65535, "x".repeat(65535), false); test_ascii_string!(test_hello_10, "hello world!".repeat(30), true); test_ascii_string!(test_hello_1000, "hello Achilles!".repeat(1000), false); #[test] fn test_ascii_char() { use ascii::AsciiChar; let buf = to_vec(&AsciiChar::Dot).unwrap(); assert_eq!(".".as_bytes(), buf); assert_eq!(AsciiChar::Dot, from_slice::(&buf).unwrap()); from_slice::(&[b'\x80']).unwrap_err(); } borsh-1.5.7/tests/roundtrip/test_btree_map.rs000064400000000000000000000047411046102023000175140ustar 00000000000000use alloc::{ collections::{BTreeMap, BTreeSet}, string::{String, ToString}, vec, vec::Vec, }; use borsh::{from_slice, BorshSerialize}; macro_rules! btreeset_test_template [ [$test_name: ident, $($key: expr),* ] => [ #[allow(unused_mut)] #[allow(redundant_semicolons)] #[test] fn $test_name() { let mut set = BTreeSet::new(); set_insert_deser_assert_macro!(set, data, $($key),*); let actual_set = from_slice::>(&data).unwrap(); assert_eq!(set, actual_set); } ] ]; macro_rules! btreemap_test_template [ [$test_name: ident, $($key: expr => $value: expr),* ] => [ #[allow(unused_mut)] #[allow(redundant_semicolons)] #[test] fn $test_name() { let mut map = BTreeMap::new(); map_insert_deser_assert_macro!(map, data, $($key => $value),*); let actual_map = from_slice::>(&data).unwrap(); assert_eq!(map, actual_map); } ] ]; btreeset_test_template!(test_empty_btreeset,); btreeset_test_template!(test_1_element_btreeset, "one".to_string()); btreeset_test_template!( test_2_element_btreeset, "one".to_string(), "different".to_string() ); btreeset_test_template!( test_default_btreeset, "foo".to_string(), "many".to_string(), "various".to_string(), "different".to_string(), "keys".to_string(), "one".to_string() ); btreemap_test_template!(test_default_btreemap, "foo".to_string() => "bar".to_string(), "one".to_string() => "two".to_string() ); btreemap_test_template!(test_empty_btreemap,); btreemap_test_template!(test_1_element_btreemap, "one".to_string() => "element".to_string() ); btreemap_test_template!(test_8_element_btreemap, "one".to_string() => "element".to_string(), "key".to_string() => "powers".to_string(), "more".to_string() => "of".to_string(), "various".to_string() => "two".to_string(), "different".to_string() => "are".to_string(), "keys".to_string() => "always".to_string(), "where".to_string() => "unpredictable".to_string(), "nowhere".to_string() => "pile".to_string() ); #[cfg(feature = "de_strict_order")] const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order"; set_wrong_order_test!(test_btreeset_deser_err_wrong_order, BTreeSet); map_wrong_order_test!(test_btreemap_deser_err_wrong_order, BTreeMap); borsh-1.5.7/tests/roundtrip/test_cells.rs000064400000000000000000000010531046102023000166510ustar 00000000000000use alloc::string::{String, ToString}; #[test] fn test_cell_roundtrip() { let cell = core::cell::Cell::new(42u32); let out = borsh::to_vec(&cell).unwrap(); let cell_round: core::cell::Cell = borsh::from_slice(&out).unwrap(); assert_eq!(cell, cell_round); } #[test] fn test_ref_cell_roundtrip() { let rcell = core::cell::RefCell::new("str".to_string()); let out = borsh::to_vec(&rcell).unwrap(); let rcell_round: core::cell::RefCell = borsh::from_slice(&out).unwrap(); assert_eq!(rcell, rcell_round); } borsh-1.5.7/tests/roundtrip/test_cow.rs000064400000000000000000000032401046102023000163370ustar 00000000000000use borsh::{from_slice, to_vec}; use core::{matches, ops::Deref}; use alloc::string::ToString; use alloc::{borrow::Cow, vec}; #[test] fn test_cow_str() { let input: Cow<'_, str> = Cow::Borrowed("static input"); let encoded = to_vec(&input).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let out: Cow<'_, str> = from_slice(&encoded).unwrap(); assert!(matches!(out, Cow::Owned(..))); assert_eq!(input, out); assert_eq!(out, "static input"); } #[test] fn test_cow_byte_slice() { let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let input: Cow<'_, [u8]> = Cow::Borrowed(&arr); let encoded = to_vec(&input).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let out: Cow<'_, [u8]> = from_slice(&encoded).unwrap(); assert!(matches!(out, Cow::Owned(..))); assert_eq!(input, out); assert_eq!(out, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); } #[test] fn test_cow_slice_of_cow_str() { let arr = [ Cow::Borrowed("first static input"), Cow::Owned("second static input".to_string()), ]; let input: Cow<'_, [Cow<'_, str>]> = Cow::Borrowed(&arr); let encoded = to_vec(&input).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let out: Cow<'_, [Cow<'_, str>]> = from_slice(&encoded).unwrap(); assert!(matches!(out, Cow::Owned(..))); for element in out.deref() { assert!(matches!(element, Cow::Owned(..))); } assert_eq!(input, out); assert_eq!( out, vec![ Cow::Borrowed("first static input"), Cow::Borrowed("second static input"), ] ); } borsh-1.5.7/tests/roundtrip/test_hash_map.rs000064400000000000000000000132161046102023000173330ustar 00000000000000#[cfg(feature = "std")] use core::hash::BuildHasher; #[cfg(feature = "hashbrown")] use hashbrown::{HashMap, HashSet}; #[cfg(feature = "std")] use std::collections::{ hash_map::{DefaultHasher, RandomState}, HashMap, HashSet, }; #[cfg(not(feature = "std"))] use core::iter::IntoIterator; use alloc::{ string::{String, ToString}, vec, vec::Vec, }; use borsh::{from_slice, BorshSerialize}; macro_rules! hashset_test_template [ [$test_name: ident, $($key: expr),* ] => [ #[allow(unused_mut)] #[allow(redundant_semicolons)] #[test] fn $test_name() { let mut set = HashSet::new(); set_insert_deser_assert_macro!(set, data, $($key),*); let actual_set = from_slice::>(&data).unwrap(); assert_eq!(set, actual_set); } ] ]; macro_rules! hashmap_test_template [ [$test_name: ident, $($key: expr => $value: expr),* ] => [ #[allow(unused_mut)] #[allow(redundant_semicolons)] #[test] fn $test_name() { let mut map = HashMap::new(); map_insert_deser_assert_macro!(map, data, $($key => $value),*); let actual_map = from_slice::>(&data).unwrap(); assert_eq!(map, actual_map); } ] ]; #[derive(Default)] #[cfg(feature = "std")] struct NewHasher(RandomState); #[cfg(feature = "std")] impl BuildHasher for NewHasher { type Hasher = DefaultHasher; fn build_hasher(&self) -> DefaultHasher { self.0.build_hasher() } } #[cfg(feature = "std")] macro_rules! generic_hashset_test_template [ [$test_name: ident, $($key: expr),* ] => [ #[allow(unused_mut)] #[allow(redundant_semicolons)] #[test] fn $test_name() { let mut set = HashSet::with_hasher(NewHasher::default()); set_insert_deser_assert_macro!(set, data, $($key),*); let actual_set = from_slice::>(&data).unwrap(); assert_eq!(set, actual_set); } ] ]; #[cfg(feature = "std")] macro_rules! generic_hashmap_test_template [ [$test_name: ident, $($key: expr => $value: expr),* ] => [ #[allow(unused_mut)] #[allow(redundant_semicolons)] #[test] fn $test_name() { let mut map = HashMap::with_hasher(NewHasher::default()); map_insert_deser_assert_macro!(map, data, $($key => $value),*); let actual_map = from_slice::>(&data).unwrap(); assert_eq!(map, actual_map); } ] ]; hashset_test_template!(test_empty_hashset,); hashset_test_template!(test_1_element_hashset, "one".to_string()); hashset_test_template!( test_2_element_hashset, "one".to_string(), "different".to_string() ); hashset_test_template!( test_default_hashset, "foo".to_string(), "many".to_string(), "various".to_string(), "different".to_string(), "keys".to_string(), "one".to_string() ); #[cfg(feature = "std")] generic_hashset_test_template!(test_empty_generic_hashset,); #[cfg(feature = "std")] generic_hashset_test_template!(test_1_element_generic_hashset, "one".to_string()); #[cfg(feature = "std")] generic_hashset_test_template!( test_2_element_generic_hashset, "one".to_string(), "different".to_string() ); #[cfg(feature = "std")] generic_hashset_test_template!( test_generic_hashset, "foo".to_string(), "many".to_string(), "various".to_string(), "different".to_string(), "keys".to_string(), "one".to_string() ); hashmap_test_template!(test_default_hashmap, "foo".to_string() => "bar".to_string(), "one".to_string() => "two".to_string() ); hashmap_test_template!(test_empty_hashmap,); hashmap_test_template!(test_1_element_hashmap, "one".to_string() => "element".to_string() ); hashmap_test_template!(test_8_element_hashmap, "one".to_string() => "element".to_string(), "key".to_string() => "powers".to_string(), "more".to_string() => "of".to_string(), "various".to_string() => "two".to_string(), "different".to_string() => "are".to_string(), "keys".to_string() => "always".to_string(), "where".to_string() => "unpredictable".to_string(), "nowhere".to_string() => "pile".to_string() ); #[cfg(feature = "std")] generic_hashmap_test_template!(test_generic_hash_hashmap, "foo".to_string() => "bar".to_string(), "one".to_string() => "two".to_string() ); #[cfg(feature = "std")] generic_hashmap_test_template!(test_empty_generic_hashmap,); #[cfg(feature = "std")] generic_hashmap_test_template!(test_1_element_generic_hashmap, "one".to_string() => "element".to_string() ); #[cfg(feature = "std")] generic_hashmap_test_template!(test_8_element_generic_hashmap, "one".to_string() => "element".to_string(), "key".to_string() => "powers".to_string(), "more".to_string() => "of".to_string(), "various".to_string() => "two".to_string(), "different".to_string() => "are".to_string(), "keys".to_string() => "always".to_string(), "where".to_string() => "unpredictable".to_string(), "nowhere".to_string() => "pile".to_string() ); #[cfg(feature = "de_strict_order")] const ERROR_WRONG_ORDER_OF_KEYS: &str = "keys were not serialized in ascending order"; set_wrong_order_test!(test_hashset_deser_err_wrong_order, HashSet); #[cfg(feature = "std")] set_wrong_order_test!(test_generic_hashset_deser_err_wrong_order, HashSet); map_wrong_order_test!(test_hashmap_deser_err_wrong_order, HashMap); #[cfg(feature = "std")] map_wrong_order_test!(test_generic_hashmap_deser_err_wrong_order, HashMap); borsh-1.5.7/tests/roundtrip/test_indexmap.rs000064400000000000000000000025271046102023000173630ustar 00000000000000use borsh::BorshDeserialize; use indexmap::{IndexMap, IndexSet}; #[test] // Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L100 // license: MIT OR Apache-2.0 fn test_indexmap_roundtrip() { let original_map: IndexMap = { let mut map = IndexMap::new(); map.insert(1, 2); map.insert(3, 4); map.insert(5, 6); map }; let serialized_map = borsh::to_vec(&original_map).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(serialized_map); let deserialized_map: IndexMap = BorshDeserialize::try_from_slice(&serialized_map).unwrap(); assert_eq!(original_map, deserialized_map); } #[test] // Taken from https://github.com/indexmap-rs/indexmap/blob/dd06e5773e4f91748396c67d00c83637f5c0dd49/src/borsh.rs#L115 // license: MIT OR Apache-2.0 fn test_indexset_roundtrip() { let mut original_set = IndexSet::new(); [1, 2, 3, 4, 5, 6].iter().for_each(|&i| { original_set.insert(i); }); let serialized_set = borsh::to_vec(&original_set).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(serialized_set); let deserialized_set: IndexSet = BorshDeserialize::try_from_slice(&serialized_set).unwrap(); assert_eq!(original_set, deserialized_set); } borsh-1.5.7/tests/roundtrip/test_ip_addr.rs000064400000000000000000000041001046102023000171450ustar 00000000000000use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; #[test] fn test_ipv4_addr_roundtrip_enum() { let original = IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)); let encoded = borsh::to_vec(&original).expect("Serialization failed"); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let decoded = borsh::from_slice::(&encoded).expect("Deserialization failed"); assert_eq!(original, decoded); } #[test] fn test_ipv4_addr_roundtrip() { let original = Ipv4Addr::new(192, 168, 0, 1); let encoded = borsh::to_vec(&original).expect("Serialization failed"); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let decoded = borsh::from_slice::(&encoded).expect("Deserialization failed"); assert_eq!(original, decoded); } #[test] fn test_ipv6_addr_roundtrip_enum() { let original = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); let encoded = borsh::to_vec(&original).expect("Serialization failed"); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let decoded = borsh::from_slice::(&encoded).expect("Deserialization failed"); assert_eq!(original, decoded); } #[test] fn test_ipv6_addr_roundtrip() { let original = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1); let encoded = borsh::to_vec(&original).expect("Serialization failed"); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let decoded = borsh::from_slice::(&encoded).expect("Deserialization failed"); assert_eq!(original, decoded); } #[test] fn test_ipaddr_vec_roundtrip() { let original = vec![ IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)), IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)), IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)), ]; let encoded = borsh::to_vec(&original).expect("Serialization failed"); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let decoded = borsh::from_slice::>(&encoded).expect("Deserialization failed"); assert_eq!(original, decoded); } borsh-1.5.7/tests/roundtrip/test_nonzero_integers.rs000064400000000000000000000013761046102023000211510ustar 00000000000000use borsh::from_slice; use core::num::*; #[test] fn test_nonzero_integer_u8() { let bytes = &[1]; assert_eq!(from_slice::(bytes).unwrap().get(), 1); } #[test] fn test_nonzero_integer_u32() { let bytes = &[255, 0, 0, 0]; assert_eq!(from_slice::(bytes).unwrap().get(), 255); } #[test] fn test_nonzero_integer_usize() { let bytes = &[1, 1, 0, 0, 0, 0, 0, 0]; assert_eq!(from_slice::(bytes).unwrap().get(), 257); } #[test] fn test_nonzero_integer_i64() { let bytes = &[255; 8]; assert_eq!(from_slice::(bytes).unwrap().get(), -1); } #[test] fn test_nonzero_integer_i16b() { let bytes = &[0, 0b1000_0000]; assert_eq!(from_slice::(bytes).unwrap().get(), i16::MIN); } borsh-1.5.7/tests/roundtrip/test_primitives.rs000064400000000000000000000015261046102023000177470ustar 00000000000000use borsh::{from_slice, to_vec}; macro_rules! test_primitive { ($test_name: ident, $v: expr, $t: ty) => { #[test] fn $test_name() { let expected: $t = $v; let buf = to_vec(&expected).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(buf); let actual = from_slice::<$t>(&buf).expect("failed to deserialize"); assert_eq!(actual, expected); } }; } test_primitive!(test_isize_neg, -100isize, isize); test_primitive!(test_isize_pos, 100isize, isize); test_primitive!(test_isize_min, isize::min_value(), isize); test_primitive!(test_isize_max, isize::max_value(), isize); test_primitive!(test_usize, 100usize, usize); test_primitive!(test_usize_min, usize::min_value(), usize); test_primitive!(test_usize_max, usize::max_value(), usize); borsh-1.5.7/tests/roundtrip/test_range.rs000064400000000000000000000004311046102023000166420ustar 00000000000000#[test] fn test_ranges() { let want = (1..2, 3..=4, 5.., ..6, ..=7, ..); let encoded = borsh::to_vec(&want).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(encoded); let got = borsh::from_slice(&encoded).unwrap(); assert_eq!(want, got); } borsh-1.5.7/tests/roundtrip/test_rc.rs000064400000000000000000000021541046102023000161560ustar 00000000000000pub use alloc::{rc, sync}; use borsh::{from_slice, to_vec}; #[test] fn test_rc_roundtrip() { let value = rc::Rc::new(8u8); let serialized = to_vec(&value).unwrap(); let deserialized = from_slice::>(&serialized).unwrap(); assert_eq!(value, deserialized); } #[test] fn test_slice_rc() { let original: &[i32] = &[1, 2, 3, 4, 6, 9, 10]; let shared: rc::Rc<[i32]> = rc::Rc::from(original); let serialized = to_vec(&shared).unwrap(); let deserialized = from_slice::>(&serialized).unwrap(); assert_eq!(original, &*deserialized); } #[test] fn test_arc_roundtrip() { let value = sync::Arc::new(8u8); let serialized = to_vec(&value).unwrap(); let deserialized = from_slice::>(&serialized).unwrap(); assert_eq!(value, deserialized); } #[test] fn test_slice_arc() { let original: &[i32] = &[1, 2, 3, 4, 6, 9, 10]; let shared: sync::Arc<[i32]> = sync::Arc::from(original); let serialized = to_vec(&shared).unwrap(); let deserialized = from_slice::>(&serialized).unwrap(); assert_eq!(original, &*deserialized); } borsh-1.5.7/tests/roundtrip/test_strings.rs000064400000000000000000000025351046102023000172460ustar 00000000000000use alloc::string::String; use borsh::{from_slice, to_vec}; /// Verifies serialisation and deserialisation of the given string. /// /// Returns serialised representation of the string. fn check_string(value: &str) -> alloc::vec::Vec { // Encoding is the same as Vec with UTF-8 encoded string. let buf = to_vec(value.as_bytes()).unwrap(); assert_eq!(buf, to_vec(value).unwrap()); assert_eq!(buf, to_vec(&String::from(value)).unwrap()); // Check round trip. assert_eq!(value, from_slice::(&buf).unwrap()); buf } macro_rules! test_string { ($test_name: ident, $str: expr, $snap:expr) => { #[test] fn $test_name() { let value = String::from($str); let _buf = check_string(&value); #[cfg(feature = "std")] if $snap { insta::assert_debug_snapshot!(_buf); } } }; } test_string!(test_empty_string, "", true); test_string!(test_a, "a", true); test_string!(test_hello_world, "hello world", true); test_string!(test_x_1024, "x".repeat(1024), true); test_string!(test_x_4096, "x".repeat(4096), false); test_string!(test_x_65535, "x".repeat(65535), false); test_string!(test_hello_10, "hello world!".repeat(30), true); test_string!(test_hello_1000, "hello world!".repeat(1000), false); test_string!(test_non_ascii, "💩", true); borsh-1.5.7/tests/roundtrip/test_tuple.rs000064400000000000000000000004761046102023000167100ustar 00000000000000use borsh::{from_slice, to_vec}; #[test] fn test_unary_tuple() { let expected = (true,); let buf = to_vec(&expected).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(buf); let actual = from_slice::<(bool,)>(&buf).expect("failed to deserialize"); assert_eq!(actual, expected); } borsh-1.5.7/tests/roundtrip/test_vecs.rs000064400000000000000000000023511046102023000165110ustar 00000000000000use borsh::{from_slice, to_vec}; use alloc::{ string::{String, ToString}, vec, vec::Vec, }; macro_rules! test_vec { ($v: expr, $t: ty, $snap: expr) => { let buf = to_vec(&$v).unwrap(); #[cfg(feature = "std")] if $snap { insta::assert_debug_snapshot!(buf); } let actual_v: Vec<$t> = from_slice(&buf).expect("failed to deserialize"); assert_eq!(actual_v, $v); }; } macro_rules! test_vecs { ($test_name: ident, $el: expr, $t: ty) => { #[test] fn $test_name() { test_vec!(Vec::<$t>::new(), $t, true); test_vec!(vec![$el], $t, true); test_vec!(vec![$el; 10], $t, true); test_vec!(vec![$el; 100], $t, true); test_vec!(vec![$el; 1000], $t, false); // one assumes that the concept has been proved test_vec!(vec![$el; 10000], $t, false); } }; } test_vecs!(test_vec_u8, 100u8, u8); test_vecs!(test_vec_i8, 100i8, i8); test_vecs!(test_vec_u32, 1000000000u32, u32); test_vecs!(test_vec_f32, 1000000000.0f32, f32); test_vecs!(test_vec_string, "a".to_string(), String); test_vecs!(test_vec_vec_u8, vec![100u8; 10], Vec); test_vecs!(test_vec_vec_u32, vec![100u32; 10], Vec); borsh-1.5.7/tests/schema/container_extension/test_max_size.rs000064400000000000000000000141241046102023000226610ustar 00000000000000use crate::common_macro::schema_imports::*; #[track_caller] fn test_ok(want: usize) { let schema = BorshSchemaContainer::for_type::(); assert_eq!(Ok(want), schema.max_serialized_size()); } #[track_caller] fn test_err(err: SchemaMaxSerializedSizeError) { let schema = BorshSchemaContainer::for_type::(); assert_eq!(Err(err), schema.max_serialized_size()); } const MAX_LEN: usize = u32::MAX as usize; #[test] fn max_serialized_size_primitives() { test_ok::<()>(0); test_ok::(1); test_ok::(4); test_ok::(8); test_ok::(1); test_ok::(2); test_ok::(4); test_ok::(8); test_ok::(16); test_ok::(1); test_ok::(2); test_ok::(4); test_ok::(8); test_ok::(16); test_ok::(1); test_ok::(2); test_ok::(4); test_ok::(8); test_ok::(16); test_ok::(1); test_ok::(2); test_ok::(4); test_ok::(8); test_ok::(16); test_ok::(8); test_ok::(8); test_ok::(8); } #[test] fn max_serialized_size_built_in_types() { test_ok::(0); test_ok::>(2); test_ok::>(8); test_ok::>(1); test_ok::>(2); test_ok::>(9); test_ok::>>(1 + 4 + MAX_LEN); test_ok::<()>(0); test_ok::<(u8,)>(1); test_ok::<(u8, u32)>(5); test_ok::<[u8; 0]>(0); test_ok::<[u8; 16]>(16); test_ok::<[[u8; 4]; 4]>(16); test_ok::<[u16; 0]>(0); test_ok::<[u16; 16]>(32); test_ok::<[[u16; 4]; 4]>(32); test_ok::>(4 + MAX_LEN); test_ok::(4 + MAX_LEN); test_err::>>(SchemaMaxSerializedSizeError::Overflow); test_ok::>>(4 + MAX_LEN * 4); test_ok::<[[[(); MAX_LEN]; MAX_LEN]; MAX_LEN]>(0); } #[test] fn max_serialized_size_derived_types() { #[derive(BorshSchema)] pub struct Empty; #[derive(BorshSchema)] pub struct Named { _foo: usize, _bar: [u8; 15], } #[derive(BorshSchema)] #[allow(unused)] pub struct Unnamed(usize, [u8; 15]); #[derive(BorshSchema)] struct Multiple { _usz0: usize, _usz1: usize, _usz2: usize, _vec0: Vec, _vec1: Vec, } #[derive(BorshSchema)] #[allow(unused)] struct Recursive(Option>); test_ok::(0); test_ok::(23); test_ok::(23); test_ok::(3 * 8 + 2 * (4 + MAX_LEN * 8)); test_err::(SchemaMaxSerializedSizeError::Overflow); test_err::(SchemaMaxSerializedSizeError::Recursive); } #[test] fn max_serialized_size_custom_enum() { #[allow(dead_code)] enum Maybe { Just(T), Nothing, } impl BorshSchema for Maybe { fn declaration() -> Declaration { let res = format!(r#"Maybe<{}, {}>"#, N, T::declaration()); res } fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Enum { tag_width: N, variants: vec![ (0, "Just".into(), T::declaration()), (1, "Nothing".into(), <()>::declaration()), ], }; add_definition(Self::declaration(), definition, definitions); T::add_definitions_recursively(definitions); <()>::add_definitions_recursively(definitions); } } test_ok::>(0); test_ok::>(2); test_ok::>(8); test_ok::>(1); test_ok::>(3); test_ok::>(9); test_ok::>(4); test_ok::>(6); test_ok::>(12); } #[test] fn max_serialized_size_bound_vec() { #[allow(dead_code)] struct BoundVec; impl BorshSchema for BoundVec { fn declaration() -> Declaration { format!("BoundVec<{}, {}>", W, N) } fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: W, length_range: 0..=N, elements: "u8".to_string(), }; add_definition(Self::declaration(), definition, definitions); u8::add_definitions_recursively(definitions); } } test_ok::>(4); test_ok::>(4 + u16::MAX as usize); test_ok::>(24); test_ok::>(1); test_ok::>(1 + u16::MAX as usize); test_ok::>(21); test_ok::>(0); test_ok::>(u16::MAX as usize); test_ok::>(20); } #[test] fn max_serialized_size_small_vec() { #[allow(dead_code)] struct SmallVec(core::marker::PhantomData); impl BorshSchema for SmallVec { fn declaration() -> Declaration { format!(r#"SmallVec<{}>"#, T::declaration()) } fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: 1, length_range: 0..=u8::MAX as u64, elements: T::declaration(), }; add_definition(Self::declaration(), definition, definitions); T::add_definitions_recursively(definitions); } } test_ok::>(u8::MAX as usize + 1); test_ok::>(u8::MAX as usize * 2 + 1); } borsh-1.5.7/tests/schema/container_extension/test_schema_validate.rs000064400000000000000000000051001046102023000241450ustar 00000000000000use crate::common_macro::schema_imports::*; use alloc::{boxed::Box, collections::BTreeMap, format, string::ToString, vec::Vec}; #[track_caller] fn test_ok() { let schema = BorshSchemaContainer::for_type::(); assert_eq!(Ok(()), schema.validate()); } #[track_caller] fn test_err(err: SchemaContainerValidateError) { let schema = BorshSchemaContainer::for_type::(); assert_eq!(Err(err), schema.validate()); } #[test] fn validate_for_derived_types() { #[derive(BorshSchema)] pub struct Empty; #[derive(BorshSchema)] pub struct Named { _foo: usize, _bar: [u8; 15], } #[derive(BorshSchema)] #[allow(unused)] pub struct Unnamed(usize, [u8; 15]); #[derive(BorshSchema)] #[allow(unused)] struct Recursive(Option>); #[derive(BorshSchema)] #[allow(unused)] struct RecursiveSequence(Vec); // thankfully, this one cannot be constructed #[derive(BorshSchema)] #[allow(unused)] struct RecursiveArray(Box<[RecursiveArray; 3]>); test_ok::(); test_ok::(); test_ok::(); test_ok::(); test_ok::(); test_ok::(); test_ok::(); test_ok::<[(); 300]>(); } #[test] fn validate_for_zst_sequences() { test_err::>>(SchemaContainerValidateError::ZSTSequence( "Vec<()>".to_string(), )); test_err::>(SchemaContainerValidateError::ZSTSequence( "Vec".to_string(), )); } #[test] fn validate_bound_vec() { #[allow(dead_code)] struct BoundVec; impl BorshSchema for BoundVec { fn declaration() -> Declaration { format!("BoundVec<{}, {}>", W, N) } fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Sequence { length_width: W, length_range: 0..=N, elements: "u8".to_string(), }; add_definition(Self::declaration(), definition, definitions); u8::add_definitions_recursively(definitions); } } test_ok::>(); test_err::>(SchemaContainerValidateError::TagTooNarrow( "BoundVec<1, 65535>".to_string(), )); test_ok::>(); test_ok::>(); } borsh-1.5.7/tests/schema/schema_conflict/test_schema_conflict.rs000064400000000000000000000127761046102023000232410ustar 00000000000000use crate::common_macro::schema_imports::*; use alloc::{ collections::{BTreeMap, BTreeSet}, format, string::ToString, vec::Vec, }; struct ConflictingSchema; impl BorshSchema for ConflictingSchema { #[inline] fn add_definitions_recursively(definitions: &mut BTreeMap) { let fields = Fields::Empty; let def = Definition::Struct { fields }; add_definition(Self::declaration(), def, definitions); } #[inline] fn declaration() -> Declaration { "i64".into() } } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_conflict() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); ::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_vec() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_range() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively( &mut defs, ); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_slice() { let mut defs = Default::default(); <[i64] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); <[ConflictingSchema] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_array() { let mut defs = Default::default(); <[i64; 10] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); <[ConflictingSchema; 10] as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_option() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } #[allow(unused)] #[derive(borsh::BorshSchema)] struct GenericStruct { field: T, } #[test] fn test_implicit_conflict_struct() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively( &mut defs, ); // NOTE: the contents of `defs` depend on the order of 2 above lines // this loophole is needed to enable derives for recursive structs/enums } #[allow(unused)] #[derive(borsh::BorshSchema)] struct SelfConflictingStruct { field_1: i64, field_2: ConflictingSchema, } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_self_conflicting_struct() { let mut defs = Default::default(); ::add_definitions_recursively(&mut defs); } #[allow(unused)] #[derive(borsh::BorshSchema)] enum GenericEnum { A { field: T }, B(u64), } #[test] fn test_implicit_conflict_enum() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); // NOTE: the contents of `defs` depend on the order of 2 above lines // this loophole is needed to enable derives for recursive structs/enums } #[allow(unused)] #[derive(borsh::BorshSchema)] enum SelfConflictingEnum { A { field: i64 }, B { field: ConflictingSchema }, } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_self_conflicting_enum() { let mut defs = Default::default(); ::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_result() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_btreemap() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_btreeset() { let mut defs = Default::default(); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } #[test] #[should_panic(expected = "Redefining type schema for i64")] fn test_implicit_conflict_tuple() { let mut defs = Default::default(); <(i64, u8) as borsh::BorshSchema>::add_definitions_recursively(&mut defs); <(ConflictingSchema, u8) as borsh::BorshSchema>::add_definitions_recursively(&mut defs); } borsh-1.5.7/tests/schema/test_arrays.rs000064400000000000000000000031551046102023000162670ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn simple_array() { let actual_name = <[u64; 32]>::declaration(); let mut actual_defs = schema_map!(); <[u64; 32]>::add_definitions_recursively(&mut actual_defs); assert_eq!("[u64; 32]", actual_name); assert_eq!( schema_map! { "[u64; 32]" => Definition::Sequence { length_width: Definition::ARRAY_LENGTH_WIDTH, length_range: 32..=32, elements: "u64".to_string() }, "u64" => Definition::Primitive(8) }, actual_defs ); } #[test] fn nested_array() { let actual_name = <[[[u64; 9]; 10]; 32]>::declaration(); let mut actual_defs = schema_map!(); <[[[u64; 9]; 10]; 32]>::add_definitions_recursively(&mut actual_defs); assert_eq!("[[[u64; 9]; 10]; 32]", actual_name); assert_eq!( schema_map! { "[u64; 9]" => Definition::Sequence { length_width: Definition::ARRAY_LENGTH_WIDTH, length_range: 9..=9, elements: "u64".to_string() }, "[[u64; 9]; 10]" => Definition::Sequence { length_width: Definition::ARRAY_LENGTH_WIDTH, length_range: 10..=10, elements: "[u64; 9]".to_string() }, "[[[u64; 9]; 10]; 32]" => Definition::Sequence { length_width: Definition::ARRAY_LENGTH_WIDTH, length_range: 32..=32, elements: "[[u64; 9]; 10]".to_string() }, "u64" => Definition::Primitive(8) }, actual_defs ); } borsh-1.5.7/tests/schema/test_ascii_strings.rs000064400000000000000000000021331046102023000176220ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn test_ascii_strings() { assert_eq!("AsciiString", ascii::AsciiStr::declaration()); assert_eq!("AsciiString", ascii::AsciiString::declaration()); assert_eq!("AsciiChar", ascii::AsciiChar::declaration()); let want_char = schema_map! { "AsciiChar" => Definition::Primitive(1) }; let mut actual_defs = schema_map!(); ascii::AsciiChar::add_definitions_recursively(&mut actual_defs); assert_eq!(want_char, actual_defs); let want = schema_map! { "AsciiString" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "AsciiChar".to_string() }, "AsciiChar" => Definition::Primitive(1) }; let mut actual_defs = schema_map!(); ascii::AsciiStr::add_definitions_recursively(&mut actual_defs); assert_eq!(want, actual_defs); let mut actual_defs = schema_map!(); ascii::AsciiString::add_definitions_recursively(&mut actual_defs); assert_eq!(want, actual_defs); } borsh-1.5.7/tests/schema/test_box.rs000064400000000000000000000004251046102023000155530ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn boxed_schema() { let boxed_declaration = Box::::declaration(); assert_eq!("String", boxed_declaration); let boxed_declaration = Box::<[u8]>::declaration(); assert_eq!("Vec", boxed_declaration); } borsh-1.5.7/tests/schema/test_btree_map.rs000064400000000000000000000036661046102023000167330ustar 00000000000000use crate::common_macro::schema_imports::*; use alloc::collections::{BTreeMap, BTreeSet}; #[test] fn b_tree_map() { let actual_name = BTreeMap::::declaration(); let mut actual_defs = schema_map!(); BTreeMap::::add_definitions_recursively(&mut actual_defs); assert_eq!("BTreeMap", actual_name); assert_eq!( schema_map! { "BTreeMap" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(u64, String)".to_string(), } , "(u64, String)" => Definition::Tuple { elements: vec![ "u64".to_string(), "String".to_string()]}, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } #[test] fn b_tree_set() { let actual_name = BTreeSet::::declaration(); let mut actual_defs = schema_map!(); BTreeSet::::add_definitions_recursively(&mut actual_defs); assert_eq!("BTreeSet", actual_name); assert_eq!( schema_map! { "BTreeSet" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "String".to_string(), }, "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } borsh-1.5.7/tests/schema/test_cells.rs000064400000000000000000000021661046102023000160710ustar 00000000000000use crate::common_macro::schema_imports::*; fn common_map_i32() -> BTreeMap { schema_map! { "i32" => Definition::Primitive(4) } } fn common_map_slice_i32() -> BTreeMap { schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "i32".to_string() }, "i32" => Definition::Primitive(4) } } #[test] fn test_cell() { assert_eq!("i32", as BorshSchema>::declaration()); let mut actual_defs = schema_map!(); as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!(common_map_i32(), actual_defs); } #[test] fn test_ref_cell_vec() { assert_eq!( "Vec", > as BorshSchema>::declaration() ); let mut actual_defs = schema_map!(); > as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!(common_map_slice_i32(), actual_defs); } borsh-1.5.7/tests/schema/test_cow.rs000064400000000000000000000041211046102023000155500ustar 00000000000000use crate::common_macro::schema_imports::*; use alloc::borrow::Cow; #[test] fn test_cow_str() { assert_eq!("String", as BorshSchema>::declaration()); let mut actual_defs = schema_map!(); as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!( schema_map! { "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } #[test] fn test_cow_byte_slice() { assert_eq!("Vec", as BorshSchema>::declaration()); let mut actual_defs = schema_map!(); as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!( schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string(), }, "u8" => Definition::Primitive(1) }, actual_defs ); } #[test] fn test_cow_slice_of_cow_str() { assert_eq!( "Vec", ]> as BorshSchema>::declaration() ); let mut actual_defs = schema_map!(); ]> as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!( schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "String".to_string(), }, "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } borsh-1.5.7/tests/schema/test_enum_discriminants.rs000064400000000000000000000066161046102023000206660ustar 00000000000000use crate::common_macro::schema_imports::*; #[allow(unused)] #[derive(BorshSchema)] #[borsh(use_discriminant = true)] #[repr(i16)] enum XY { A, B = 20, C, D(u32, u32), E = 10, F(u64), } #[test] fn test_schema_discriminant_no_unit_type() { assert_eq!("XY".to_string(), XY::declaration()); let mut defs = Default::default(); XY::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "XY" => Definition::Enum { tag_width: 1, variants: vec![ (0, "A".to_string(), "XYA".to_string()), (20, "B".to_string(), "XYB".to_string()), (21, "C".to_string(), "XYC".to_string()), (22, "D".to_string(), "XYD".to_string()), (10, "E".to_string(), "XYE".to_string()), (11, "F".to_string(), "XYF".to_string()) ] }, "XYA" => Definition::Struct{ fields: Fields::Empty }, "XYB" => Definition::Struct{ fields: Fields::Empty }, "XYC" => Definition::Struct{ fields: Fields::Empty }, "XYD" => Definition::Struct{ fields: Fields::UnnamedFields( vec!["u32".to_string(), "u32".to_string()] )}, "XYE" => Definition::Struct{ fields: Fields::Empty }, "XYF" => Definition::Struct{ fields: Fields::UnnamedFields( vec!["u64".to_string()] )}, "u32" => Definition::Primitive(4), "u64" => Definition::Primitive(8) }, defs ); } #[allow(unused)] #[derive(BorshSchema)] #[borsh(use_discriminant = false)] #[repr(i16)] enum XYNoDiscriminant { A, B = 20, C, D(u32, u32), E = 10, F(u64), } #[test] fn test_schema_discriminant_no_unit_type_no_use_discriminant() { assert_eq!( "XYNoDiscriminant".to_string(), XYNoDiscriminant::declaration() ); let mut defs = Default::default(); XYNoDiscriminant::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "XYNoDiscriminant" => Definition::Enum { tag_width: 1, variants: vec![ (0, "A".to_string(), "XYNoDiscriminantA".to_string()), (1, "B".to_string(), "XYNoDiscriminantB".to_string()), (2, "C".to_string(), "XYNoDiscriminantC".to_string()), (3, "D".to_string(), "XYNoDiscriminantD".to_string()), (4, "E".to_string(), "XYNoDiscriminantE".to_string()), (5, "F".to_string(), "XYNoDiscriminantF".to_string()) ] }, "XYNoDiscriminantA" => Definition::Struct{ fields: Fields::Empty }, "XYNoDiscriminantB" => Definition::Struct{ fields: Fields::Empty }, "XYNoDiscriminantC" => Definition::Struct{ fields: Fields::Empty }, "XYNoDiscriminantD" => Definition::Struct{ fields: Fields::UnnamedFields( vec!["u32".to_string(), "u32".to_string()] )}, "XYNoDiscriminantE" => Definition::Struct{ fields: Fields::Empty }, "XYNoDiscriminantF" => Definition::Struct{ fields: Fields::UnnamedFields( vec!["u64".to_string()] )}, "u32" => Definition::Primitive(4), "u64" => Definition::Primitive(8) }, defs ); } borsh-1.5.7/tests/schema/test_generic_enums.rs000064400000000000000000000240731046102023000176130ustar 00000000000000use crate::common_macro::schema_imports::*; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(feature = "std")] use std::collections::HashMap; #[test] pub fn complex_enum_generics() { #[derive(borsh::BorshSchema)] struct Tomatoes; #[derive(borsh::BorshSchema)] struct Cucumber; #[derive(borsh::BorshSchema)] struct Oil; #[derive(borsh::BorshSchema)] struct Wrapper; #[derive(borsh::BorshSchema)] struct Filling; #[allow(unused)] #[derive(borsh::BorshSchema)] enum A { Bacon, Eggs, Salad(Tomatoes, C, Oil), Sausage { wrapper: W, filling: Filling }, } assert_eq!( "A".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "Cucumber" => Definition::Struct {fields: Fields::Empty}, "ASalad" => Definition::Struct{ fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil".to_string()]) }, "ABacon" => Definition::Struct {fields: Fields::Empty}, "Oil" => Definition::Struct {fields: Fields::Empty}, "A" => Definition::Enum { tag_width: 1, variants: vec![ (0, "Bacon".to_string(), "ABacon".to_string()), (1, "Eggs".to_string(), "AEggs".to_string()), (2, "Salad".to_string(), "ASalad".to_string()), (3, "Sausage".to_string(), "ASausage".to_string()) ] }, "Wrapper" => Definition::Struct {fields: Fields::Empty}, "Tomatoes" => Definition::Struct {fields: Fields::Empty}, "ASausage" => Definition::Struct { fields: Fields::NamedFields(vec![ ("wrapper".to_string(), "Wrapper".to_string()), ("filling".to_string(), "Filling".to_string()) ]) }, "AEggs" => Definition::Struct {fields: Fields::Empty}, "Filling" => Definition::Struct {fields: Fields::Empty} }, defs ); } // Checks that recursive definitions work. Also checks that re-instantiations of templated types work. #[cfg(hash_collections)] #[test] pub fn complex_enum_generics2() { #[derive(borsh::BorshSchema)] struct Tomatoes; #[derive(borsh::BorshSchema)] struct Cucumber; #[allow(unused)] #[derive(borsh::BorshSchema)] struct Oil { seeds: HashMap, liquid: Option, } #[allow(unused)] #[derive(borsh::BorshSchema)] struct Wrapper { foo: Option, bar: Box>, } #[derive(borsh::BorshSchema)] struct Filling; #[allow(unused)] #[derive(borsh::BorshSchema)] enum A { Bacon, Eggs, Salad(Tomatoes, C, Oil), Sausage { wrapper: W, filling: Filling }, } assert_eq!( "A>".to_string(), >>::declaration() ); let mut defs = Default::default(); >>::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A>" => Definition::Enum { tag_width: 1, variants: vec![ (0, "Bacon".to_string(), "ABacon".to_string()), (1, "Eggs".to_string(), "AEggs".to_string()), (2, "Salad".to_string(), "ASalad".to_string()), (3, "Sausage".to_string(), "ASausage>".to_string()) ] }, "A" => Definition::Enum { tag_width: 1, variants: vec![ (0, "Bacon".to_string(), "ABacon".to_string()), (1, "Eggs".to_string(), "AEggs".to_string()), (2, "Salad".to_string(), "ASalad".to_string()), (3, "Sausage".to_string(), "ASausage".to_string()) ] }, "ABacon" => Definition::Struct {fields: Fields::Empty}, "AEggs" => Definition::Struct {fields: Fields::Empty}, "ASalad" => Definition::Struct {fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil".to_string()])}, "ASalad" => Definition::Struct { fields: Fields::UnnamedFields( vec!["Tomatoes".to_string(), "String".to_string(), "Oil".to_string() ])}, "ASausage>" => Definition::Struct {fields: Fields::NamedFields(vec![("wrapper".to_string(), "Wrapper".to_string()), ("filling".to_string(), "Filling".to_string())])}, "ASausage" => Definition::Struct{ fields: Fields::NamedFields(vec![("wrapper".to_string(), "String".to_string()), ("filling".to_string(), "Filling".to_string())])}, "Cucumber" => Definition::Struct {fields: Fields::Empty}, "Filling" => Definition::Struct {fields: Fields::Empty}, "HashMap" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(u64, String)".to_string(), }, "Oil" => Definition::Struct { fields: Fields::NamedFields(vec![("seeds".to_string(), "HashMap".to_string()), ("liquid".to_string(), "Option".to_string())])}, "Option" => Definition::Enum { tag_width: 1, variants: vec![ (0, "None".to_string(), "()".to_string()), (1, "Some".to_string(), "String".to_string()) ] }, "Option" => Definition::Enum { tag_width: 1, variants: vec![ (0, "None".to_string(), "()".to_string()), (1, "Some".to_string(), "u64".to_string()) ] }, "Tomatoes" => Definition::Struct {fields: Fields::Empty}, "(u64, String)" => Definition::Tuple {elements: vec!["u64".to_string(), "String".to_string()]}, "Wrapper" => Definition::Struct{ fields: Fields::NamedFields(vec![("foo".to_string(), "Option".to_string()), ("bar".to_string(), "A".to_string())])}, "u64" => Definition::Primitive(8), "()" => Definition::Primitive(0), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } fn common_map_associated() -> BTreeMap { schema_map! { "EnumParametrized" => Definition::Enum { tag_width: 1, variants: vec![ (0, "B".to_string(), "EnumParametrizedB".to_string()), (1, "C".to_string(), "EnumParametrizedC".to_string()) ] }, "EnumParametrizedB" => Definition::Struct { fields: Fields::NamedFields(vec![ ("x".to_string(), "BTreeMap".to_string()), ("y".to_string(), "String".to_string()), ("z".to_string(), "i8".to_string()) ])}, "EnumParametrizedC" => Definition::Struct{ fields: Fields::UnnamedFields(vec!["String".to_string(), "u16".to_string()])}, "BTreeMap" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(u32, u16)".to_string(), }, "(u32, u16)" => Definition::Tuple { elements: vec!["u32".to_string(), "u16".to_string()]}, "u32" => Definition::Primitive(4), "i8" => Definition::Primitive(1), "u16" => Definition::Primitive(2), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) } } #[test] pub fn generic_associated_item1() { trait TraitName { type Associated; #[allow(unused)] fn method(&self); } impl TraitName for u32 { type Associated = i8; fn method(&self) {} } #[allow(unused)] #[derive(borsh::BorshSchema)] enum EnumParametrized where K: TraitName, K: core::cmp::Ord, V: core::cmp::Ord, { B { x: BTreeMap, y: String, z: K::Associated, }, C(T, u16), } assert_eq!( "EnumParametrized".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!(common_map_associated(), defs); } #[test] pub fn generic_associated_item2() { trait TraitName { type Associated; #[allow(unused)] fn method(&self); } impl TraitName for u32 { type Associated = i8; fn method(&self) {} } #[allow(unused)] #[derive(borsh::BorshSchema)] enum EnumParametrized where K: TraitName, K: core::cmp::Ord, V: core::cmp::Ord, { B { x: BTreeMap, y: String, #[borsh(schema(params = "K => ::Associated"))] z: ::Associated, }, C(T, u16), } assert_eq!( "EnumParametrized".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!(common_map_associated(), defs); } borsh-1.5.7/tests/schema/test_generic_structs.rs000064400000000000000000000145671046102023000202020ustar 00000000000000use crate::common_macro::schema_imports::*; #[cfg(feature = "hashbrown")] use hashbrown::HashMap; #[cfg(feature = "std")] use std::collections::HashMap; #[test] pub fn wrapper_struct() { #[derive(borsh::BorshSchema)] struct A(T); assert_eq!("A".to_string(), >::declaration()); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A" => Definition::Struct {fields: Fields::UnnamedFields(vec!["u64".to_string()])}, "u64" => Definition::Primitive(8) }, defs ); } #[test] pub fn tuple_struct_params() { #[derive(borsh::BorshSchema)] struct A(K, V); assert_eq!( "A".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A" => Definition::Struct { fields: Fields::UnnamedFields(vec![ "u64".to_string(), "String".to_string() ])}, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } #[cfg(hash_collections)] #[test] pub fn simple_generics() { #[derive(borsh::BorshSchema)] struct A { _f1: HashMap, _f2: String, } assert_eq!( "A".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A" => Definition::Struct { fields: Fields::NamedFields(vec![ ("_f1".to_string(), "HashMap".to_string()), ("_f2".to_string(), "String".to_string()) ]) }, "HashMap" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(u64, String)".to_string(), }, "(u64, String)" => Definition::Tuple{elements: vec!["u64".to_string(), "String".to_string()]}, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } fn common_map_associated() -> BTreeMap { schema_map! { "Parametrized" => Definition::Struct { fields: Fields::NamedFields(vec![ ("field".to_string(), "i8".to_string()), ("another".to_string(), "String".to_string()) ]) }, "i8" => Definition::Primitive(1), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) } } #[test] pub fn generic_associated_item() { trait TraitName { type Associated; #[allow(unused)] fn method(&self); } impl TraitName for u32 { type Associated = i8; fn method(&self) {} } #[allow(unused)] #[derive(borsh::BorshSchema)] struct Parametrized where T: TraitName, { field: T::Associated, another: V, } assert_eq!( "Parametrized".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!(common_map_associated(), defs); } #[test] pub fn generic_associated_item2() { trait TraitName { type Associated; #[allow(unused)] fn method(&self); } impl TraitName for u32 { type Associated = i8; fn method(&self) {} } #[allow(unused)] #[derive(borsh::BorshSchema)] struct Parametrized where T: TraitName, { #[borsh(schema(params = "T => ::Associated"))] field: ::Associated, another: V, } assert_eq!( "Parametrized".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!(common_map_associated(), defs); } #[test] pub fn generic_associated_item3() { trait TraitName { type Associated; #[allow(unused)] fn method(&self); } impl TraitName for u32 { type Associated = i8; fn method(&self) {} } #[allow(unused)] #[derive(borsh::BorshSchema)] struct Parametrized where T: TraitName, { #[borsh(schema(params = "T => T, T => ::Associated"))] field: (::Associated, T), another: V, } assert_eq!( "Parametrized".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "Parametrized" => Definition::Struct { fields: Fields::NamedFields(vec![ ("field".to_string(), "(i8, u32)".to_string()), ("another".to_string(), "String".to_string()) ]) }, "(i8, u32)" => Definition::Tuple { elements: vec!["i8".to_string(), "u32".to_string()] }, "i8" => Definition::Primitive(1), "u32" => Definition::Primitive(4), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } borsh-1.5.7/tests/schema/test_hash_map.rs000064400000000000000000000040421046102023000165420ustar 00000000000000use crate::common_macro::schema_imports::*; #[cfg(feature = "hashbrown")] use hashbrown::{HashMap, HashSet}; #[cfg(feature = "std")] use std::collections::{HashMap, HashSet}; #[test] fn simple_map() { let actual_name = HashMap::::declaration(); let mut actual_defs = schema_map!(); HashMap::::add_definitions_recursively(&mut actual_defs); assert_eq!("HashMap", actual_name); assert_eq!( schema_map! { "HashMap" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(u64, String)".to_string(), } , "(u64, String)" => Definition::Tuple { elements: vec![ "u64".to_string(), "String".to_string()], }, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } #[test] fn simple_set() { let actual_name = HashSet::::declaration(); let mut actual_defs = schema_map!(); HashSet::::add_definitions_recursively(&mut actual_defs); assert_eq!("HashSet", actual_name); assert_eq!( schema_map! { "HashSet" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "String".to_string(), }, "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } borsh-1.5.7/tests/schema/test_ip_addr.rs000064400000000000000000000005071046102023000163660ustar 00000000000000use crate::common_macro::schema_imports::*; use std::net::IpAddr; #[test] fn ip_addr_schema() { let actual_name = IpAddr::declaration(); assert_eq!("IpAddr", actual_name); let mut defs = Default::default(); IpAddr::add_definitions_recursively(&mut defs); insta::assert_snapshot!(format!("{:#?}", defs)); } borsh-1.5.7/tests/schema/test_option.rs000064400000000000000000000032441046102023000162750ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn simple_option() { let actual_name = Option::::declaration(); let mut actual_defs = schema_map!(); Option::::add_definitions_recursively(&mut actual_defs); assert_eq!("Option", actual_name); assert_eq!( schema_map! { "Option" => Definition::Enum { tag_width: 1, variants: vec![ (0, "None".to_string(), "()".to_string()), (1, "Some".to_string(), "u64".to_string()), ] }, "u64" => Definition::Primitive(8), "()" => Definition::Primitive(0) }, actual_defs ); } #[test] fn nested_option() { let actual_name = Option::>::declaration(); let mut actual_defs = schema_map!(); Option::>::add_definitions_recursively(&mut actual_defs); assert_eq!("Option>", actual_name); assert_eq!( schema_map! { "Option" => Definition::Enum { tag_width: 1, variants: vec![ (0, "None".to_string(), "()".to_string()), (1, "Some".to_string(), "u64".to_string()), ] }, "Option>" => Definition::Enum { tag_width: 1, variants: vec![ (0, "None".to_string(), "()".to_string()), (1, "Some".to_string(), "Option".to_string()), ] }, "u64" => Definition::Primitive(8), "()" => Definition::Primitive(0) }, actual_defs ); } borsh-1.5.7/tests/schema/test_phantom_data.rs000064400000000000000000000026371046102023000174310ustar 00000000000000use crate::common_macro::schema_imports::*; use core::marker::PhantomData; #[test] fn phantom_data_schema() { let phantom_declaration = PhantomData::::declaration(); assert_eq!("()", phantom_declaration); let phantom_declaration = PhantomData::>::declaration(); assert_eq!("()", phantom_declaration); } #[test] pub fn generic_struct_with_phantom_data_derived() { #[allow(unused)] #[derive(borsh::BorshSchema)] struct Parametrized { field: K, another: PhantomData, } assert_eq!( "Parametrized".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "Parametrized" => Definition::Struct { fields: Fields::NamedFields(vec![ ("field".to_string(), "String".to_string()), ("another".to_string(), "()".to_string()) ]) }, "()" => Definition::Primitive(0), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } borsh-1.5.7/tests/schema/test_primitives.rs000064400000000000000000000011501046102023000171520ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn isize_schema() { let schema = schema_container_of::(); assert_eq!( schema, BorshSchemaContainer::new( "i64".to_string(), schema_map! { "i64" => Definition::Primitive(8) } ) ) } #[test] fn usize_schema() { let schema = schema_container_of::(); assert_eq!( schema, BorshSchemaContainer::new( "u64".to_string(), schema_map! { "u64" => Definition::Primitive(8) } ) ) } borsh-1.5.7/tests/schema/test_range.rs000064400000000000000000000030101046102023000160500ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn range() { assert_eq!("RangeFull", ::declaration()); let mut actual_defs = schema_map!(); ::add_definitions_recursively(&mut actual_defs); assert_eq!( schema_map! { "RangeFull" => Definition::Struct { fields: Fields::Empty } }, actual_defs ); let actual_name = >::declaration(); let mut actual_defs = schema_map!(); >::add_definitions_recursively(&mut actual_defs); assert_eq!("Range", actual_name); assert_eq!( schema_map! { "Range" => Definition::Struct { fields: Fields::NamedFields(vec![ ("start".into(), "u64".into()), ("end".into(), "u64".into()), ]) }, "u64" => Definition::Primitive(8) }, actual_defs ); let actual_name = >::declaration(); let mut actual_defs = schema_map!(); >::add_definitions_recursively(&mut actual_defs); assert_eq!("RangeTo", actual_name); assert_eq!( schema_map! { "RangeTo" => Definition::Struct { fields: Fields::NamedFields(vec![ ("end".into(), "u64".into()), ]) }, "u64" => Definition::Primitive(8) }, actual_defs ); } borsh-1.5.7/tests/schema/test_rc.rs000064400000000000000000000031551046102023000153720ustar 00000000000000use crate::common_macro::schema_imports::*; use alloc::{rc, sync}; fn common_map_i32() -> BTreeMap { schema_map! { "i32" => Definition::Primitive(4) } } fn common_map_slice_i32() -> BTreeMap { schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "i32".to_string() }, "i32" => Definition::Primitive(4) } } #[test] fn test_rc() { assert_eq!("i32", as BorshSchema>::declaration()); let mut actual_defs = schema_map!(); as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!(common_map_i32(), actual_defs); } #[test] fn test_slice_rc() { assert_eq!("Vec", as BorshSchema>::declaration()); let mut actual_defs = schema_map!(); as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!(common_map_slice_i32(), actual_defs); } #[test] fn test_arc() { assert_eq!("i32", as BorshSchema>::declaration()); let mut actual_defs = schema_map!(); as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!(common_map_i32(), actual_defs); } #[test] fn test_slice_arc() { assert_eq!("Vec", as BorshSchema>::declaration()); let mut actual_defs = schema_map!(); as BorshSchema>::add_definitions_recursively(&mut actual_defs); assert_eq!(common_map_slice_i32(), actual_defs); } borsh-1.5.7/tests/schema/test_recursive_enums.rs000064400000000000000000000032531046102023000202030ustar 00000000000000use crate::common_macro::schema_imports::*; #[allow(unused)] #[derive(borsh::BorshSchema)] enum ERecD { B { x: String, y: i32 }, C(u8, Vec), } #[test] pub fn recursive_enum_schema() { let mut defs = Default::default(); ERecD::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "ERecD" => Definition::Enum { tag_width: 1, variants: vec![ (0, "B".to_string(), "ERecDB".to_string()), (1, "C".to_string(), "ERecDC".to_string()), ] }, "ERecDB" => Definition::Struct { fields: Fields::NamedFields ( vec![ ("x".to_string(), "String".to_string()), ("y".to_string(), "i32".to_string()), ] ) }, "ERecDC" => Definition::Struct { fields: Fields::UnnamedFields( vec![ "u8".to_string(), "Vec".to_string(), ]) }, "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "ERecD".to_string(), }, "i32" => Definition::Primitive(4), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } borsh-1.5.7/tests/schema/test_recursive_structs.rs000064400000000000000000000027331046102023000205650ustar 00000000000000use crate::common_macro::schema_imports::*; #[allow(unused)] #[derive(borsh::BorshSchema)] struct CRecC { a: String, b: BTreeMap, } #[test] pub fn recursive_struct_schema() { let mut defs = Default::default(); CRecC::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "CRecC" => Definition::Struct { fields: Fields::NamedFields( vec![ ( "a".to_string(), "String".to_string(), ), ( "b".to_string(), "BTreeMap".to_string(), ), ] ) }, "BTreeMap" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(String, CRecC)".to_string(), }, "(String, CRecC)" => Definition::Tuple {elements: vec!["String".to_string(), "CRecC".to_string()]}, "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } borsh-1.5.7/tests/schema/test_schema_with_third_party.rs000064400000000000000000000115411046102023000216700ustar 00000000000000use crate::common_macro::schema_imports::*; // use alloc::collections::BTreeMap; #[allow(unused)] struct ThirdParty(BTreeMap); #[allow(unused)] mod third_party_impl { use crate::common_macro::schema_imports::*; pub(super) fn declaration( ) -> borsh::schema::Declaration { let params = vec![::declaration(), ::declaration()]; format!(r#"{}<{}>"#, "ThirdParty", params.join(", ")) } pub(super) fn add_definitions_recursively( definitions: &mut BTreeMap, ) { let fields = borsh::schema::Fields::UnnamedFields(vec![ as borsh::BorshSchema>::declaration(), ]); let definition = borsh::schema::Definition::Struct { fields }; let no_recursion_flag = definitions.get(&declaration::()).is_none(); borsh::schema::add_definition(declaration::(), definition, definitions); if no_recursion_flag { as borsh::BorshSchema>::add_definitions_recursively(definitions); } } } #[allow(unused)] #[derive(BorshSchema)] struct A { #[borsh(schema(with_funcs( declaration = "third_party_impl::declaration::", definitions = "third_party_impl::add_definitions_recursively::" )))] x: ThirdParty, y: u64, } #[allow(unused)] #[derive(BorshSchema)] enum C { C3(u64, u64), C4( u64, #[borsh(schema(with_funcs( declaration = "third_party_impl::declaration::", definitions = "third_party_impl::add_definitions_recursively::" )))] ThirdParty, ), } #[test] pub fn struct_overriden() { assert_eq!( "A".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A" => Definition::Struct { fields: Fields::NamedFields(vec![ ("x".to_string(), "ThirdParty".to_string()), ("y".to_string(), "u64".to_string())] )}, "ThirdParty" => Definition::Struct { fields: Fields::UnnamedFields(vec![ "BTreeMap".to_string(), ]) }, "BTreeMap"=> Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(u64, String)".to_string(), }, "(u64, String)" => Definition::Tuple { elements: vec!["u64".to_string(), "String".to_string()]}, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } #[test] pub fn enum_overriden() { assert_eq!( "C".to_string(), >::declaration() ); let mut defs = Default::default(); >::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "C" => Definition::Enum { tag_width: 1, variants: vec![ (0, "C3".to_string(), "CC3".to_string()), (1, "C4".to_string(), "CC4".to_string()) ] }, "CC3" => Definition::Struct { fields: Fields::UnnamedFields(vec!["u64".to_string(), "u64".to_string()]) }, "CC4" => Definition::Struct { fields: Fields::UnnamedFields(vec![ "u64".to_string(), "ThirdParty".to_string() ]) }, "ThirdParty" => Definition::Struct { fields: Fields::UnnamedFields(vec![ "BTreeMap".to_string(), ]) }, "BTreeMap"=> Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "(u64, String)".to_string(), }, "(u64, String)" => Definition::Tuple { elements: vec!["u64".to_string(), "String".to_string()]}, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } borsh-1.5.7/tests/schema/test_simple_enums.rs000064400000000000000000000111401046102023000174570ustar 00000000000000use crate::common_macro::schema_imports::*; use borsh::{try_from_slice_with_schema, try_to_vec_with_schema}; #[test] pub fn simple_enum() { #[allow(dead_code)] #[derive(borsh::BorshSchema)] enum A { Bacon, Eggs, } // https://github.com/near/borsh-rs/issues/112 #[allow(unused)] impl A { pub fn declaration() -> usize { 42 } } assert_eq!("A".to_string(), ::declaration()); let mut defs = Default::default(); A::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "ABacon" => Definition::Struct{ fields: Fields::Empty }, "AEggs" => Definition::Struct{ fields: Fields::Empty }, "A" => Definition::Enum { tag_width: 1, variants: vec![(0, "Bacon".to_string(), "ABacon".to_string()), (1, "Eggs".to_string(), "AEggs".to_string())] } }, defs ); } #[test] pub fn single_field_enum() { #[allow(dead_code)] #[derive(borsh::BorshSchema)] enum A { Bacon, } assert_eq!("A".to_string(), A::declaration()); let mut defs = Default::default(); A::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "ABacon" => Definition::Struct {fields: Fields::Empty}, "A" => Definition::Enum { tag_width: 1, variants: vec![(0, "Bacon".to_string(), "ABacon".to_string())] } }, defs ); } #[test] pub fn complex_enum_with_schema() { #[derive( borsh::BorshSchema, Default, borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Debug, )] struct Tomatoes; #[derive( borsh::BorshSchema, Default, borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Debug, )] struct Cucumber; #[derive( borsh::BorshSchema, Default, borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Debug, )] struct Oil; #[derive( borsh::BorshSchema, Default, borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Debug, )] struct Wrapper; #[derive( borsh::BorshSchema, Default, borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Debug, )] struct Filling; #[derive( borsh::BorshSchema, borsh::BorshSerialize, borsh::BorshDeserialize, PartialEq, Debug, )] enum A { Bacon, Eggs, Salad(Tomatoes, Cucumber, Oil), Sausage { wrapper: Wrapper, filling: Filling }, } impl Default for A { fn default() -> Self { A::Sausage { wrapper: Default::default(), filling: Default::default(), } } } // First check schema. assert_eq!("A".to_string(), A::declaration()); let mut defs = Default::default(); A::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "Cucumber" => Definition::Struct {fields: Fields::Empty}, "ASalad" => Definition::Struct{ fields: Fields::UnnamedFields(vec!["Tomatoes".to_string(), "Cucumber".to_string(), "Oil".to_string()])}, "ABacon" => Definition::Struct {fields: Fields::Empty}, "Oil" => Definition::Struct {fields: Fields::Empty}, "A" => Definition::Enum { tag_width: 1, variants: vec![ (0, "Bacon".to_string(), "ABacon".to_string()), (1, "Eggs".to_string(), "AEggs".to_string()), (2, "Salad".to_string(), "ASalad".to_string()), (3, "Sausage".to_string(), "ASausage".to_string()) ] }, "Wrapper" => Definition::Struct {fields: Fields::Empty}, "Tomatoes" => Definition::Struct {fields: Fields::Empty}, "ASausage" => Definition::Struct { fields: Fields::NamedFields(vec![ ("wrapper".to_string(), "Wrapper".to_string()), ("filling".to_string(), "Filling".to_string()) ])}, "AEggs" => Definition::Struct {fields: Fields::Empty}, "Filling" => Definition::Struct {fields: Fields::Empty} }, defs ); // Then check that we serialize and deserialize with schema. let obj = A::default(); let data = try_to_vec_with_schema(&obj).unwrap(); #[cfg(feature = "std")] insta::assert_debug_snapshot!(data); let obj2: A = try_from_slice_with_schema(&data).unwrap(); assert_eq!(obj, obj2); } borsh-1.5.7/tests/schema/test_simple_structs.rs000064400000000000000000000062741046102023000200530ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] pub fn unit_struct() { #[derive(borsh::BorshSchema)] struct A; // https://github.com/near/borsh-rs/issues/112 #[allow(unused)] impl A { pub fn declaration() -> usize { 42 } } assert_eq!("A".to_string(), ::declaration()); let mut defs = Default::default(); A::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A" => Definition::Struct {fields: Fields::Empty} }, defs ); } #[test] pub fn simple_struct() { #[derive(borsh::BorshSchema)] struct A { _f1: u64, _f2: String, } assert_eq!("A".to_string(), A::declaration()); let mut defs = Default::default(); A::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A" => Definition::Struct{ fields: Fields::NamedFields(vec![ ("_f1".to_string(), "u64".to_string()), ("_f2".to_string(), "String".to_string()) ])}, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } #[test] pub fn tuple_struct() { #[derive(borsh::BorshSchema)] #[allow(unused)] struct A(u64, String); assert_eq!("A".to_string(), A::declaration()); let mut defs = Default::default(); A::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "A" => Definition::Struct {fields: Fields::UnnamedFields(vec![ "u64".to_string(), "String".to_string() ])}, "u64" => Definition::Primitive(8), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, defs ); } #[test] pub fn boxed() { #[derive(borsh::BorshSchema)] struct A { _f1: Box, _f2: Box, _f3: Box<[u8]>, } assert_eq!("A".to_string(), A::declaration()); let mut defs = Default::default(); A::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string(), }, "A" => Definition::Struct{ fields: Fields::NamedFields(vec![ ("_f1".to_string(), "u64".to_string()), ("_f2".to_string(), "String".to_string()), ("_f3".to_string(), "Vec".to_string()) ])}, "u64" => Definition::Primitive(8), "u8" => Definition::Primitive(1), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() } }, defs ); } borsh-1.5.7/tests/schema/test_strings.rs000064400000000000000000000021721046102023000164550ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn test_string() { let actual_name = str::declaration(); assert_eq!("String", actual_name); let actual_name = String::declaration(); assert_eq!("String", actual_name); let mut actual_defs = schema_map!(); String::add_definitions_recursively(&mut actual_defs); assert_eq!( schema_map! { "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); let mut actual_defs = schema_map!(); str::add_definitions_recursively(&mut actual_defs); assert_eq!( schema_map! { "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } borsh-1.5.7/tests/schema/test_tuple.rs000064400000000000000000000046461046102023000161250ustar 00000000000000use crate::common_macro::schema_imports::*; #[test] fn test_unary_tuple_schema() { assert_eq!("(bool,)", <(bool,)>::declaration()); let mut defs = Default::default(); <(bool,)>::add_definitions_recursively(&mut defs); assert_eq!( schema_map! { "(bool,)" => Definition::Tuple { elements: vec!["bool".to_string()] }, "bool" => Definition::Primitive(1) }, defs ); } #[test] fn simple_tuple() { let actual_name = <(u64, core::num::NonZeroU16, String)>::declaration(); let mut actual_defs = schema_map!(); <(u64, core::num::NonZeroU16, String)>::add_definitions_recursively(&mut actual_defs); assert_eq!("(u64, NonZeroU16, String)", actual_name); assert_eq!( schema_map! { "(u64, NonZeroU16, String)" => Definition::Tuple { elements: vec![ "u64".to_string(), "NonZeroU16".to_string(), "String".to_string() ] }, "u64" => Definition::Primitive(8), "NonZeroU16" => Definition::Primitive(2), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() }, "u8" => Definition::Primitive(1) }, actual_defs ); } #[test] fn nested_tuple() { let actual_name = <(u64, (u8, bool), String)>::declaration(); let mut actual_defs = schema_map!(); <(u64, (u8, bool), String)>::add_definitions_recursively(&mut actual_defs); assert_eq!("(u64, (u8, bool), String)", actual_name); assert_eq!( schema_map! { "(u64, (u8, bool), String)" => Definition::Tuple { elements: vec![ "u64".to_string(), "(u8, bool)".to_string(), "String".to_string(), ]}, "(u8, bool)" => Definition::Tuple { elements: vec![ "u8".to_string(), "bool".to_string()]}, "u64" => Definition::Primitive(8), "u8" => Definition::Primitive(1), "bool" => Definition::Primitive(1), "String" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u8".to_string() } }, actual_defs ); } borsh-1.5.7/tests/schema/test_vecs.rs000064400000000000000000000060061046102023000157240ustar 00000000000000use crate::common_macro::schema_imports::*; use alloc::collections::{VecDeque, LinkedList}; macro_rules! test_vec_like_collection_schema { [$test_name: ident, $type: ident] => [ #[test] fn $test_name() { let actual_name = $type::::declaration(); let mut actual_defs = schema_map!(); $type::::add_definitions_recursively(&mut actual_defs); assert_eq!(format!("{}", stringify!($type)), actual_name); assert_eq!( schema_map! { actual_name => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u64".to_string(), }, "u64" => Definition::Primitive(8) }, actual_defs ); } ]; } test_vec_like_collection_schema!(simple_vec, Vec); test_vec_like_collection_schema!(vec_deque, VecDeque); test_vec_like_collection_schema!(linked_list, LinkedList); #[test] fn nested_vec() { let actual_name = Vec::>::declaration(); let mut actual_defs = schema_map!(); Vec::>::add_definitions_recursively(&mut actual_defs); assert_eq!("Vec>", actual_name); assert_eq!( schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "u64".to_string(), }, "Vec>" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "Vec".to_string(), }, "u64" => Definition::Primitive(8) }, actual_defs ); } #[test] fn slice_schema_container() { let schema = schema_container_of::<[i64]>(); assert_eq!( schema, BorshSchemaContainer::new( "Vec".to_string(), schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "i64".to_string(), }, "i64" => Definition::Primitive(8) } ) ) } #[test] fn vec_schema_container() { let schema = schema_container_of::>(); assert_eq!( schema, BorshSchemaContainer::new( "Vec".to_string(), schema_map! { "Vec" => Definition::Sequence { length_width: Definition::DEFAULT_LENGTH_WIDTH, length_range: Definition::DEFAULT_LENGTH_RANGE, elements: "i64".to_string(), }, "i64" => Definition::Primitive(8) } ) ) } borsh-1.5.7/tests/smoke.rs000064400000000000000000000020701046102023000136000ustar 00000000000000#![cfg_attr(not(feature = "std"), no_std)] // Smoke tests that ensure that we don't accidentally remove top-level // re-exports in a minor release. #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(not(feature = "std"))] use alloc::vec; use borsh::{self, from_slice}; #[cfg(feature = "unstable__schema")] use borsh::{schema_container_of, try_from_slice_with_schema}; #[cfg(feature = "unstable__schema")] #[test] fn test_to_vec() { let value = 42u8; let seriazeble = (schema_container_of::(), value); let serialized = borsh::to_vec(&seriazeble).unwrap(); #[cfg(feature = "std")] println!("serialized: {:?}", serialized); let deserialized = try_from_slice_with_schema::(&serialized).unwrap(); assert_eq!(value, deserialized); } #[test] fn test_to_writer() { let value = 42u8; let mut serialized = vec![0; 1]; // serialized: [2, 0, 0, 0, 117, 56, 0, 0, 0, 0, 42] borsh::to_writer(&mut serialized[..], &value).unwrap(); let deserialized = from_slice::(&serialized).unwrap(); assert_eq!(value, deserialized); } borsh-1.5.7/tests/tests.rs000064400000000000000000000073621046102023000136350ustar 00000000000000#![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; #[macro_use] mod common_macro; mod custom_reader { #[cfg(feature = "derive")] mod test_custom_reader; } /// this module doesn't contain runnable tests; /// it's included into module tree to ensure derived code doesn't raise compilation /// errors #[rustfmt::skip] #[cfg(feature = "derive")] mod compile_derives { mod test_macro_namespace_collisions; #[allow(unused)] mod test_generic_structs; mod test_generic_enums; mod test_recursive_structs; #[cfg(feature = "unstable__schema")] mod schema { mod test_generic_enums; } } /// These are full roundtrip `BorshSerialize`/`BorshDeserialize` tests #[rustfmt::skip] mod roundtrip { mod test_strings; #[cfg(feature = "ascii")] mod test_ascii_strings; mod test_arrays; mod test_vecs; mod test_tuple; mod test_primitives; #[cfg(feature = "std")] mod test_ip_addr; mod test_nonzero_integers; mod test_range; // mod test_phantom_data; // NOTE: there's nothing corresponding to `schema::test_phantom_data` // mod test_option; // NOTE: there's nothing corresponding to `schema::test_option` // mod test_box; // NOTE: there's nothing corresponding to `schema::test_box` #[cfg(hash_collections)] mod test_hash_map; mod test_btree_map; mod test_cow; mod test_cells; #[cfg(feature = "rc")] mod test_rc; #[cfg(feature = "indexmap")] mod test_indexmap; #[cfg(feature = "derive")] mod requires_derive_category { // mod test_simple_structs; // NOTE: there's nothing corresponding to `schema::test_simple_structs` mod test_generic_structs; mod test_simple_enums; mod test_generic_enums; mod test_recursive_structs; mod test_recursive_enums; mod test_serde_with_third_party; mod test_enum_discriminants; #[cfg(feature = "bytes")] mod test_ultimate_many_features_combined; #[cfg(feature = "bson")] mod test_bson_object_ids; } } /// These are `BorshSchema` tests for various types #[cfg(feature = "unstable__schema")] #[rustfmt::skip] mod schema { #[cfg(feature = "ascii")] mod test_ascii_strings; mod test_strings; mod test_arrays; mod test_vecs; mod test_tuple; mod test_primitives; #[cfg(feature = "std")] mod test_ip_addr; // mod test_nonzero_integers; // NOTE: there's nothing corresponding to `roundtrip::test_nonzero_integers` mod test_range; mod test_phantom_data; mod test_option; mod test_box; #[cfg(hash_collections)] mod test_hash_map; mod test_btree_map; mod test_cow; mod test_cells; #[cfg(feature = "rc")] mod test_rc; mod test_simple_structs; mod test_generic_structs; mod test_simple_enums; mod test_generic_enums; mod test_recursive_structs; mod test_recursive_enums; mod test_schema_with_third_party; // NOTE: this test corresponds to `roundtrip::test_serde_with_third_party` mod test_enum_discriminants; // mod test_ultimate_many_features_combined; // NOTE: there's nothing corresponding to `roundtrip::test_ultimate_many_features_combined` // mod test_bson_object_ids; // NOTE: there's nothing corresponding to `roundtrip::test_bson_object_ids` mod schema_conflict { mod test_schema_conflict; } mod container_extension { mod test_schema_validate; mod test_max_size; } } mod deserialization_errors { #[cfg(feature = "ascii")] mod test_ascii_strings; mod test_cells; mod test_initial; } mod init_in_deserialize { #[cfg(feature = "derive")] mod test_init_in_deserialize; } mod zero_sized_types { #[cfg(feature = "derive")] mod test_zero_sized_types_forbidden; } borsh-1.5.7/tests/zero_sized_types/test_zero_sized_types_forbidden.rs000064400000000000000000000077461046102023000245560ustar 00000000000000use alloc::{string::ToString, vec, vec::Vec}; #[cfg(feature = "std")] use std::collections::{HashMap, HashSet}; #[cfg(feature = "hashbrown")] use hashbrown::{HashMap, HashSet}; use alloc::collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}; use borsh::from_slice; use borsh::to_vec; use borsh::BorshDeserialize; use borsh::BorshSerialize; use borsh::error::ERROR_ZST_FORBIDDEN; #[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug, Eq, PartialOrd, Ord, Hash)] struct A(); #[test] fn test_deserialize_vec_of_zst() { let v = [0u8, 0u8, 0u8, 64u8]; let res = from_slice::>(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_serialize_vec_of_zst() { let v = vec![A()]; let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_serialize_vec_of_unit_type() { let v = vec![(), (), ()]; let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_serialize_vec_of_vec_of_unit_type() { let v: Vec> = vec![vec![(), (), ()]]; let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_deserialize_vec_deque_of_zst() { let v = [0u8, 0u8, 0u8, 64u8]; let res = from_slice::>(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_serialize_vec_deque_of_zst() { let v: VecDeque = vec![A()].into(); let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_deserialize_linked_list_of_zst() { let v = [0u8, 0u8, 0u8, 64u8]; let res = from_slice::>(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_serialize_linked_list_of_zst() { let v: LinkedList = vec![A()].into_iter().collect(); let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_deserialize_btreeset_of_zst() { let v = [0u8, 0u8, 0u8, 64u8]; let res = from_slice::>(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_serialize_btreeset_of_zst() { let v: BTreeSet = vec![A()].into_iter().collect(); let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[cfg(hash_collections)] #[test] fn test_deserialize_hashset_of_zst() { let v = [0u8, 0u8, 0u8, 64u8]; let res = from_slice::>(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[cfg(hash_collections)] #[test] fn test_serialize_hashset_of_zst() { let v: HashSet = vec![A()].into_iter().collect(); let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_deserialize_btreemap_of_zst() { let v = [0u8, 0u8, 0u8, 64u8]; let res = from_slice::>(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[test] fn test_serialize_btreemap_of_zst() { let v: BTreeMap = vec![(A(), 42u64)].into_iter().collect(); let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[cfg(hash_collections)] #[test] fn test_deserialize_hashmap_of_zst() { let v = [0u8, 0u8, 0u8, 64u8]; let res = from_slice::>(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[cfg(hash_collections)] #[test] fn test_serialize_hashmap_of_zst() { let v: HashMap = vec![(A(), 42u64)].into_iter().collect(); let res = to_vec(&v); assert_eq!(res.unwrap_err().to_string(), ERROR_ZST_FORBIDDEN); } #[derive(BorshDeserialize, BorshSerialize, PartialEq, Debug)] struct B(u32); #[test] fn test_deserialize_non_zst() { let v = [1, 0, 0, 0, 64, 0, 0, 0]; let res = Vec::::try_from_slice(&v); assert!(res.is_ok()); } #[test] fn test_serialize_non_zst() { let v = vec![B(1)]; let res = to_vec(&v); assert!(res.is_ok()); }