boxcar-0.2.14/.cargo_vcs_info.json0000644000000001360000000000100123760ustar { "git": { "sha1": "0beb7bde288226e378a6479760066bab230ba50b" }, "path_in_vcs": "" }boxcar-0.2.14/Cargo.lock0000644000000557250000000000100103670ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "boxcar" version = "0.2.14" dependencies = [ "criterion", "loom", ] [[package]] name = "bumpalo" version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" dependencies = [ "cfg-if", "crossbeam-utils", "lazy_static", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ "cfg-if", "lazy_static", ] [[package]] name = "crunchy" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "generator" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" dependencies = [ "cfg-if", "libc", "log", "rustversion", "windows", ] [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "is-terminal" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi 0.4.0", "libc", "windows-sys", ] [[package]] name = "itertools" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "js-sys" version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "loom" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ "cfg-if", "generator", "scoped-tls", "tracing", "tracing-subscriber", ] [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata", ] [[package]] name = "memoffset" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "num-traits" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi 0.1.19", "libc", ] [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "plotters" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" [[package]] name = "plotters-svg" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" dependencies = [ "plotters-backend", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ "autocfg", "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "lazy_static", "num_cpus", ] [[package]] name = "regex" version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "regex-syntax 0.7.5", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.25", ] [[package]] name = "regex-syntax" version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "regex-syntax" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rustversion" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", "syn 1.0.86", ] [[package]] name = "serde_json" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "syn" version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "walkdir" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" dependencies = [ "bumpalo", "lazy_static", "log", "proc-macro2", "quote", "syn 1.0.86", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" dependencies = [ "proc-macro2", "quote", "syn 1.0.86", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" [[package]] name = "web-sys" version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core", "windows-targets", ] [[package]] name = "windows-core" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ "windows-implement", "windows-interface", "windows-result", "windows-strings", "windows-targets", ] [[package]] name = "windows-implement" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", "syn 2.0.98", ] [[package]] name = "windows-interface" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", "syn 2.0.98", ] [[package]] name = "windows-result" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ "windows-targets", ] [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", "windows-targets", ] [[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" boxcar-0.2.14/Cargo.toml0000644000000027610000000000100104020ustar # 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 = "2021" rust-version = "1.72.0" name = "boxcar" version = "0.2.14" authors = ["Ibraheem Ahmed "] build = false exclude = [ ".gitignore", ".github/**", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A concurrent, append-only vector" readme = "README.md" keywords = [ "concurrent", "vector", "atomic", "lock-free", ] categories = [ "concurrency", "data-structures", ] license = "MIT" repository = "https://github.com/ibraheemdev/boxcar" [lib] name = "boxcar" path = "src/lib.rs" [[test]] name = "loom" path = "tests/loom.rs" [[test]] name = "loom_buckets" path = "tests/loom_buckets.rs" [[test]] name = "vec" path = "tests/vec.rs" [[bench]] name = "bench" path = "benches/bench.rs" harness = false [dependencies] [dev-dependencies.criterion] version = "0.5" [target."cfg(loom)".dependencies.loom] version = "0.7" optional = true [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(loom)"] boxcar-0.2.14/Cargo.toml.orig000064400000000000000000000012551046102023000140600ustar 00000000000000[package] name = "boxcar" version = "0.2.14" authors = ["Ibraheem Ahmed "] edition = "2021" license = "MIT" rust-version = "1.72.0" readme = "README.md" description = "A concurrent, append-only vector" repository = "https://github.com/ibraheemdev/boxcar" categories = ["concurrency", "data-structures"] keywords = ["concurrent", "vector", "atomic", "lock-free"] exclude = [ ".gitignore", ".github/**", ] [[bench]] name = "bench" harness = false [dependencies] [target.'cfg(loom)'.dependencies] loom = { version = "0.7", optional = true } [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)'] } [dev-dependencies] criterion = "0.5" boxcar-0.2.14/LICENSE000064400000000000000000000020141046102023000121700ustar 00000000000000MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. boxcar-0.2.14/README.md000064400000000000000000000030641046102023000124500ustar 00000000000000# `boxcar` [crates.io](https://crates.io/crates/boxcar) [github](https://github.com/ibraheemdev/boxcar) [docs.rs](https://docs.rs/boxcar) A concurrent, append-only vector. The vector provided by this crate supports lock-free `get` and `push` operations. The vector grows internally but never reallocates, so element addresses are stable for the lifetime of the vector. Additionally, both `get` and `push` run in constant-time. ## Examples Appending an element to a vector and retrieving it: ```rust let vec = boxcar::Vec::new(); let i = vec.push(42); assert_eq!(vec[i], 42); ``` The vector can be modified by multiple threads concurrently: ```rust let vec = boxcar::Vec::new(); // Spawn a few threads that append to the vector. std::thread::scope(|s| for i in 0..6 { let vec = &vec; s.spawn(move || { // Push through the shared reference. vec.push(i); }); }); for i in 0..6 { assert!(vec.iter().any(|(_, &x)| x == i)); } ``` Elements can be mutated through fine-grained locking: ```rust let vec = boxcar::Vec::new(); std::thread::scope(|s| { // Insert an element. vec.push(std::sync::Mutex::new(0)); s.spawn(|| { // Mutate through the lock. *vec[0].lock().unwrap() += 1; }); }); let x = vec[0].lock().unwrap(); assert_eq!(*x, 1); ``` boxcar-0.2.14/benches/bench.rs000064400000000000000000000102521046102023000142220ustar 00000000000000// adapted from: https://github.com/hawkw/sharded-slab/blob/main/benches/bench.rs // which carries the following MIT license: // // Copyright (c) 2019 Eliza Weisman // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use std::{ sync::{Arc, Barrier, RwLock}, thread, time::{Duration, Instant}, }; #[derive(Clone)] struct Harness { start: Arc, end: Arc, vec: Arc, } const THREADS: usize = 12; impl Harness where T: Send + Sync + 'static, { fn new(vec: Arc) -> Self { Self { start: Arc::new(Barrier::new(THREADS + 1)), end: Arc::new(Barrier::new(THREADS + 1)), vec, } } fn run(&self, f: impl FnOnce(&T) + Send + Copy + 'static) -> Duration { for _ in 0..THREADS { let start = self.start.clone(); let end = self.end.clone(); let vec = self.vec.clone(); thread::spawn(move || { start.wait(); f(&*vec); end.wait(); }); } self.start.wait(); let t0 = Instant::now(); self.end.wait(); t0.elapsed() } } fn write_read(c: &mut Criterion) { const WORKLOAD: &[usize] = &[100, 1000, 10_000, 50_000, 100_000]; let mut group = c.benchmark_group("push_get"); group.measurement_time(Duration::from_secs(15)); for i in WORKLOAD { group.bench_with_input(BenchmarkId::new("boxcar::Vec<_>", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = Harness::new(Arc::new(boxcar::Vec::new())); let elapsed = bench.run(move |vec: &boxcar::Vec| { let v: Vec<_> = (0..i).map(|_| vec.push(true)).collect(); for i in v { assert!(vec.get(i).unwrap()); } }); total += elapsed; } total }) }); group.bench_with_input(BenchmarkId::new("RwLock>", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = Harness::new(Arc::new(RwLock::new(Vec::new()))); let elapsed = bench.run(move |vec: &RwLock>| { let v: Vec<_> = (0..i) .map(|_| { let mut vec = vec.write().unwrap(); vec.push(true); vec.len() - 1 }) .collect(); for i in v { assert!(vec.read().unwrap().get(i).unwrap()); } }); total += elapsed; } total }) }); } group.finish(); } criterion_group!(benches, write_read); criterion_main!(benches); boxcar-0.2.14/src/buckets.rs000064400000000000000000001361321046102023000137710ustar 00000000000000//! The low-level primitive behind [`Vec`](crate::Vec): a lazily-initialized array implemented as a //! sequence of buckets with sizes of increasing powers of two. use ::alloc::alloc::{self, alloc_zeroed, handle_alloc_error}; use ::alloc::boxed::Box; use ::alloc::vec::{self, Vec}; use core::cmp; use core::fmt::{self, Debug, Formatter}; use core::hint::unreachable_unchecked; use core::iter::FusedIterator; use core::mem::size_of; use core::num::NonZeroUsize; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr; use core::slice; use core::sync::atomic; use crate::loom::atomic::AtomicPtr; use crate::loom::AtomicMut as _; /// The low-level primitive behind [`Vec`](crate::Vec): a lazily-initialized array implemented as a /// sequence of buckets with sizes of increasing powers of two. /// /// The `BUCKETS` generic parameter controls the maximum capacity, and the inline size, of the /// type. See [`buckets_for_index_bits`] for a convenient way to calculate its desired value. pub struct Buckets { buckets: [AtomicPtr; BUCKETS], } // Safety: // - `T: Send` is required since we drop our `T`s when the type is dropped. // - `T: Sync` is not required since we own our `T`s wholly. unsafe impl Send for Buckets {} // Safety: // - `T: Send` is not required because one can neither put values into the `Buckets` nor take values // out of the `Buckets` from only a shared reference. One can force new values to be added to the // `Buckets`, however since those values are zeroed it doesn't matter which thread the zeroing // happened on. // - `T: Sync` is required because we provide shared access to the data in the buckets from a // shared reference. unsafe impl Sync for Buckets {} // Since we act like we own a `T`, we inherit its unwind-safety-ness. impl UnwindSafe for Buckets {} impl RefUnwindSafe for Buckets {} impl Buckets { #[cfg(not(loom))] #[allow(clippy::declare_interior_mutable_const)] const NULL_PTR: AtomicPtr = AtomicPtr::new(ptr::null_mut()); /// Construct a new, empty, `Buckets`. #[cfg(not(loom))] pub const fn new() -> Self { Self { buckets: [Self::NULL_PTR; BUCKETS], } } #[cfg(loom)] pub fn new() -> Self { Self { buckets: [(); BUCKETS].map(|_| AtomicPtr::new(ptr::null_mut())), } } /// Get the bucket at the given index. fn bucket(&self, i: BucketIndex) -> &AtomicPtr { // Safety: Ensured by the invariant of `BucketIndex`. unsafe { self.buckets.get_unchecked(i.0) } } /// Get a unique reference to the bucket at the given index. fn bucket_mut(&mut self, i: BucketIndex) -> &mut AtomicPtr { // Safety: Ensured by the invariant of `BucketIndex`. unsafe { self.buckets.get_unchecked_mut(i.0) } } /// Take ownership over the bucket at the given index. fn take_bucket(&mut self, i: BucketIndex) -> Option> { // Take the pointer, replacing it with null. let bucket = self.bucket_mut(i); let ptr = bucket.read_mut(); bucket.write_mut(ptr::null_mut()); // Ensure the pointer is non-null. ptr::NonNull::new(ptr)?; // Safety: Guaranteed by our invariants. // We know we won't double-free because we just set the pointer to null. Some(unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(ptr, i.len().get())) }) } /// Retrieve the value at the specified index, or `None` if it has not been allocated yet. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// let index = buckets::Index::new(20).unwrap(); /// /// assert_eq!(buckets.get(index), None); /// buckets.get_or_alloc(index); /// assert_eq!(*buckets.get(index).unwrap(), 0); /// ``` pub fn get(&self, index: Index) -> Option<&T> { let location = index.location(); let bucket = self.bucket(location.bucket); // Acquire is necessary because we access the bucket afterward. let ptr = bucket.load(atomic::Ordering::Acquire); if ptr.is_null() { return None; } // Safety: // - By our invariants, the index is in bounds. // - We loaded the bucket pointer with `Acquire`, allowing us to access the allocation. Some(unsafe { &*ptr.add(location.entry) }) } /// Retrieve a unique reference to the value at the specified index, or `None` if it has not /// been allocated yet. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// let index = buckets::Index::new(20).unwrap(); /// /// assert_eq!(buckets.get_mut(index), None); /// buckets.get_or_alloc(index); /// assert_eq!(*buckets.get_mut(index).unwrap(), 0); /// ``` pub fn get_mut(&mut self, index: Index) -> Option<&mut T> { let location = index.location(); let bucket = self.bucket_mut(location.bucket); let ptr = bucket.read_mut(); if ptr.is_null() { return None; } // Safety: By our invariants, the index is in bounds and the pointer is valid. Some(unsafe { &mut *ptr.add(location.entry) }) } /// Retrieve the value at the specified index, without performing bounds checking. /// /// # Safety /// /// The element must be allocated. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// let index = buckets::Index::new(20).unwrap(); /// buckets.reserve_mut(index); /// assert_eq!(*unsafe { buckets.get_unchecked(index) }, 0); /// ``` pub unsafe fn get_unchecked(&self, index: Index) -> &T { let location = index.location(); let bucket = self.bucket(location.bucket); // We only need `Relaxed`, because the caller guarantees that the bucket is already allocated. // In theory, we could get away with an unsynchronized read here, but there's an open question // as to whether or not unsynchronized reads race with failing RMWs: // https://github.com/rust-lang/unsafe-code-guidelines/issues/355 let ptr = bucket.load(atomic::Ordering::Relaxed); // Safety: // - By our invariants, the index is in bounds. // - The caller ensures that we can access the allocation. unsafe { &*ptr.add(location.entry) } } /// Retrieve unique reference to the value at the specified index, without performing bounds /// checking. /// /// # Safety /// /// The element must be allocated. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// let index = buckets::Index::new(20).unwrap(); /// buckets.reserve_mut(index); /// assert_eq!(*unsafe { buckets.get_unchecked_mut(index) }, 0); /// ``` pub unsafe fn get_unchecked_mut(&mut self, index: Index) -> &mut T { let location = index.location(); let bucket = self.bucket_mut(location.bucket); let ptr = bucket.read_mut(); // Safety: // - By our invariants, the index is in bounds. // - The caller ensures that we can access the allocation. unsafe { &mut *ptr.add(location.entry) } } /// Retrieve the value at the specified index, or allocate the bucket if it hasn't been /// allocated yet. /// /// # Example /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// /// let index = buckets::Index::new(48).unwrap(); /// assert_eq!(*buckets.get_or_alloc(index), 0); /// /// *buckets.get_mut(index).unwrap() += 3; /// assert_eq!(*buckets.get_or_alloc(index), 3); /// /// // Prior indices are not necessary allocated. /// assert_eq!(buckets.get(buckets::Index::new(0).unwrap()), None); /// ``` /// /// # Panics /// /// May panic if `index` is too large or allocation fails. pub fn get_or_alloc(&self, index: Index) -> &T where T: MaybeZeroable, { let location = index.location(); // If we are close to the end of this bucket, we eagerly allocate the next one to reduce // later contention. if location.entry == (location.bucket_len.get() - (location.bucket_len.get() >> 3)) { self.alloc_bucket_after(index); } let bucket = self.bucket(location.bucket); // Acquire is necessary because we access the bucket afterward. let mut ptr = bucket.load(atomic::Ordering::Acquire) as *const T; if ptr.is_null() { // Panics: Let `i` be `index.get()`. To avoid panics, the condition is: // // location.bucket_len * size_of::() < isize::MAX + 1 // ⇔ 2 ^ floor(log2(i + SKIPPED_ENTRIES + 1)) < (isize::MAX + 1) / size_of::() // ⇔ i + SKIPPED_ENTRIES + 1 < ((isize::MAX + 1) / size_of::()).next_power_of_two() // ⇔ i < ((isize::MAX + 1) / size_of::()).next_power_of_two() - SKIPPED_ENTRIES - 1 // // Since `SKIPPED_ENTRIES` is an implementation detail, the caller can't enforce this. // But the formula may be useful anyway. ptr = allocate_race_and_get(bucket, location.bucket_len); } // Safety: // - The pointer is non-null. // - By our invariants, the index is in bounds. // - We loaded the bucket pointer with `Acquire`, allowing us to access the allocation. unsafe { &*ptr.add(location.entry) } } /// Eagerly allocate the bucket after the bucket containing the provided index. #[cold] #[inline(never)] fn alloc_bucket_after(&self, index: Index) where T: MaybeZeroable, { if let Some(new_index) = index.after_bucket().advance() { allocate_race(self.bucket(new_index), new_index.len()); } } /// Retrieve a unique reference to the value at the specified index, or allocate the bucket if /// it hasn't been allocated yet. /// /// # Example /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// /// let index = buckets::Index::new(48).unwrap(); /// assert_eq!(*buckets.get_or_alloc_mut(index), 0); /// /// *buckets.get_or_alloc_mut(index) += 3; /// assert_eq!(*buckets.get(index).unwrap(), 3); /// /// // Prior indices are not necessary allocated. /// assert_eq!(buckets.get(buckets::Index::new(0).unwrap()), None); /// ``` /// /// # Panics /// /// May panic if `index` is too large or allocation fails. pub fn get_or_alloc_mut(&mut self, index: Index) -> &mut T where T: MaybeZeroable, { let location = index.location(); let bucket = self.bucket_mut(location.bucket); let mut ptr = bucket.read_mut(); if ptr.is_null() { // See `get_or_alloc` for the panicking conditions. ptr = Box::into_raw(allocate_slice::(location.bucket_len)).cast::(); bucket.write_mut(ptr); } // Safety: By our invariants, the index is in bounds and the pointer is valid. unsafe { &mut *ptr.add(location.entry) } } /// Reserve capacity up to and including the provided index. /// /// After calling this method, [`.get_or_alloc(n)`](Self::get_or_alloc) is guaranteed not to /// allocate. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let buckets = >::new(); /// let index = buckets::Index::new(20).unwrap(); /// assert_eq!(buckets.get(index), None); /// /// buckets.reserve(index); /// assert_eq!(*buckets.get(index).unwrap(), 0); /// ``` /// /// # Panics /// /// May panic if `index` is too large or allocation fails. pub fn reserve(&self, index: Index) where T: MaybeZeroable, { // Start at the current bucket and work our way backwards. let mut cursor = index.after_bucket(); while let Some(index) = cursor.retreat() { let bucket = self.bucket(index); // If the bucket is allocated, we're done. It's technically possible that a later // bucket got allocated but all the threads racing to allocate the earlier bucket // died, or no such threads existed. This case is rare, and quitting early anyway is // benign. // // Since we only check if the bucket exists and don't access its data, `Relaxed` is // sufficient. if !bucket.load(atomic::Ordering::Relaxed).is_null() { break; } // Otherwise, race to allocate the bucket. allocate_race(bucket, index.len()); } } /// Reserve capacity up to and including the provided index. /// /// Unlike [`reserve`](Self::reserve), this method takes a mutable reference to the `Buckets`, /// avoiding synchronization. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// let index = buckets::Index::new(20).unwrap(); /// /// buckets.reserve_mut(index); /// assert_eq!(*buckets.get(index).unwrap(), 0); /// ``` /// /// # Panics /// /// May panic if `index` is too large or allocation fails. pub fn reserve_mut(&mut self, index: Index) where T: MaybeZeroable, { // The same algorithm as in `reserve`. let mut cursor = index.after_bucket(); while let Some(index) = cursor.retreat() { let bucket = self.bucket_mut(index); if !bucket.read_mut().is_null() { break; } let ptr = Box::into_raw(allocate_slice::(index.len())); bucket.write_mut(ptr.cast::()); } } /// Truncate the `Buckets`, keeping at least the first `n` elements. /// /// This method truncates to the smallest capacity that preserves any items before /// `n`, but may include subsequent elements due to the bucket layout. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// /// // We reserve capacity for 1000 elements, so all elements will be present. /// buckets.reserve_mut(buckets::Index::new(1000).unwrap()); /// assert_eq!(*buckets.get(buckets::Index::new(12).unwrap()).unwrap(), 0); /// assert_eq!(*buckets.get(buckets::Index::new(1000).unwrap()).unwrap(), 0); /// /// // If we truncate to a smaller capacity, the later elements will not be present. /// buckets.truncate(buckets::Index::new(13).unwrap()); /// assert_eq!(*buckets.get(buckets::Index::new(12).unwrap()).unwrap(), 0); /// assert_eq!(buckets.get(buckets::Index::new(1000).unwrap()), None); /// ``` pub fn truncate(&mut self, n: Index) { let mut cursor = n.after_lower_buckets(); while let Some(bucket) = cursor.advance() { self.take_bucket(bucket); } } /// Iterate over `(index, &value)` pairs of the `Buckets`. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// buckets.reserve_mut(buckets::Index::new(25).unwrap()); /// assert!(25 < buckets.iter().count()); /// ``` pub fn iter(&self) -> Iter<'_, T, BUCKETS> { self.into_iter() } /// Iterate over `(index, &mut value)` pairs of the `Buckets`. /// /// # Examples /// /// ``` /// # use boxcar::buckets::{self, Buckets}; /// let mut buckets = >::new(); /// buckets.reserve_mut(buckets::Index::new(25).unwrap()); /// assert!(25 < buckets.iter_mut().count()); /// /// for (i, (_, val)) in buckets.iter_mut().enumerate() { /// *val = i as u8; /// } /// assert_eq!(*buckets.get(buckets::Index::new(10).unwrap()).unwrap(), 10); /// ``` pub fn iter_mut(&mut self) -> IterMut<'_, T, BUCKETS> { self.into_iter() } } impl Drop for Buckets { fn drop(&mut self) { self.truncate(Index::new(0).unwrap()); } } impl Default for Buckets { fn default() -> Self { Self::new() } } impl Debug for Buckets { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.iter()).finish() } } /// Types with a [`Default`] implementation that may or may not additionally support safe /// initialization with all zero bytes. /// /// The provided implementations of this trait are only for example purposes, as it is /// expected to be implemented manually for user-defined types. /// /// # Safety /// /// If [`zeroable`](Self::zeroable) returns true, the all-zeros bit pattern must be a valid instance /// of this type. pub unsafe trait MaybeZeroable: Default { /// Returns `true` if the all-zeros bit pattern is a valid instance of the type. fn zeroable() -> bool; } unsafe impl MaybeZeroable for u8 { fn zeroable() -> bool { true } } unsafe impl MaybeZeroable for u16 { fn zeroable() -> bool { true } } /// Race to allocate a bucket. /// /// # Panics /// /// `len * size_of::()` must not overflow an `isize`. #[cold] #[inline(never)] #[must_use] fn allocate_race_and_get(bucket: &AtomicPtr, len: NonZeroUsize) -> *const T { // Panics: Ensured by caller. let ptr = Box::into_raw(allocate_slice::(len)); match bucket.compare_exchange( ptr::null_mut(), ptr.cast::(), // `Release` is necessary to allow accesses to the allocation on other threads to // happen-after the allocation. We load from the pointer with `Acquire` as necessary. atomic::Ordering::Release, // `Acquire` is necessary to allow the caller to use the returned pointer. atomic::Ordering::Acquire, ) { Ok(_) => ptr.cast::(), // If we fail the race, just deallocate our now-useless bucket and continue. Err(new_ptr) => { drop(unsafe { Box::from_raw(ptr) }); new_ptr } } } /// Like [`allocate_race_and_get`], but doesn’t return a pointer. /// /// # Panics /// /// `len * size_of::()` must not overflow an `isize`. fn allocate_race(bucket: &AtomicPtr, len: NonZeroUsize) { // Panics: Ensured by caller. let ptr = Box::into_raw(allocate_slice::(len)); match bucket.compare_exchange( ptr::null_mut(), ptr.cast::(), // `Release` is necessary to allow accesses to the allocation on other threads to // happen-after the allocation. We load from the pointer with `Acquire` as necessary. atomic::Ordering::Release, // Unlike in `allocate_race_and_get`, we don't need the returned pointer, so just use // `Relaxed` here. atomic::Ordering::Relaxed, ) { Ok(_) => {} // If we fail the race, just deallocate our now-useless bucket and continue. Err(_) => drop(unsafe { Box::from_raw(ptr) }), } } /// Allocate a zeroed array. /// /// # Panics /// /// `len * size_of::()` must not overflow an `isize`. fn allocate_slice(len: NonZeroUsize) -> Box<[T]> { if size_of::() == 0 { return Box::new([]); } if T::zeroable() { // Panics: Ensured by caller. let layout = alloc::Layout::array::(len.get()).unwrap(); // Safety: `len` is a `NonZeroUsize`, and we just ensured that `T` is not zero-sized. // Therefore, `layout` has a non-zero size. let ptr = unsafe { alloc_zeroed(layout) }.cast::(); if ptr.is_null() { handle_alloc_error(layout); } unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(ptr, len.get())) } } else { let mut vec = Vec::new(); vec.resize_with(len.get(), T::default); vec.into_boxed_slice() } } /// An valid index into [`Buckets`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Index { /// The original index plus `SKIPPED_ENTRIES` plus one. /// /// Invariant: `SKIPPED_ENTRIES < inner ≤ ENTRIES_WITH_SKIPPED`. inner: NonZeroUsize, } /// To avoid small allocations, we skip a number of small buckets at the start. /// /// Note that some code relies on `SKIPPED_BUCKETS` being at least 1, e.g. `Index::into_raw` and /// `Index::next_buckets`. Other than that, it can be anything. const SKIPPED_BUCKETS: usize = 5; const SKIPPED_ENTRIES: usize = 2_usize.pow(SKIPPED_BUCKETS as u32) - 1; impl Index { /// If there were in fact `BUCKETS + SKIPPED_BUCKETS` buckets, this is the number of entries /// there would be. /// /// This is equal to `2 ^ (BUCKETS + SKIPPED_BUCKETS) - 1`, but calculated in a way that avoids /// overflow. const ENTRIES_WITH_SKIPPED: usize = { let mut total = 0; let mut i = 0; while i < BUCKETS + SKIPPED_BUCKETS { total += 2_usize.pow(i as u32); i += 1; } total }; /// The real number of entries is just the above figure with the number of skipped entries /// subtracted. const ENTRIES: usize = Self::ENTRIES_WITH_SKIPPED - SKIPPED_ENTRIES; /// Construct a new `Index`. /// /// Returns `None` if the index is out of bounds. Note that [`buckets_for_index_bits`] can be used to guarantee /// that this function returns `None` for any values greater than `2 ^ bits`, but it may also do so for smaller /// values. pub const fn new(i: usize) -> Option { if i < Self::ENTRIES { // Safety: What we just checked. Some(unsafe { Self::new_unchecked(i) }) } else { None } } /// Construct a new `Index` without bounds-checking. /// /// # Safety /// /// `Index::new(i)` must return `Some`. pub const unsafe fn new_unchecked(i: usize) -> Self { // Safety: Ensured by the caller. // We use this to elide bounds checks below. if i >= Self::ENTRIES { unsafe { unreachable_unchecked() }; } // Panics: This addition never overflows: // i < ENTRIES // ⇒ i < ENTRIES_WITH_SKIPPED - SKIPPED_ENTRIES ≤ usize::MAX - SKIPPED_ENTRIES // ⇒ i + SKIPPED_ENTRIES < usize::MAX // ⇒ i + SKIPPED_ENTRIES + 1 ≤ usize::MAX // The compiler can tell this, so we just panic. let Some(inner) = i.checked_add(SKIPPED_ENTRIES + 1) else { unreachable!() }; // Panics: `inner` is always nonzero, since we just added one to it and didn't overflow. // Because we use `checked_add`, the compiler can tell this, so we just panic. let Some(inner) = NonZeroUsize::new(inner) else { unreachable!() }; Self { inner } } /// Construct a new `Index`, returning the maximum index if it is out-of-bounds. pub fn new_saturating(i: usize) -> Self { // Panics: cmp::min(i, Self::ENTRIES - 1) ≤ Self::ENTRIES - 1 < Self::ENTRIES Self::new(cmp::min(i, Self::ENTRIES - 1)).unwrap() } /// Get the index passed into [`Index::new`]. pub const fn get(self) -> usize { self.inner.get() - (SKIPPED_ENTRIES + 1) } /// Convert an `Index` into its raw representation. /// /// The returned value is guaranteed not to be equal to one. Additional range guarantees are /// provided if [`buckets_for_index_bits`] is used. /// /// Indices are guaranteed to be represented contiguously. pub const fn into_raw(self) -> NonZeroUsize { // Assert the invariants of the type. debug_assert!(SKIPPED_ENTRIES < self.inner.get()); debug_assert!(self.inner.get() <= Self::ENTRIES_WITH_SKIPPED); // Since `1 ≤ SKIPPED_ENTRIES`, we know that `2 ≤ SKIPPED_ENTRIES + 1 ≤ inner`. self.inner } /// Reconstruct an `Index` from its raw representation. /// /// # Safety /// /// The given index must be at an in-bounds offset from an index previously returned by /// [`into_raw`](Self::into_raw). pub const unsafe fn from_raw_unchecked(inner: usize) -> Self { debug_assert!(SKIPPED_ENTRIES < inner); debug_assert!(inner <= Self::ENTRIES_WITH_SKIPPED); // Panics: Ensured by caller. let inner = unsafe { NonZeroUsize::new_unchecked(inner) }; Self { inner } } /// Reconstruct an `Index` from its raw representation, failing if the index is out of bounds /// on the positive side. /// /// # Safety /// /// The given index must be at a positive offset from an index previously returned by /// [`into_raw`](Self::into_raw). pub const unsafe fn from_raw_checked_above(inner: usize) -> Option { if inner <= Self::ENTRIES_WITH_SKIPPED { // Safety: The lower bound is ensured by the caller, and we just checked the upper one. Some(unsafe { Self::from_raw_unchecked(inner) }) } else { None } } /// Reconstruct an `Index` from its raw representation, failing if the index is out of bounds. pub const fn from_raw_checked(inner: usize) -> Option { if SKIPPED_ENTRIES < inner { // Safety: We just checked the lower bound. unsafe { Self::from_raw_checked_above(inner) } } else { None } } /// Split apart this index into the underlying bucket index, bucket length, and offset into the /// bucket. fn location(self) -> Location { // We need to calculate: // - the bucket index `0 ≤ bucket < BUCKETS`, and // - the entry index `0 ≤ entry < 2 ^ (bucket + SKIPPED_BUCKETS)`. // // Let `i` be the index that includes skipped entries, i.e. `self.inner - 1`. // Let `b` be the bucket index that includes skipped buckets, // i.e. `bucket + SKIPPED_BUCKETS`. // Note that the bucket at index-including-skipped-buckets `j` has length `2 ^ j`. // The following equivalent equalities must be satisfied: // // i = (0..b).map(|j| 2 ^ j).sum() + entry // ⇔ i = 2 ^ b - 1 + entry // ⇔ i + 1 - entry = 2 ^ b // ⇔ b = log2(i + 1 - entry) // = floor(log2(i + 1)) // // To justify the last step, observe that, for a given power of two `x` and real number `y`, // `x ≤ y < 2 * x` iff `floor(log2(y)) = log2(x)`. Since we have a power of two // `i + 1 - entry`, we can prove this works: // // i + 1 - entry ≤ i + 1 < 2 * (i + 1 - entry) // ⇔ -entry ≤ 0 < i + 1 - 2 * entry // ⇔ 0 ≤ entry and entry < i + 1 - entry = 2 ^ b // // Which holds by definition of `entry`. We can thus set `b = floor(log2(i + 1)) // //= floor(log2(self.inner)) = self.inner.ilog2()`. let b = self.inner.ilog2(); // The compiler can tell that this conversion never fails, so we just unwrap. let b_usize = usize::try_from(b).unwrap(); // The bucket length is `2 ^ b` a.k.a. `1 << b`. // // Since `b` is the result of an `ilog2`, we know this can never overflow. The compiler // knows this, so we just unwrap. Since the operation doesn't overflow and starts with `1`, // we also know the result is nonzero. The compiler also knows this, so we just unwrap. let bucket_len = NonZeroUsize::new(1_usize.checked_shl(b).unwrap()).unwrap(); // Recall that: i + 1 - entry = 2 ^ b // Therefore: entry = i + 1 - 2 ^ b = self.inner - bucket_len // // We now prove that `0 ≤ entry < 2 ^ b`: // // 0 ≤ entry < 2 ^ b // ⇔ 2 ^ b ≤ i + 1 < 2 ^ (b + 1) // ⇔ 2 ^ floor(log2(i + 1)) ≤ i + 1 < 2 ^ (floor(log2(i + 1)) + 1) // // Which is obviously true: `i + 1` is contained between the powers of two beneath and // above it. let entry = self.inner.get() - bucket_len.get(); // By definition, `b = bucket + SKIPPED_BUCKETS`, so `bucket = b - SKIPPED_BUCKETS`. // // We prove that `0 ≤ bucket < BUCKETS`: // // 0 ≤ bucket < BUCKETS // ⇔ SKIPPED_BUCKETS ≤ b < BUCKETS + SKIPPED_BUCKETS // ⇔ SKIPPED_BUCKETS ≤ floor(log2(i + 1)) < BUCKETS + SKIPPED_BUCKETS // ⇔ SKIPPED_ENTRIES + 1 ≤ 2 ^ floor(log2(i + 1)) < ENTRIES_WITH_SKIPPED + 1 // // And this holds by `inner`'s invariant: since the value `i + 1` itself is between those // two powers of two, the power of two beneath it will also be contained in the range. let bucket = BucketIndex(b_usize - SKIPPED_BUCKETS); Location { bucket, bucket_len, entry, } } /// Returns `true` if this index is the first in its bucket. pub fn is_first_in_bucket(self) -> bool { self.location().entry == 0 } } impl Debug for Index { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { ::fmt(&self.into_raw().get(), f) } } /// The location of a value in a `Buckets`. #[derive(Clone)] struct Location { /// Which bucket the value is in. bucket: BucketIndex, /// How many entries are in this bucket. bucket_len: NonZeroUsize, /// The index of the entry within the bucket's array. Less than `bucket_len`. entry: usize, } /// The index of a bucket. /// /// Invariant: The inner index is less than `BUCKETS`. #[derive(Clone, Copy)] struct BucketIndex(usize); impl BucketIndex { /// Get the index of the first entry in this bucket. fn first(self) -> Index { // The desired index `inner - 1` satisfies these equalities: // // inner - 1 = (0..bucket + SKIPPED_BUCKETS).map(|j| 2 ^ j).sum() + 0 // = 2 ^ (bucket + SKIPPED_BUCKETS) - 1 // ⇔ inner = 2 ^ (bucket + SKIPPED_BUCKETS) // Safety: This is the invariant of the type. if self.0 >= BUCKETS { unsafe { unreachable_unchecked() }; } // All of these operations can be proven by the compiler not to overflow because of the // above assert; hence, we just unwrap freely knowing that it'll be eliminated. let b = self.0.checked_add(SKIPPED_BUCKETS).unwrap(); let inner = 1_usize.checked_shl(b.try_into().unwrap()).unwrap(); Index::from_raw_checked(inner).unwrap() } /// Get the length of the bucket. fn len(self) -> NonZeroUsize { // self.first() computes `2 ^ (bucket + SKIPPED_BUCKETS)`, which is also the bucket length. self.first().into_raw() } } /// Calculate the largest value of the `BUCKETS` generic parameter such that every element is given /// an index less than `2 ^ bits` (though not every index less than `2 ^ bits` may be given an /// element). /// /// This ensures both that [`Index::new`] will reject all values greater than `2 ^ bits` and that /// [`Index::into_raw`] will return values less than `2 ^ bits`. /// /// # Panics /// /// `bits` may not exceed `usize::BITS`. pub const fn buckets_for_index_bits(bits: u32) -> usize { assert!(bits <= usize::BITS); // We want the largest value of `BUCKETS` such that `>::ENTRIES ≤ 2 ^ bits`: // // >::ENTRIES ≤ 2 ^ bits // ⇔ 2 ^ (BUCKETS + SKIPPED_BUCKETS) - 2 ^ SKIPPED_BUCKETS ≤ 2 ^ bits // ⇔ BUCKETS + SKIPPED_BUCKETS ≤ log2(2 ^ bits + 2 ^ SKIPPED_BUCKETS) // ⇔ BUCKETS ≤ log2(2 ^ bits + 2 ^ SKIPPED_BUCKETS) - SKIPPED_BUCKETS // ≤ ceil(log2(2 ^ bits + 2 ^ SKIPPED_BUCKETS)) - SKIPPED_BUCKETS // = max(bits, SKIPPED_BUCKETS) - SKIPPED_BUCKETS // = max(bits - SKIPPED_BUCKETS, 0) // // This `as` never overflows, since we know `bits ≤ usize::BITS < usize::MAX`. (bits as usize).saturating_sub(SKIPPED_BUCKETS) } impl Index { /// Get the bucket cursor located between the bucket this index is in and the next bucket. fn after_bucket(self) -> BucketCursor { let location = self.location(); // We can add one without overflowing because `bucket < BUCKETS`. BucketCursor(location.bucket.0 + 1) } /// Get the bucket cursor located after every bucket that has `Index`es lower than this one (and /// before all buckets whose every `Index` is greater than or equal to this one). /// /// In other words, if the index is the first entry in the bucket, get the bucket cursor before /// the current bucket, otherwise get the bucket cursor after the current bucket. fn after_lower_buckets(self) -> BucketCursor { // Every bucket with index `b - SKIPPED_BUCKETS` whose every `Index` is greater than or // equal to this one satisfies the following equivalent properties: // // ∀ i ≥ (0..b).map(|j| 2 ^ j).sum(), i ≥ self.inner - 1 // ⇔ self.inner - 1 ≤ (0..b).map(|j| 2 ^ j).sum() // = 2 ^ b - 1 // ⇔ log2(self.inner) ≤ b // // Thus, the first such bucket is the smallest integer `b` satisfying the inequality, i.e. // `ceil(log2(self.inner))`, which may be alternately expressed as // `floor(log2(self.inner - 1)) + 1`. Note that these formulae diverge in the case where // `self.inner = 1`; however, since `1 ≤ SKIPPED_BUCKETS` this cannot happen. // Panics: Can always subtract one from a `NonZero`. // // The compiler knows this, so we just unwrap. let inner_minus_one = self.into_raw().get().checked_sub(1).unwrap(); // Calculate the bucket cursor index, which is the index of the first non-lower bucket. // // bucket = b - SKIPPED_BUCKETS // = floor(log2(self.inner - 1)) + 1 - SKIPPED_BUCKETS // // We do the calculation in this order to ensure that overflow does not occur, // again relying on `1 ≤ SKIPPED_BUCKETS`: // // SKIPPED_BUCKETS = log2(SKIPPED_ENTRIES + 1) // ≤ log2(self.inner) // ≤ ceil(log2(self.inner)) // = floor(log2(self.inner - 1)) + 1 // ⇒ 0 ≤ floor(log2(self.inner - 1)) + 1 - SKIPPED_BUCKETS // // Panics (ilog2): `SKIPPED_BUCKETS` is at least 1, meaning that `2 ≤ self.inner`. // The compiler can tell that `ilog2` always gives a valid `usize`, so we just unwrap. let bucket = usize::try_from(inner_minus_one.ilog2()).unwrap() + 1 - SKIPPED_BUCKETS; BucketCursor(bucket) } } /// A point between two buckets – if each bucket is a fence, this represents a fencepost. /// /// Invariant: `self.0 ≤ BUCKETS`. #[derive(Clone, Default)] struct BucketCursor(usize); impl BucketCursor { /// Advance this cursor and return the bucket index that was advanced over. fn advance(&mut self) -> Option> { if self.0 >= BUCKETS { return None; } // This is okay because we just ensured `self.0 < BUCKETS`. let index = BucketIndex(self.0); self.0 += 1; Some(index) } /// Move this cursor backward and return the bucket index that was advanced over. fn retreat(&mut self) -> Option> { self.0 = self.0.checked_sub(1)?; // This is okay because our invariant is that `self.0 ≤ BUCKETS`, therefore // `self.0 - 1 < BUCKETS`. Some(BucketIndex(self.0)) } } impl<'a, T, const BUCKETS: usize> IntoIterator for &'a Buckets { type Item = (Index, &'a T); type IntoIter = Iter<'a, T, BUCKETS>; fn into_iter(self) -> Self::IntoIter { Iter { buckets: self, bucket: BucketCursor::default(), iter: [].iter(), index: 0, } } } /// An iterator over shared references to the elements in a [`Buckets`]. #[must_use] pub struct Iter<'a, T, const BUCKETS: usize> { buckets: &'a Buckets, bucket: BucketCursor, iter: slice::Iter<'a, T>, /// The `Index` of the next element if there is one, or an invalid index otherwise. index: usize, } impl<'a, T, const BUCKETS: usize> Iterator for Iter<'a, T, BUCKETS> { type Item = (Index, &'a T); fn next(&mut self) -> Option { loop { // If we have more elements in the current bucket, yield them first. if let Some(item) = self.iter.next() { // Safety: Invariant of the `index` field. let index = unsafe { Index::from_raw_unchecked(self.index) }; // This might overflow, which is okay because `self.iter` would be empty. self.index = self.index.wrapping_add(1); return Some((index, item)); } let bucket_index = self.bucket.advance()?; let bucket = self.buckets.bucket(bucket_index); // `Acquire` is necessary because we access the pointer afterward. let ptr = bucket.load(atomic::Ordering::Acquire); // Skip over unallocated buckets. We need to check every bucket since it's possible // later buckets got allocated first. if !ptr.is_null() { // Safety: Guaranteed by the invariants of `Buckets`. let slice = unsafe { slice::from_raw_parts(ptr, bucket_index.len().get()) }; self.iter = slice.iter(); self.index = bucket_index.first().into_raw().get(); } } } } // Valid since `BucketIter` is fused. impl FusedIterator for Iter<'_, T, BUCKETS> {} impl Clone for Iter<'_, T, BUCKETS> { fn clone(&self) -> Self { Self { buckets: self.buckets, bucket: self.bucket.clone(), iter: self.iter.clone(), index: self.index, } } } impl<'a, T, const BUCKETS: usize> IntoIterator for &'a mut Buckets { type Item = (Index, &'a mut T); type IntoIter = IterMut<'a, T, BUCKETS>; fn into_iter(self) -> Self::IntoIter { IterMut { buckets: self, bucket: BucketCursor::default(), iter: [].iter_mut(), index: 0, } } } /// An iterator over unique references to the elements in a [`Buckets`]. #[must_use] pub struct IterMut<'a, T, const BUCKETS: usize> { buckets: &'a mut Buckets, bucket: BucketCursor, iter: slice::IterMut<'a, T>, /// The index of the next element if there is one, or an invalid index otherwise. index: usize, } impl<'a, T, const BUCKETS: usize> Iterator for IterMut<'a, T, BUCKETS> { type Item = (Index, &'a mut T); fn next(&mut self) -> Option { loop { // If we have more elements in the current bucket, yield them first. if let Some(item) = self.iter.next() { // Safety: Invariant of the `index` field. let index = unsafe { Index::from_raw_unchecked(self.index) }; // This might overflow, which is okay because `self.iter` would be empty. self.index = self.index.wrapping_add(1); return Some((index, item)); } let bucket_index = self.bucket.advance()?; let bucket = self.buckets.bucket_mut(bucket_index); let ptr = bucket.read_mut(); // Skip over unallocated buckets. We need to check every bucket since it's possible // later buckets got allocated first. if !ptr.is_null() { // Safety: Guaranteed by the invariants of `Buckets`. let slice = unsafe { slice::from_raw_parts_mut(ptr, bucket_index.len().get()) }; self.iter = slice.iter_mut(); self.index = bucket_index.first().into_raw().get(); } } } } // Valid since `BucketIter` is fused. impl FusedIterator for IterMut<'_, T, BUCKETS> {} impl IntoIterator for Buckets { type Item = (Index, T); type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter { buckets: self, bucket: BucketCursor::default(), iter: Vec::new().into_iter(), index: 0, } } } /// An iterator over the elements in a [`Buckets`]. #[must_use] pub struct IntoIter { buckets: Buckets, bucket: BucketCursor, iter: vec::IntoIter, /// The index of the next element if there is one, or an invalid index otherwise. index: usize, } impl Iterator for IntoIter { type Item = (Index, T); fn next(&mut self) -> Option { loop { // If we have more elements in the current bucket, yield them first. if let Some(item) = self.iter.next() { // Safety: Invariant of the `index` field. let index = unsafe { Index::from_raw_unchecked(self.index) }; // This might overflow, which is okay because `self.iter` would be empty. self.index = self.index.wrapping_add(1); return Some((index, item)); } let bucket_index = self.bucket.advance()?; // Skip over unallocated buckets. We need to check every bucket since it's possible // later buckets got allocated first. if let Some(bucket) = self.buckets.take_bucket(bucket_index) { self.iter = Vec::from(bucket).into_iter(); self.index = bucket_index.first().into_raw().get(); } } } } // Valid since `BucketIter` is fused. impl FusedIterator for IntoIter {} #[cfg(test)] mod tests { use super::buckets_for_index_bits; use super::Buckets; use super::Index; use super::MaybeZeroable; use crate::buckets::SKIPPED_BUCKETS; use crate::buckets::SKIPPED_ENTRIES; use alloc::vec::Vec; use core::cell::Cell; std::thread_local!(static COUNTER: Cell = const { Cell::new(0) }); #[derive(Default)] struct Helper; unsafe impl MaybeZeroable for Helper { fn zeroable() -> bool { true } } impl Drop for Helper { fn drop(&mut self) { COUNTER.with(|c| c.set(c.get() + 1)); } } fn drops_with(f: impl FnOnce()) -> usize { COUNTER.with(|c| c.set(0)); f(); COUNTER.with(|c| c.get()) } fn drops(buckets: Buckets) -> usize { drops_with(|| drop(buckets)) } #[test] fn new() { assert_eq!(drops(>::new()), 0); assert_eq!( drops(>::new()), 0 ); } #[test] fn reserve() { let buckets = >::new(); buckets.reserve(Index::new(0).unwrap()); assert_eq!(drops(buckets), SKIPPED_ENTRIES + 1); let buckets = >::new(); buckets.reserve(Index::new(SKIPPED_ENTRIES).unwrap()); assert_eq!(drops(buckets), SKIPPED_ENTRIES + 1); let buckets = >::new(); buckets.reserve(Index::new(SKIPPED_ENTRIES + 1).unwrap()); assert_eq!(drops(buckets), (SKIPPED_ENTRIES + 1) * 3); let buckets = >::new(); let total = (1 << (5 + SKIPPED_BUCKETS)) - SKIPPED_ENTRIES - 1; assert_eq!(>::new(total), None); buckets.reserve(Index::new(total - 1).unwrap()); assert_eq!(drops(buckets), total); } #[test] fn truncate_exact() { let mut buckets = >::new(); let first_in_second_bucket = Index::new(SKIPPED_ENTRIES + 1).unwrap(); buckets.reserve(first_in_second_bucket); assert_eq!( drops_with(|| buckets.truncate(Index::new(SKIPPED_ENTRIES + 2).unwrap())), 0, ); assert_eq!( drops_with(|| buckets.truncate(first_in_second_bucket)), (SKIPPED_ENTRIES + 1) * 2 ); assert!(buckets.get(first_in_second_bucket).is_none()); assert_eq!(drops(buckets), SKIPPED_ENTRIES + 1); } #[test] fn get_or_alloc() { let buckets = >::new(); buckets.get_or_alloc(Index::new(48).unwrap()); assert_eq!(drops(buckets), 64); } #[test] fn iter() { let mut buckets = >::new(); // buckets go 32, 64, 128, 256; we allocate the second and fourth buckets.get_or_alloc(Index::new(48).unwrap()); buckets.get_or_alloc(Index::new(225).unwrap()); let indices = buckets.iter().map(|(i, _)| i).collect::>(); assert_eq!(indices.len(), 320); for &i in &indices { assert_eq!(*buckets.get(i).unwrap(), 0); } for (i, (index, val)) in buckets.iter_mut().enumerate() { assert_eq!(indices[i], index); *val = i as u16; } for (i, (index, val)) in buckets.into_iter().enumerate() { assert_eq!(indices[i], index); assert_eq!(val, i as u16); } } #[test] fn location() { let index = >::new(0).unwrap(); assert_eq!(index.location().bucket.0, 0); assert_eq!(index.location().bucket_len.get(), 32); assert_eq!(index.location().entry, 0); let index = >::new(31).unwrap(); assert_eq!(index.location().bucket.0, 0); assert_eq!(index.location().bucket_len.get(), 32); assert_eq!(index.location().entry, 31); let index = >::new(34).unwrap(); assert_eq!(index.location().bucket.0, 1); assert_eq!(index.location().bucket_len.get(), 64); assert_eq!(index.location().entry, 2); let max = usize::MAX - SKIPPED_ENTRIES - 1; assert_eq!( >::new(max + 1), None ); let index = >::new(max).unwrap(); assert_eq!( index.location().bucket.0, (usize::BITS as usize) - SKIPPED_BUCKETS - 1 ); assert_eq!(index.location().bucket_len.get(), usize::MAX / 2 + 1); assert_eq!(index.location().entry, usize::MAX / 2); } } boxcar-0.2.14/src/lib.rs000064400000000000000000000006311046102023000130710ustar 00000000000000#![doc = include_str!("../README.md")] #![deny(unsafe_op_in_unsafe_fn)] #![allow(clippy::needless_doctest_main)] #![no_std] extern crate alloc; #[cfg(test)] extern crate std; mod loom; pub mod buckets; pub mod vec; #[doc(inline)] pub use buckets::Buckets; #[doc(inline)] pub use vec::Vec; // Reexports for backward compatibility. #[doc(hidden)] pub use vec::IntoIter; #[doc(hidden)] pub use vec::Iter; boxcar-0.2.14/src/loom.rs000064400000000000000000000053441046102023000132770ustar 00000000000000pub use self::inner::*; pub trait AtomicMut { fn read_mut(&mut self) -> T; fn write_mut(&mut self, value: T); } #[cfg(loom)] mod inner { pub use loom::{cell, sync::atomic}; impl super::AtomicMut for atomic::AtomicBool { fn read_mut(&mut self) -> bool { self.load(atomic::Ordering::Relaxed) } fn write_mut(&mut self, value: bool) { self.store(value, atomic::Ordering::Relaxed) } } impl super::AtomicMut for atomic::AtomicUsize { fn read_mut(&mut self) -> usize { self.load(atomic::Ordering::Relaxed) } fn write_mut(&mut self, value: usize) { self.store(value, atomic::Ordering::Relaxed) } } impl super::AtomicMut<*mut T> for atomic::AtomicPtr { fn read_mut(&mut self) -> *mut T { self.load(atomic::Ordering::Relaxed) } fn write_mut(&mut self, value: *mut T) { self.store(value, atomic::Ordering::Relaxed) } } } #[cfg(not(loom))] mod inner { pub use core::sync::atomic; impl super::AtomicMut for atomic::AtomicBool { #[inline(always)] fn read_mut(&mut self) -> bool { *self.get_mut() } #[inline(always)] fn write_mut(&mut self, value: bool) { *self.get_mut() = value; } } impl super::AtomicMut for atomic::AtomicUsize { #[inline(always)] fn read_mut(&mut self) -> usize { *self.get_mut() } #[inline(always)] fn write_mut(&mut self, value: usize) { *self.get_mut() = value; } } impl super::AtomicMut<*mut T> for atomic::AtomicPtr { #[inline(always)] fn read_mut(&mut self) -> *mut T { *self.get_mut() } #[inline(always)] fn write_mut(&mut self, value: *mut T) { *self.get_mut() = value; } } pub mod cell { pub struct UnsafeCell(core::cell::UnsafeCell); impl UnsafeCell { #[inline(always)] pub fn new(val: T) -> Self { Self(core::cell::UnsafeCell::new(val)) } #[inline(always)] pub fn into_inner(self) -> T { self.0.into_inner() } #[inline(always)] pub fn with(&self, f: F) -> R where F: FnOnce(*const T) -> R, { f(self.0.get()) } #[inline(always)] pub fn with_mut(&self, f: F) -> R where F: FnOnce(*mut T) -> R, { f(self.0.get()) } } } } boxcar-0.2.14/src/vec/mod.rs000064400000000000000000000311321046102023000136570ustar 00000000000000//! A concurrent, append-only vector. //! //! See [the crate documentation](crate) and [`Vec`] for details. use core::fmt; use core::iter::FusedIterator; use core::ops::Index; mod raw; /// Creates a [`Vec`] containing the given elements. /// /// `vec!` allows `Vec`s to be defined with the same syntax as array expressions. /// There are two forms of this macro: /// /// - Create a [`Vec`] containing a given list of elements: /// /// ``` /// let vec = vec![1, 2, 3]; /// assert_eq!(vec[0], 1); /// assert_eq!(vec[1], 2); /// assert_eq!(vec[2], 3); /// ``` /// /// - Create a [`Vec`] from a given element and size: /// /// ``` /// let vec = vec![1; 3]; /// assert_eq!(vec, [1, 1, 1]); /// ``` #[macro_export] macro_rules! vec { () => { $crate::Vec::new() }; ($elem:expr; $n:expr) => {{ let n = $n; let mut vec = $crate::Vec::with_capacity(n); let iter = ::core::iter::Iterator::take(::core::iter::repeat($elem), n); ::core::iter::Extend::extend(&mut vec, iter); vec }}; ($($x:expr),+ $(,)?) => ( <$crate::Vec<_> as ::core::iter::FromIterator<_>>::from_iter([$($x),+]) ); } /// A concurrent, append-only vector. /// /// See [the crate documentation](crate) for details. /// /// # Notes /// /// The bucket array is stored inline, meaning that the `Vec` type /// is quite large on the stack. It is expected that you store it behind /// an [`Arc`](std::sync::Arc) or similar. pub struct Vec { raw: raw::Vec, } impl Default for Vec { fn default() -> Vec { Vec::new() } } impl Vec { /// Constructs a new, empty `Vec`. /// /// # Examples /// /// ``` /// let vec: boxcar::Vec = boxcar::Vec::new(); /// ``` #[inline] #[cfg(not(loom))] pub const fn new() -> Vec { Vec { raw: raw::Vec::new(), } } #[cfg(loom)] pub fn new() -> Vec { Vec { raw: raw::Vec::new(), } } /// Constructs a new, empty `Vec` with the specified capacity. /// /// The vector will be able to hold at least `capacity` elements /// without reallocating. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::with_capacity(10); /// /// for i in 0..10 { /// // Will not allocate. /// vec.push(i); /// } /// /// // May allocate. /// vec.push(11); /// ``` #[inline] pub fn with_capacity(capacity: usize) -> Vec { Vec { raw: raw::Vec::with_capacity(capacity), } } /// Reserves capacity for at least `additional` more elements to be inserted /// in the given `Vec`. The collection may reserve more space to avoid /// frequent reallocations. /// /// Does nothing if capacity is already sufficient. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::new(); /// vec.reserve(10); /// /// for i in 0..10 { /// // Will not allocate. /// vec.push(i); /// } /// /// // May allocate. /// vec.push(11); /// ``` pub fn reserve(&self, additional: usize) { self.raw.reserve(additional) } /// Appends an element to the back of the vector, /// returning the index it was inserted into. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![1, 2]; /// assert_eq!(vec.push(3), 2); /// assert_eq!(vec, [1, 2, 3]); /// ``` #[inline] pub fn push(&self, value: T) -> usize { self.raw.push(value) } /// Appends the element returned from the closure `f` to the back of the vector /// at the index supplied to the closure. /// /// Returns the index that the element was inserted into. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![0, 1]; /// vec.push_with(|index| index); /// assert_eq!(vec, [0, 1, 2]); /// ``` #[inline] pub fn push_with(&self, f: F) -> usize where F: FnOnce(usize) -> T, { self.raw.push_with(f) } /// Returns the number of elements in the vector. /// /// Note that due to concurrent writes, it is not guaranteed /// that all elements `0..vec.count()` are initialized. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::new(); /// assert_eq!(vec.count(), 0); /// vec.push(1); /// vec.push(2); /// assert_eq!(vec.count(), 2); /// ``` #[inline] pub fn count(&self) -> usize { self.raw.count() } /// Returns `true` if the vector contains no elements. /// /// # Examples /// /// ``` /// let vec = boxcar::Vec::new(); /// assert!(vec.is_empty()); /// /// vec.push(1); /// assert!(!vec.is_empty()); /// ``` #[inline] pub fn is_empty(&self) -> bool { self.count() == 0 } /// Returns a reference to the element at the given index. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![10, 40, 30]; /// assert_eq!(Some(&40), vec.get(1)); /// assert_eq!(None, vec.get(3)); /// ``` #[inline] pub fn get(&self, index: usize) -> Option<&T> { self.raw.get(index) } /// Returns a mutable reference to the element at the given index. /// /// # Examples /// /// ``` /// let mut vec = boxcar::vec![10, 40, 30]; /// assert_eq!(Some(&mut 40), vec.get_mut(1)); /// assert_eq!(None, vec.get_mut(3)); /// ``` #[inline] pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { self.raw.get_mut(index) } /// Returns a reference to an element, without doing bounds /// checking or verifying that the element is fully initialized. /// /// For a safe alternative see [`get`](Vec::get). /// /// # Safety /// /// Calling this method with an out-of-bounds index, or for an element that /// is being concurrently initialized is **undefined behavior**, even if /// the resulting reference is not used. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![1, 2, 4]; /// /// unsafe { /// assert_eq!(vec.get_unchecked(1), &2); /// } /// ``` #[inline] pub unsafe fn get_unchecked(&self, index: usize) -> &T { // Safety: Guaranteed by caller. unsafe { self.raw.get_unchecked(index) } } /// Returns a mutable reference to an element, without doing bounds /// checking or verifying that the element is fully initialized. /// /// For a safe alternative see [`get`](Vec::get). /// /// # Safety /// /// Calling this method with an out-of-bounds index is **undefined /// behavior**, even if the resulting reference is not used. /// /// # Examples /// /// ``` /// let mut vec = boxcar::vec![1, 2, 4]; /// /// unsafe { /// assert_eq!(vec.get_unchecked_mut(1), &mut 2); /// } /// ``` #[inline] pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { // Safety: Guaranteed by caller. unsafe { self.raw.get_unchecked_mut(index) } } /// Returns an iterator over the vector. /// /// Values are yielded in the form `(index, value)`. The vector may /// have in-progress concurrent writes that create gaps, so `index` /// may not be strictly sequential. /// /// # Examples /// /// ``` /// let vec = boxcar::vec![1, 2, 4]; /// let mut iterator = vec.iter(); /// /// assert_eq!(iterator.next(), Some((0, &1))); /// assert_eq!(iterator.next(), Some((1, &2))); /// assert_eq!(iterator.next(), Some((2, &4))); /// assert_eq!(iterator.next(), None); /// ``` #[inline] pub fn iter(&self) -> Iter<'_, T> { Iter { raw: self.raw.iter(), min_remaining: self.count(), } } /// Clears the vector, removing all values. /// /// Note that this method has no effect on the allocated capacity /// of the vector. /// /// # Examples /// /// ``` /// let mut vec = boxcar::Vec::new(); /// vec.push(1); /// vec.push(2); /// /// vec.clear(); /// assert!(vec.is_empty()); /// /// vec.push(3); // Will not allocate. /// ``` #[inline] pub fn clear(&mut self) { self.raw.clear(); } } impl Index for Vec { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { &self.raw[index] } } impl IntoIterator for Vec { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter { raw: self.raw.into_iter(), } } } impl<'a, T> IntoIterator for &'a Vec { type Item = (usize, &'a T); type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// An iterator that moves out of a vector. /// /// This struct is created by the `into_iter` method on [`Vec`] /// (provided by the [`IntoIterator`] trait). pub struct IntoIter { raw: raw::IntoIter, } impl Iterator for IntoIter { type Item = T; #[inline] fn next(&mut self) -> Option { self.raw.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.raw.size_hint() } } impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { self.raw.len() } } impl FusedIterator for IntoIter {} /// An iterator over the elements of a [`Vec`]. /// /// Note that the iterator is [fused](FusedIterator) on creation, and /// will not continue to check for concurrently inserted elements. /// /// See [`Vec::iter`] for details. pub struct Iter<'a, T> { raw: raw::Iter<'a, T>, min_remaining: usize, } impl FusedIterator for Iter<'_, T> {} impl<'a, T> Clone for Iter<'a, T> { fn clone(&self) -> Iter<'a, T> { Iter { raw: self.raw.clone(), min_remaining: self.min_remaining, } } } impl<'a, T> Iterator for Iter<'a, T> { type Item = (usize, &'a T); #[inline] fn next(&mut self) -> Option { let item = self.raw.next()?; self.min_remaining = self.min_remaining.saturating_sub(1); Some(item) } #[inline] fn size_hint(&self) -> (usize, Option) { (self.min_remaining, None) } } impl fmt::Debug for Iter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct Contents<'a, T>(&'a T); impl fmt::Debug for Contents<'_, T> where T: Iterator + Clone, T::Item: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.0.clone()).finish() } } f.debug_tuple("Iter").field(&Contents(self)).finish() } } impl FromIterator for Vec { fn from_iter>(iter: I) -> Self { let iter = iter.into_iter(); let (lower, _) = iter.size_hint(); let vec = Vec::with_capacity(lower); for value in iter { vec.push(value); } vec } } impl Extend for Vec { #[inline] fn extend>(&mut self, iter: I) { let iter = iter.into_iter(); let (lower, _) = iter.size_hint(); self.reserve(lower); for value in iter { self.push(value); } } } impl Clone for Vec { fn clone(&self) -> Vec { self.iter().map(|(_, x)| x).cloned().collect() } } impl fmt::Debug for Vec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter().map(|(_, v)| v)).finish() } } impl PartialEq for Vec { fn eq(&self, other: &Self) -> bool { if self.count() != other.count() { return false; } // Ensure indexes are checked along with values to handle gaps in the vector. for (index, value) in self.iter() { if other.get(index) != Some(value) { return false; } } true } } impl PartialEq for Vec where A: AsRef<[T]>, T: PartialEq, { fn eq(&self, other: &A) -> bool { let other = other.as_ref(); if self.count() != other.len() { return false; } // Ensure indexes are checked along with values to handle gaps in the vector. for (index, value) in self.iter() { if other.get(index) != Some(value) { return false; } } true } } impl Eq for Vec {} boxcar-0.2.14/src/vec/raw.rs000064400000000000000000000343731046102023000137030ustar 00000000000000#![allow(clippy::declare_interior_mutable_const)] use core::mem::{self, MaybeUninit}; use core::ops::Index; use core::panic::RefUnwindSafe; use core::ptr; use crate::buckets::{self, buckets_for_index_bits, Buckets, MaybeZeroable}; use crate::loom::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::loom::cell::UnsafeCell; use crate::loom::AtomicMut; /// A lock-free, append-only vector. pub struct Vec { /// A counter used to retrieve a unique index to push to. /// /// Stores a `buckets::Index` that may be out-of-bounds on /// the positive side, but never overflows. /// /// This value may be more than the true length as it will /// be incremented before values are actually stored. inflight: AtomicUsize, /// Buckets of length 32, 64 .. 2^62. buckets: Buckets, BUCKETS>, /// The number of initialized elements in this vector. count: AtomicUsize, } impl Vec { /// Create an empty vector. #[cfg(not(loom))] pub const fn new() -> Vec { let Some(zero) = >::new(0) else { unreachable!(); }; Vec { inflight: AtomicUsize::new(zero.into_raw().get()), buckets: Buckets::new(), count: AtomicUsize::new(0), } } /// Create an empty vector. #[cfg(loom)] pub fn new() -> Vec { Vec { inflight: AtomicUsize::new(>::new(0).unwrap().into_raw().get()), buckets: Buckets::new(), count: AtomicUsize::new(0), } } /// Constructs a new, empty `Vec` with the specified capacity. #[inline] pub fn with_capacity(capacity: usize) -> Vec { let mut this = Self::new(); if let Some(highest_index) = capacity.checked_sub(1) { this.buckets .reserve_mut(buckets::Index::new_saturating(highest_index)); } this } /// Returns the number of elements in the vector. #[inline] pub fn count(&self) -> usize { // The `Acquire` here synchronizes with the `Release` increment // when an entry is added to the vector. self.count.load(Ordering::Acquire) } /// Returns a reference to the element at the given index. #[inline] pub fn get(&self, index: usize) -> Option<&T> { // If the index is out-of-bounds, or the bucket is not allocated, return `None`. let entry = self.buckets.get(buckets::Index::new(index)?)?; if entry.active.load(Ordering::Acquire) { // Safety: The entry is active. unsafe { return Some(entry.value_unchecked()) } } // The entry is uninitialized. None } /// Returns a reference to the element at the given index. /// /// # Safety /// /// The entry at `index` must be initialized. #[inline] pub unsafe fn get_unchecked(&self, index: usize) -> &T { // Safety: Caller guarantees the bucket and entry are initialized. unsafe { let entry = self .buckets .get_unchecked(buckets::Index::new_unchecked(index)); (*entry).value_unchecked() } } /// Returns a mutable reference to the element at the given index. #[inline] pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { // If the index is out-of-bounds, or the bucket is not allocated, return `None`. let entry = self.buckets.get_mut(buckets::Index::new(index)?)?; if entry.active.read_mut() { // Safety: The entry is active. unsafe { return Some(entry.value_unchecked_mut()) } } // The entry is uninitialized. None } /// /// # Safety /// /// The entry at `index` must be initialized. #[inline] pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { // Safety: Caller guarantees the bucket and entry are initialized. unsafe { let entry = self .buckets .get_unchecked_mut(buckets::Index::new_unchecked(index)); (*entry).value_unchecked_mut() } } /// Returns a unique index for insertion. #[inline] fn next_index(&self) -> buckets::Index { // The inflight counter cannot exceed `isize::MAX`, allowing it to catch capacity overflow. // // Note that the `Relaxed` ordering here is sufficient, as we only care about // the index being unique and do not use it for synchronization. let index = self.inflight.fetch_add(1, Ordering::Relaxed); // Safety: We cannot overflow. let Some(index) = (unsafe { buckets::Index::from_raw_checked_above(index) }) else { self.next_index_overflow(); }; index } #[cold] #[inline(never)] fn next_index_overflow(&self) -> ! { // We could alternatively abort here, as `Arc` does. But we decrement and panic instead // to keep in line with `Vec`'s behavior. Assuming that `isize::MAX` concurrent threads // don't call this method, it is still impossible for it to overflow. self.inflight.fetch_sub(1, Ordering::Relaxed); panic!("capacity overflow"); } /// Appends the element returned from the closure to the back of the vector /// at the index represented by the `usize` passed to closure. /// /// This allows for use of the would-be index to be utilized within the /// element. #[inline] pub fn push_with(&self, f: F) -> usize where F: FnOnce(usize) -> T, { // Acquire a unique index to insert into. let index = self.next_index(); let value = f(index.get()); // Safety: `next_index` is always in-bounds and unique. unsafe { self.write(index, value) } } /// Appends an element to the back of the vector. #[inline] pub fn push(&self, value: T) -> usize { // Safety: `next_index` is always in-bounds and unique. unsafe { self.write(self.next_index(), value) } } /// Write an element at the given index. /// /// # Safety /// /// The index must be unique. #[inline] unsafe fn write(&self, index: buckets::Index, value: T) -> usize { let entry = self.buckets.get_or_alloc(index); unsafe { // Safety: We have unique access to this entry (ensured by // the caller). // // 1. It is impossible for another thread to attempt a `push` // to this location as we retrieved it from `next_index`. // // 2. Any thread trying to `get` this entry will see `!active` // and will not try to access it. entry .slot .with_mut(|slot| slot.write(MaybeUninit::new(value))); // Let other threads know that this entry is active. // // Note that this `Release` write synchronizes with the `Acquire` // load in `Vec::get`. entry.active.store(true, Ordering::Release); } // Increase the element count. // // The `Release` here is not strictly necessary, but does // allow users to use `count` for some sort of synchronization // in terms of the number of initialized elements. self.count.fetch_add(1, Ordering::Release); // Return the index of the entry that we initialized. index.get() } /// Reserves capacity for at least `additional` more elements to be inserted in /// the vector. /// /// The collection may reserve more space to avoid frequent reallocations. pub fn reserve(&self, additional: usize) { let len = self.count.load(Ordering::Acquire); if let Some(highest_index) = len.saturating_add(additional).checked_sub(1) { self.buckets .reserve(buckets::Index::new_saturating(highest_index)); } } /// Iterates over shared references to values in the vector. #[inline] pub fn iter(&self) -> Iter<'_, T> { Iter { inner: self.buckets.iter(), // Snapshot the number of potentially active entries when the iterator is // created, so we know how many entries we have to check. The alternative // is to always check every single entry and/or bucket. Note that yielding // `self.count` entries is not enough; we may end up yielding the *wrong* // `self.count` entries, as the gaps in-between the entries we want to yield // may get filled concurrently. // Safety: We cannot overflow. after_inflight: unsafe { buckets::Index::from_raw_checked_above(self.inflight.load(Ordering::Relaxed)) }, } } /// Iterates over owned values in the vector. #[inline] pub fn into_iter(mut self) -> IntoIter { IntoIter { inner: self.buckets.into_iter(), remaining: self.count.read_mut(), } } /// Clear every element in the vector. #[inline] pub fn clear(&mut self) { // Consume and reset every entry in the vector. self.buckets .iter_mut() .filter_map(|(_, entry)| entry.take()) .take(self.count.read_mut()) .for_each(drop); // Reset the count. self.count.write_mut(0); self.inflight .write_mut(>::new(0).unwrap().into_raw().get()); } } impl Index for Vec { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { #[cold] #[inline(never)] fn assert_failed(index: usize) -> ! { panic!("index {index} is uninitialized"); } match self.get(index) { Some(value) => value, None => assert_failed(index), } } } /// A possibly uninitialized entry in the vector. struct Entry { /// A flag indicating whether or not this entry is initialized. active: AtomicBool, /// The entry's value. slot: UnsafeCell>, } impl Default for Entry { fn default() -> Self { Self { active: AtomicBool::new(false), slot: UnsafeCell::new(MaybeUninit::uninit()), } } } // Safety: `AtomicBool` and `MaybeUninit` are all zeroable outside Loom. unsafe impl MaybeZeroable for Entry { fn zeroable() -> bool { // Loom's types are not zeroable; thus we fall back to `Default`. cfg!(not(loom)) } } // Safety: An `Entry` is owned and owns its data, so sending an // entry only sends that data, hence `T: Send`. unsafe impl Send for Entry {} // Safety: Sharing an `Entry` exposes shared access to the // data inside, hence `T: Sync`. Additionally, the `Entry` // may act as a channel, exposing owned access of elements // to other threads, hence we also require `T: Send`. unsafe impl Sync for Entry {} // In an ideal world, we might require `T: UnwindSafe + RefUnwindSafe` // here, with similar reasoning to above. But this is here for backward // compatibility. Also, who ever worried about unwind safety :P impl RefUnwindSafe for Entry {} impl Entry { /// Returns a reference to the value in this entry. /// /// # Safety /// /// The value must be initialized. #[inline] unsafe fn value_unchecked(&self) -> &T { // Safety: Guaranteed by caller. self.slot.with(|slot| unsafe { (*slot).assume_init_ref() }) } /// Returns a mutable reference to the value in this entry. /// // # Safety // // The value must be initialized. #[inline] unsafe fn value_unchecked_mut(&mut self) -> &mut T { // Safety: Guaranteed by caller. self.slot .with_mut(|slot| unsafe { (*slot).assume_init_mut() }) } /// Takes the value in the entry, if there is one. fn take(&mut self) -> Option { if self.active.read_mut() { // Mark the entry as uninitialized so it is not accessed after this. self.active.write_mut(false); let value = mem::replace(&mut self.slot, UnsafeCell::new(MaybeUninit::uninit())); // Safety: We just verified that the entry is initialized. Some(unsafe { value.into_inner().assume_init() }) } else { None } } } impl Drop for Entry { fn drop(&mut self) { if self.active.read_mut() { // Safety: We have `&mut self` and verifid that the value is initialized. unsafe { ptr::drop_in_place(self.slot.with_mut(|slot| (*slot).as_mut_ptr())) } } } } /// The number of buckets in a vector. const BUCKETS: usize = buckets_for_index_bits(usize::BITS - 1); pub struct Iter<'a, T> { inner: buckets::Iter<'a, Entry, BUCKETS>, after_inflight: Option>, } impl Clone for Iter<'_, T> { fn clone(&self) -> Self { Self { inner: self.inner.clone(), after_inflight: self.after_inflight, } } } impl<'a, T> Iterator for Iter<'a, T> { type Item = (usize, &'a T); fn next(&mut self) -> Option { loop { let (index, entry) = self.inner.next()?; if self .after_inflight .is_some_and(|after_inflight| index >= after_inflight) { return None; } if entry.active.load(Ordering::Acquire) { // Safety: We just verified that the entry is initialized. return Some((index.get(), unsafe { entry.value_unchecked() })); } } } } pub struct IntoIter { inner: buckets::IntoIter, BUCKETS>, remaining: usize, } impl Iterator for IntoIter { type Item = T; fn next(&mut self) -> Option { loop { self.remaining = self.remaining.checked_sub(1)?; let (_, mut entry) = self.inner.next()?; if let Some(value) = entry.take() { return Some(value); } } } fn size_hint(&self) -> (usize, Option) { (self.remaining, Some(self.remaining)) } } impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { self.remaining } } boxcar-0.2.14/tests/loom.rs000064400000000000000000000044121046102023000136450ustar 00000000000000#![cfg(loom)] use loom::thread; use std::sync::Arc; #[test] fn write_write() { loom::model(|| { let vec = Arc::new(boxcar::Vec::new()); let v1 = vec.clone(); let v2 = vec.clone(); let t1 = thread::spawn(move || v1.push(1)); let t2 = thread::spawn(move || v2.push(2)); let i1 = t1.join().unwrap(); let i2 = t2.join().unwrap(); assert_eq!(vec[i1], 1); assert_eq!(vec[i2], 2); assert_eq!(vec.count(), 2); }); } #[test] fn read_write() { loom::model(|| { let vec = Arc::new(boxcar::Vec::new()); let v1 = vec.clone(); let v2 = vec.clone(); let t1 = thread::spawn(move || v1.push(1)); let t2 = thread::spawn(move || loop { let Some(&v) = v2.get(0) else { thread::yield_now(); continue; }; break v; }); t1.join().unwrap(); let val = t2.join().unwrap(); assert_eq!(val, 1); assert_eq!(vec.count(), 1); }); } #[test] fn iter_linearizability() { loom::model(|| { let vec = Arc::new(boxcar::Vec::new()); let v1 = vec.clone(); let v2 = vec.clone(); let t1 = thread::spawn(move || v1.push(1)); let t2 = thread::spawn(move || { v2.push(2); assert!(v2.iter().find(|&(_, x)| *x == 2).is_some()); }); t1.join().unwrap(); t2.join().unwrap(); }); } #[test] fn mixed() { loom::model(|| { let vec = Arc::new(boxcar::Vec::new()); let v1 = vec.clone(); let v2 = vec.clone(); let v3 = vec.clone(); let t1 = thread::spawn(move || { v1.push(0); }); let t2 = thread::spawn(move || { v2.push(1); }); let t3 = thread::spawn(move || { for i in 0..2 { if let Some(&v) = v3.get(i) { assert!(v == 0 || v == 1); }; } }); t1.join().unwrap(); t2.join().unwrap(); t3.join().unwrap(); assert_eq!(vec.count(), 2); let mut values = vec.iter().map(|(_, &v)| v).collect::>(); values.sort(); assert_eq!(values, (0..2).collect::>()); }); } boxcar-0.2.14/tests/loom_buckets.rs000064400000000000000000000012251046102023000153640ustar 00000000000000#![cfg(loom)] use std::sync::Arc; use boxcar::buckets; use boxcar::Buckets; use loom::thread; #[test] fn get_or_alloc() { loom::model(|| { let buckets = Arc::new(>::new()); let b1 = buckets.clone(); let b2 = buckets.clone(); let b3 = buckets.clone(); let index = buckets::Index::new(0).unwrap(); let t1 = thread::spawn(move || _ = b1.get_or_alloc(index)); let t2 = thread::spawn(move || _ = b2.get_or_alloc(index)); let t3 = thread::spawn(move || buckets.iter().for_each(drop)); t1.join().unwrap(); t2.join().unwrap(); t3.join().unwrap(); }); } boxcar-0.2.14/tests/vec.rs000064400000000000000000000065031046102023000134570ustar 00000000000000use std::sync::atomic::{AtomicUsize, Ordering}; use std::{sync::Barrier, thread}; #[test] fn simple() { let vec = boxcar::vec![0, 1, 2]; assert_eq!(vec[0], 0); assert_eq!(vec[1], 1); assert_eq!(vec[2], 2); for x in 3..1000 { let i = vec.push(x); assert_eq!(vec[i], x); } for i in 0..1000 { assert_eq!(vec[i], i); } for (i, &x) in vec.iter() { assert_eq!(i, x); } for (i, x) in vec.into_iter().enumerate() { assert_eq!(i, x); } } #[test] fn simple_boxed() { let vec = boxcar::vec![Box::new(0), Box::new(1), Box::new(2)]; assert_eq!(vec[0], Box::new(0)); assert_eq!(vec[1], Box::new(1)); assert_eq!(vec[2], Box::new(2)); for x in 3..1000 { let i = vec.push(Box::new(x)); assert_eq!(*vec[i], x); } for i in 0..1000 { assert_eq!(*vec[i], i); } for (i, x) in vec.iter() { assert_eq!(i, **x); } for (i, x) in vec.into_iter().enumerate() { assert_eq!(i, *x); } } #[test] fn clear() { struct T<'a>(&'a AtomicUsize); impl Drop for T<'_> { fn drop(&mut self) { self.0.fetch_add(1, Ordering::Relaxed); } } let drops = AtomicUsize::new(0); let mut vec = boxcar::Vec::new(); vec.push(T(&drops)); vec.push(T(&drops)); let first_ptr: *const _ = vec.iter().next().unwrap().1 as _; vec.clear(); assert_eq!(vec.count(), 0); assert_eq!(vec.iter().count(), 0); assert_eq!(drops.swap(0, Ordering::Relaxed), 2); vec.clear(); assert_eq!(vec.count(), 0); assert_eq!(vec.iter().count(), 0); assert_eq!(drops.load(Ordering::Relaxed), 0); vec.push(T(&drops)); let ptr: *const _ = vec.iter().next().unwrap().1 as _; assert_eq!(ptr, first_ptr); drop(vec); assert_eq!(drops.load(Ordering::Relaxed), 1); } #[test] fn fused_iterator() { let vec = boxcar::vec![0, 1, 2]; assert_eq!(vec.iter().collect::>(), [(0, &0), (1, &1), (2, &2)]); let iter = vec.iter(); vec.push(3); assert_eq!(iter.collect::>(), [(0, &0), (1, &1), (2, &2)]); } #[test] fn stress() { let vec = boxcar::Vec::new(); let barrier = Barrier::new(6); thread::scope(|s| { s.spawn(|| { barrier.wait(); for i in 0..1000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 1000..2000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 2000..3000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 3000..4000 { vec.push(i); } }); s.spawn(|| { barrier.wait(); for i in 0..10_000 { if let Some(&x) = vec.get(i) { assert!(x < 4000); } } }); s.spawn(|| { barrier.wait(); for (i, &x) in vec.iter() { assert!(x < 4000); assert!(vec[i] < 4000); } }); }); assert_eq!(vec.count(), 4000); let mut sorted = vec.into_iter().collect::>(); sorted.sort(); assert_eq!(sorted, (0..4000).collect::>()); }