├── .github ├── dependabot.yml └── workflows │ ├── bench.yml │ ├── codspeed.yml │ ├── release-plz.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches └── bench.rs ├── src ├── impls.rs ├── impls │ ├── codec.rs │ ├── columns.rs │ ├── deduplicate.rs │ ├── huffman_container.rs │ ├── index.rs │ ├── mirror.rs │ ├── option.rs │ ├── result.rs │ ├── slice.rs │ ├── slice_owned.rs │ ├── storage.rs │ ├── string.rs │ ├── tuple.rs │ └── vec.rs └── lib.rs └── tests ├── cow.rs ├── person.rs └── recursive.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | # Check for updates every Monday 6 | schedule: 7 | interval: "weekly" 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/bench.yml: -------------------------------------------------------------------------------- 1 | name: "Bench Suite" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | bench: 9 | name: cargo bench 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions-rust-lang/setup-rust-toolchain@v1 14 | with: 15 | toolchain: nightly 16 | - name: Cargo bench 17 | run: cargo +nightly bench | tee output.txt 18 | # Run `github-action-benchmark` action 19 | - name: Store benchmark result 20 | uses: benchmark-action/github-action-benchmark@v1 21 | with: 22 | tool: 'cargo' 23 | output-file-path: output.txt 24 | github-token: ${{ secrets.GITHUB_TOKEN }} 25 | auto-push: true 26 | fail-on-alert: true 27 | -------------------------------------------------------------------------------- /.github/workflows/codspeed.yml: -------------------------------------------------------------------------------- 1 | name: codspeed-benchmarks 2 | 3 | on: 4 | # Run on pushes to the main branch 5 | push: 6 | branches: 7 | - "main" 8 | # Run on pull requests 9 | pull_request: 10 | # `workflow_dispatch` allows CodSpeed to trigger backtest 11 | # performance analysis in order to generate initial data. 12 | workflow_dispatch: 13 | 14 | jobs: 15 | benchmarks: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions-rust-lang/setup-rust-toolchain@v1 20 | 21 | - name: Build the codspeed binary 22 | run: cargo install cargo-codspeed 23 | 24 | - name: Build the benchmark targets 25 | run: cargo codspeed build 26 | 27 | - name: Run the benchmarks 28 | uses: CodSpeedHQ/action@v3 29 | with: 30 | run: cargo codspeed run 31 | token: ${{ secrets.CODSPEED_TOKEN }} 32 | -------------------------------------------------------------------------------- /.github/workflows/release-plz.yml: -------------------------------------------------------------------------------- 1 | name: Release-plz 2 | 3 | permissions: 4 | pull-requests: write 5 | contents: write 6 | 7 | on: 8 | push: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | release-plz: 14 | name: Release-plz 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: Install Rust toolchain 22 | uses: actions-rust-lang/setup-rust-toolchain@v1 23 | - name: Run release-plz 24 | uses: MarcoIeni/release-plz-action@v0.5 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Test Suite" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | os: 13 | - ubuntu 14 | - macos 15 | - windows 16 | toolchain: 17 | - stable 18 | - 1.78 19 | name: cargo test on ${{ matrix.os }}, rust ${{ matrix.toolchain }} 20 | runs-on: ${{ matrix.os }}-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions-rust-lang/setup-rust-toolchain@v1 24 | with: 25 | components: clippy 26 | toolchain: ${{ matrix.toolchain }} 27 | - name: Cargo test 28 | run: cargo test 29 | - name: Cargo clippy 30 | run: cargo clippy 31 | 32 | # Check formatting with rustfmt 33 | formatting: 34 | name: cargo fmt 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v4 38 | # Ensure rustfmt is installed and setup problem matcher 39 | - uses: actions-rust-lang/setup-rust-toolchain@v1 40 | with: 41 | components: rustfmt 42 | - name: Rustfmt Check 43 | uses: actions-rust-lang/rustfmt@v1 44 | 45 | bench: 46 | name: cargo bench 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v4 50 | - uses: actions-rust-lang/setup-rust-toolchain@v1 51 | with: 52 | toolchain: nightly 53 | - name: Cargo bench 54 | run: cargo +nightly bench | tee output.txt 55 | # Run `github-action-benchmark` action 56 | - name: Store benchmark result 57 | uses: benchmark-action/github-action-benchmark@v1 58 | with: 59 | tool: 'cargo' 60 | output-file-path: output.txt 61 | fail-on-alert: true 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.5.0](https://github.com/antiguru/flatcontainer/compare/v0.4.1...v0.5.0) - 2024-06-26 10 | 11 | ### Other 12 | - Rename Containerized to RegionPreference and add owned type ([#47](https://github.com/antiguru/flatcontainer/pull/47)) 13 | - Use vectors as regions ([#46](https://github.com/antiguru/flatcontainer/pull/46)) 14 | - Efficient cloning of regions and flat stack ([#45](https://github.com/antiguru/flatcontainer/pull/45)) 15 | - Use OffsetOptimized in consecutive offset pairs ([#43](https://github.com/antiguru/flatcontainer/pull/43)) 16 | - Add reserve items to consecutive offset pairs ([#42](https://github.com/antiguru/flatcontainer/pull/42)) 17 | - Improve GatCow test ([#41](https://github.com/antiguru/flatcontainer/pull/41)) 18 | 19 | ## [0.4.1](https://github.com/antiguru/flatcontainer/compare/v0.4.0...v0.4.1) - 2024-06-17 20 | 21 | ### Other 22 | - Add missing Ord and ReserveItems impls ([#39](https://github.com/antiguru/flatcontainer/pull/39)) 23 | - Huffman container ([#20](https://github.com/antiguru/flatcontainer/pull/20)) 24 | - Fix warning on Rust 1.79 ([#38](https://github.com/antiguru/flatcontainer/pull/38)) 25 | - Move complex tests to separate folder ([#34](https://github.com/antiguru/flatcontainer/pull/34)) 26 | 27 | ## [0.3.2](https://github.com/antiguru/flatcontainer/compare/v0.3.1...v0.3.2) - 2024-05-28 28 | 29 | ### Other 30 | - Thinking about relating owned types and read items ([#31](https://github.com/antiguru/flatcontainer/pull/31)) 31 | - Introduce reborrow to enable lifetime variance ([#32](https://github.com/antiguru/flatcontainer/pull/32)) 32 | 33 | ## [0.3.1](https://github.com/antiguru/flatcontainer/compare/v0.3.0...v0.3.1) - 2024-05-24 34 | 35 | ### Other 36 | - Update recommended version to 0.3 ([#29](https://github.com/antiguru/flatcontainer/pull/29)) 37 | 38 | ## [0.3.0](https://github.com/antiguru/flatcontainer/compare/v0.2.0...v0.3.0) - 2024-05-24 39 | 40 | ### Other 41 | - Replace CopyOnto by Push ([#28](https://github.com/antiguru/flatcontainer/pull/28)) 42 | - Fix bench, add to ci 43 | 44 | ## [0.2.0](https://github.com/antiguru/flatcontainer/compare/v0.1.0...v0.2.0) - 2024-03-13 45 | 46 | ### Changes 47 | - Merge pull request [#23](https://github.com/antiguru/flatcontainer/pull/23) from antiguru/slice_implementations 48 | - Rename CopyRegion to OwnedRegion and relax trait bounds 49 | - Remove CopyOnto requirement for ReadItem 50 | - Remove index parameter from columns region 51 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flatcontainer" 3 | version = "0.5.0" 4 | edition = "2021" 5 | authors = ["Moritz Hoffmann "] 6 | description = "A flat container representation for Rust" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/antiguru/flatcontainer" 9 | rust-version = "1.65" 10 | 11 | [dependencies] 12 | cfg-if = "1.0" 13 | paste = "1.0" 14 | serde = { version = "1.0", optional = true, features = ["derive"]} 15 | 16 | [features] 17 | default = ["serde"] 18 | 19 | [profile.bench] 20 | debug = 2 21 | codegen-units = 1 22 | lto = true 23 | 24 | [dev-dependencies] 25 | bencher = "0.1.5" 26 | codspeed-bencher-compat = "2.6.0" 27 | 28 | [[bench]] 29 | name = "bench" 30 | harness = false 31 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | flatcontainer 2 | ======= 3 | 4 | A flat container for Rust. 5 | 6 | ```toml 7 | [dependencies] 8 | flatcontainer = "0.4" 9 | ``` 10 | 11 | ## Example 12 | 13 | ```rust 14 | use flatcontainer::FlatStack; 15 | 16 | let r: Result<_, u16> = Ok("abc"); 17 | let mut c = FlatStack::default_impl::>(); 18 | c.copy(&r); 19 | assert_eq!(r, c.get(0)); 20 | ``` 21 | 22 | ## Details 23 | 24 | `flatcontainer` is a library that provides abstractions and implementations for 25 | flattening collections of nested data structures into dense allocations. At its 26 | core, a `Region` trait describes how to represent data in memory, with the option 27 | to extract a lifetimed representation of the original data, which can be different 28 | from what the caller had initially. 29 | 30 | `flatcontainer` decouples the write-half of a container from its storage and read 31 | interface to permit a multitude of types to be presented to the same region. For 32 | example, a region containing string-like objects and only promising access to 33 | `&str` can accept anything that looks like a string, i.e., `String`, `&str` and 34 | references to said types. A region's write-half should be implemented using the 35 | [`Push`] trait. 36 | 37 | Regions permit data access through opaque indexes, which the caller is responsible 38 | for memorizing. An index grants access to the region-specific data representation, 39 | and although it might be inspected, it should not be minted or modified in any 40 | way. As far as a region is concerned, an index is an opaque type with no particular 41 | meaning attached to it, unless specified differently by the region. 42 | 43 | Regions roughly follow two patterns: Either they fan out to other regions, or they 44 | behave as terminal nodes and explicitly contain storage. A [`Result`][ResultRegion] region 45 | dispatches to an ok and error region, and uses its index type to distinguish where 46 | to look up a value. A region for slices has storage to remember slices of indexes 47 | where the index can be used to look up the datum's representation in an inner 48 | region. 49 | 50 | `flatcontainer` provides the [`FlatStack`] type, an exemplary implementation of how to 51 | implement a collection of items that supports pushing additional elements, 52 | and retrieval by an index to previously pushed elements. It can be used in many 53 | places that simply want to use a flat data representation, but it leaves potential 54 | for optimization behind. Specifically, indexes, although opaque, follow a 55 | simple structure where a more efficient storage can save memory instead of blindly 56 | writing down all values. 57 | 58 | All region implementations should be considered examples on how to implement specific 59 | regions that are tailored to the needs of the type, and characteristics of the data 60 | encountered in practice. This crate provides a [`RegionPreference`] trait to let types 61 | express their suggested region, but users can select a different region, as long as 62 | it is compatible with the types to be stored. For example, a vector suggests to use 63 | a slice-based region, but a client might know that the inner type is copy, and hence 64 | better uses a sliced-based region that does not fan out to a region for the individual 65 | elements. 66 | 67 | ## Safety 68 | 69 | This crate is safe to use, and all unsafe code can be explained locally. 70 | At the moment, this is only for assuming that utf-8 data is correct, which is true by 71 | construction. 72 | 73 | ## Features 74 | 75 | The `serde` feature controls whether types implement support for serializing and deserializing 76 | data. Enabled by default. 77 | 78 | ## Performance and design considerations 79 | 80 | A goal of flatcontainer is to store `O(n)` objects in less than `O(n)` allocations, 81 | for example in `O(log n)`, or if pre-sized correctly, in constant allocations. It 82 | can achieve this by laying out data densely in memory. This comes with benefits, and 83 | restrictions. It allows fast sequential access as the CPU can prefetch data, and it 84 | avoids loading data into caches that is not needed. Reducing the number of allocations 85 | limits chatter with the allocator. On the flip side, the regions are append-only, and 86 | copying a value destructs its original form. Reading owned data requires copying or 87 | cloning. 88 | 89 | Flatcontainer's region abstraction requires the user to store indexes. Without specific 90 | knowledge about a region, it's likely the index is best stored in a vector. In many cases, 91 | we know more about the data, and can use a better approach to storing indexes. 92 | 93 | * Regions storing slices often have an index that looks like `(start, end)`. It is easy 94 | to observe that the previous element's end is equal to the current element's start, 95 | so it should only be stored once. This can reduce the size of the index per element from 96 | 16 to 8 bytes. 97 | * For index values that fit in 32 bits, we only need 4 bytes in memory. We can specialize 98 | a container for indexes that uses `u32` to store values smaller than 2^32, and use `u64` 99 | otherwise. 100 | * The index into regions storing constant-sized elements often looks like strided numbers, 101 | e.g., 0, 2, 4, 8, ..., which we can represent using constant memory by remembering the 102 | stride and length. We can extend this to storing a tail of elements equals to the last 103 | stride by adding another count. Such an index container uses 0 bits in the limit! 104 | * Consult the [index] module for types specialized to storing indexes using less bits. 105 | 106 | Flatcontainer provides some implementations of these concepts. To merge adjacent start-end 107 | pairs, wrap a region in a [`ConsecutiveIndexPairs`] region. It stores indexes and presents 108 | outwards as a dense sequence of 0, 1, 2, ... 109 | 110 | A [`CollapseSequence`] region remembers the index of the last element, and if a new element 111 | equals the last, it'll return the previous index again instead of storing the same element 112 | multiple times. This is limited to the direct predecessor, because otherwise storing 113 | indexes and scanning through previous elements would be too expensive. 114 | 115 | [index]: impls::index 116 | [`ConsecutiveIndexPairs`]: impls::deduplicate::ConsecutiveIndexPairs 117 | [`CollapseSequence`]: impls::deduplicate::CollapseSequence 118 | 119 | ## Comparison to columnation 120 | 121 | Flatcontainer takes several ideas from [`columnation`](https://github.com/frankmcsherry/columnation). 122 | It uses the concept of regions to abstract dense allocations while still identifying 123 | individual objects. Where columnation returns owned objects that need to be treated as 124 | if they were references, flatcontainer returns an opaque index, which a region can 125 | translate to a lifetimed type. Columnation reads and writes the same type, which forces 126 | it to accept owned data and present look-alike data outwards, forcing the user to be 127 | careful when dropping data. Flatcontainer avoids these issues because indexes do not 128 | contain pointers to owned data. 129 | 130 | Flatcontainer offers similar performance to columnation, and is faster in some 131 | situations. For storing strings, flatcontainer provides an index of `(start, end)` 132 | to reconstruct the original input as a reference, while columnation returns an 133 | owned string, which roughly looks like `(pointer, size, capacity)`. Instead of 24 bytes 134 | overhead on a 64-bit CPU, we have 16 byte overhead. We can reduce the 16 byte overhead 135 | to 8 by observing that consecutive entries start at the previous' end, allowing us to 136 | reconstruct the end by looking at the next element's start. 137 | 138 | #### License 139 | 140 | 141 | Licensed under either of Apache License, Version 142 | 2.0 or MIT license at your option. 143 | 144 | 145 |
146 | 147 | 148 | Unless you explicitly state otherwise, any contribution intentionally submitted 149 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 150 | be dual licensed as above, without any additional terms or conditions. 151 | 152 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | //! A simple benchmark for flatcontainer, adopted from `columnation`'s benchmark. 2 | 3 | use codspeed_bencher_compat::{benchmark_group, benchmark_main, Bencher}; 4 | use flatcontainer::impls::deduplicate::{CollapseSequence, ConsecutiveIndexPairs}; 5 | use flatcontainer::impls::index::IndexOptimized; 6 | use flatcontainer::impls::tuple::{TupleABCRegion, TupleABRegion}; 7 | use flatcontainer::{ 8 | ColumnsRegion, FlatStack, MirrorRegion, OwnedRegion, Push, Region, RegionPreference, 9 | ReserveItems, SliceRegion, StringRegion, 10 | }; 11 | 12 | fn empty_copy(bencher: &mut Bencher) { 13 | _bench_copy(bencher, vec![(); 1024]); 14 | } 15 | fn u64_copy(bencher: &mut Bencher) { 16 | _bench_copy(bencher, vec![0u64; 1024]); 17 | } 18 | fn u32x2_copy(bencher: &mut Bencher) { 19 | _bench_copy(bencher, vec![(0u32, 0u32); 1024]); 20 | } 21 | fn u8_u64_copy(bencher: &mut Bencher) { 22 | _bench_copy(bencher, vec![(0u8, 0u64); 512]); 23 | } 24 | fn str10_copy(bencher: &mut Bencher) { 25 | _bench_copy(bencher, vec!["grawwwwrr!"; 1024]); 26 | } 27 | fn string10_copy(bencher: &mut Bencher) { 28 | _bench_copy(bencher, vec![format!("grawwwwrr!"); 1024]); 29 | } 30 | fn string20_copy(bencher: &mut Bencher) { 31 | _bench_copy(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); 32 | } 33 | fn vec_u_s_copy(bencher: &mut Bencher) { 34 | _bench_copy( 35 | bencher, 36 | vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], 37 | ); 38 | } 39 | fn vec_u_vn_s_copy(bencher: &mut Bencher) { 40 | _bench_copy( 41 | bencher, 42 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 43 | ); 44 | } 45 | 46 | fn empty_copy_region(bencher: &mut Bencher) { 47 | _bench_copy_region::, _>(bencher, vec![(); 1024]); 48 | } 49 | fn u64_copy_region(bencher: &mut Bencher) { 50 | _bench_copy_region::, _>(bencher, vec![0u64; 1024]); 51 | } 52 | fn u32x2_copy_region(bencher: &mut Bencher) { 53 | _bench_copy_region::, _>(bencher, vec![(0u32, 0u32); 1024]); 54 | } 55 | fn u8_u64_copy_region(bencher: &mut Bencher) { 56 | _bench_copy_region::, _>(bencher, vec![(0u8, 0u64); 512]); 57 | } 58 | fn str10_copy_region(bencher: &mut Bencher) { 59 | _bench_copy_region::, _>(bencher, vec!["grawwwwrr!"; 1024]); 60 | } 61 | fn str100_copy_region(bencher: &mut Bencher) { 62 | _bench_copy_region::, _>(bencher, vec!["grawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrr!!!!!!!!!grawwwwrr!"; 1024]); 63 | } 64 | fn string10_copy_region(bencher: &mut Bencher) { 65 | _bench_copy_region::, _>(bencher, vec![format!("grawwwwrr!"); 1024]); 66 | } 67 | fn string10_copy_region_collapse(bencher: &mut Bencher) { 68 | _bench_copy_region::< 69 | SliceRegion>, IndexOptimized>, 70 | _, 71 | >(bencher, vec![format!("grawwwwrr!"); 1024]); 72 | } 73 | fn string20_copy_region(bencher: &mut Bencher) { 74 | _bench_copy_region::, _>( 75 | bencher, 76 | vec![format!("grawwwwrr!!!!!!!!!!!"); 512], 77 | ); 78 | } 79 | fn vec_u_s_copy_region(bencher: &mut Bencher) { 80 | _bench_copy_region::, StringRegion>>>, _>( 81 | bencher, 82 | vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], 83 | ); 84 | } 85 | fn vec_u_vn_s_copy_region(bencher: &mut Bencher) { 86 | _bench_copy_region::< 87 | SliceRegion, OwnedRegion<_>, StringRegion>>>, 88 | _, 89 | >( 90 | bencher, 91 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 92 | ); 93 | } 94 | fn vec_u_vn_s_copy_region_column(bencher: &mut Bencher) { 95 | _bench_copy_region::< 96 | SliceRegion< 97 | ColumnsRegion< 98 | TupleABCRegion< 99 | MirrorRegion<_>, 100 | CollapseSequence>, 101 | CollapseSequence, 102 | >, 103 | >, 104 | >, 105 | _, 106 | >( 107 | bencher, 108 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 109 | ); 110 | } 111 | 112 | fn empty_clone(bencher: &mut Bencher) { 113 | _bench_clone(bencher, vec![(); 1024]); 114 | } 115 | fn u64_clone(bencher: &mut Bencher) { 116 | _bench_clone(bencher, vec![0u64; 1024]); 117 | } 118 | fn u32x2_clone(bencher: &mut Bencher) { 119 | _bench_clone(bencher, vec![(0u32, 0u32); 1024]); 120 | } 121 | fn u8_u64_clone(bencher: &mut Bencher) { 122 | _bench_clone(bencher, vec![(0u8, 0u64); 512]); 123 | } 124 | fn str10_clone(bencher: &mut Bencher) { 125 | _bench_clone(bencher, vec!["grawwwwrr!"; 1024]); 126 | } 127 | fn string10_clone(bencher: &mut Bencher) { 128 | _bench_clone(bencher, vec![format!("grawwwwrr!"); 1024]); 129 | } 130 | fn string20_clone(bencher: &mut Bencher) { 131 | _bench_clone(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); 132 | } 133 | fn vec_u_s_clone(bencher: &mut Bencher) { 134 | _bench_clone( 135 | bencher, 136 | vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], 137 | ); 138 | } 139 | fn vec_u_vn_s_clone(bencher: &mut Bencher) { 140 | _bench_clone( 141 | bencher, 142 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 143 | ); 144 | } 145 | 146 | fn empty_realloc(bencher: &mut Bencher) { 147 | _bench_realloc(bencher, vec![(); 1024]); 148 | } 149 | fn u64_realloc(bencher: &mut Bencher) { 150 | _bench_realloc(bencher, vec![0u64; 1024]); 151 | } 152 | fn u32x2_realloc(bencher: &mut Bencher) { 153 | _bench_realloc(bencher, vec![(0u32, 0u32); 1024]); 154 | } 155 | fn u8_u64_realloc(bencher: &mut Bencher) { 156 | _bench_realloc(bencher, vec![(0u8, 0u64); 512]); 157 | } 158 | fn str10_realloc(bencher: &mut Bencher) { 159 | _bench_realloc(bencher, vec!["grawwwwrr!"; 1024]); 160 | } 161 | fn string10_realloc(bencher: &mut Bencher) { 162 | _bench_realloc(bencher, vec![format!("grawwwwrr!"); 1024]); 163 | } 164 | fn string20_realloc(bencher: &mut Bencher) { 165 | _bench_realloc(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); 166 | } 167 | fn vec_u_s_realloc(bencher: &mut Bencher) { 168 | _bench_realloc( 169 | bencher, 170 | vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], 171 | ); 172 | } 173 | fn vec_u_vn_s_realloc(bencher: &mut Bencher) { 174 | _bench_realloc( 175 | bencher, 176 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 177 | ); 178 | } 179 | 180 | fn empty_prealloc(bencher: &mut Bencher) { 181 | _bench_prealloc(bencher, vec![(); 1024]); 182 | } 183 | fn u64_prealloc(bencher: &mut Bencher) { 184 | _bench_prealloc(bencher, vec![0u64; 1024]); 185 | } 186 | fn u32x2_prealloc(bencher: &mut Bencher) { 187 | _bench_prealloc(bencher, vec![(0u32, 0u32); 1024]); 188 | } 189 | fn u8_u64_prealloc(bencher: &mut Bencher) { 190 | _bench_prealloc(bencher, vec![(0u8, 0u64); 512]); 191 | } 192 | fn str10_prealloc(bencher: &mut Bencher) { 193 | _bench_prealloc(bencher, vec!["grawwwwrr!"; 1024]); 194 | } 195 | fn string10_prealloc(bencher: &mut Bencher) { 196 | _bench_prealloc(bencher, vec![format!("grawwwwrr!"); 1024]); 197 | } 198 | fn string20_prealloc(bencher: &mut Bencher) { 199 | _bench_prealloc(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); 200 | } 201 | fn vec_u_s_prealloc(bencher: &mut Bencher) { 202 | _bench_prealloc( 203 | bencher, 204 | vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], 205 | ); 206 | } 207 | fn vec_u_vn_s_prealloc(bencher: &mut Bencher) { 208 | _bench_prealloc( 209 | bencher, 210 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 211 | ); 212 | } 213 | 214 | fn empty_copy_flat(bencher: &mut Bencher) { 215 | _bench_copy_flat_preference(bencher, vec![(); 1024]); 216 | } 217 | fn u64_copy_flat(bencher: &mut Bencher) { 218 | _bench_copy_flat_preference(bencher, vec![0u64; 1024]); 219 | } 220 | fn u32x2_copy_flat(bencher: &mut Bencher) { 221 | _bench_copy_flat_preference(bencher, vec![(0u32, 0u32); 1024]); 222 | } 223 | fn u8_u64_copy_flat(bencher: &mut Bencher) { 224 | _bench_copy_flat_preference(bencher, vec![(0u8, 0u64); 512]); 225 | } 226 | fn str10_copy_flat(bencher: &mut Bencher) { 227 | _bench_copy_flat_preference(bencher, vec!["grawwwwrr!"; 1024]); 228 | } 229 | fn str100_copy_flat(bencher: &mut Bencher) { 230 | _bench_copy_flat_preference(bencher, vec!["grawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrr!!!!!!!!!grawwwwrr!"; 1024]); 231 | } 232 | fn string10_copy_flat(bencher: &mut Bencher) { 233 | _bench_copy_flat_preference(bencher, vec![format!("grawwwwrr!"); 1024]); 234 | } 235 | fn string20_copy_flat(bencher: &mut Bencher) { 236 | _bench_copy_flat_preference(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); 237 | } 238 | fn vec_u_s_copy_flat(bencher: &mut Bencher) { 239 | _bench_copy_flat_preference( 240 | bencher, 241 | vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], 242 | ); 243 | } 244 | fn vec_u_vn_s_copy_flat(bencher: &mut Bencher) { 245 | _bench_copy_flat_preference( 246 | bencher, 247 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 248 | ); 249 | } 250 | 251 | fn empty_copy_flat_region(bencher: &mut Bencher) { 252 | _bench_copy_flat::, _>(bencher, vec![(); 1024]); 253 | } 254 | fn u64_copy_flat_region(bencher: &mut Bencher) { 255 | _bench_copy_flat::, _>(bencher, vec![0u64; 1024]); 256 | } 257 | fn u32x2_copy_flat_region(bencher: &mut Bencher) { 258 | _bench_copy_flat::, _>(bencher, vec![(0u32, 0u32); 1024]); 259 | } 260 | fn u8_u64_copy_flat_region(bencher: &mut Bencher) { 261 | _bench_copy_flat::, _>(bencher, vec![(0u8, 0u64); 512]); 262 | } 263 | fn str10_copy_flat_region(bencher: &mut Bencher) { 264 | _bench_copy_flat::, _>(bencher, vec!["grawwwwrr!"; 1024]); 265 | } 266 | fn str100_copy_flat_region(bencher: &mut Bencher) { 267 | _bench_copy_flat::, _>(bencher, vec!["grawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrrgrawwwwrr!!!!!!!!!grawwwwrr!"; 1024]); 268 | } 269 | fn string10_copy_flat_region(bencher: &mut Bencher) { 270 | _bench_copy_flat::, _>(bencher, vec![format!("grawwwwrr!"); 1024]); 271 | } 272 | fn string20_copy_flat_region(bencher: &mut Bencher) { 273 | _bench_copy_flat::, _>( 274 | bencher, 275 | vec![format!("grawwwwrr!!!!!!!!!!!"); 512], 276 | ); 277 | } 278 | fn vec_u_s_copy_flat_region(bencher: &mut Bencher) { 279 | _bench_copy_flat::, StringRegion>>>, _>( 280 | bencher, 281 | vec![vec![(0u64, "grawwwwrr!".to_string()); 32]; 32], 282 | ); 283 | } 284 | fn vec_u_vn_s_copy_flat_region(bencher: &mut Bencher) { 285 | _bench_copy_flat::< 286 | SliceRegion, OwnedRegion<_>, StringRegion>>>, 287 | _, 288 | >( 289 | bencher, 290 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 291 | ); 292 | } 293 | fn vec_u_vn_s_copy_flat_region_column(bencher: &mut Bencher) { 294 | _bench_copy_flat::< 295 | SliceRegion< 296 | ColumnsRegion< 297 | TupleABCRegion< 298 | MirrorRegion<_>, 299 | CollapseSequence>, 300 | CollapseSequence, 301 | >, 302 | >, 303 | >, 304 | _, 305 | >( 306 | bencher, 307 | vec![vec![(0u64, vec![(); 1 << 40], "grawwwwrr!".to_string()); 32]; 32], 308 | ); 309 | } 310 | 311 | fn set_bytes(target: &mut u64, bytes: usize) { 312 | if std::env::var("BYTES").is_ok() { 313 | *target = bytes as u64; 314 | } 315 | } 316 | 317 | fn _bench_copy(bencher: &mut Bencher, record: T) 318 | where 319 | for<'a> ::Region: Push<&'a T>, 320 | { 321 | // prepare encoded data for bencher.bytes 322 | let mut arena = FlatStack::default_impl::(); 323 | 324 | bencher.iter(|| { 325 | arena.clear(); 326 | for _ in 0..1024 { 327 | arena.copy(&record); 328 | } 329 | }); 330 | let (mut siz, mut cap) = (0, 0); 331 | arena.heap_size(|this_siz, this_cap| { 332 | siz += this_siz; 333 | cap += this_cap 334 | }); 335 | set_bytes(&mut bencher.bytes, siz); 336 | } 337 | 338 | fn _bench_copy_region(bencher: &mut Bencher, record: T) 339 | where 340 | for<'a> R: Push<&'a T>, 341 | { 342 | // prepare encoded data for bencher.bytes 343 | let mut arena = FlatStack::::default(); 344 | 345 | bencher.iter(|| { 346 | arena.clear(); 347 | for _ in 0..1024 { 348 | arena.copy(&record); 349 | } 350 | }); 351 | let (mut siz, mut cap) = (0, 0); 352 | arena.heap_size(|this_siz, this_cap| { 353 | siz += this_siz; 354 | cap += this_cap 355 | }); 356 | set_bytes(&mut bencher.bytes, siz); 357 | } 358 | 359 | fn _bench_clone(bencher: &mut Bencher, record: T) { 360 | // prepare encoded data for bencher.bytes 361 | let mut arena = Vec::new(); 362 | 363 | bencher.iter(|| { 364 | arena.clear(); 365 | for _ in 0..1024 { 366 | arena.push(record.clone()); 367 | } 368 | }); 369 | } 370 | 371 | fn _bench_realloc(bencher: &mut Bencher, record: T) 372 | where 373 | for<'a> ::Region: Push<&'a T>, 374 | { 375 | let mut arena = FlatStack::default_impl::(); 376 | bencher.iter(|| { 377 | // prepare encoded data for bencher.bytes 378 | arena = FlatStack::default_impl::(); 379 | for _ in 0..1024 { 380 | arena.copy(&record); 381 | } 382 | }); 383 | let (mut siz, mut cap) = (0, 0); 384 | arena.heap_size(|this_siz, this_cap| { 385 | siz += this_siz; 386 | cap += this_cap 387 | }); 388 | } 389 | 390 | fn _bench_prealloc(bencher: &mut Bencher, record: T) 391 | where 392 | for<'a> ::Region: ReserveItems<&'a T> + Push<&'a T>, 393 | { 394 | let mut arena = FlatStack::default_impl::(); 395 | bencher.iter(|| { 396 | arena = FlatStack::default_impl::(); 397 | // prepare encoded data for bencher.bytes 398 | arena.reserve_items(std::iter::repeat(&record).take(1024)); 399 | for _ in 0..1024 { 400 | arena.copy(&record); 401 | } 402 | }); 403 | let (mut siz, mut cap) = (0, 0); 404 | arena.heap_size(|this_siz, this_cap| { 405 | siz += this_siz; 406 | cap += this_cap 407 | }); 408 | set_bytes(&mut bencher.bytes, siz); 409 | } 410 | 411 | fn _bench_copy_flat_preference(bencher: &mut Bencher, record: T) 412 | where 413 | T: RegionPreference, 414 | for<'a> ::Region: 415 | Push<&'a T> + Push<<::Region as Region>::ReadItem<'a>> + Clone, 416 | { 417 | _bench_copy_flat::(bencher, record) 418 | } 419 | 420 | fn _bench_copy_flat(bencher: &mut Bencher, record: T) 421 | where 422 | for<'a> R: Region + Push<&'a T> + Push<::ReadItem<'a>> + Clone, 423 | { 424 | // prepare encoded data for bencher.bytes 425 | let mut arena = FlatStack::::default(); 426 | for _ in 0..1024 { 427 | arena.copy(&record); 428 | } 429 | let mut target = FlatStack::::default(); 430 | 431 | bencher.iter(|| { 432 | target.clone_from(&arena); 433 | }); 434 | let (mut siz, mut cap) = (0, 0); 435 | arena.heap_size(|this_siz, this_cap| { 436 | siz += this_siz; 437 | cap += this_cap 438 | }); 439 | set_bytes(&mut bencher.bytes, siz); 440 | } 441 | 442 | benchmark_group!( 443 | clone, 444 | empty_clone, 445 | str10_clone, 446 | string10_clone, 447 | string20_clone, 448 | u32x2_clone, 449 | u64_clone, 450 | u8_u64_clone, 451 | vec_u_s_clone, 452 | vec_u_vn_s_clone, 453 | ); 454 | benchmark_group!( 455 | copy, 456 | empty_copy, 457 | str10_copy, 458 | string10_copy, 459 | string20_copy, 460 | u32x2_copy, 461 | u64_copy, 462 | u8_u64_copy, 463 | vec_u_s_copy, 464 | vec_u_vn_s_copy, 465 | ); 466 | benchmark_group!( 467 | copy_flat, 468 | empty_copy_flat, 469 | str100_copy_flat, 470 | str10_copy_flat, 471 | string10_copy_flat, 472 | string20_copy_flat, 473 | u32x2_copy_flat, 474 | u64_copy_flat, 475 | u8_u64_copy_flat, 476 | vec_u_s_copy_flat, 477 | vec_u_vn_s_copy_flat, 478 | ); 479 | benchmark_group!( 480 | copy_region, 481 | empty_copy_flat_region, 482 | empty_copy_region, 483 | str100_copy_flat_region, 484 | str100_copy_region, 485 | str10_copy_flat_region, 486 | str10_copy_region, 487 | string10_copy_flat_region, 488 | string10_copy_region, 489 | string10_copy_region_collapse, 490 | string20_copy_flat_region, 491 | string20_copy_region, 492 | u32x2_copy_flat_region, 493 | u32x2_copy_region, 494 | u64_copy_flat_region, 495 | u64_copy_region, 496 | u8_u64_copy_flat_region, 497 | u8_u64_copy_region, 498 | vec_u_s_copy_flat_region, 499 | vec_u_s_copy_region, 500 | vec_u_vn_s_copy_flat_region, 501 | vec_u_vn_s_copy_flat_region_column, 502 | vec_u_vn_s_copy_region, 503 | vec_u_vn_s_copy_region_column, 504 | ); 505 | benchmark_group!( 506 | alloc, 507 | empty_prealloc, 508 | empty_realloc, 509 | str10_prealloc, 510 | str10_realloc, 511 | string10_prealloc, 512 | string10_realloc, 513 | string20_prealloc, 514 | string20_realloc, 515 | u32x2_prealloc, 516 | u32x2_realloc, 517 | u64_prealloc, 518 | u64_realloc, 519 | u8_u64_prealloc, 520 | u8_u64_realloc, 521 | vec_u_s_prealloc, 522 | vec_u_s_realloc, 523 | vec_u_vn_s_prealloc, 524 | vec_u_vn_s_realloc, 525 | ); 526 | benchmark_main!(clone, copy, copy_flat, copy_region, alloc); 527 | -------------------------------------------------------------------------------- /src/impls.rs: -------------------------------------------------------------------------------- 1 | //! Various region implementations. 2 | 3 | pub mod codec; 4 | pub mod columns; 5 | pub mod deduplicate; 6 | pub mod huffman_container; 7 | pub mod index; 8 | pub mod mirror; 9 | pub mod option; 10 | pub mod result; 11 | pub mod slice; 12 | pub mod slice_owned; 13 | pub mod storage; 14 | pub mod string; 15 | pub mod tuple; 16 | mod vec; 17 | -------------------------------------------------------------------------------- /src/impls/codec.rs: -------------------------------------------------------------------------------- 1 | //! A region that encodes its contents. 2 | 3 | use crate::{OwnedRegion, Push, Region}; 4 | 5 | pub use self::misra_gries::MisraGries; 6 | pub use dictionary::DictionaryCodec; 7 | 8 | // TODO: Consolidation comes from Differential. 9 | 10 | /// Sorts and consolidates `vec`. 11 | /// 12 | /// This method will sort `vec` and then consolidate runs of more than one entry with 13 | /// identical first elements by accumulating the second elements of the pairs. Should the final 14 | /// accumulation be zero, the element is discarded. 15 | fn consolidate(vec: &mut Vec<(T, usize)>) { 16 | consolidate_from(vec, 0); 17 | } 18 | 19 | /// Sorts and consolidate `vec[offset..]`. 20 | /// 21 | /// This method will sort `vec[offset..]` and then consolidate runs of more than one entry with 22 | /// identical first elements by accumulating the second elements of the pairs. Should the final 23 | /// accumulation be zero, the element is discarded. 24 | fn consolidate_from(vec: &mut Vec<(T, usize)>, offset: usize) { 25 | let length = consolidate_slice(&mut vec[offset..]); 26 | vec.truncate(offset + length); 27 | } 28 | 29 | /// Sorts and consolidates a slice, returning the valid prefix length. 30 | fn consolidate_slice(slice: &mut [(T, usize)]) -> usize { 31 | if slice.len() > 1 { 32 | // We could do an insertion-sort like initial scan which builds up sorted, consolidated runs. 33 | // In a world where there are not many results, we may never even need to call in to merge sort. 34 | slice.sort_by(|x, y| x.0.cmp(&y.0)); 35 | 36 | // Counts the number of distinct known-non-zero accumulations. Indexes the write location. 37 | let mut offset = 0; 38 | let mut accum = slice[offset].1; 39 | 40 | for index in 1..slice.len() { 41 | if slice[index].0 == slice[index - 1].0 { 42 | accum += slice[index].1; 43 | } else { 44 | if accum != 0 { 45 | slice.swap(offset, index - 1); 46 | slice[offset].1.clone_from(&accum); 47 | offset += 1; 48 | } 49 | accum.clone_from(&slice[index].1); 50 | } 51 | } 52 | if accum != 0 { 53 | slice.swap(offset, slice.len() - 1); 54 | slice[offset].1 = accum; 55 | offset += 1; 56 | } 57 | 58 | offset 59 | } else { 60 | slice.iter().filter(|x| x.1 != 0).count() 61 | } 62 | } 63 | 64 | /// A region that encodes its data in a codec `C`. 65 | #[derive(Default, Debug)] 66 | pub struct CodecRegion> { 67 | inner: R, 68 | codec: C, 69 | } 70 | 71 | impl Clone for CodecRegion { 72 | fn clone(&self) -> Self { 73 | Self { 74 | inner: self.inner.clone(), 75 | codec: self.codec.clone(), 76 | } 77 | } 78 | fn clone_from(&mut self, source: &Self) { 79 | self.inner.clone_from(&source.inner); 80 | self.codec.clone_from(&source.codec); 81 | } 82 | } 83 | 84 | impl Region for CodecRegion 85 | where 86 | for<'a> R: Region = &'a [u8]> + 'a, 87 | { 88 | type Owned = Vec; 89 | type ReadItem<'a> = &'a [u8] 90 | where 91 | Self: 'a; 92 | 93 | type Index = R::Index; 94 | 95 | /// Construct a region that can absorb the contents of `regions` in the future. 96 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 97 | where 98 | Self: 'a, 99 | { 100 | let codec = C::new_from(regions.clone().map(|r| &r.codec)); 101 | Self { 102 | inner: R::merge_regions(regions.map(|r| &r.inner)), 103 | codec, 104 | } 105 | } 106 | 107 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 108 | self.codec.decode(self.inner.index(index)) 109 | } 110 | 111 | fn reserve_regions<'a, I>(&mut self, regions: I) 112 | where 113 | Self: 'a, 114 | I: Iterator + Clone, 115 | { 116 | self.inner.reserve_regions(regions.map(|r| &r.inner)); 117 | } 118 | 119 | fn clear(&mut self) { 120 | self.inner.clear(); 121 | self.codec = Default::default(); 122 | } 123 | 124 | fn heap_size(&self, mut callback: F) { 125 | self.inner.heap_size(&mut callback); 126 | self.codec.heap_size(callback); 127 | } 128 | 129 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 130 | where 131 | C: 'a, 132 | { 133 | item 134 | } 135 | } 136 | 137 | impl Push<&[u8]> for CodecRegion 138 | where 139 | for<'a> R: Region = &'a [u8]> + Push<&'a [u8]> + 'a, 140 | { 141 | fn push(&mut self, item: &[u8]) -> as Region>::Index { 142 | self.codec.encode(item, &mut self.inner) 143 | } 144 | } 145 | 146 | /// Encode and decode byte strings. 147 | pub trait Codec: Default { 148 | /// Decodes an input byte slice into a sequence of byte slices. 149 | fn decode<'a>(&'a self, bytes: &'a [u8]) -> &'a [u8]; 150 | /// Encodes a sequence of byte slices into an output byte slice. 151 | fn encode(&mut self, bytes: &[u8], output: &mut R) -> R::Index 152 | where 153 | for<'a> R: Region + Push<&'a [u8]>; 154 | /// Constructs a new instance of `Self` from accumulated statistics. 155 | /// These statistics should cover the data the output expects to see. 156 | fn new_from<'a, I: Iterator + Clone>(stats: I) -> Self 157 | where 158 | Self: 'a; 159 | /// Diagnostic information about the state of the codec. 160 | fn report(&self) {} 161 | 162 | /// Heap size, size - capacity 163 | fn heap_size(&self, callback: F); 164 | } 165 | 166 | mod dictionary { 167 | 168 | use crate::{Push, Region}; 169 | use std::collections::BTreeMap; 170 | 171 | pub use super::{BytesMap, Codec, MisraGries}; 172 | 173 | /// A type that can both encode and decode sequences of byte slices. 174 | #[derive(Default, Debug)] 175 | pub struct DictionaryCodec { 176 | encode: BTreeMap, u8>, 177 | decode: BytesMap, 178 | stats: (MisraGries>, [u64; 4]), 179 | bytes: usize, 180 | total: usize, 181 | } 182 | 183 | impl Codec for DictionaryCodec { 184 | /// Decode a sequence of byte slices. 185 | fn decode<'a>(&'a self, bytes: &'a [u8]) -> &'a [u8] { 186 | if let Some(bytes) = self.decode.get(bytes[0].into()) { 187 | bytes 188 | } else { 189 | bytes 190 | } 191 | } 192 | 193 | /// Encode a sequence of byte slices. 194 | /// 195 | /// Encoding also records statistics about the structure of the input. 196 | fn encode(&mut self, bytes: &[u8], output: &mut R) -> R::Index 197 | where 198 | for<'a> R: Region + Push<&'a [u8]>, 199 | { 200 | self.total += bytes.len(); 201 | // If we have an index referencing `bytes`, use the index key. 202 | let index = if let Some(b) = self.encode.get(bytes) { 203 | self.bytes += 1; 204 | output.push([*b].as_slice()) 205 | } else { 206 | self.bytes += bytes.len(); 207 | output.push(bytes) 208 | }; 209 | // Stats stuff. 210 | self.stats.0.insert(bytes.to_owned()); 211 | let tag = bytes[0]; 212 | let tag_idx: usize = (tag % 4).into(); 213 | self.stats.1[tag_idx] |= 1 << (tag >> 2); 214 | 215 | index 216 | } 217 | 218 | /// Construct a new encoder from supplied statistics. 219 | fn new_from<'a, I: Iterator + Clone>(stats: I) -> Self { 220 | // Collect most popular bytes from combined containers. 221 | let mut mg = MisraGries::default(); 222 | for (thing, count) in stats.clone().flat_map(|stats| stats.stats.0.clone().done()) { 223 | mg.update(thing, count); 224 | } 225 | let mut mg = mg.done().into_iter(); 226 | // Establish encoding and decoding rules. 227 | let mut encode = BTreeMap::new(); 228 | let mut decode = BytesMap::default(); 229 | for tag in 0..=255 { 230 | let tag_idx: usize = (tag % 4).into(); 231 | let shift = tag >> 2; 232 | let or = stats 233 | .clone() 234 | .fold(0, |acc, stats| acc | stats.stats.1[tag_idx]); 235 | if (or >> shift) & 0x01 != 0 { 236 | decode.push(None); 237 | } else if let Some((next_bytes, _count)) = mg.next() { 238 | decode.push(Some(&next_bytes[..])); 239 | encode.insert(next_bytes, tag); 240 | } 241 | } 242 | 243 | Self { 244 | encode, 245 | decode, 246 | stats: (MisraGries::default(), [0u64; 4]), 247 | bytes: 0, 248 | total: 0, 249 | } 250 | } 251 | 252 | fn report(&self) { 253 | let mut tags_used = 0; 254 | tags_used += self.stats.1[0].count_ones(); 255 | tags_used += self.stats.1[1].count_ones(); 256 | tags_used += self.stats.1[2].count_ones(); 257 | tags_used += self.stats.1[3].count_ones(); 258 | let mg = self.stats.0.clone().done(); 259 | let mut bytes = 0; 260 | for (vec, _count) in &mg { 261 | bytes += vec.len(); 262 | } 263 | // if self.total > 10000 && !mg.is_empty() { 264 | println!( 265 | "\t{:?}v{:?}: {:?} -> {:?} + {:?} = (x{:?})", 266 | tags_used, 267 | mg.len(), 268 | self.total, 269 | self.bytes, 270 | bytes, 271 | self.total / (self.bytes + bytes), 272 | ); 273 | // } 274 | } 275 | 276 | fn heap_size(&self, _callback: F) { 277 | // Lazy 278 | } 279 | } 280 | } 281 | 282 | /// A map from `0 .. something` to `Option<&[u8]>`. 283 | /// 284 | /// Non-empty slices are pushed in order, and can be retrieved by index. 285 | /// Pushing an empty slice is equivalent to pushing `None`. 286 | #[derive(Debug)] 287 | pub struct BytesMap { 288 | offsets: Vec, 289 | bytes: Vec, 290 | } 291 | impl Default for BytesMap { 292 | fn default() -> Self { 293 | Self { 294 | offsets: vec![0], 295 | bytes: Vec::new(), 296 | } 297 | } 298 | } 299 | impl BytesMap { 300 | fn push(&mut self, input: Option<&[u8]>) { 301 | if let Some(bytes) = input { 302 | self.bytes.extend(bytes); 303 | } 304 | self.offsets.push(self.bytes.len()); 305 | } 306 | fn get(&self, index: usize) -> Option<&[u8]> { 307 | if index < self.offsets.len() - 1 { 308 | let lower = self.offsets[index]; 309 | let upper = self.offsets[index + 1]; 310 | if lower < upper { 311 | Some(&self.bytes[lower..upper]) 312 | } else { 313 | None 314 | } 315 | } else { 316 | None 317 | } 318 | } 319 | #[allow(dead_code)] 320 | fn len(&self) -> usize { 321 | self.offsets.len() - 1 322 | } 323 | } 324 | 325 | mod misra_gries { 326 | 327 | /// Maintains a summary of "heavy hitters" in a presented collection of items. 328 | #[derive(Clone, Debug)] 329 | pub struct MisraGries { 330 | inner: Vec<(T, usize)>, 331 | } 332 | 333 | impl Default for MisraGries { 334 | fn default() -> Self { 335 | Self { 336 | inner: Vec::with_capacity(1024), 337 | } 338 | } 339 | } 340 | 341 | impl MisraGries { 342 | /// Inserts an additional element to the summary. 343 | pub fn insert(&mut self, element: T) { 344 | self.update(element, 1); 345 | } 346 | /// Inserts multiple copies of an element to the summary. 347 | pub fn update(&mut self, element: T, count: usize) { 348 | self.inner.push((element, count)); 349 | if self.inner.len() == self.inner.capacity() { 350 | self.tidy(); 351 | } 352 | } 353 | /// Allocates a Misra-Gries summary which intends to hold up to `k` examples. 354 | /// 355 | /// After `n` insertions it will contain only elements that were inserted at least `n/k` times. 356 | /// The actual memory use is proportional to `2 * k`, so that we can amortize the consolidation. 357 | #[must_use] 358 | pub fn with_capacity(k: usize) -> Self { 359 | Self { 360 | inner: Vec::with_capacity(2 * k), 361 | } 362 | } 363 | 364 | /// Completes the summary, and extracts the items and their counts. 365 | #[must_use] 366 | pub fn done(mut self) -> Vec<(T, usize)> { 367 | use super::consolidate; 368 | consolidate(&mut self.inner); 369 | self.inner.sort_by(|x, y| y.1.cmp(&x.1)); 370 | self.inner 371 | } 372 | 373 | /// Internal method that reduces the summary down to at most `k-1` distinct items, by repeatedly 374 | /// removing sets of `k` distinct items. The removal is biased towards the lowest counts, so as 375 | /// to preserve fidelity around the larger counts, for whatever that is worth. 376 | fn tidy(&mut self) { 377 | use super::consolidate; 378 | consolidate(&mut self.inner); 379 | self.inner.sort_by(|x, y| y.1.cmp(&x.1)); 380 | let k = self.inner.capacity() / 2; 381 | if self.inner.len() > k { 382 | let sub_weight = self.inner[k].1 - 1; 383 | self.inner.truncate(k); 384 | for (_, weight) in &mut self.inner { 385 | *weight -= sub_weight; 386 | } 387 | while self.inner.last().map(|x| x.1) == Some(0) { 388 | self.inner.pop(); 389 | } 390 | } 391 | } 392 | } 393 | } 394 | 395 | #[cfg(test)] 396 | mod tests { 397 | use super::{Codec, CodecRegion, DictionaryCodec}; 398 | use crate::*; 399 | 400 | #[test] 401 | fn test_simple() { 402 | let mut r = CodecRegion::::default(); 403 | 404 | for _ in 0..1000 { 405 | let index = r.push("abc".as_bytes()); 406 | assert_eq!("abc".as_bytes(), r.index(index)); 407 | } 408 | 409 | let mut r2 = CodecRegion::default(); 410 | 411 | for _ in 0..1000 { 412 | let index = r2.push("abc".as_bytes()); 413 | assert_eq!("abc".as_bytes(), r2.index(index)); 414 | } 415 | 416 | let mut r3 = CodecRegion::merge_regions([&r, &r2].into_iter()); 417 | 418 | for _ in 0..2000 { 419 | let index = r3.push("abc".as_bytes()); 420 | assert_eq!("abc".as_bytes(), r3.index(index)); 421 | } 422 | 423 | println!("new container after inserts:"); 424 | r.codec.report(); 425 | println!("second new container after inserts:"); 426 | r2.codec.report(); 427 | println!("new container with merged stats after inserts:"); 428 | r3.codec.report(); 429 | } 430 | 431 | #[test] 432 | fn test_multi() { 433 | let mut regions = Vec::new(); 434 | for _ in 0..8 { 435 | regions.push(CodecRegion::::default()); 436 | } 437 | for _ in 0..1000 { 438 | for r in &mut regions { 439 | let _ = r.push("abcdef".as_bytes()); 440 | let _ = r.push("defghi".as_bytes()); 441 | } 442 | } 443 | 444 | let mut merged = CodecRegion::merge_regions(regions.iter()); 445 | 446 | for _ in 0..2000 { 447 | let index = merged.push("abcdef".as_bytes()); 448 | assert_eq!("abcdef".as_bytes(), merged.index(index)); 449 | let index = merged.push("defghi".as_bytes()); 450 | assert_eq!("defghi".as_bytes(), merged.index(index)); 451 | } 452 | 453 | println!("new container with merged stats after inserts:"); 454 | merged.codec.report(); 455 | } 456 | 457 | #[test] 458 | fn test_heap_size() { 459 | let mut regions = Vec::new(); 460 | for _ in 0..8 { 461 | regions.push(CodecRegion::::default()); 462 | } 463 | for _ in 0..1000 { 464 | for r in &mut regions { 465 | let _ = r.push("abcdef".as_bytes()); 466 | let _ = r.push("defghi".as_bytes()); 467 | } 468 | } 469 | 470 | let mut merged = CodecRegion::merge_regions(regions.iter()); 471 | 472 | for _ in 0..2000 { 473 | let index = merged.push("abcdef".as_bytes()); 474 | assert_eq!("abcdef".as_bytes(), merged.index(index)); 475 | let index = merged.push("defghi".as_bytes()); 476 | assert_eq!("defghi".as_bytes(), merged.index(index)); 477 | } 478 | 479 | let (mut size, mut cap, mut cnt) = (0, 0, 0); 480 | merged.heap_size(|siz, ca| { 481 | size += siz; 482 | cap += ca; 483 | cnt += 1; 484 | }); 485 | 486 | assert!(cnt > 0); 487 | 488 | let mut merged2 = CodecRegion::merge_regions(std::iter::once(&merged)); 489 | 490 | for _ in 0..2000 { 491 | let index = merged2.push("abcdef".as_bytes()); 492 | assert_eq!("abcdef".as_bytes(), merged2.index(index)); 493 | let index = merged2.push("defghi".as_bytes()); 494 | assert_eq!("defghi".as_bytes(), merged2.index(index)); 495 | } 496 | 497 | let (mut size, mut cap, mut cnt) = (0, 0, 0); 498 | merged2.heap_size(|siz, ca| { 499 | size += siz; 500 | cap += ca; 501 | cnt += 1; 502 | }); 503 | assert!(cnt > 0); 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /src/impls/columns.rs: -------------------------------------------------------------------------------- 1 | //! A region to contain a variable number of columns. 2 | 3 | use std::fmt::Debug; 4 | use std::iter::Zip; 5 | use std::slice::Iter; 6 | 7 | #[cfg(feature = "serde")] 8 | use serde::{Deserialize, Serialize}; 9 | 10 | use crate::impls::deduplicate::ConsecutiveIndexPairs; 11 | use crate::impls::index::{IndexContainer, IndexOptimized}; 12 | use crate::{IntoOwned, PushIter}; 13 | use crate::{OwnedRegion, Push, Region}; 14 | 15 | /// A region that can store a variable number of elements per row. 16 | /// 17 | /// The region is backed by a number of columns, where the number depends on 18 | /// the length of the longest row encountered. For pushed row, the region 19 | /// remembers the indices into each column that populated. Rows can have different 20 | /// lengths, which means that only the first columns will contain a value. 21 | /// 22 | /// All columns have the same type `R`. 23 | /// 24 | /// # Examples 25 | /// 26 | /// Copy a table-like structure: 27 | /// ``` 28 | /// # use flatcontainer::impls::deduplicate::ConsecutiveIndexPairs; 29 | /// # use flatcontainer::{ColumnsRegion, Push, Region, StringRegion}; 30 | /// let data = [ 31 | /// vec![], 32 | /// vec!["1"], 33 | /// vec!["2", "3"], 34 | /// vec!["4", "5", "6"], 35 | /// vec!["7", "8"], 36 | /// vec!["9"], 37 | /// vec![], 38 | /// ]; 39 | /// 40 | /// let mut r = >>::default(); 41 | /// 42 | /// let mut indices = Vec::with_capacity(data.len()); 43 | /// 44 | /// for row in &data { 45 | /// let index = r.push(row); 46 | /// indices.push(index); 47 | /// } 48 | /// 49 | /// for (&index, row) in indices.iter().zip(&data) { 50 | /// assert!(row.iter().copied().eq(r.index(index).iter())); 51 | /// } 52 | /// ``` 53 | #[derive(Debug)] 54 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 55 | #[cfg_attr( 56 | feature = "serde", 57 | serde(bound = " 58 | R: Serialize + for<'a> Deserialize<'a>, 59 | R::Index: Serialize + for<'a> Deserialize<'a>, 60 | O: Serialize + for<'a> Deserialize<'a>, 61 | ") 62 | )] 63 | pub struct ColumnsRegion 64 | where 65 | R: Region, 66 | { 67 | /// Indices to address rows in `inner`. For each row, we remember 68 | /// an index for each column. 69 | indices: ConsecutiveIndexPairs, O>, 70 | /// Storage for columns. 71 | inner: Vec, 72 | } 73 | 74 | impl Clone for ColumnsRegion 75 | where 76 | R: Region + Clone, 77 | O: Clone, 78 | { 79 | fn clone(&self) -> Self { 80 | Self { 81 | indices: self.indices.clone(), 82 | inner: self.inner.clone(), 83 | } 84 | } 85 | 86 | fn clone_from(&mut self, source: &Self) { 87 | self.indices.clone_from(&source.indices); 88 | self.inner.clone_from(&source.inner); 89 | } 90 | } 91 | 92 | impl Region for ColumnsRegion 93 | where 94 | R: Region, 95 | O: IndexContainer, 96 | { 97 | type Owned = Vec; 98 | type ReadItem<'a> = ReadColumns<'a, R> where Self: 'a; 99 | type Index = , IndexOptimized> as Region>::Index; 100 | 101 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 102 | where 103 | Self: 'a, 104 | { 105 | let cols = regions.clone().map(|r| r.inner.len()).max().unwrap_or(0); 106 | 107 | let mut inner = Vec::with_capacity(cols); 108 | for col in 0..cols { 109 | inner.push(R::merge_regions( 110 | regions.clone().filter_map(|r| r.inner.get(col)), 111 | )); 112 | } 113 | 114 | Self { 115 | indices: ConsecutiveIndexPairs::merge_regions(regions.map(|r| &r.indices)), 116 | inner, 117 | } 118 | } 119 | 120 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 121 | ReadColumns(Ok(ReadColumnsInner { 122 | columns: &self.inner, 123 | index: self.indices.index(index), 124 | })) 125 | } 126 | 127 | fn reserve_regions<'a, I>(&mut self, regions: I) 128 | where 129 | Self: 'a, 130 | I: Iterator + Clone, 131 | { 132 | for region in regions.clone() { 133 | while self.inner.len() < region.inner.len() { 134 | self.inner.push(R::default()); 135 | } 136 | } 137 | for (index, inner) in self.inner.iter_mut().enumerate() { 138 | inner.reserve_regions(regions.clone().filter_map(|r| r.inner.get(index))); 139 | } 140 | } 141 | 142 | fn clear(&mut self) { 143 | for inner in &mut self.inner { 144 | inner.clear(); 145 | } 146 | self.indices.clear(); 147 | } 148 | 149 | fn heap_size(&self, mut callback: F) { 150 | let size_of_r = std::mem::size_of::(); 151 | callback( 152 | self.inner.len() * size_of_r, 153 | self.inner.capacity() * size_of_r, 154 | ); 155 | for inner in &self.inner { 156 | inner.heap_size(&mut callback); 157 | } 158 | self.indices.heap_size(callback); 159 | } 160 | 161 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 162 | where 163 | Self: 'a, 164 | { 165 | item 166 | } 167 | } 168 | 169 | impl Default for ColumnsRegion 170 | where 171 | R: Region, 172 | O: IndexContainer, 173 | { 174 | fn default() -> Self { 175 | Self { 176 | indices: ConsecutiveIndexPairs::default(), 177 | inner: Vec::default(), 178 | } 179 | } 180 | } 181 | 182 | /// Read the values of a row. 183 | pub struct ReadColumns<'a, R>(Result, &'a [R::Owned]>) 184 | where 185 | R: Region; 186 | 187 | struct ReadColumnsInner<'a, R> 188 | where 189 | R: Region, 190 | { 191 | /// Storage for columns. 192 | columns: &'a [R], 193 | /// Indices to retrieve values from columns. 194 | index: &'a [R::Index], 195 | } 196 | 197 | impl<'a, R> Clone for ReadColumns<'a, R> 198 | where 199 | R: Region, 200 | { 201 | fn clone(&self) -> Self { 202 | *self 203 | } 204 | } 205 | 206 | impl<'a, R> Clone for ReadColumnsInner<'a, R> 207 | where 208 | R: Region, 209 | { 210 | fn clone(&self) -> Self { 211 | *self 212 | } 213 | } 214 | 215 | impl<'a, R> Copy for ReadColumns<'a, R> where R: Region {} 216 | impl<'a, R> Copy for ReadColumnsInner<'a, R> where R: Region {} 217 | 218 | impl<'a, R> Debug for ReadColumns<'a, R> 219 | where 220 | R: Region, 221 | R::ReadItem<'a>: Debug, 222 | { 223 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 224 | f.debug_list().entries(self).finish() 225 | } 226 | } 227 | 228 | impl<'a, R> ReadColumns<'a, R> 229 | where 230 | R: Region, 231 | { 232 | /// Iterate the individual values of a row. 233 | #[must_use] 234 | pub fn iter(&'a self) -> ReadColumnsIter<'a, R> { 235 | self.into_iter() 236 | } 237 | 238 | /// Get the element at `offset`. 239 | #[must_use] 240 | pub fn get(&self, offset: usize) -> R::ReadItem<'a> { 241 | match &self.0 { 242 | Ok(inner) => inner.get(offset), 243 | Err(slice) => IntoOwned::borrow_as(&slice[offset]), 244 | } 245 | } 246 | 247 | /// Returns the length of this row. 248 | #[must_use] 249 | pub fn len(&self) -> usize { 250 | match &self.0 { 251 | Ok(inner) => inner.len(), 252 | Err(slice) => slice.len(), 253 | } 254 | } 255 | 256 | /// Returns `true` if this row is empty. 257 | #[must_use] 258 | pub fn is_empty(&self) -> bool { 259 | match &self.0 { 260 | Ok(inner) => inner.is_empty(), 261 | Err(slice) => slice.is_empty(), 262 | } 263 | } 264 | } 265 | impl<'a, R> ReadColumnsInner<'a, R> 266 | where 267 | R: Region, 268 | { 269 | /// Get the element at `offset`. 270 | #[must_use] 271 | pub fn get(&self, offset: usize) -> R::ReadItem<'a> { 272 | self.columns[offset].index(self.index[offset]) 273 | } 274 | 275 | /// Returns the length of this row. 276 | #[must_use] 277 | pub fn len(&self) -> usize { 278 | self.index.len() 279 | } 280 | 281 | /// Returns `true` if this row is empty. 282 | #[must_use] 283 | pub fn is_empty(&self) -> bool { 284 | self.index.is_empty() 285 | } 286 | } 287 | 288 | impl<'a, R> IntoOwned<'a> for ReadColumns<'a, R> 289 | where 290 | R: Region, 291 | { 292 | type Owned = Vec; 293 | 294 | #[inline] 295 | fn into_owned(self) -> Self::Owned { 296 | self.iter().map(IntoOwned::into_owned).collect() 297 | } 298 | 299 | fn clone_onto(self, other: &mut Self::Owned) { 300 | let r = std::cmp::min(self.len(), other.len()); 301 | for (item, target) in self.iter().zip(other.iter_mut()) { 302 | item.clone_onto(target); 303 | } 304 | other.extend(self.iter().skip(r).map(IntoOwned::into_owned)); 305 | other.truncate(self.len()); 306 | } 307 | 308 | fn borrow_as(owned: &'a Self::Owned) -> Self { 309 | Self(Err(owned.as_slice())) 310 | } 311 | } 312 | 313 | impl<'a, R> IntoIterator for &ReadColumns<'a, R> 314 | where 315 | R: Region, 316 | { 317 | type Item = R::ReadItem<'a>; 318 | type IntoIter = ReadColumnsIter<'a, R>; 319 | 320 | fn into_iter(self) -> Self::IntoIter { 321 | match self.0 { 322 | Ok(inner) => ReadColumnsIter(Ok(ReadColumnsIterInner { 323 | iter: inner.index.iter().zip(inner.columns.iter()), 324 | })), 325 | Err(slice) => ReadColumnsIter(Err(slice.iter())), 326 | } 327 | } 328 | } 329 | 330 | /// An iterator over the elements of a row. 331 | pub struct ReadColumnsIter<'a, R: Region>(Result, Iter<'a, R::Owned>>); 332 | 333 | /// An iterator over the elements of a row. 334 | pub struct ReadColumnsIterInner<'a, R: Region> { 335 | iter: Zip, Iter<'a, R>>, 336 | } 337 | 338 | impl<'a, R> Iterator for ReadColumnsIter<'a, R> 339 | where 340 | R: Region, 341 | { 342 | type Item = R::ReadItem<'a>; 343 | 344 | fn next(&mut self) -> Option { 345 | match &mut self.0 { 346 | Ok(inner) => inner.next(), 347 | Err(slice) => slice.next().map(IntoOwned::borrow_as), 348 | } 349 | } 350 | 351 | fn size_hint(&self) -> (usize, Option) { 352 | match &self.0 { 353 | Ok(inner) => inner.size_hint(), 354 | Err(slice) => slice.size_hint(), 355 | } 356 | } 357 | } 358 | 359 | impl<'a, R> ExactSizeIterator for ReadColumnsIter<'a, R> where R: Region {} 360 | 361 | impl<'a, R> Iterator for ReadColumnsIterInner<'a, R> 362 | where 363 | R: Region, 364 | { 365 | type Item = R::ReadItem<'a>; 366 | 367 | fn next(&mut self) -> Option { 368 | self.iter.next().map(|(&i, r)| r.index(i)) 369 | } 370 | 371 | fn size_hint(&self) -> (usize, Option) { 372 | self.iter.size_hint() 373 | } 374 | } 375 | 376 | impl Push> for ColumnsRegion 377 | where 378 | for<'a> R: Region + Push<::ReadItem<'a>>, 379 | O: IndexContainer, 380 | { 381 | fn push(&mut self, item: ReadColumns<'_, R>) -> as Region>::Index { 382 | // Ensure all required regions exist. 383 | while self.inner.len() < item.len() { 384 | self.inner.push(R::default()); 385 | } 386 | 387 | let iter = item 388 | .iter() 389 | .zip(&mut self.inner) 390 | .map(|(value, region)| region.push(value)); 391 | self.indices.push(PushIter(iter)) 392 | } 393 | } 394 | 395 | impl<'a, R, O, T> Push<&'a [T]> for ColumnsRegion 396 | where 397 | R: Region + Push<&'a T>, 398 | O: IndexContainer, 399 | { 400 | fn push(&mut self, item: &'a [T]) -> as Region>::Index { 401 | // Ensure all required regions exist. 402 | while self.inner.len() < item.len() { 403 | self.inner.push(R::default()); 404 | } 405 | 406 | let iter = item 407 | .iter() 408 | .zip(&mut self.inner) 409 | .map(|(value, region)| region.push(value)); 410 | self.indices.push(PushIter(iter)) 411 | } 412 | } 413 | 414 | impl Push<[T; N]> for ColumnsRegion 415 | where 416 | R: Region + Push, 417 | O: IndexContainer, 418 | { 419 | fn push(&mut self, item: [T; N]) -> as Region>::Index { 420 | // Ensure all required regions exist. 421 | while self.inner.len() < item.len() { 422 | self.inner.push(R::default()); 423 | } 424 | 425 | let iter = item 426 | .into_iter() 427 | .zip(&mut self.inner) 428 | .map(|(value, region)| region.push(value)); 429 | self.indices.push(PushIter(iter)) 430 | } 431 | } 432 | 433 | impl<'a, R, O, T, const N: usize> Push<&'a [T; N]> for ColumnsRegion 434 | where 435 | R: Region + Push<&'a T>, 436 | O: IndexContainer, 437 | { 438 | fn push(&mut self, item: &'a [T; N]) -> as Region>::Index { 439 | // Ensure all required regions exist. 440 | while self.inner.len() < item.len() { 441 | self.inner.push(R::default()); 442 | } 443 | 444 | let iter = item 445 | .iter() 446 | .zip(&mut self.inner) 447 | .map(|(value, region)| region.push(value)); 448 | self.indices.push(PushIter(iter)) 449 | } 450 | } 451 | 452 | impl Push> for ColumnsRegion 453 | where 454 | R: Region + Push, 455 | O: IndexContainer, 456 | { 457 | fn push(&mut self, item: Vec) -> as Region>::Index { 458 | // Ensure all required regions exist. 459 | while self.inner.len() < item.len() { 460 | self.inner.push(R::default()); 461 | } 462 | 463 | let iter = item 464 | .into_iter() 465 | .zip(&mut self.inner) 466 | .map(|(value, region)| region.push(value)); 467 | self.indices.push(PushIter(iter)) 468 | } 469 | } 470 | 471 | impl<'a, R, O, T> Push<&'a Vec> for ColumnsRegion 472 | where 473 | R: Region + Push<&'a T>, 474 | O: IndexContainer, 475 | { 476 | fn push(&mut self, item: &'a Vec) -> as Region>::Index { 477 | // Ensure all required regions exist. 478 | while self.inner.len() < item.len() { 479 | self.inner.push(R::default()); 480 | } 481 | 482 | let iter = item 483 | .iter() 484 | .zip(&mut self.inner) 485 | .map(|(value, region)| region.push(value)); 486 | self.indices.push(PushIter(iter)) 487 | } 488 | } 489 | 490 | impl Push> for ColumnsRegion 491 | where 492 | R: Region + Push, 493 | O: IndexContainer, 494 | I: IntoIterator, 495 | I::IntoIter: ExactSizeIterator, 496 | { 497 | #[inline] 498 | fn push(&mut self, item: PushIter) -> as Region>::Index { 499 | let iter = item.0.into_iter().enumerate().map(|(index, value)| { 500 | // Ensure all required regions exist. 501 | if self.inner.len() <= index { 502 | self.inner.push(R::default()); 503 | } 504 | self.inner[index].push(value) 505 | }); 506 | self.indices.push(PushIter(iter)) 507 | } 508 | } 509 | 510 | #[cfg(test)] 511 | mod tests { 512 | use crate::impls::deduplicate::{CollapseSequence, ConsecutiveIndexPairs}; 513 | use crate::{MirrorRegion, OwnedRegion, Push, PushIter, Region, StringRegion}; 514 | 515 | use super::*; 516 | 517 | #[test] 518 | fn test_matrix() { 519 | let data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; 520 | 521 | let mut r = ColumnsRegion::>::default(); 522 | 523 | let mut indices = Vec::with_capacity(data.len()); 524 | 525 | for row in &data { 526 | let index = r.push(row.as_slice()); 527 | indices.push(index); 528 | } 529 | 530 | for (index, row) in indices.iter().zip(&data) { 531 | assert!(row.iter().copied().eq(r.index(index).iter())); 532 | } 533 | } 534 | 535 | #[test] 536 | fn test_ragged() { 537 | let data = [ 538 | [].as_slice(), 539 | [1].as_slice(), 540 | [2, 3].as_slice(), 541 | [4, 5, 6].as_slice(), 542 | [7, 8].as_slice(), 543 | [9].as_slice(), 544 | [].as_slice(), 545 | ]; 546 | 547 | let mut r = ColumnsRegion::>::default(); 548 | 549 | let mut indices = Vec::with_capacity(data.len()); 550 | 551 | for row in &data { 552 | let index = r.push(*row); 553 | indices.push(index); 554 | } 555 | 556 | for (index, row) in indices.iter().zip(&data) { 557 | assert!(row.iter().copied().eq(r.index(index).iter())); 558 | } 559 | 560 | println!("{r:?}"); 561 | } 562 | 563 | #[test] 564 | fn test_ragged_string_vec() { 565 | let data = vec![ 566 | vec![], 567 | vec!["1".to_string()], 568 | vec!["2".to_string(), "3".to_string()], 569 | vec!["4".to_string(), "5".to_string(), "6".to_string()], 570 | vec!["7".to_string(), "8".to_string()], 571 | vec!["9".to_string()], 572 | vec![], 573 | ]; 574 | 575 | let mut r = 576 | ColumnsRegion::>>::default(); 577 | 578 | let mut indices = Vec::with_capacity(data.len()); 579 | 580 | for row in &data { 581 | let index = r.push(row); 582 | indices.push(index); 583 | } 584 | 585 | for (index, row) in indices.iter().zip(&data) { 586 | assert!(row.iter().eq(r.index(index).iter())); 587 | } 588 | 589 | println!("{r:?}"); 590 | } 591 | 592 | #[test] 593 | fn test_ragged_str_vec() { 594 | let data = [ 595 | vec![], 596 | vec!["1"], 597 | vec!["2", "3"], 598 | vec!["4", "5", "6"], 599 | vec!["7", "8"], 600 | vec!["9"], 601 | vec![], 602 | ]; 603 | 604 | let mut r = ColumnsRegion::>::default(); 605 | 606 | let mut indices = Vec::with_capacity(data.len()); 607 | 608 | for row in &data { 609 | let index = r.push(row); 610 | indices.push(index); 611 | } 612 | 613 | for (index, row) in indices.iter().zip(&data) { 614 | assert!(row.iter().eq(r.index(index).iter())); 615 | } 616 | 617 | println!("{r:?}"); 618 | } 619 | 620 | #[test] 621 | fn test_ragged_str_iter() { 622 | let data = [ 623 | vec![], 624 | vec!["1"], 625 | vec!["2", "3"], 626 | vec!["4", "5", "6"], 627 | vec!["7", "8"], 628 | vec!["9"], 629 | vec![], 630 | ]; 631 | 632 | let mut r = ColumnsRegion::>::default(); 633 | 634 | let mut indices = Vec::with_capacity(data.len()); 635 | 636 | for row in &data { 637 | let index = r.push(PushIter(row.iter())); 638 | indices.push(index); 639 | } 640 | 641 | for (index, row) in indices.iter().zip(&data) { 642 | assert!(row.iter().eq(r.index(index).iter())); 643 | } 644 | 645 | assert_eq!("1", r.index(indices[1]).get(0)); 646 | assert_eq!(1, r.index(indices[1]).len()); 647 | assert!(!r.index(indices[1]).is_empty()); 648 | assert!(r.index(indices[0]).is_empty()); 649 | 650 | println!("{r:?}"); 651 | } 652 | 653 | #[test] 654 | fn read_columns_push() { 655 | let data = [[[1]; 4]; 4]; 656 | 657 | let mut r = >>::default(); 658 | let mut r2 = >>::default(); 659 | 660 | for row in &data { 661 | let idx = r.push(row); 662 | let idx2 = r2.push(r.index(idx)); 663 | assert!(r.index(idx).iter().eq(r2.index(idx2).iter())); 664 | } 665 | } 666 | 667 | #[test] 668 | #[should_panic] 669 | fn test_clear() { 670 | let data = [[[1]; 4]; 4]; 671 | 672 | let mut r = >>::default(); 673 | 674 | let mut idx = None; 675 | for row in &data { 676 | idx = Some(r.push(row)); 677 | } 678 | 679 | r.clear(); 680 | let _ = r.index(idx.unwrap()); 681 | } 682 | 683 | #[test] 684 | fn copy_reserve_regions() { 685 | let data = [[[1]; 4]; 4]; 686 | 687 | let mut r = >>::default(); 688 | 689 | for row in &data { 690 | let _ = r.push(row); 691 | } 692 | for row in data { 693 | let _ = r.push(row); 694 | } 695 | 696 | let mut r2 = >>::default(); 697 | r2.reserve_regions(std::iter::once(&r)); 698 | 699 | let mut cap = 0; 700 | r2.heap_size(|_, c| cap += c); 701 | assert!(cap > 0); 702 | } 703 | 704 | #[test] 705 | fn test_merge_regions() { 706 | let data = [ 707 | vec![], 708 | vec!["1"], 709 | vec!["2", "3"], 710 | vec!["4", "5", "6"], 711 | vec!["7", "8"], 712 | vec!["9"], 713 | vec![], 714 | ]; 715 | 716 | let mut r = ColumnsRegion::>::default(); 717 | 718 | for row in &data { 719 | let _ = r.push(PushIter(row.iter())); 720 | } 721 | 722 | let (mut siz1, mut cap1) = (0, 0); 723 | r.heap_size(|s, c| { 724 | siz1 += s; 725 | cap1 += c; 726 | }); 727 | 728 | let mut r2 = ColumnsRegion::merge_regions(std::iter::once(&r)); 729 | for row in &data { 730 | let _ = r2.push(PushIter(row.iter())); 731 | } 732 | 733 | let (mut siz2, mut cap2) = (0, 0); 734 | r2.heap_size(|s, c| { 735 | siz2 += s; 736 | cap2 += c; 737 | }); 738 | assert!(cap2 <= cap1); 739 | } 740 | } 741 | -------------------------------------------------------------------------------- /src/impls/deduplicate.rs: -------------------------------------------------------------------------------- 1 | //! Simple deduplication of equal consecutive items. 2 | 3 | #[cfg(feature = "serde")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::impls::index::{IndexContainer, IndexOptimized}; 7 | use crate::{Push, Region, ReserveItems}; 8 | 9 | /// A region to deduplicate consecutive equal items. 10 | /// 11 | /// # Examples 12 | /// 13 | /// The following example shows that two inserts can result in the same index. 14 | /// ``` 15 | /// use flatcontainer::impls::deduplicate::CollapseSequence; 16 | /// use flatcontainer::{Push, StringRegion}; 17 | /// let mut r = >::default(); 18 | /// 19 | /// assert_eq!(r.push("abc"), r.push("abc")); 20 | /// ``` 21 | #[derive(Debug)] 22 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 23 | pub struct CollapseSequence { 24 | /// Inner region. 25 | inner: R, 26 | /// The index of the last pushed item. 27 | last_index: Option, 28 | } 29 | 30 | impl Clone for CollapseSequence { 31 | fn clone(&self) -> Self { 32 | Self { 33 | inner: self.inner.clone(), 34 | last_index: self.last_index, 35 | } 36 | } 37 | 38 | fn clone_from(&mut self, source: &Self) { 39 | self.inner.clone_from(&source.inner); 40 | self.last_index = source.last_index; 41 | } 42 | } 43 | 44 | impl Default for CollapseSequence { 45 | fn default() -> Self { 46 | Self { 47 | inner: R::default(), 48 | last_index: None, 49 | } 50 | } 51 | } 52 | 53 | impl Region for CollapseSequence { 54 | type Owned = R::Owned; 55 | type ReadItem<'a> = R::ReadItem<'a> where Self: 'a; 56 | type Index = R::Index; 57 | 58 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 59 | where 60 | Self: 'a, 61 | { 62 | Self { 63 | inner: R::merge_regions(regions.map(|r| &r.inner)), 64 | last_index: None, 65 | } 66 | } 67 | 68 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 69 | self.inner.index(index) 70 | } 71 | 72 | fn reserve_regions<'a, I>(&mut self, regions: I) 73 | where 74 | Self: 'a, 75 | I: Iterator + Clone, 76 | { 77 | self.inner.reserve_regions(regions.map(|r| &r.inner)); 78 | } 79 | 80 | fn clear(&mut self) { 81 | self.inner.clear(); 82 | self.last_index = None; 83 | } 84 | 85 | fn heap_size(&self, callback: F) { 86 | self.inner.heap_size(callback); 87 | } 88 | 89 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 90 | where 91 | Self: 'a, 92 | { 93 | R::reborrow(item) 94 | } 95 | } 96 | 97 | impl Push for CollapseSequence 98 | where 99 | R: Region + Push, 100 | for<'a> T: PartialEq>, 101 | { 102 | fn push(&mut self, item: T) -> as Region>::Index { 103 | if let Some(last_index) = self.last_index { 104 | if item == self.inner.index(last_index) { 105 | return last_index; 106 | } 107 | } 108 | let index = self.inner.push(item); 109 | self.last_index = Some(index); 110 | index 111 | } 112 | } 113 | 114 | /// Transform an index of `(usize, usize)` to a sequence of `0..`. Requires the pairs to 115 | /// be dense, i.e., `(i, j)` is followed by `(j, k)`. 116 | /// 117 | /// Defers to region `R` for storing items, and uses index container `O` to 118 | /// remeber indices. By default, `O` is `Vec`. 119 | /// 120 | /// # Examples 121 | /// 122 | /// The following example shows that two inserts into a copy region have a collapsible index: 123 | /// ``` 124 | /// use flatcontainer::impls::deduplicate::{CollapseSequence, ConsecutiveIndexPairs}; 125 | /// use flatcontainer::{Push, OwnedRegion, Region, StringRegion}; 126 | /// let mut r = >>::default(); 127 | /// 128 | /// let index = r.push(&b"abc"); 129 | /// assert_eq!(b"abc", r.index(index)); 130 | /// ``` 131 | #[derive(Debug)] 132 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 133 | pub struct ConsecutiveIndexPairs { 134 | /// Wrapped region 135 | inner: R, 136 | /// Storage for indices. Always stores element 0. 137 | indices: O, 138 | /// The most recent end of the index pair of region `R`. 139 | last_index: usize, 140 | } 141 | 142 | impl Clone for ConsecutiveIndexPairs { 143 | fn clone(&self) -> Self { 144 | Self { 145 | inner: self.inner.clone(), 146 | indices: self.indices.clone(), 147 | last_index: self.last_index, 148 | } 149 | } 150 | 151 | fn clone_from(&mut self, source: &Self) { 152 | self.inner.clone_from(&source.inner); 153 | self.indices.clone_from(&source.indices); 154 | self.last_index = source.last_index; 155 | } 156 | } 157 | 158 | impl Default for ConsecutiveIndexPairs 159 | where 160 | R: Default, 161 | O: IndexContainer, 162 | { 163 | #[inline] 164 | fn default() -> Self { 165 | let mut d = Self { 166 | inner: Default::default(), 167 | indices: Default::default(), 168 | last_index: 0, 169 | }; 170 | d.indices.push(0); 171 | d 172 | } 173 | } 174 | 175 | impl Region for ConsecutiveIndexPairs 176 | where 177 | R: Region, 178 | O: IndexContainer, 179 | { 180 | type Owned = R::Owned; 181 | type ReadItem<'a> = R::ReadItem<'a> 182 | where 183 | Self: 'a; 184 | 185 | type Index = usize; 186 | 187 | #[inline] 188 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 189 | where 190 | Self: 'a, 191 | { 192 | let mut indices = O::default(); 193 | indices.push(0); 194 | Self { 195 | inner: R::merge_regions(regions.map(|r| &r.inner)), 196 | indices, 197 | last_index: 0, 198 | } 199 | } 200 | 201 | #[inline] 202 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 203 | self.inner 204 | .index((self.indices.index(index), self.indices.index(index + 1))) 205 | } 206 | 207 | #[inline] 208 | fn reserve_regions<'a, I>(&mut self, regions: I) 209 | where 210 | Self: 'a, 211 | I: Iterator + Clone, 212 | { 213 | self.inner.reserve_regions(regions.map(|r| &r.inner)); 214 | } 215 | 216 | #[inline] 217 | fn clear(&mut self) { 218 | self.last_index = 0; 219 | self.inner.clear(); 220 | self.indices.clear(); 221 | self.indices.push(0); 222 | } 223 | 224 | #[inline] 225 | fn heap_size(&self, mut callback: F) { 226 | self.indices.heap_size(&mut callback); 227 | self.inner.heap_size(callback); 228 | } 229 | 230 | #[inline] 231 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 232 | where 233 | Self: 'a, 234 | { 235 | R::reborrow(item) 236 | } 237 | } 238 | 239 | impl Push for ConsecutiveIndexPairs 240 | where 241 | R: Region + Push, 242 | O: IndexContainer, 243 | { 244 | #[inline] 245 | fn push(&mut self, item: T) -> as Region>::Index { 246 | let index = self.inner.push(item); 247 | debug_assert_eq!(index.0, self.last_index); 248 | self.last_index = index.1; 249 | self.indices.push(index.1); 250 | self.indices.len() - 2 251 | } 252 | } 253 | 254 | impl ReserveItems for ConsecutiveIndexPairs 255 | where 256 | R: Region + ReserveItems, 257 | O: IndexContainer, 258 | { 259 | fn reserve_items(&mut self, items: I) 260 | where 261 | I: Iterator + Clone, 262 | { 263 | self.inner.reserve_items(items); 264 | } 265 | } 266 | 267 | #[cfg(test)] 268 | mod tests { 269 | use crate::impls::deduplicate::{CollapseSequence, ConsecutiveIndexPairs}; 270 | use crate::impls::index::IndexOptimized; 271 | use crate::{FlatStack, Push, StringRegion}; 272 | 273 | #[test] 274 | fn test_dedup_flatstack() { 275 | let mut fs = FlatStack::>::default(); 276 | 277 | fs.copy("abc"); 278 | fs.copy("abc"); 279 | 280 | assert_eq!(2, fs.len()); 281 | 282 | println!("{fs:?}"); 283 | } 284 | 285 | #[test] 286 | fn test_dedup_region() { 287 | let mut r = CollapseSequence::::default(); 288 | 289 | assert_eq!(r.push("abc"), r.push("abc")); 290 | 291 | println!("{r:?}"); 292 | } 293 | 294 | #[test] 295 | fn test_index_optimized() { 296 | let mut r = 297 | CollapseSequence::>::default(); 298 | 299 | for _ in 0..1000 { 300 | let _ = r.push("abc"); 301 | } 302 | 303 | println!("{r:?}"); 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/impls/index.rs: -------------------------------------------------------------------------------- 1 | //! Types to store indexes. 2 | 3 | #[cfg(feature = "serde")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::impls::storage::Storage; 7 | 8 | /// A container to store indices. 9 | pub trait IndexContainer: Storage { 10 | /// Iterator over the elements. 11 | type Iter<'a>: Iterator + Clone 12 | where 13 | Self: 'a; 14 | 15 | /// Lookup an index. May panic for invalid indexes. 16 | fn index(&self, index: usize) -> T; 17 | 18 | /// Accepts a newly pushed element. 19 | fn push(&mut self, item: T); 20 | 21 | /// Extend from iterator. Must be [`ExactSizeIterator`] to efficiently 22 | /// pre-allocate. 23 | fn extend>(&mut self, iter: I) 24 | where 25 | I::IntoIter: ExactSizeIterator; 26 | 27 | /// Returns an iterator over the elements. 28 | fn iter(&self) -> Self::Iter<'_>; 29 | } 30 | 31 | /// A container for offsets that can represent strides of offsets. 32 | /// 33 | /// Does not implement [`IndexContainer`] because it cannot accept arbitrary pushes. Instead, 34 | /// its `push` method returns a boolean to indicate whether the push was successful or not. 35 | /// 36 | /// This type can absorb sequences of the form `0, stride, 2 * stride, 3 * stride, ...` and 37 | /// saturates in a repeated last element. 38 | #[derive(Eq, PartialEq, Debug, Default, Clone, Copy)] 39 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 40 | pub enum Stride { 41 | /// No push has occurred. 42 | #[default] 43 | Empty, 44 | /// Pushed a single 0. 45 | Zero, 46 | /// `Striding(stride, count)`: `count` many steps of stride `stride` have been pushed. 47 | Striding(usize, usize), 48 | /// `Saturated(stride, count, reps)`: `count` many steps of stride `stride`, followed by 49 | /// `reps` repetitions of the last element have been pushed. 50 | Saturated(usize, usize, usize), 51 | } 52 | 53 | impl Stride { 54 | /// Accepts or rejects a newly pushed element. 55 | #[must_use] 56 | #[inline] 57 | pub fn push(&mut self, item: usize) -> bool { 58 | match self { 59 | Stride::Empty => { 60 | if item == 0 { 61 | *self = Stride::Zero; 62 | true 63 | } else { 64 | false 65 | } 66 | } 67 | Stride::Zero => { 68 | *self = Stride::Striding(item, 2); 69 | true 70 | } 71 | Stride::Striding(stride, count) => { 72 | if item == *stride * *count { 73 | *count += 1; 74 | true 75 | } else if item == *stride * (*count - 1) { 76 | *self = Stride::Saturated(*stride, *count, 1); 77 | true 78 | } else { 79 | false 80 | } 81 | } 82 | Stride::Saturated(stride, count, reps) => { 83 | if item == *stride * (*count - 1) { 84 | *reps += 1; 85 | true 86 | } else { 87 | false 88 | } 89 | } 90 | } 91 | } 92 | 93 | /// Lookup the element at `index`. 94 | /// 95 | /// # Panics 96 | /// 97 | /// Panics for out-of-bounds accesses, i.e., if `index` greater or equal to 98 | /// [`len`](Stride::len). 99 | #[must_use] 100 | #[inline] 101 | pub fn index(&self, index: usize) -> usize { 102 | match self { 103 | Stride::Empty => { 104 | panic!("Empty Stride") 105 | } 106 | Stride::Zero => 0, 107 | Stride::Striding(stride, _steps) => *stride * index, 108 | Stride::Saturated(stride, steps, _reps) => { 109 | if index < *steps { 110 | *stride * index 111 | } else { 112 | *stride * (*steps - 1) 113 | } 114 | } 115 | } 116 | } 117 | 118 | /// Returns the number of elements. 119 | #[must_use] 120 | #[inline] 121 | pub fn len(&self) -> usize { 122 | match self { 123 | Stride::Empty => 0, 124 | Stride::Zero => 1, 125 | Stride::Striding(_stride, steps) => *steps, 126 | Stride::Saturated(_stride, steps, reps) => *steps + *reps, 127 | } 128 | } 129 | 130 | /// Returns `true` if empty. 131 | #[must_use] 132 | #[inline] 133 | pub fn is_empty(&self) -> bool { 134 | matches!(self, Stride::Empty) 135 | } 136 | 137 | /// Removes all elements. 138 | #[inline] 139 | pub fn clear(&mut self) { 140 | *self = Self::default(); 141 | } 142 | 143 | /// Return an iterator over the elements. 144 | #[must_use] 145 | #[inline] 146 | pub fn iter(&self) -> StrideIter { 147 | StrideIter { 148 | strided: *self, 149 | index: 0, 150 | } 151 | } 152 | } 153 | 154 | /// An iterator over the elements of an [`Stride`]. 155 | #[derive(Clone, Copy)] 156 | pub struct StrideIter { 157 | strided: Stride, 158 | index: usize, 159 | } 160 | 161 | impl Iterator for StrideIter { 162 | type Item = usize; 163 | 164 | #[inline] 165 | fn next(&mut self) -> Option { 166 | if self.index < self.strided.len() { 167 | let item = self.strided.index(self.index); 168 | self.index += 1; 169 | Some(item) 170 | } else { 171 | None 172 | } 173 | } 174 | } 175 | 176 | /// A list of unsigned integers that uses `u32` elements as long as they are small enough, and switches to `u64` once they are not. 177 | #[derive(Eq, PartialEq, Clone, Debug, Default)] 178 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 179 | pub struct IndexList { 180 | /// Indexes that fit within a `u32`. 181 | pub smol: S, 182 | /// Indexes that either do not fit in a `u32`, or are inserted after some offset that did not fit. 183 | pub chonk: L, 184 | } 185 | 186 | impl IndexList 187 | where 188 | S: IndexContainer, 189 | L: IndexContainer, 190 | { 191 | /// Allocate a new list with a specified capacity. 192 | #[must_use] 193 | #[inline] 194 | pub fn with_capacity(cap: usize) -> Self { 195 | Self { 196 | smol: S::with_capacity(cap), 197 | chonk: L::default(), 198 | } 199 | } 200 | 201 | /// Inserts the index, as a `u32` if that is still on the table. 202 | /// 203 | /// # Panics 204 | /// 205 | /// Panics if `usize` does not fit in `u64`. 206 | #[inline] 207 | pub fn push(&mut self, index: usize) { 208 | if self.chonk.is_empty() { 209 | if let Ok(smol) = index.try_into() { 210 | self.smol.push(smol); 211 | } else { 212 | self.chonk.push(index.try_into().unwrap()); 213 | } 214 | } else { 215 | self.chonk.push(index.try_into().unwrap()); 216 | } 217 | } 218 | 219 | /// Like [`std::ops::Index`], which we cannot implement as it must return a `&usize`. 220 | /// 221 | /// # Panics 222 | /// 223 | /// Panics if the index is out of bounds, i.e., it is larger or equal to the length. 224 | #[must_use] 225 | #[inline] 226 | pub fn index(&self, index: usize) -> usize { 227 | if index < self.smol.len() { 228 | self.smol.index(index).try_into().unwrap() 229 | } else { 230 | let index = index - self.smol.len(); 231 | self.chonk.index(index).try_into().unwrap() 232 | } 233 | } 234 | /// The number of offsets in the list. 235 | #[must_use] 236 | #[inline] 237 | pub fn len(&self) -> usize { 238 | self.smol.len() + self.chonk.len() 239 | } 240 | 241 | /// Returns `true` if this list contains no elements. 242 | #[must_use] 243 | #[inline] 244 | pub fn is_empty(&self) -> bool { 245 | self.smol.is_empty() && self.chonk.is_empty() 246 | } 247 | 248 | /// Reserve space for `additional` elements. 249 | #[inline] 250 | pub fn reserve(&mut self, additional: usize) { 251 | self.smol.reserve(additional); 252 | } 253 | 254 | /// Remove all elements. 255 | #[inline] 256 | pub fn clear(&mut self) { 257 | self.smol.clear(); 258 | self.chonk.clear(); 259 | } 260 | 261 | #[inline] 262 | fn heap_size(&self, mut callback: F) { 263 | self.smol.heap_size(&mut callback); 264 | self.chonk.heap_size(callback); 265 | } 266 | } 267 | 268 | impl Storage for IndexList 269 | where 270 | S: IndexContainer, 271 | L: IndexContainer, 272 | { 273 | #[inline] 274 | fn with_capacity(capacity: usize) -> Self { 275 | Self::with_capacity(capacity) 276 | } 277 | 278 | #[inline] 279 | fn reserve(&mut self, additional: usize) { 280 | self.reserve(additional) 281 | } 282 | 283 | #[inline] 284 | fn clear(&mut self) { 285 | self.clear() 286 | } 287 | 288 | #[inline] 289 | fn heap_size(&self, callback: F) { 290 | self.heap_size(callback) 291 | } 292 | 293 | #[inline] 294 | fn len(&self) -> usize { 295 | self.len() 296 | } 297 | 298 | #[inline] 299 | fn is_empty(&self) -> bool { 300 | self.is_empty() 301 | } 302 | } 303 | 304 | impl IndexContainer for IndexList 305 | where 306 | S: IndexContainer, 307 | L: IndexContainer, 308 | { 309 | type Iter<'a> = IndexListIter, L::Iter<'a>> where Self: 'a; 310 | 311 | #[inline] 312 | fn index(&self, index: usize) -> usize { 313 | self.index(index) 314 | } 315 | 316 | #[inline] 317 | fn push(&mut self, item: usize) { 318 | self.push(item) 319 | } 320 | 321 | #[inline] 322 | fn extend>(&mut self, iter: I) 323 | where 324 | I::IntoIter: ExactSizeIterator, 325 | { 326 | for item in iter { 327 | self.push(item); 328 | } 329 | } 330 | 331 | #[inline] 332 | fn iter(&self) -> Self::Iter<'_> { 333 | IndexListIter { 334 | smol: self.smol.iter(), 335 | chonk: self.chonk.iter(), 336 | } 337 | } 338 | } 339 | 340 | /// An iterator over the elements of an [`IndexList`]. 341 | #[derive(Clone, Copy)] 342 | pub struct IndexListIter { 343 | smol: S, 344 | chonk: L, 345 | } 346 | 347 | impl Iterator for IndexListIter 348 | where 349 | S: Iterator, 350 | L: Iterator, 351 | { 352 | type Item = usize; 353 | 354 | #[inline] 355 | fn next(&mut self) -> Option { 356 | self.smol 357 | .next() 358 | .map(|x| x as usize) 359 | .or_else(|| self.chonk.next().map(|x| x as usize)) 360 | } 361 | } 362 | 363 | /// An offset container implementation that first tries to recognize strides, and then spilles into 364 | /// a regular offset list. 365 | #[derive(Eq, PartialEq, Default, Debug, Clone)] 366 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 367 | pub struct IndexOptimized, L = Vec> { 368 | strided: Stride, 369 | spilled: IndexList, 370 | } 371 | 372 | impl Storage for IndexOptimized 373 | where 374 | S: IndexContainer, 375 | L: IndexContainer, 376 | { 377 | #[inline] 378 | fn with_capacity(_capacity: usize) -> Self { 379 | // `self.strided` doesn't have any capacity, and we don't know the structure of the data. 380 | Self::default() 381 | } 382 | 383 | #[inline] 384 | fn clear(&mut self) { 385 | self.spilled.clear(); 386 | self.strided = Stride::default(); 387 | } 388 | 389 | #[inline] 390 | fn len(&self) -> usize { 391 | self.strided.len() + self.spilled.len() 392 | } 393 | 394 | #[inline] 395 | fn is_empty(&self) -> bool { 396 | self.strided.is_empty() && self.spilled.is_empty() 397 | } 398 | 399 | #[inline] 400 | fn reserve(&mut self, additional: usize) { 401 | if !self.spilled.is_empty() { 402 | self.spilled.reserve(additional); 403 | } 404 | } 405 | 406 | #[inline] 407 | fn heap_size(&self, callback: F) { 408 | self.spilled.heap_size(callback); 409 | } 410 | } 411 | 412 | impl IndexContainer for IndexOptimized 413 | where 414 | S: IndexContainer, 415 | L: IndexContainer, 416 | { 417 | type Iter<'a> = IndexOptimizedIter, L::Iter<'a>> where Self: 'a; 418 | 419 | fn index(&self, index: usize) -> usize { 420 | if index < self.strided.len() { 421 | self.strided.index(index) 422 | } else { 423 | self.spilled.index(index - self.strided.len()) 424 | } 425 | } 426 | 427 | fn push(&mut self, item: usize) { 428 | if self.spilled.is_empty() { 429 | let inserted = self.strided.push(item); 430 | if !inserted { 431 | self.spilled.push(item); 432 | } 433 | } else { 434 | self.spilled.push(item); 435 | } 436 | } 437 | 438 | fn extend>(&mut self, iter: I) 439 | where 440 | I::IntoIter: ExactSizeIterator, 441 | { 442 | for item in iter { 443 | self.push(item); 444 | } 445 | } 446 | 447 | fn iter(&self) -> Self::Iter<'_> { 448 | IndexOptimizedIter { 449 | strided: self.strided.iter(), 450 | spilled: self.spilled.iter(), 451 | } 452 | } 453 | } 454 | 455 | /// An iterator over the elements of an [`IndexOptimized`]. 456 | #[derive(Clone, Copy)] 457 | pub struct IndexOptimizedIter { 458 | strided: StrideIter, 459 | spilled: IndexListIter, 460 | } 461 | 462 | impl Iterator for IndexOptimizedIter 463 | where 464 | S: Iterator, 465 | L: Iterator, 466 | { 467 | type Item = usize; 468 | 469 | fn next(&mut self) -> Option { 470 | self.strided.next().or_else(|| self.spilled.next()) 471 | } 472 | } 473 | 474 | impl IndexContainer for Vec { 475 | type Iter<'a> = std::iter::Copied> where Self: 'a; 476 | 477 | fn index(&self, index: usize) -> T { 478 | self[index] 479 | } 480 | 481 | #[inline] 482 | fn push(&mut self, item: T) { 483 | self.push(item); 484 | } 485 | 486 | fn extend>(&mut self, iter: I) 487 | where 488 | I::IntoIter: ExactSizeIterator, 489 | { 490 | Extend::extend(self, iter); 491 | } 492 | 493 | fn iter(&self) -> Self::Iter<'_> { 494 | self.as_slice().iter().copied() 495 | } 496 | } 497 | 498 | #[cfg(test)] 499 | mod tests { 500 | use crate::impls::deduplicate::ConsecutiveIndexPairs; 501 | use crate::{Push, Region, SliceRegion, StringRegion}; 502 | 503 | use super::*; 504 | 505 | #[test] 506 | fn test_index_optimized() { 507 | fn copy, T>(r: &mut R, item: T) -> R::Index { 508 | r.push(item) 509 | } 510 | 511 | let mut r = SliceRegion::< 512 | ConsecutiveIndexPairs, 513 | IndexOptimized, 514 | >::default(); 515 | let idx = copy(&mut r, ["abc"]); 516 | assert_eq!("abc", r.index(idx).get(0)) 517 | } 518 | 519 | #[test] 520 | fn test_index_optimized_clear() { 521 | let mut oo = ::default(); 522 | oo.push(0); 523 | assert_eq!(oo.len(), 1); 524 | oo.clear(); 525 | assert_eq!(oo.len(), 0); 526 | assert!(oo.is_empty()); 527 | oo.push(9999999999); 528 | assert_eq!(oo.len(), 1); 529 | oo.clear(); 530 | assert_eq!(oo.len(), 0); 531 | } 532 | 533 | #[test] 534 | fn test_index_optimized_reserve() { 535 | let mut oo = ::default(); 536 | oo.push(9999999999); 537 | assert_eq!(oo.len(), 1); 538 | oo.reserve(1); 539 | } 540 | 541 | #[test] 542 | fn test_index_optimized_heap_size() { 543 | let mut oo = ::default(); 544 | oo.push(9999999999); 545 | let mut cap = 0; 546 | oo.heap_size(|_, ca| { 547 | cap += ca; 548 | }); 549 | assert!(cap > 0); 550 | } 551 | 552 | #[test] 553 | fn test_index_stride_push() { 554 | let mut os = Stride::default(); 555 | assert_eq!(os.len(), 0); 556 | assert!(os.is_empty()); 557 | assert!(os.push(0)); 558 | assert_eq!(os.index(0), 0); 559 | assert_eq!(os.len(), 1); 560 | assert!(!os.is_empty()); 561 | assert!(os.push(2)); 562 | assert_eq!(os.len(), 2); 563 | assert_eq!(os.index(1), 2); 564 | assert!(os.push(4)); 565 | assert_eq!(os.len(), 3); 566 | assert_eq!(os.index(2), 4); 567 | assert!(os.push(4)); 568 | assert_eq!(os.len(), 4); 569 | assert_eq!(os.index(3), 4); 570 | assert!(os.push(4)); 571 | assert_eq!(os.len(), 5); 572 | assert_eq!(os.index(1), 2); 573 | assert_eq!(os.index(4), 4); 574 | assert!(!os.push(5)); 575 | os.clear(); 576 | assert!(!os.push(1)); 577 | } 578 | 579 | #[test] 580 | fn test_chonk() { 581 | let mut ol = , Vec<_>>>::default(); 582 | ol.push(usize::MAX); 583 | assert_eq!(usize::MAX, ol.index(0)); 584 | } 585 | 586 | #[test] 587 | #[should_panic] 588 | fn test_index_stride_index() { 589 | let os = Stride::default(); 590 | let _ = os.index(0); 591 | } 592 | } 593 | -------------------------------------------------------------------------------- /src/impls/mirror.rs: -------------------------------------------------------------------------------- 1 | //! A region that copies its inputs. 2 | 3 | use std::fmt::{Debug, Formatter}; 4 | use std::marker::PhantomData; 5 | 6 | #[cfg(feature = "serde")] 7 | use serde::{Deserialize, Serialize}; 8 | 9 | use crate::{Index, IntoOwned, Push, Region, RegionPreference, ReserveItems}; 10 | 11 | /// A region for types where the read item type is equal to the index type. 12 | /// 13 | /// This region is useful where the type is not larger than roughly two `usize`s (or 1.5x with 14 | /// some optimizations), or looking up the value is too costly. For larger copy types, the memory 15 | /// required to store the copy type and an index is only marginally bigger, with the benefit 16 | /// that the index remains compact. 17 | /// 18 | /// # Examples 19 | /// 20 | /// For [`MirrorRegion`]s, we can index with a copy type: 21 | /// ``` 22 | /// # use flatcontainer::{MirrorRegion, Region}; 23 | /// let r = >::default(); 24 | /// let output: u8 = r.index(42); 25 | /// assert_eq!(output, 42); 26 | /// ``` 27 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)] 28 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 29 | pub struct MirrorRegion(PhantomData); 30 | 31 | impl Default for MirrorRegion { 32 | fn default() -> Self { 33 | Self(PhantomData) 34 | } 35 | } 36 | 37 | impl Debug for MirrorRegion { 38 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 39 | write!(f, "MirrorRegion<{}>", std::any::type_name::()) 40 | } 41 | } 42 | 43 | impl Region for MirrorRegion 44 | where 45 | for<'a> T: Index + IntoOwned<'a, Owned = T>, 46 | { 47 | type Owned = T; 48 | type ReadItem<'a> = T where T: 'a; 49 | type Index = T; 50 | 51 | #[inline] 52 | fn merge_regions<'a>(_regions: impl Iterator + Clone) -> Self 53 | where 54 | Self: 'a, 55 | { 56 | Self::default() 57 | } 58 | 59 | #[inline] 60 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 61 | index 62 | } 63 | 64 | #[inline(always)] 65 | fn reserve_regions<'a, I>(&mut self, _regions: I) 66 | where 67 | Self: 'a, 68 | I: Iterator + Clone, 69 | { 70 | // No storage 71 | } 72 | 73 | #[inline(always)] 74 | fn clear(&mut self) { 75 | // No storage 76 | } 77 | 78 | #[inline] 79 | fn heap_size(&self, _callback: F) { 80 | // No storage 81 | } 82 | 83 | #[inline] 84 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 85 | where 86 | Self: 'a, 87 | { 88 | item 89 | } 90 | } 91 | 92 | impl Push for MirrorRegion 93 | where 94 | for<'a> T: Index + IntoOwned<'a, Owned = T>, 95 | { 96 | #[inline(always)] 97 | fn push(&mut self, item: T) -> T { 98 | item 99 | } 100 | } 101 | 102 | impl Push<&T> for MirrorRegion 103 | where 104 | for<'a> T: Index + IntoOwned<'a, Owned = T>, 105 | { 106 | #[inline(always)] 107 | fn push(&mut self, item: &T) -> T { 108 | *item 109 | } 110 | } 111 | 112 | impl Push<&&T> for MirrorRegion 113 | where 114 | for<'a> T: Index + IntoOwned<'a, Owned = T>, 115 | { 116 | #[inline(always)] 117 | fn push(&mut self, item: &&T) -> T { 118 | **item 119 | } 120 | } 121 | 122 | impl ReserveItems for MirrorRegion 123 | where 124 | for<'a> T: Index + IntoOwned<'a, Owned = T>, 125 | { 126 | #[inline(always)] 127 | fn reserve_items(&mut self, _items: I) 128 | where 129 | I: Iterator + Clone, 130 | { 131 | // No storage 132 | } 133 | } 134 | 135 | impl<'a, T> ReserveItems<&'a T> for MirrorRegion 136 | where 137 | for<'b> T: Index + IntoOwned<'b, Owned = T>, 138 | { 139 | #[inline(always)] 140 | fn reserve_items(&mut self, _items: I) 141 | where 142 | I: Iterator + Clone, 143 | { 144 | // No storage 145 | } 146 | } 147 | 148 | macro_rules! implement_for { 149 | ($index_type:ty) => { 150 | impl RegionPreference for $index_type { 151 | type Owned = Self; 152 | type Region = MirrorRegion; 153 | } 154 | 155 | impl<'a> IntoOwned<'a> for $index_type { 156 | type Owned = $index_type; 157 | 158 | #[inline] 159 | fn into_owned(self) -> Self::Owned { 160 | self 161 | } 162 | 163 | #[inline] 164 | fn clone_onto(self, other: &mut Self::Owned) { 165 | *other = self; 166 | } 167 | 168 | #[inline] 169 | fn borrow_as(owned: &'a Self::Owned) -> Self { 170 | *owned 171 | } 172 | } 173 | }; 174 | } 175 | 176 | implement_for!(()); 177 | implement_for!(bool); 178 | implement_for!(char); 179 | 180 | implement_for!(u8); 181 | implement_for!(u16); 182 | implement_for!(u32); 183 | implement_for!(u64); 184 | implement_for!(u128); 185 | implement_for!(usize); 186 | 187 | implement_for!(i8); 188 | implement_for!(i16); 189 | implement_for!(i32); 190 | implement_for!(i64); 191 | implement_for!(i128); 192 | implement_for!(isize); 193 | 194 | implement_for!(f32); 195 | implement_for!(f64); 196 | 197 | implement_for!(std::num::Wrapping); 198 | implement_for!(std::num::Wrapping); 199 | implement_for!(std::num::Wrapping); 200 | implement_for!(std::num::Wrapping); 201 | implement_for!(std::num::Wrapping); 202 | implement_for!(std::num::Wrapping); 203 | 204 | implement_for!(std::time::Duration); 205 | 206 | #[cfg(test)] 207 | mod tests { 208 | use crate::ReserveItems; 209 | 210 | use super::*; 211 | 212 | #[test] 213 | fn test_reserve_regions() { 214 | let mut r = MirrorRegion::::default(); 215 | ReserveItems::reserve_items(&mut r, std::iter::once(0)); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/impls/option.rs: -------------------------------------------------------------------------------- 1 | //! A region that stores options. 2 | 3 | #[cfg(feature = "serde")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; 7 | 8 | impl RegionPreference for Option { 9 | type Owned = Option; 10 | type Region = OptionRegion; 11 | } 12 | 13 | /// A region to hold [`Option`]s. 14 | /// 15 | /// # Examples 16 | /// 17 | /// The region can hold options: 18 | /// ``` 19 | /// # use flatcontainer::{RegionPreference, Push, OptionRegion, Region}; 20 | /// let mut r = ::Region>>::default(); 21 | /// 22 | /// let some_index = r.push(Some(123)); 23 | /// // Type annotations required for `None`: 24 | /// let none_index = r.push(Option::::None); 25 | /// 26 | /// assert_eq!(Some(123), r.index(some_index)); 27 | /// assert_eq!(None, r.index(none_index)); 28 | /// ``` 29 | #[derive(Default, Debug)] 30 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 31 | pub struct OptionRegion { 32 | inner: R, 33 | } 34 | 35 | impl Clone for OptionRegion { 36 | fn clone(&self) -> Self { 37 | Self { 38 | inner: self.inner.clone(), 39 | } 40 | } 41 | 42 | fn clone_from(&mut self, source: &Self) { 43 | self.inner.clone_from(&source.inner); 44 | } 45 | } 46 | 47 | impl Region for OptionRegion { 48 | type Owned = Option; 49 | type ReadItem<'a> = Option<::ReadItem<'a>> where Self: 'a; 50 | type Index = Option; 51 | 52 | #[inline] 53 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 54 | where 55 | Self: 'a, 56 | { 57 | Self { 58 | inner: R::merge_regions(regions.map(|r| &r.inner)), 59 | } 60 | } 61 | 62 | #[inline] 63 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 64 | index.map(|t| self.inner.index(t)) 65 | } 66 | 67 | #[inline] 68 | fn reserve_regions<'a, I>(&mut self, regions: I) 69 | where 70 | Self: 'a, 71 | I: Iterator + Clone, 72 | { 73 | self.inner.reserve_regions(regions.map(|r| &r.inner)); 74 | } 75 | 76 | #[inline] 77 | fn clear(&mut self) { 78 | self.inner.clear(); 79 | } 80 | 81 | #[inline] 82 | fn heap_size(&self, callback: F) { 83 | self.inner.heap_size(callback); 84 | } 85 | 86 | #[inline] 87 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 88 | where 89 | Self: 'a, 90 | { 91 | item.map(R::reborrow) 92 | } 93 | } 94 | 95 | impl<'a, T> IntoOwned<'a> for Option 96 | where 97 | T: IntoOwned<'a>, 98 | { 99 | type Owned = Option; 100 | 101 | #[inline] 102 | fn into_owned(self) -> Self::Owned { 103 | self.map(IntoOwned::into_owned) 104 | } 105 | 106 | #[inline] 107 | fn clone_onto(self, other: &mut Self::Owned) { 108 | match (self, other) { 109 | (Some(item), Some(target)) => T::clone_onto(item, target), 110 | (Some(item), target) => *target = Some(T::into_owned(item)), 111 | (None, target) => *target = None, 112 | } 113 | } 114 | 115 | #[inline] 116 | fn borrow_as(owned: &'a Self::Owned) -> Self { 117 | owned.as_ref().map(T::borrow_as) 118 | } 119 | } 120 | 121 | impl Push> for OptionRegion 122 | where 123 | TR: Region + Push, 124 | { 125 | #[inline] 126 | fn push(&mut self, item: Option) -> as Region>::Index { 127 | item.map(|t| self.inner.push(t)) 128 | } 129 | } 130 | 131 | impl<'a, T: 'a, TR> Push<&'a Option> for OptionRegion 132 | where 133 | TR: Region + Push<&'a T>, 134 | { 135 | #[inline] 136 | fn push(&mut self, item: &'a Option) -> as Region>::Index { 137 | item.as_ref().map(|t| self.inner.push(t)) 138 | } 139 | } 140 | 141 | impl ReserveItems> for OptionRegion 142 | where 143 | TR: Region + ReserveItems, 144 | { 145 | #[inline] 146 | fn reserve_items(&mut self, items: I) 147 | where 148 | I: Iterator> + Clone, 149 | { 150 | // Clippy is confused about using `flatten` here, which we cannot use because 151 | // the iterator isn't `Clone`. 152 | #[allow(clippy::filter_map_identity)] 153 | self.inner.reserve_items(items.filter_map(|r| r)); 154 | } 155 | } 156 | 157 | impl<'a, T: 'a, TR> ReserveItems<&'a Option> for OptionRegion 158 | where 159 | TR: Region + ReserveItems<&'a T>, 160 | { 161 | #[inline] 162 | fn reserve_items(&mut self, items: I) 163 | where 164 | I: Iterator> + Clone, 165 | { 166 | self.inner.reserve_items(items.filter_map(|r| r.as_ref())); 167 | } 168 | } 169 | 170 | #[cfg(test)] 171 | mod tests { 172 | use crate::{MirrorRegion, OwnedRegion, Region, ReserveItems}; 173 | 174 | use super::*; 175 | 176 | #[test] 177 | fn test_reserve() { 178 | let mut r = >>::default(); 179 | ReserveItems::reserve_items(&mut r, [Some(0), None].iter()); 180 | 181 | ReserveItems::reserve_items(&mut r, [Some(0), None].into_iter()); 182 | } 183 | 184 | #[test] 185 | fn test_heap_size() { 186 | let mut r = >>::default(); 187 | ReserveItems::reserve_items(&mut r, [Some([1; 1]), None].iter()); 188 | let mut cap = 0; 189 | r.heap_size(|_, ca| { 190 | cap += ca; 191 | }); 192 | assert!(cap > 0); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/impls/result.rs: -------------------------------------------------------------------------------- 1 | //! A region that stores results. 2 | 3 | #[cfg(feature = "serde")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; 7 | 8 | impl RegionPreference for Result { 9 | type Owned = Result; 10 | type Region = ResultRegion; 11 | } 12 | 13 | /// A region to hold [`Result`]s. 14 | /// 15 | /// # Examples 16 | /// 17 | /// Add results to a result region: 18 | /// ``` 19 | /// use flatcontainer::{RegionPreference, Push, Region, ResultRegion}; 20 | /// let mut r = 21 | /// ::Region, ::Region>>::default(); 22 | /// 23 | /// let ok_index = r.push(Result::<(), String>::Ok(())); 24 | /// let err_index = r.push(Result::<(), String>::Err("Error".to_string())); 25 | /// 26 | /// assert_eq!(Ok(()), r.index(ok_index)); 27 | /// assert_eq!(Err("Error"), r.index(err_index)); 28 | /// ``` 29 | #[derive(Default, Debug)] 30 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 31 | pub struct ResultRegion { 32 | oks: T, 33 | errs: E, 34 | } 35 | 36 | impl Clone for ResultRegion { 37 | fn clone(&self) -> Self { 38 | Self { 39 | oks: self.oks.clone(), 40 | errs: self.errs.clone(), 41 | } 42 | } 43 | 44 | fn clone_from(&mut self, source: &Self) { 45 | self.oks.clone_from(&source.oks); 46 | self.errs.clone_from(&source.errs); 47 | } 48 | } 49 | 50 | impl Region for ResultRegion 51 | where 52 | T: Region, 53 | E: Region, 54 | { 55 | type Owned = Result; 56 | type ReadItem<'a> = Result, E::ReadItem<'a>> where Self: 'a; 57 | type Index = Result; 58 | 59 | #[inline] 60 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 61 | where 62 | Self: 'a, 63 | { 64 | Self { 65 | oks: T::merge_regions(regions.clone().map(|r| &r.oks)), 66 | errs: E::merge_regions(regions.map(|r| &r.errs)), 67 | } 68 | } 69 | 70 | #[inline] 71 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 72 | match index { 73 | Ok(index) => Ok(self.oks.index(index)), 74 | Err(index) => Err(self.errs.index(index)), 75 | } 76 | } 77 | 78 | #[inline] 79 | fn reserve_regions<'a, I>(&mut self, regions: I) 80 | where 81 | Self: 'a, 82 | I: Iterator + Clone, 83 | { 84 | self.oks.reserve_regions(regions.clone().map(|r| &r.oks)); 85 | self.errs.reserve_regions(regions.map(|r| &r.errs)); 86 | } 87 | 88 | #[inline] 89 | fn clear(&mut self) { 90 | self.oks.clear(); 91 | self.errs.clear(); 92 | } 93 | 94 | #[inline] 95 | fn heap_size(&self, mut callback: F) { 96 | self.oks.heap_size(&mut callback); 97 | self.errs.heap_size(callback); 98 | } 99 | 100 | #[inline] 101 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 102 | where 103 | Self: 'a, 104 | { 105 | item.map(T::reborrow).map_err(E::reborrow) 106 | } 107 | } 108 | 109 | impl<'a, T, E> IntoOwned<'a> for Result 110 | where 111 | T: IntoOwned<'a>, 112 | E: IntoOwned<'a>, 113 | { 114 | type Owned = Result; 115 | 116 | #[inline] 117 | fn into_owned(self) -> Self::Owned { 118 | self.map(T::into_owned).map_err(E::into_owned) 119 | } 120 | 121 | #[inline] 122 | fn clone_onto(self, other: &mut Self::Owned) { 123 | match (self, other) { 124 | (Ok(item), Ok(target)) => T::clone_onto(item, target), 125 | (Err(item), Err(target)) => E::clone_onto(item, target), 126 | (Ok(item), target) => *target = Ok(T::into_owned(item)), 127 | (Err(item), target) => *target = Err(E::into_owned(item)), 128 | } 129 | } 130 | 131 | #[inline] 132 | fn borrow_as(owned: &'a Self::Owned) -> Self { 133 | owned.as_ref().map(T::borrow_as).map_err(E::borrow_as) 134 | } 135 | } 136 | 137 | impl Push> for ResultRegion 138 | where 139 | TC: Region + Push, 140 | EC: Region + Push, 141 | { 142 | #[inline] 143 | fn push(&mut self, item: Result) -> as Region>::Index { 144 | match item { 145 | Ok(t) => Ok(self.oks.push(t)), 146 | Err(e) => Err(self.errs.push(e)), 147 | } 148 | } 149 | } 150 | 151 | impl<'a, T: 'a, TC, E: 'a, EC> Push<&'a Result> for ResultRegion 152 | where 153 | TC: Region + Push<&'a T>, 154 | EC: Region + Push<&'a E>, 155 | { 156 | #[inline] 157 | fn push(&mut self, item: &'a Result) -> as Region>::Index { 158 | match item { 159 | Ok(t) => Ok(self.oks.push(t)), 160 | Err(e) => Err(self.errs.push(e)), 161 | } 162 | } 163 | } 164 | 165 | impl ReserveItems> for ResultRegion 166 | where 167 | TC: Region + ReserveItems, 168 | EC: Region + ReserveItems, 169 | { 170 | #[inline] 171 | fn reserve_items(&mut self, items: I) 172 | where 173 | I: Iterator> + Clone, 174 | { 175 | self.oks.reserve_items(items.clone().filter_map(|r| r.ok())); 176 | self.errs.reserve_items(items.filter_map(|r| r.err())); 177 | } 178 | } 179 | 180 | impl<'a, T: 'a, TC, E: 'a, EC> ReserveItems<&'a Result> for ResultRegion 181 | where 182 | TC: Region + ReserveItems<&'a T>, 183 | EC: Region + ReserveItems<&'a E>, 184 | { 185 | #[inline] 186 | fn reserve_items(&mut self, items: I) 187 | where 188 | I: Iterator> + Clone, 189 | { 190 | self.oks 191 | .reserve_items(items.clone().filter_map(|r| r.as_ref().ok())); 192 | self.errs 193 | .reserve_items(items.filter_map(|r| r.as_ref().err())); 194 | } 195 | } 196 | 197 | #[cfg(test)] 198 | mod tests { 199 | use crate::{MirrorRegion, OwnedRegion, Region, ReserveItems}; 200 | 201 | use super::*; 202 | 203 | #[test] 204 | fn test_reserve() { 205 | let mut r = , MirrorRegion>>::default(); 206 | ReserveItems::reserve_items(&mut r, [Ok(0), Err(1)].iter()); 207 | 208 | ReserveItems::reserve_items(&mut r, [Ok(0), Err(1)].into_iter()); 209 | } 210 | 211 | #[test] 212 | fn test_heap_size() { 213 | let mut r = , OwnedRegion>>::default(); 214 | ReserveItems::reserve_items(&mut r, [Ok([1; 0]), Err([1; 1])].iter()); 215 | let mut cap = 0; 216 | r.heap_size(|_, ca| { 217 | cap += ca; 218 | }); 219 | assert!(cap > 0); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/impls/slice.rs: -------------------------------------------------------------------------------- 1 | //! A region that stores slices. 2 | 3 | use std::cmp::Ordering; 4 | use std::fmt::{Debug, Formatter}; 5 | use std::ops::{Deref, Range}; 6 | 7 | #[cfg(feature = "serde")] 8 | use serde::{Deserialize, Serialize}; 9 | 10 | use crate::impls::index::IndexContainer; 11 | use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; 12 | 13 | impl RegionPreference for Vec { 14 | type Owned = Vec; 15 | type Region = SliceRegion; 16 | } 17 | 18 | impl RegionPreference for [T] { 19 | type Owned = Vec; 20 | type Region = SliceRegion; 21 | } 22 | 23 | impl RegionPreference for [T; N] { 24 | type Owned = Vec; 25 | type Region = SliceRegion; 26 | } 27 | 28 | /// A container representing slices of data. 29 | /// 30 | /// Reading from this region is more involved than for others, because the data only exists in 31 | /// an indexable representation. The read item is a [`ReadSlice`], which can be iterated or indexed. 32 | /// However, it is not possible to represent the data as a slice, simply because the slice doesn't 33 | /// exist. 34 | /// 35 | /// # Examples 36 | /// 37 | /// We fill some data into a slice region and use the [`ReadSlice`] to extract it later. 38 | /// ``` 39 | /// use flatcontainer::{RegionPreference, Push, Region, SliceRegion}; 40 | /// let mut r = ::Region>>::default(); 41 | /// 42 | /// let panagram_en = "The quick fox jumps over the lazy dog" 43 | /// .split(" ") 44 | /// .collect::>(); 45 | /// let panagram_de = "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich" 46 | /// .split(" ") 47 | /// .collect::>(); 48 | /// 49 | /// let en_index = r.push(&panagram_en); 50 | /// let de_index = r.push(&panagram_de); 51 | /// 52 | /// assert!(panagram_de.into_iter().eq(r.index(de_index))); 53 | /// assert!(panagram_en.into_iter().eq(r.index(en_index))); 54 | /// 55 | /// assert_eq!(r.index(de_index).get(2), "jagen"); 56 | /// ``` 57 | #[derive(Debug)] 58 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 59 | pub struct SliceRegion::Index>> { 60 | /// Container of slices. 61 | slices: O, 62 | /// Inner region. 63 | inner: R, 64 | } 65 | 66 | impl Clone for SliceRegion 67 | where 68 | R: Region + Clone, 69 | O: Clone, 70 | { 71 | fn clone(&self) -> Self { 72 | Self { 73 | slices: self.slices.clone(), 74 | inner: self.inner.clone(), 75 | } 76 | } 77 | 78 | fn clone_from(&mut self, source: &Self) { 79 | self.slices.clone_from(&source.slices); 80 | self.inner.clone_from(&source.inner); 81 | } 82 | } 83 | 84 | impl> Region for SliceRegion { 85 | type Owned = Vec; 86 | type ReadItem<'a> = ReadSlice<'a, R, O> where Self: 'a; 87 | type Index = (usize, usize); 88 | 89 | #[inline] 90 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 91 | where 92 | Self: 'a, 93 | { 94 | Self { 95 | slices: O::default(), 96 | inner: R::merge_regions(regions.map(|r| &r.inner)), 97 | } 98 | } 99 | 100 | #[inline] 101 | fn index(&self, (start, end): Self::Index) -> Self::ReadItem<'_> { 102 | ReadSlice(Ok(ReadSliceInner { 103 | region: self, 104 | start, 105 | end, 106 | })) 107 | } 108 | 109 | #[inline] 110 | fn reserve_regions<'a, I>(&mut self, regions: I) 111 | where 112 | Self: 'a, 113 | I: Iterator + Clone, 114 | { 115 | self.slices 116 | .reserve(regions.clone().map(|r| r.slices.len()).sum()); 117 | self.inner.reserve_regions(regions.map(|r| &r.inner)); 118 | } 119 | 120 | #[inline] 121 | fn clear(&mut self) { 122 | self.slices.clear(); 123 | self.inner.clear(); 124 | } 125 | 126 | #[inline] 127 | fn heap_size(&self, mut callback: F) { 128 | self.slices.heap_size(&mut callback); 129 | self.inner.heap_size(callback); 130 | } 131 | 132 | #[inline] 133 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 134 | where 135 | Self: 'a, 136 | { 137 | item 138 | } 139 | } 140 | 141 | impl> Default for SliceRegion { 142 | #[inline] 143 | fn default() -> Self { 144 | Self { 145 | slices: O::default(), 146 | inner: R::default(), 147 | } 148 | } 149 | } 150 | 151 | /// A helper to read data out of a slice region. 152 | pub struct ReadSlice<'a, R: Region, O: IndexContainer = Vec<::Index>>( 153 | Result, &'a [R::Owned]>, 154 | ); 155 | 156 | impl> ReadSlice<'_, R, O> { 157 | /// Read the n-th item from the underlying region. 158 | /// 159 | /// # Panics 160 | /// 161 | /// Panics if the index is out of bounds, i.e., it is larger than the 162 | /// length of this slice representation. 163 | #[inline] 164 | #[must_use] 165 | pub fn get(&self, index: usize) -> R::ReadItem<'_> { 166 | match &self.0 { 167 | Ok(inner) => inner.get(index), 168 | Err(slice) => IntoOwned::borrow_as(&slice[index]), 169 | } 170 | } 171 | 172 | /// The number of elements in this slice. 173 | #[must_use] 174 | pub fn len(&self) -> usize { 175 | match self.0 { 176 | Ok(inner) => inner.len(), 177 | Err(slice) => slice.len(), 178 | } 179 | } 180 | 181 | /// Returns `true` if the slice is empty. 182 | #[must_use] 183 | pub fn is_empty(&self) -> bool { 184 | match self.0 { 185 | Ok(inner) => inner.is_empty(), 186 | Err(slice) => slice.is_empty(), 187 | } 188 | } 189 | 190 | /// Returns an iterator over all contained items. 191 | #[must_use] 192 | pub fn iter(&self) -> ::IntoIter { 193 | self.into_iter() 194 | } 195 | } 196 | 197 | impl> PartialEq for ReadSlice<'_, R, O> 198 | where 199 | for<'a> R::ReadItem<'a>: PartialEq, 200 | { 201 | fn eq(&self, other: &Self) -> bool { 202 | self.iter().eq(*other) 203 | } 204 | } 205 | 206 | impl> Eq for ReadSlice<'_, R, O> where 207 | for<'a> R::ReadItem<'a>: Eq 208 | { 209 | } 210 | 211 | impl> PartialOrd for ReadSlice<'_, R, O> 212 | where 213 | for<'a> R::ReadItem<'a>: PartialOrd, 214 | { 215 | fn partial_cmp(&self, other: &Self) -> Option { 216 | self.iter().partial_cmp(*other) 217 | } 218 | } 219 | 220 | impl> Ord for ReadSlice<'_, R, O> 221 | where 222 | for<'a> R::ReadItem<'a>: Ord, 223 | { 224 | fn cmp(&self, other: &Self) -> Ordering { 225 | self.iter().cmp(*other) 226 | } 227 | } 228 | 229 | struct ReadSliceInner<'a, R: Region, O: IndexContainer = Vec<::Index>> { 230 | region: &'a SliceRegion, 231 | start: usize, 232 | end: usize, 233 | } 234 | 235 | impl> ReadSliceInner<'_, R, O> { 236 | /// Read the n-th item from the underlying region. 237 | /// 238 | /// # Panics 239 | /// 240 | /// Panics if the index is out of bounds, i.e., it is larger than the 241 | /// length of this slice representation. 242 | #[inline] 243 | #[must_use] 244 | pub fn get(&self, index: usize) -> R::ReadItem<'_> { 245 | assert!( 246 | index <= self.end - self.start, 247 | "Index {index} out of bounds {} ({}..{})", 248 | self.end - self.start, 249 | self.start, 250 | self.end 251 | ); 252 | self.region 253 | .inner 254 | .index(self.region.slices.index(self.start + index)) 255 | } 256 | 257 | /// The number of elements in this slice. 258 | #[must_use] 259 | pub fn len(&self) -> usize { 260 | self.end - self.start 261 | } 262 | 263 | /// Returns `true` if the slice is empty. 264 | #[must_use] 265 | pub fn is_empty(&self) -> bool { 266 | self.start == self.end 267 | } 268 | } 269 | 270 | impl> Debug for ReadSlice<'_, R, O> 271 | where 272 | for<'a> R::ReadItem<'a>: Debug, 273 | { 274 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 275 | f.debug_list().entries(self.iter()).finish() 276 | } 277 | } 278 | 279 | impl> Clone for ReadSlice<'_, R, O> { 280 | #[inline] 281 | fn clone(&self) -> Self { 282 | *self 283 | } 284 | } 285 | 286 | impl> Clone for ReadSliceInner<'_, R, O> { 287 | #[inline] 288 | fn clone(&self) -> Self { 289 | *self 290 | } 291 | } 292 | 293 | impl> Copy for ReadSlice<'_, R, O> {} 294 | impl> Copy for ReadSliceInner<'_, R, O> {} 295 | 296 | impl<'a, R, O> IntoOwned<'a> for ReadSlice<'a, R, O> 297 | where 298 | R: Region, 299 | O: IndexContainer, 300 | { 301 | type Owned = Vec; 302 | 303 | #[inline] 304 | fn into_owned(self) -> Self::Owned { 305 | self.iter().map(IntoOwned::into_owned).collect() 306 | } 307 | 308 | #[inline] 309 | fn clone_onto(self, other: &mut Self::Owned) { 310 | let r = std::cmp::min(self.len(), other.len()); 311 | for (item, target) in self.iter().zip(other.iter_mut()) { 312 | item.clone_onto(target); 313 | } 314 | other.extend(self.iter().skip(r).map(IntoOwned::into_owned)); 315 | other.truncate(self.len()); 316 | } 317 | 318 | #[inline] 319 | fn borrow_as(owned: &'a Self::Owned) -> Self { 320 | Self(Err(owned.as_slice())) 321 | } 322 | } 323 | 324 | impl<'a, R: Region, O: IndexContainer> IntoIterator for ReadSlice<'a, R, O> { 325 | type Item = R::ReadItem<'a>; 326 | type IntoIter = ReadSliceIter<'a, R, O>; 327 | 328 | #[inline] 329 | fn into_iter(self) -> Self::IntoIter { 330 | match self.0 { 331 | Ok(inner) => { 332 | ReadSliceIter(Ok(ReadSliceIterInner(inner.region, inner.start..inner.end))) 333 | } 334 | Err(slice) => ReadSliceIter(Err(slice.iter())), 335 | } 336 | } 337 | } 338 | 339 | /// An iterator over the items read from a slice region. 340 | #[derive(Debug)] 341 | pub struct ReadSliceIter<'a, C: Region, O: IndexContainer>( 342 | Result, std::slice::Iter<'a, C::Owned>>, 343 | ); 344 | 345 | impl<'a, C: Region, O: IndexContainer> Clone for ReadSliceIter<'a, C, O> { 346 | #[inline] 347 | fn clone(&self) -> Self { 348 | Self(self.0.clone()) 349 | } 350 | } 351 | 352 | /// An iterator over the items read from a slice region. 353 | #[derive(Debug)] 354 | pub struct ReadSliceIterInner<'a, C: Region, O: IndexContainer>( 355 | &'a SliceRegion, 356 | Range, 357 | ); 358 | 359 | impl<'a, C: Region, O: IndexContainer> Clone for ReadSliceIterInner<'a, C, O> { 360 | #[inline] 361 | fn clone(&self) -> Self { 362 | Self(self.0, self.1.clone()) 363 | } 364 | } 365 | 366 | impl<'a, C: Region, O: IndexContainer> Iterator for ReadSliceIter<'a, C, O> { 367 | type Item = C::ReadItem<'a>; 368 | 369 | #[inline] 370 | fn next(&mut self) -> Option { 371 | match &mut self.0 { 372 | Ok(inner) => inner.next(), 373 | Err(iter) => iter.next().map(IntoOwned::borrow_as), 374 | } 375 | } 376 | } 377 | 378 | impl<'a, R, O> ExactSizeIterator for ReadSliceIter<'a, R, O> 379 | where 380 | R: Region, 381 | O: IndexContainer, 382 | std::slice::Iter<'a, R::Owned>: ExactSizeIterator, 383 | ReadSliceIterInner<'a, R, O>: ExactSizeIterator, 384 | { 385 | } 386 | 387 | impl<'a, C: Region, O: IndexContainer> Iterator for ReadSliceIterInner<'a, C, O> { 388 | type Item = C::ReadItem<'a>; 389 | 390 | #[inline] 391 | fn next(&mut self) -> Option { 392 | self.1 393 | .next() 394 | .map(|idx| self.0.inner.index(self.0.slices.index(idx))) 395 | } 396 | } 397 | 398 | impl<'a, R, O> ExactSizeIterator for ReadSliceIterInner<'a, R, O> 399 | where 400 | R: Region, 401 | O: IndexContainer, 402 | Range: ExactSizeIterator, 403 | { 404 | } 405 | 406 | impl<'a, C, T, O> Push<&'a [T]> for SliceRegion 407 | where 408 | C: Region + Push<&'a T>, 409 | O: IndexContainer, 410 | { 411 | #[inline] 412 | fn push(&mut self, item: &'a [T]) -> as Region>::Index { 413 | let start = self.slices.len(); 414 | self.slices.extend(item.iter().map(|t| self.inner.push(t))); 415 | (start, self.slices.len()) 416 | } 417 | } 418 | 419 | impl<'a, T, R, O> ReserveItems<&'a [T]> for SliceRegion 420 | where 421 | R: Region + ReserveItems<&'a T>, 422 | O: IndexContainer, 423 | { 424 | #[inline] 425 | fn reserve_items(&mut self, items: I) 426 | where 427 | I: Iterator + Clone, 428 | { 429 | self.slices.reserve(items.clone().map(<[T]>::len).sum()); 430 | self.inner.reserve_items(items.flatten()); 431 | } 432 | } 433 | 434 | impl Push> for SliceRegion 435 | where 436 | C: Region + Push, 437 | O: IndexContainer, 438 | { 439 | #[inline] 440 | fn push(&mut self, item: Vec) -> as Region>::Index { 441 | let start = self.slices.len(); 442 | self.slices 443 | .extend(item.into_iter().map(|t| self.inner.push(t))); 444 | (start, self.slices.len()) 445 | } 446 | } 447 | 448 | impl Push<&Vec> for SliceRegion 449 | where 450 | for<'a> C: Region + Push<&'a T>, 451 | O: IndexContainer, 452 | { 453 | #[inline] 454 | fn push(&mut self, item: &Vec) -> as Region>::Index { 455 | self.push(item.as_slice()) 456 | } 457 | } 458 | 459 | impl<'a, C, T, O> Push<&&'a Vec> for SliceRegion 460 | where 461 | C: Region + Push<&'a T>, 462 | O: IndexContainer, 463 | { 464 | #[inline] 465 | fn push(&mut self, item: &&'a Vec) -> as Region>::Index { 466 | self.push(item.as_slice()) 467 | } 468 | } 469 | 470 | impl<'a, T, R, O> ReserveItems<&'a Vec> for SliceRegion 471 | where 472 | for<'b> R: Region + ReserveItems<&'b T>, 473 | O: IndexContainer, 474 | { 475 | #[inline] 476 | fn reserve_items(&mut self, items: I) 477 | where 478 | I: Iterator> + Clone, 479 | { 480 | self.reserve_items(items.map(Deref::deref)); 481 | } 482 | } 483 | 484 | impl<'a, C, O> Push> for SliceRegion 485 | where 486 | C: Region + Push<::ReadItem<'a>>, 487 | O: IndexContainer, 488 | { 489 | #[inline] 490 | fn push(&mut self, item: ReadSlice<'a, C, O>) -> as Region>::Index { 491 | match item.0 { 492 | Ok(inner) => self.push(inner), 493 | Err(slice) => { 494 | let start_len = self.slices.len(); 495 | for item in slice.iter().map(IntoOwned::borrow_as) { 496 | let index = self.inner.push(item); 497 | self.slices.push(index); 498 | } 499 | (start_len, self.slices.len()) 500 | } 501 | } 502 | } 503 | } 504 | 505 | impl<'a, C, O> Push> for SliceRegion 506 | where 507 | C: Region + Push<::ReadItem<'a>>, 508 | O: IndexContainer, 509 | { 510 | #[inline] 511 | fn push(&mut self, item: ReadSliceInner<'a, C, O>) -> as Region>::Index { 512 | let ReadSliceInner { region, start, end } = item; 513 | let start_len = self.slices.len(); 514 | for index in start..end { 515 | let index = region.slices.index(index); 516 | let index = self.inner.push(region.inner.index(index)); 517 | self.slices.push(index); 518 | } 519 | (start_len, self.slices.len()) 520 | } 521 | } 522 | 523 | impl Push<[T; N]> for SliceRegion 524 | where 525 | for<'a> R: Region + Push<&'a T>, 526 | O: IndexContainer, 527 | { 528 | #[inline] 529 | fn push(&mut self, item: [T; N]) -> as Region>::Index { 530 | self.push(item.as_slice()) 531 | } 532 | } 533 | 534 | impl<'a, T, R, O, const N: usize> Push<&'a [T; N]> for SliceRegion 535 | where 536 | R: Region + Push<&'a T>, 537 | O: IndexContainer, 538 | { 539 | #[inline] 540 | fn push(&mut self, item: &'a [T; N]) -> as Region>::Index { 541 | self.push(item.as_slice()) 542 | } 543 | } 544 | 545 | impl<'a, T, R, O, const N: usize> Push<&&'a [T; N]> for SliceRegion 546 | where 547 | R: Region + Push<&'a T>, 548 | O: IndexContainer, 549 | { 550 | #[inline] 551 | fn push(&mut self, item: &&'a [T; N]) -> as Region>::Index { 552 | self.push(item.as_slice()) 553 | } 554 | } 555 | 556 | impl<'a, T, R, O, const N: usize> ReserveItems<&'a [T; N]> for SliceRegion 557 | where 558 | R: Region + ReserveItems<&'a T>, 559 | O: IndexContainer, 560 | { 561 | fn reserve_items(&mut self, items: I) 562 | where 563 | I: Iterator + Clone, 564 | { 565 | self.reserve_items(items.map(<[T; N]>::as_slice)); 566 | } 567 | } 568 | 569 | impl<'a, R, O> ReserveItems> for SliceRegion 570 | where 571 | R: Region + ReserveItems<::ReadItem<'a>> + 'a, 572 | O: IndexContainer, 573 | { 574 | fn reserve_items(&mut self, items: I) 575 | where 576 | I: Iterator> + Clone, 577 | { 578 | self.slices 579 | .reserve(items.clone().map(|read_slice| read_slice.len()).sum()); 580 | self.inner.reserve_items(items.flatten()); 581 | } 582 | } 583 | 584 | #[cfg(test)] 585 | mod tests { 586 | use super::*; 587 | use crate::{MirrorRegion, Push, Region}; 588 | 589 | #[test] 590 | fn read_slice() { 591 | let s = [1, 2, 3, 4]; 592 | let mut r = >>::default(); 593 | 594 | let index = r.push(s); 595 | 596 | assert!(s.iter().copied().eq(r.index(index).iter())); 597 | 598 | let index = r.push(s); 599 | let slice = r.index(index); 600 | assert_eq!(s.len(), slice.len()); 601 | assert!(!slice.is_empty()); 602 | assert_eq!(s.get(0), Some(&1)); 603 | assert_eq!(s.get(1), Some(&2)); 604 | assert_eq!(s.get(2), Some(&3)); 605 | assert_eq!(s.get(3), Some(&4)); 606 | 607 | let index = <_ as Push<[u8; 0]>>::push(&mut r, []); 608 | let slice = r.index(index); 609 | assert_eq!(0, slice.len()); 610 | assert!(slice.is_empty()); 611 | } 612 | 613 | #[test] 614 | #[should_panic] 615 | fn test_get_out_of_bounds() { 616 | let mut r = >>::default(); 617 | let index = r.push([1; 4]); 618 | 619 | // Index 4 is out of bounds and expected to panic. 620 | let _ = r.index(index).get(4); 621 | } 622 | 623 | #[test] 624 | fn test_read_slice_debug() { 625 | let mut r = >>::default(); 626 | let index = r.push([1; 4]); 627 | 628 | assert_eq!("[1, 1, 1, 1]", format!("{:?}", r.index(index))); 629 | } 630 | 631 | #[test] 632 | fn test_read_slice_clone() { 633 | let mut r = >>::default(); 634 | let index = r.push([1; 4]); 635 | 636 | assert_eq!("[1, 1, 1, 1]", format!("{:?}", r.index(index).clone())); 637 | } 638 | 639 | #[test] 640 | fn test_read_slice_eq() { 641 | let mut r = >>::default(); 642 | let index = r.push([1; 4]); 643 | 644 | assert_eq!( 645 | as IntoOwned>::borrow_as(&vec![1; 4]), 646 | r.index(index) 647 | ); 648 | assert_ne!( 649 | as IntoOwned>::borrow_as(&vec![0; 4]), 650 | r.index(index) 651 | ); 652 | assert_ne!( 653 | as IntoOwned>::borrow_as(&vec![1; 5]), 654 | r.index(index) 655 | ); 656 | } 657 | 658 | #[test] 659 | fn test_read_slice_cmp() { 660 | let mut r = >>::default(); 661 | let index = r.push([1; 4]); 662 | 663 | assert_eq!( 664 | Ordering::Less, 665 | as IntoOwned>::borrow_as(&vec![0; 4]).cmp(&r.index(index)) 666 | ); 667 | assert_eq!( 668 | Ordering::Equal, 669 | as IntoOwned>::borrow_as(&vec![1; 4]).cmp(&r.index(index)) 670 | ); 671 | assert_eq!( 672 | Ordering::Greater, 673 | as IntoOwned>::borrow_as(&vec![2; 4]).cmp(&r.index(index)) 674 | ); 675 | 676 | assert_eq!( 677 | Ordering::Less, 678 | as IntoOwned>::borrow_as(&vec![1; 3]).cmp(&r.index(index)) 679 | ); 680 | assert_eq!( 681 | Ordering::Equal, 682 | as IntoOwned>::borrow_as(&vec![1; 4]).cmp(&r.index(index)) 683 | ); 684 | assert_eq!( 685 | Ordering::Greater, 686 | as IntoOwned>::borrow_as(&vec![1; 5]).cmp(&r.index(index)) 687 | ); 688 | } 689 | 690 | #[test] 691 | fn test_reserve_ref_slice() { 692 | let mut r = >>::default(); 693 | r.reserve_items(std::iter::once([1; 4].as_slice())); 694 | let mut cap = 0; 695 | r.heap_size(|_, ca| { 696 | cap += ca; 697 | }); 698 | assert!(cap > 0); 699 | } 700 | 701 | #[test] 702 | fn test_reserve_ref_vec() { 703 | let mut r = >>::default(); 704 | r.reserve_items(std::iter::once(&vec![1; 4])); 705 | let mut cap = 0; 706 | r.heap_size(|_, ca| { 707 | cap += ca; 708 | }); 709 | assert!(cap > 0); 710 | } 711 | 712 | #[test] 713 | fn test_reserve_ref_array() { 714 | let mut r = >>::default(); 715 | r.reserve_items(std::iter::once(&[1; 4])); 716 | let mut cap = 0; 717 | r.heap_size(|_, ca| { 718 | cap += ca; 719 | }); 720 | assert!(cap > 0); 721 | } 722 | } 723 | -------------------------------------------------------------------------------- /src/impls/slice_owned.rs: -------------------------------------------------------------------------------- 1 | //! A region that stores slices of copy types. 2 | 3 | use std::marker::PhantomData; 4 | 5 | #[cfg(feature = "serde")] 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::impls::storage::{PushStorage, Storage}; 9 | use crate::{Push, PushIter, Region, ReserveItems}; 10 | 11 | /// A container for owned types. 12 | /// 13 | /// The container can absorb any type, and stores an owned version of the type, similarly to what 14 | /// vectors do. We recommend using this container for copy types, but there is no restriction in 15 | /// the implementation, and in fact it can correctly store owned values, although any data owned 16 | /// by `T` is regular heap-allocated data, and not contained in regions. 17 | /// 18 | /// # Examples 19 | /// 20 | /// ``` 21 | /// use flatcontainer::{Push, OwnedRegion, Region}; 22 | /// let mut r = >::default(); 23 | /// 24 | /// let panagram_en = "The quick fox jumps over the lazy dog"; 25 | /// let panagram_de = "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich"; 26 | /// 27 | /// let en_index = r.push(panagram_en.as_bytes()); 28 | /// let de_index = r.push(panagram_de.as_bytes()); 29 | /// 30 | /// assert_eq!(panagram_de.as_bytes(), r.index(de_index)); 31 | /// assert_eq!(panagram_en.as_bytes(), r.index(en_index)); 32 | /// ``` 33 | #[derive(Debug)] 34 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 35 | pub struct OwnedRegion> { 36 | slices: S, 37 | _marker: PhantomData, 38 | } 39 | 40 | impl Clone for OwnedRegion { 41 | fn clone(&self) -> Self { 42 | Self { 43 | slices: self.slices.clone(), 44 | _marker: PhantomData, 45 | } 46 | } 47 | 48 | fn clone_from(&mut self, source: &Self) { 49 | self.slices.clone_from(&source.slices); 50 | } 51 | } 52 | 53 | impl Region for OwnedRegion 54 | where 55 | [T]: ToOwned, 56 | S: Storage + std::ops::Index, Output = [T]>, 57 | { 58 | type Owned = <[T] as ToOwned>::Owned; 59 | type ReadItem<'a> = &'a [T] where Self: 'a; 60 | type Index = (usize, usize); 61 | 62 | #[inline] 63 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 64 | where 65 | Self: 'a, 66 | { 67 | Self { 68 | slices: S::merge_regions(regions.map(|r| &r.slices)), 69 | _marker: PhantomData, 70 | } 71 | } 72 | 73 | #[inline] 74 | fn index(&self, (start, end): Self::Index) -> Self::ReadItem<'_> { 75 | &self.slices[start..end] 76 | } 77 | 78 | #[inline] 79 | fn reserve_regions<'a, I>(&mut self, regions: I) 80 | where 81 | Self: 'a, 82 | I: Iterator + Clone, 83 | { 84 | self.slices.reserve_regions(regions.map(|r| &r.slices)); 85 | } 86 | 87 | #[inline] 88 | fn clear(&mut self) { 89 | self.slices.clear(); 90 | } 91 | 92 | #[inline] 93 | fn heap_size(&self, callback: F) { 94 | self.slices.heap_size(callback); 95 | } 96 | 97 | #[inline] 98 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 99 | where 100 | Self: 'a, 101 | { 102 | item 103 | } 104 | } 105 | 106 | impl> Default for OwnedRegion { 107 | #[inline] 108 | fn default() -> Self { 109 | Self { 110 | slices: S::default(), 111 | _marker: PhantomData, 112 | } 113 | } 114 | } 115 | 116 | impl Push<[T; N]> for OwnedRegion 117 | where 118 | [T]: ToOwned, 119 | S: Storage 120 | + for<'a> PushStorage> 121 | + std::ops::Index, Output = [T]>, 122 | { 123 | #[inline] 124 | fn push(&mut self, item: [T; N]) -> as Region>::Index { 125 | let start = self.slices.len(); 126 | self.slices.push_storage(PushIter(item)); 127 | (start, self.slices.len()) 128 | } 129 | } 130 | 131 | impl Push<&[T; N]> for OwnedRegion 132 | where 133 | T: Clone, 134 | S: Storage 135 | + for<'a> PushStorage<&'a [T]> 136 | + std::ops::Index, Output = [T]>, 137 | { 138 | #[inline] 139 | fn push(&mut self, item: &[T; N]) -> as Region>::Index { 140 | self.push(item.as_slice()) 141 | } 142 | } 143 | 144 | impl Push<&&[T; N]> for OwnedRegion 145 | where 146 | T: Clone, 147 | S: Storage 148 | + for<'a> PushStorage<&'a [T]> 149 | + std::ops::Index, Output = [T]>, 150 | { 151 | #[inline] 152 | fn push(&mut self, item: &&[T; N]) -> as Region>::Index { 153 | self.push(*item) 154 | } 155 | } 156 | 157 | impl<'b, T, S, const N: usize> ReserveItems<&'b [T; N]> for OwnedRegion 158 | where 159 | T: Clone, 160 | S: Storage + std::ops::Index, Output = [T]>, 161 | { 162 | #[inline] 163 | fn reserve_items(&mut self, items: I) 164 | where 165 | I: Iterator + Clone, 166 | { 167 | self.slices.reserve(items.map(|i| i.len()).sum()); 168 | } 169 | } 170 | 171 | impl Push<&[T]> for OwnedRegion 172 | where 173 | T: Clone, 174 | S: Storage 175 | + for<'a> PushStorage<&'a [T]> 176 | + std::ops::Index, Output = [T]>, 177 | { 178 | #[inline] 179 | fn push(&mut self, item: &[T]) -> as Region>::Index { 180 | let start = self.slices.len(); 181 | self.slices.push_storage(item); 182 | (start, self.slices.len()) 183 | } 184 | } 185 | 186 | impl> Push<&&[T]> for OwnedRegion 187 | where 188 | for<'a> Self: Push<&'a [T]>, 189 | { 190 | #[inline] 191 | fn push(&mut self, item: &&[T]) -> as Region>::Index { 192 | self.push(*item) 193 | } 194 | } 195 | 196 | impl<'b, T, S> ReserveItems<&'b [T]> for OwnedRegion 197 | where 198 | [T]: ToOwned, 199 | S: Storage + std::ops::Index, Output = [T]>, 200 | { 201 | #[inline] 202 | fn reserve_items(&mut self, items: I) 203 | where 204 | I: Iterator + Clone, 205 | { 206 | self.slices.reserve(items.map(<[T]>::len).sum()); 207 | } 208 | } 209 | 210 | impl Push> for OwnedRegion 211 | where 212 | [T]: ToOwned, 213 | S: Storage 214 | + for<'a> PushStorage<&'a mut Vec> 215 | + std::ops::Index, Output = [T]>, 216 | { 217 | #[inline] 218 | fn push(&mut self, mut item: Vec) -> as Region>::Index { 219 | let start = self.slices.len(); 220 | self.slices.push_storage(&mut item); 221 | (start, self.slices.len()) 222 | } 223 | } 224 | 225 | impl Push<&Vec> for OwnedRegion 226 | where 227 | T: Clone, 228 | S: Storage 229 | + for<'a> PushStorage<&'a [T]> 230 | + std::ops::Index, Output = [T]>, 231 | { 232 | #[inline] 233 | fn push(&mut self, item: &Vec) -> as Region>::Index { 234 | self.push(item.as_slice()) 235 | } 236 | } 237 | 238 | impl<'a, T, S> ReserveItems<&'a Vec> for OwnedRegion 239 | where 240 | [T]: ToOwned, 241 | S: Storage + std::ops::Index, Output = [T]>, 242 | { 243 | #[inline] 244 | fn reserve_items(&mut self, items: I) 245 | where 246 | I: Iterator> + Clone, 247 | { 248 | self.reserve_items(items.map(Vec::as_slice)); 249 | } 250 | } 251 | 252 | impl Push> for OwnedRegion 253 | where 254 | I: IntoIterator, 255 | ::IntoIter: ExactSizeIterator, 256 | T: Clone, 257 | S: Storage 258 | + PushStorage> 259 | + std::ops::Index, Output = [T]>, 260 | { 261 | #[inline] 262 | fn push(&mut self, item: PushIter) -> as Region>::Index { 263 | let start = self.slices.len(); 264 | self.slices.push_storage(item); 265 | (start, self.slices.len()) 266 | } 267 | } 268 | 269 | impl ReserveItems> for OwnedRegion 270 | where 271 | [T]: ToOwned, 272 | S: Storage + std::ops::Index, Output = [T]>, 273 | J: IntoIterator, 274 | { 275 | #[inline] 276 | fn reserve_items(&mut self, items: I) 277 | where 278 | I: Iterator> + Clone, 279 | { 280 | self.slices 281 | .reserve(items.flat_map(|i| i.0.into_iter()).count()); 282 | } 283 | } 284 | 285 | #[cfg(test)] 286 | mod tests { 287 | use crate::{Push, PushIter, Region, ReserveItems}; 288 | 289 | use super::*; 290 | 291 | #[test] 292 | fn test_copy_array() { 293 | let mut r = >::default(); 294 | r.reserve_items(std::iter::once(&[1; 4])); 295 | let index = r.push([1; 4]); 296 | assert_eq!([1, 1, 1, 1], r.index(index)); 297 | } 298 | 299 | #[test] 300 | fn test_copy_ref_ref_array() { 301 | let mut r = >::default(); 302 | ReserveItems::reserve_items(&mut r, std::iter::once(&[1; 4])); 303 | let index = r.push(&&[1; 4]); 304 | assert_eq!([1, 1, 1, 1], r.index(index)); 305 | } 306 | 307 | #[test] 308 | fn test_copy_vec() { 309 | let mut r = >::default(); 310 | ReserveItems::reserve_items(&mut r, std::iter::once(&vec![1; 4])); 311 | let index = r.push(&vec![1; 4]); 312 | assert_eq!([1, 1, 1, 1], r.index(index)); 313 | let index = r.push(vec![2; 4]); 314 | assert_eq!([2, 2, 2, 2], r.index(index)); 315 | } 316 | 317 | #[test] 318 | fn test_copy_iter() { 319 | let mut r = >::default(); 320 | let iter = [1; 4].into_iter(); 321 | r.reserve_items(std::iter::once(PushIter(iter.clone()))); 322 | let index = r.push(PushIter(iter)); 323 | assert_eq!([1, 1, 1, 1], r.index(index)); 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/impls/storage.rs: -------------------------------------------------------------------------------- 1 | //! Storage abstractions to represent slices of data. 2 | 3 | use crate::PushIter; 4 | 5 | /// Behavior to allocate storage. 6 | /// 7 | /// This trait does not express opinions on how to populate itself and how to extract data. Clients 8 | /// should use the [`PushStorage`] trait to insert data into storage, and appropriate 9 | /// [`Index`](std::ops::Index) bounds to extract data. 10 | pub trait Storage: Default { 11 | /// Allocate storage for at least `capacity` elements. 12 | #[must_use] 13 | fn with_capacity(capacity: usize) -> Self; 14 | 15 | /// Allocate storage large enough to absorb `regions`'s contents. 16 | #[must_use] 17 | #[inline] 18 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 19 | where 20 | Self: 'a, 21 | { 22 | Self::with_capacity(regions.map(Self::len).sum()) 23 | } 24 | 25 | /// Reserve space for `additional` elements. 26 | fn reserve(&mut self, additional: usize); 27 | 28 | /// Reserve space for `regions`. 29 | #[inline] 30 | fn reserve_regions<'a, I>(&mut self, regions: I) 31 | where 32 | Self: 'a, 33 | I: Iterator + Clone, 34 | { 35 | self.reserve(regions.map(Self::len).sum()); 36 | } 37 | 38 | /// Clear all contents, possibly retaining some allocations. 39 | fn clear(&mut self); 40 | 41 | /// Observe the heap size information (size and capacity). 42 | fn heap_size(&self, callback: F); 43 | 44 | /// Returns the number of elements. 45 | #[must_use] 46 | fn len(&self) -> usize; 47 | 48 | /// Returns `true` if empty, i.e., it doesn't contain any elements. 49 | #[must_use] 50 | fn is_empty(&self) -> bool; 51 | } 52 | 53 | impl Storage for Vec { 54 | #[inline] 55 | fn with_capacity(capacity: usize) -> Self { 56 | Vec::with_capacity(capacity) 57 | } 58 | 59 | #[inline] 60 | fn reserve(&mut self, additional: usize) { 61 | Vec::reserve(self, additional); 62 | } 63 | 64 | #[inline] 65 | fn clear(&mut self) { 66 | self.clear(); 67 | } 68 | 69 | #[inline] 70 | fn heap_size(&self, mut callback: F) { 71 | let size_of_t = std::mem::size_of::(); 72 | callback(self.len() * size_of_t, self.capacity() * size_of_t); 73 | } 74 | 75 | #[inline] 76 | #[must_use] 77 | fn len(&self) -> usize { 78 | self.len() 79 | } 80 | 81 | #[inline] 82 | #[must_use] 83 | fn is_empty(&self) -> bool { 84 | self.is_empty() 85 | } 86 | } 87 | 88 | /// Push an item into storage. 89 | pub trait PushStorage { 90 | /// Push an item into storage. 91 | fn push_storage(&mut self, item: T); 92 | } 93 | 94 | impl PushStorage<&mut Vec> for Vec { 95 | #[inline] 96 | fn push_storage(&mut self, item: &mut Vec) { 97 | self.append(item); 98 | } 99 | } 100 | 101 | impl PushStorage<&[T]> for Vec { 102 | #[inline] 103 | fn push_storage(&mut self, item: &[T]) { 104 | self.extend_from_slice(item); 105 | } 106 | } 107 | 108 | impl, T> PushStorage> for Vec { 109 | #[inline] 110 | fn push_storage(&mut self, item: PushIter) { 111 | self.extend(item.0); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/impls/string.rs: -------------------------------------------------------------------------------- 1 | //! A region that stores strings. 2 | 3 | #[cfg(feature = "serde")] 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::impls::slice_owned::OwnedRegion; 7 | use crate::{Push, Region, RegionPreference, ReserveItems}; 8 | 9 | /// A region to store strings and read `&str`. 10 | /// 11 | /// Delegates to a region `R` to store `u8` slices. By default, it uses a [`OwnedRegion`], but a 12 | /// different region can be provided, as long as it absorbs and reads items as `&[u8]`. 13 | /// 14 | /// Note that all implementations of `Push for StringRegion` must only accept valid utf-8 data 15 | /// because the region does not validate the contents when indexing. 16 | /// 17 | /// # Examples 18 | /// 19 | /// We fill some data into a string region and use extract it later. 20 | /// ``` 21 | /// use flatcontainer::{RegionPreference, Push, OwnedRegion, Region, StringRegion}; 22 | /// let mut r = ::default(); 23 | /// 24 | /// let panagram_en = "The quick fox jumps over the lazy dog"; 25 | /// let panagram_de = "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich"; 26 | /// 27 | /// let en_index = r.push(panagram_en); 28 | /// let de_index = r.push(panagram_de); 29 | /// 30 | /// assert_eq!(panagram_de, r.index(de_index)); 31 | /// assert_eq!(panagram_en, r.index(en_index)); 32 | /// ``` 33 | #[derive(Default, Debug)] 34 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 35 | pub struct StringRegion> { 36 | inner: R, 37 | } 38 | 39 | impl Clone for StringRegion { 40 | fn clone(&self) -> Self { 41 | Self { 42 | inner: self.inner.clone(), 43 | } 44 | } 45 | 46 | fn clone_from(&mut self, source: &Self) { 47 | self.inner.clone_from(&source.inner); 48 | } 49 | } 50 | 51 | impl Region for StringRegion 52 | where 53 | for<'a> R: Region = &'a [u8]> + 'a, 54 | { 55 | type Owned = String; 56 | type ReadItem<'a> = &'a str where Self: 'a ; 57 | type Index = R::Index; 58 | 59 | #[inline] 60 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 61 | where 62 | Self: 'a, 63 | { 64 | Self { 65 | inner: R::merge_regions(regions.map(|r| &r.inner)), 66 | } 67 | } 68 | 69 | #[inline] 70 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 71 | // SAFETY: All Push implementations only accept correct utf8 data 72 | unsafe { std::str::from_utf8_unchecked(self.inner.index(index)) } 73 | } 74 | 75 | #[inline] 76 | fn reserve_regions<'a, I>(&mut self, regions: I) 77 | where 78 | Self: 'a, 79 | I: Iterator + Clone, 80 | { 81 | self.inner.reserve_regions(regions.map(|r| &r.inner)); 82 | } 83 | 84 | #[inline] 85 | fn clear(&mut self) { 86 | self.inner.clear(); 87 | } 88 | 89 | #[inline] 90 | fn heap_size(&self, callback: F) { 91 | self.inner.heap_size(callback); 92 | } 93 | 94 | #[inline] 95 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 96 | where 97 | Self: 'a, 98 | { 99 | item 100 | } 101 | } 102 | 103 | impl RegionPreference for String { 104 | type Owned = Self; 105 | type Region = StringRegion; 106 | } 107 | 108 | impl RegionPreference for &str { 109 | type Owned = String; 110 | type Region = StringRegion; 111 | } 112 | 113 | impl Push for StringRegion 114 | where 115 | for<'a> R: Region = &'a [u8]> + Push<&'a [u8]> + 'a, 116 | { 117 | #[inline] 118 | fn push(&mut self, item: String) -> as Region>::Index { 119 | self.push(item.as_str()) 120 | } 121 | } 122 | 123 | impl Push<&String> for StringRegion 124 | where 125 | for<'a> R: Region = &'a [u8]> + Push<&'a [u8]> + 'a, 126 | { 127 | #[inline] 128 | fn push(&mut self, item: &String) -> as Region>::Index { 129 | self.push(item.as_str()) 130 | } 131 | } 132 | 133 | impl<'b, R> ReserveItems<&'b String> for StringRegion 134 | where 135 | for<'a> R: Region = &'a [u8]> + ReserveItems<&'a [u8]> + 'a, 136 | { 137 | #[inline] 138 | fn reserve_items(&mut self, items: I) 139 | where 140 | I: Iterator + Clone, 141 | { 142 | self.reserve_items(items.map(String::as_str)); 143 | } 144 | } 145 | 146 | impl Push<&str> for StringRegion 147 | where 148 | for<'a> R: Region = &'a [u8]> + Push<&'a [u8]> + 'a, 149 | { 150 | #[inline] 151 | fn push(&mut self, item: &str) -> as Region>::Index { 152 | self.inner.push(item.as_bytes()) 153 | } 154 | } 155 | 156 | impl Push<&&str> for StringRegion 157 | where 158 | for<'a> R: Region = &'a [u8]> + Push<&'a [u8]> + 'a, 159 | { 160 | #[inline] 161 | fn push(&mut self, item: &&str) -> as Region>::Index { 162 | self.push(*item) 163 | } 164 | } 165 | 166 | impl<'b, R> ReserveItems<&'b str> for StringRegion 167 | where 168 | for<'a> R: Region = &'a [u8]> + ReserveItems<&'a [u8]> + 'a, 169 | { 170 | #[inline] 171 | fn reserve_items(&mut self, items: I) 172 | where 173 | I: Iterator + Clone, 174 | { 175 | self.inner.reserve_items(items.map(str::as_bytes)); 176 | } 177 | } 178 | 179 | impl<'a, 'b: 'a, R> ReserveItems<&'a &'b str> for StringRegion 180 | where 181 | for<'c> R: Region = &'c [u8]> + ReserveItems<&'c [u8]> + 'c, 182 | { 183 | #[inline] 184 | fn reserve_items(&mut self, items: I) 185 | where 186 | I: Iterator + Clone, 187 | { 188 | self.reserve_items(items.copied()); 189 | } 190 | } 191 | 192 | #[cfg(test)] 193 | mod tests { 194 | use crate::{IntoOwned, Push, Region, ReserveItems, StringRegion}; 195 | 196 | #[test] 197 | fn test_inner() { 198 | let mut r = ::default(); 199 | let index = r.push("abc"); 200 | assert_eq!(r.index(index), "abc"); 201 | } 202 | 203 | #[test] 204 | fn test_reserve_items_str() { 205 | let mut r = ::default(); 206 | r.reserve_items(std::iter::repeat("abc").take(1000)); 207 | 208 | let (mut cap, mut cnt) = (0, 0); 209 | r.heap_size(|_, c| { 210 | cap += c; 211 | cnt += 1; 212 | }); 213 | 214 | assert!(cap > 0); 215 | assert!(cnt > 0); 216 | } 217 | 218 | #[test] 219 | fn test_reserve_items_ref_str() { 220 | let mut r = ::default(); 221 | r.reserve_items(std::iter::repeat(&"abc").take(1000)); 222 | 223 | let (mut cap, mut cnt) = (0, 0); 224 | r.heap_size(|_, c| { 225 | cap += c; 226 | cnt += 1; 227 | }); 228 | 229 | assert!(cap > 0); 230 | assert!(cnt > 0); 231 | } 232 | 233 | #[test] 234 | fn test_reserve_items_string() { 235 | let mut r = ::default(); 236 | r.reserve_items(std::iter::repeat(&"abc".to_owned()).take(1000)); 237 | 238 | let (mut cap, mut cnt) = (0, 0); 239 | r.heap_size(|_, c| { 240 | cap += c; 241 | cnt += 1; 242 | }); 243 | 244 | assert!(cap > 0); 245 | assert!(cnt > 0); 246 | } 247 | 248 | #[test] 249 | fn owned() { 250 | let mut r = ::default(); 251 | 252 | let idx = r.push("abc"); 253 | let reference = r.index(idx); 254 | let owned = reference.into_owned(); 255 | let idx = r.push(owned); 256 | assert_eq!("abc", r.index(idx)); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/impls/tuple.rs: -------------------------------------------------------------------------------- 1 | //! Regions that stores tuples. 2 | 3 | use paste::paste; 4 | #[cfg(feature = "serde")] 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use crate::{IntoOwned, Push, Region, RegionPreference, ReserveItems}; 8 | 9 | /// The macro creates the region implementation for tuples 10 | macro_rules! tuple_flatcontainer { 11 | ($($name:ident)+) => ( 12 | paste! { 13 | impl<$($name: RegionPreference),*> RegionPreference for ($($name,)*) { 14 | type Owned = ($($name::Owned,)*); 15 | type Region = []<$($name::Region,)*>; 16 | } 17 | 18 | /// A region for a tuple. 19 | #[allow(non_snake_case)] 20 | #[derive(Default, Debug)] 21 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 22 | pub struct []<$($name),*> { 23 | $([]: $name),* 24 | } 25 | 26 | #[allow(non_snake_case)] 27 | impl<$($name: Region + Clone),*> Clone for []<$($name),*> 28 | where 29 | $(<$name as Region>::Index: crate::Index),* 30 | { 31 | fn clone(&self) -> Self { 32 | Self { 33 | $([]: self.[].clone(),)* 34 | } 35 | } 36 | 37 | fn clone_from(&mut self, source: &Self) { 38 | $(self.[].clone_from(&source.[]);)* 39 | } 40 | } 41 | 42 | #[allow(non_snake_case)] 43 | impl<$($name: Region),*> Region for []<$($name),*> 44 | where 45 | $(<$name as Region>::Index: crate::Index),* 46 | { 47 | type Owned = ($($name::Owned,)*); 48 | type ReadItem<'a> = ($($name::ReadItem<'a>,)*) where Self: 'a; 49 | 50 | type Index = ($($name::Index,)*); 51 | 52 | #[inline] 53 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 54 | where 55 | Self: 'a, 56 | { 57 | Self { 58 | $([]: $name::merge_regions(regions.clone().map(|r| &r.[]))),* 59 | } 60 | } 61 | 62 | #[inline] fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 63 | let ($($name,)*) = index; 64 | ( 65 | $(self.[].index($name),)* 66 | ) 67 | } 68 | 69 | #[inline(always)] 70 | fn reserve_regions<'a, It>(&mut self, regions: It) 71 | where 72 | Self: 'a, 73 | It: Iterator + Clone, 74 | { 75 | $(self.[].reserve_regions(regions.clone().map(|r| &r.[]));)* 76 | } 77 | 78 | #[inline(always)] 79 | fn clear(&mut self) { 80 | $(self.[].clear();)* 81 | } 82 | 83 | #[inline] 84 | fn heap_size(&self, mut callback: Fn) { 85 | $(self.[].heap_size(&mut callback);)* 86 | } 87 | 88 | #[inline] 89 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> where Self: 'a { 90 | let ($($name,)*) = item; 91 | ( 92 | $($name::reborrow($name),)* 93 | ) 94 | } 95 | } 96 | 97 | #[allow(non_camel_case_types)] 98 | #[allow(non_snake_case)] 99 | impl<$($name, [<$name _C>]: Region ),*> Push<($($name,)*)> for []<$([<$name _C>]),*> 100 | where 101 | $([<$name _C>]: Push<$name>),* 102 | { 103 | #[inline] 104 | fn push(&mut self, item: ($($name,)*)) 105 | -> <[]<$([<$name _C>]),*> as Region>::Index { 106 | let ($($name,)*) = item; 107 | ($(self.[].push($name),)*) 108 | } 109 | } 110 | 111 | #[allow(non_camel_case_types)] 112 | #[allow(non_snake_case)] 113 | impl<'a, $($name, [<$name _C>]),*> Push<&'a ($($name,)*)> for []<$([<$name _C>]),*> 114 | where 115 | $([<$name _C>]: Region + Push<&'a $name>),* 116 | { 117 | #[inline] 118 | fn push(&mut self, item: &'a ($($name,)*)) 119 | -> <[]<$([<$name _C>]),*> as Region>::Index { 120 | let ($($name,)*) = item; 121 | ($(self.[].push($name),)*) 122 | } 123 | } 124 | 125 | #[allow(non_camel_case_types)] 126 | #[allow(non_snake_case)] 127 | impl<'a, $($name),*> IntoOwned<'a> for ($($name,)*) 128 | where 129 | $($name: IntoOwned<'a>),* 130 | { 131 | type Owned = ($($name::Owned,)*); 132 | 133 | #[inline] 134 | fn into_owned(self) -> Self::Owned { 135 | let ($($name,)*) = self; 136 | ( 137 | $($name.into_owned(),)* 138 | ) 139 | } 140 | 141 | #[inline] 142 | fn clone_onto(self, other: &mut Self::Owned) { 143 | let ($($name,)*) = self; 144 | let ($([<$name _other>],)*) = other; 145 | $($name.clone_onto([<$name _other>]);)* 146 | } 147 | 148 | #[inline] 149 | fn borrow_as(owned: &'a Self::Owned) -> Self { 150 | let ($($name,)*) = owned; 151 | ( 152 | $($name::borrow_as($name),)* 153 | ) 154 | } 155 | } 156 | 157 | #[allow(non_camel_case_types)] 158 | #[allow(non_snake_case)] 159 | impl<'a, $($name, [<$name _C>]),*> ReserveItems<&'a ($($name,)*)> for []<$([<$name _C>]),*> 160 | where 161 | $([<$name _C>]: Region + ReserveItems<&'a $name>),* 162 | { 163 | #[inline] 164 | fn reserve_items(&mut self, items: It) 165 | where 166 | It: Iterator + Clone, 167 | { 168 | tuple_flatcontainer!(reserve_items self items $($name)* @ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31); 169 | } 170 | } 171 | 172 | #[allow(non_camel_case_types)] 173 | #[allow(non_snake_case)] 174 | impl<$($name, [<$name _C>]),*> ReserveItems<($($name,)*)> for []<$([<$name _C>]),*> 175 | where 176 | $([<$name _C>]: Region + ReserveItems<$name>),* 177 | { 178 | #[inline] 179 | fn reserve_items(&mut self, items: It) 180 | where 181 | It: Iterator + Clone, 182 | { 183 | tuple_flatcontainer!(reserve_items_owned self items $($name)* @ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31); 184 | } 185 | } 186 | } 187 | ); 188 | (reserve_items $self:ident $items:ident $name0:ident $($name:ident)* @ $num0:tt $($num:tt)*) => { 189 | paste! { 190 | $self.[].reserve_items($items.clone().map(|i| &i.$num0)); 191 | tuple_flatcontainer!(reserve_items $self $items $($name)* @ $($num)*); 192 | } 193 | }; 194 | (reserve_items $self:ident $items:ident @ $($num:tt)*) => {}; 195 | (reserve_items_owned $self:ident $items:ident $name0:ident $($name:ident)* @ $num0:tt $($num:tt)*) => { 196 | paste! { 197 | $self.[].reserve_items($items.clone().map(|i| i.$num0)); 198 | tuple_flatcontainer!(reserve_items_owned $self $items $($name)* @ $($num)*); 199 | } 200 | }; 201 | (reserve_items_owned $self:ident $items:ident @ $($num:tt)*) => {}; 202 | } 203 | 204 | tuple_flatcontainer!(A); 205 | tuple_flatcontainer!(A B); 206 | tuple_flatcontainer!(A B C); 207 | tuple_flatcontainer!(A B C D); 208 | tuple_flatcontainer!(A B C D E); 209 | tuple_flatcontainer!(A B C D E F); 210 | tuple_flatcontainer!(A B C D E F G); 211 | tuple_flatcontainer!(A B C D E F G H); 212 | tuple_flatcontainer!(A B C D E F G H I); 213 | tuple_flatcontainer!(A B C D E F G H I J); 214 | tuple_flatcontainer!(A B C D E F G H I J K); 215 | tuple_flatcontainer!(A B C D E F G H I J K L); 216 | tuple_flatcontainer!(A B C D E F G H I J K L M); 217 | tuple_flatcontainer!(A B C D E F G H I J K L M N); 218 | tuple_flatcontainer!(A B C D E F G H I J K L M N O); 219 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P); 220 | cfg_if::cfg_if! { 221 | if #[cfg(not(feature="serde"))] { 222 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q); 223 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R); 224 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S); 225 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T); 226 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U); 227 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V); 228 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W); 229 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X); 230 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y); 231 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z); 232 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA); 233 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB); 234 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC); 235 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC AD); 236 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC AD AE); 237 | tuple_flatcontainer!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z AA AB AC AD AE AF); 238 | } 239 | } 240 | 241 | #[cfg(test)] 242 | mod tests { 243 | use crate::impls::tuple::TupleABCRegion; 244 | use crate::{FlatStack, MirrorRegion, Push, Region, StringRegion}; 245 | 246 | #[test] 247 | fn test_tuple() { 248 | let t = (1, 2, 3); 249 | let mut r = , MirrorRegion<_>, MirrorRegion<_>>>::default(); 250 | let index = r.push(t); 251 | assert_eq!(t, r.index(index)); 252 | 253 | let index = r.push((&1, &2, &3)); 254 | assert_eq!(t, r.index(index)); 255 | 256 | let index = r.push((&1, 2, 3)); 257 | assert_eq!(t, r.index(index)); 258 | 259 | let index = r.push(&(1, 2, 3)); 260 | assert_eq!(t, r.index(index)); 261 | 262 | let index = r.push(&(1, &2, 3)); 263 | assert_eq!(t, r.index(index)); 264 | } 265 | 266 | #[test] 267 | fn test_nested() { 268 | let t = ("abc", 2, 3); 269 | let mut r = , MirrorRegion<_>>>::default(); 270 | let index = r.push(t); 271 | assert_eq!(t, r.index(index)); 272 | 273 | let index = r.push((&"abc", &2, &3)); 274 | assert_eq!(t, r.index(index)); 275 | 276 | let index = r.push((&"abc", 2, 3)); 277 | assert_eq!(t, r.index(index)); 278 | 279 | let index = r.push(&("abc", 2, 3)); 280 | assert_eq!(t, r.index(index)); 281 | } 282 | 283 | #[test] 284 | fn test_heap_size() { 285 | let t = ("abc", 2, 3); 286 | let mut r = , MirrorRegion<_>>>::default(); 287 | 288 | let _ = r.push(t); 289 | 290 | let (mut size, mut cap, mut cnt) = (0, 0, 0); 291 | r.heap_size(|siz, ca| { 292 | size += siz; 293 | cap += ca; 294 | cnt += 1; 295 | }); 296 | 297 | println!("size {size} cap {cap} cnt {cnt}"); 298 | 299 | assert!(size > 0); 300 | assert!(cap > 0); 301 | assert!(cnt > 0); 302 | } 303 | #[test] 304 | fn test_reserve_items() { 305 | let mut c = FlatStack::default_impl::<(usize, String, Vec)>(); 306 | c.copy((1, format!("Hello"), &["abc"])); 307 | 308 | let mut c2 = FlatStack::default_impl::<(usize, String, Vec)>(); 309 | c2.reserve_items(c.iter()); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/impls/vec.rs: -------------------------------------------------------------------------------- 1 | //! Definitions to use `Vec` as a region. 2 | 3 | use crate::{Push, Region, ReserveItems}; 4 | 5 | impl Region for Vec { 6 | type Owned = T; 7 | type ReadItem<'a> = &'a T where Self: 'a; 8 | type Index = usize; 9 | 10 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 11 | where 12 | Self: 'a, 13 | { 14 | Self::with_capacity(regions.map(Vec::len).sum()) 15 | } 16 | 17 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 18 | &self[index] 19 | } 20 | 21 | fn reserve_regions<'a, I>(&mut self, regions: I) 22 | where 23 | Self: 'a, 24 | I: Iterator + Clone, 25 | { 26 | self.reserve(regions.map(Vec::len).sum()); 27 | } 28 | 29 | fn clear(&mut self) { 30 | self.clear(); 31 | } 32 | 33 | fn heap_size(&self, mut callback: F) { 34 | let size_of_t = std::mem::size_of::(); 35 | callback(self.len() * size_of_t, self.capacity() * size_of_t); 36 | } 37 | 38 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 39 | where 40 | Self: 'a, 41 | { 42 | item 43 | } 44 | } 45 | 46 | impl Push for Vec { 47 | fn push(&mut self, item: T) -> Self::Index { 48 | self.push(item); 49 | self.len() - 1 50 | } 51 | } 52 | 53 | impl Push<&T> for Vec { 54 | fn push(&mut self, item: &T) -> Self::Index { 55 | self.push(item.clone()); 56 | self.len() - 1 57 | } 58 | } 59 | 60 | impl Push<&&T> for Vec { 61 | fn push(&mut self, item: &&T) -> Self::Index { 62 | self.push((*item).clone()); 63 | self.len() - 1 64 | } 65 | } 66 | 67 | impl ReserveItems for Vec { 68 | fn reserve_items(&mut self, items: I) 69 | where 70 | I: Iterator + Clone, 71 | { 72 | self.reserve(items.count()); 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | #[test] 79 | fn vec() { 80 | use crate::{Push, Region, ReserveItems}; 81 | 82 | let mut region = Vec::::new(); 83 | let index = <_ as Push<_>>::push(&mut region, 42); 84 | assert_eq!(region.index(index), &42); 85 | 86 | let mut region = Vec::::new(); 87 | region.push(42); 88 | region.push(43); 89 | region.push(44); 90 | region.reserve_items([1, 2, 3].iter()); 91 | assert_eq!(region.index(0), &42); 92 | assert_eq!(region.index(1), &43); 93 | assert_eq!(region.index(2), &44); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/cow.rs: -------------------------------------------------------------------------------- 1 | //! What follows is an example of a Cow-like type that can be used to switch between a GAT 2 | //! and an owned type at runtime. 3 | 4 | use std::fmt::{Debug, Formatter}; 5 | use std::marker::PhantomData; 6 | 7 | use flatcontainer::{FlatStack, IntoOwned, Push, Region, StringRegion}; 8 | 9 | pub struct GatCow<'a, B> 10 | where 11 | B: IntoOwned<'a>, 12 | { 13 | inner: GatCowInner, 14 | _marker: PhantomData<()>, 15 | } 16 | 17 | enum GatCowInner { 18 | Borrowed(B), 19 | Owned(T), 20 | } 21 | 22 | impl<'a, B> From> for GatCow<'a, B> 23 | where 24 | B: IntoOwned<'a>, 25 | { 26 | fn from(inner: GatCowInner) -> Self { 27 | Self { 28 | inner, 29 | _marker: PhantomData, 30 | } 31 | } 32 | } 33 | 34 | impl<'a, B> GatCow<'a, B> 35 | where 36 | B: IntoOwned<'a> + Copy, 37 | { 38 | pub const fn is_borrowed(&self) -> bool { 39 | use GatCowInner::*; 40 | match &self.inner { 41 | Borrowed(_) => true, 42 | Owned(_) => false, 43 | } 44 | } 45 | 46 | pub const fn is_owned(&self) -> bool { 47 | !self.is_borrowed() 48 | } 49 | 50 | pub fn to_mut(&mut self) -> &mut B::Owned { 51 | match self.inner { 52 | GatCowInner::Borrowed(borrowed) => { 53 | self.inner = GatCowInner::Owned(borrowed.into_owned()); 54 | match &mut self.inner { 55 | GatCowInner::Borrowed(..) => unreachable!(), 56 | GatCowInner::Owned(owned) => owned, 57 | } 58 | } 59 | GatCowInner::Owned(ref mut owned) => owned, 60 | } 61 | } 62 | } 63 | 64 | impl<'a, B> IntoOwned<'a> for GatCow<'a, B> 65 | where 66 | B: IntoOwned<'a> + Copy, 67 | { 68 | type Owned = B::Owned; 69 | 70 | fn into_owned(self) -> B::Owned { 71 | match self.inner { 72 | GatCowInner::Borrowed(b) => b.into_owned(), 73 | GatCowInner::Owned(o) => o, 74 | } 75 | } 76 | 77 | fn clone_onto(self, other: &mut B::Owned) { 78 | match self.inner { 79 | GatCowInner::Borrowed(b) => b.clone_onto(other), 80 | GatCowInner::Owned(o) => *other = o, 81 | } 82 | } 83 | 84 | fn borrow_as(owned: &'a B::Owned) -> Self { 85 | GatCowInner::Borrowed(IntoOwned::borrow_as(owned)).into() 86 | } 87 | } 88 | 89 | impl<'a, B> Debug for GatCow<'a, B> 90 | where 91 | B: IntoOwned<'a> + Debug, 92 | B::Owned: Debug, 93 | { 94 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 95 | match &self.inner { 96 | GatCowInner::Borrowed(b) => b.fmt(f), 97 | GatCowInner::Owned(o) => o.fmt(f), 98 | } 99 | } 100 | } 101 | 102 | #[derive(Default, Debug, Clone)] 103 | struct CowRegion(R); 104 | 105 | impl Region for CowRegion 106 | where 107 | R: Region, 108 | for<'a> R::ReadItem<'a>: Copy, 109 | { 110 | type Owned = ::Owned; 111 | type ReadItem<'a> = GatCow<'a, R::ReadItem<'a>> where Self: 'a; 112 | type Index = R::Index; 113 | 114 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 115 | where 116 | Self: 'a, 117 | { 118 | Self(R::merge_regions(regions.map(|r| &r.0))) 119 | } 120 | 121 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 122 | GatCowInner::Borrowed(self.0.index(index)).into() 123 | } 124 | 125 | fn reserve_regions<'a, I>(&mut self, regions: I) 126 | where 127 | Self: 'a, 128 | I: Iterator + Clone, 129 | { 130 | self.0.reserve_regions(regions.map(|r| &r.0)) 131 | } 132 | 133 | fn clear(&mut self) { 134 | self.0.clear() 135 | } 136 | 137 | fn heap_size(&self, callback: F) { 138 | self.0.heap_size(callback) 139 | } 140 | 141 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 142 | where 143 | Self: 'a, 144 | { 145 | match item.inner { 146 | GatCowInner::Borrowed(b) => GatCowInner::Borrowed(R::reborrow(b)), 147 | GatCowInner::Owned(o) => GatCowInner::Owned(o), 148 | } 149 | .into() 150 | } 151 | } 152 | 153 | impl Push for CowRegion 154 | where 155 | R: Region + Push, 156 | for<'a> R::ReadItem<'a>: Copy, 157 | { 158 | fn push(&mut self, item: D) -> Self::Index { 159 | self.0.push(item) 160 | } 161 | } 162 | 163 | #[test] 164 | fn test_gat_cow() { 165 | let mut c = >>::default(); 166 | c.copy("abc"); 167 | 168 | assert_eq!("abc", c.get(0).into_owned()); 169 | let mut item = c.get(0); 170 | item.to_mut().push_str("def"); 171 | assert_eq!("abcdef", item.into_owned()); 172 | assert_eq!("abc", c.get(0).into_owned()); 173 | } 174 | -------------------------------------------------------------------------------- /tests/person.rs: -------------------------------------------------------------------------------- 1 | //! Test a slightly more struct with nested regions, representing people. 2 | 3 | use flatcontainer::{FlatStack, IntoOwned, Push, Region, RegionPreference, ReserveItems}; 4 | 5 | struct Person { 6 | name: String, 7 | age: u16, 8 | hobbies: Vec, 9 | } 10 | 11 | impl RegionPreference for Person { 12 | type Owned = Self; 13 | type Region = PersonRegion; 14 | } 15 | 16 | #[derive(Default)] 17 | struct PersonRegion { 18 | name_container: ::Region, 19 | age_container: ::Region, 20 | hobbies: as RegionPreference>::Region, 21 | } 22 | 23 | #[derive(Debug, Clone, Copy)] 24 | struct PersonRef<'a> { 25 | name: <::Region as Region>::ReadItem<'a>, 26 | age: <::Region as Region>::ReadItem<'a>, 27 | hobbies: < as RegionPreference>::Region as Region>::ReadItem<'a>, 28 | } 29 | 30 | impl<'a> IntoOwned<'a> for PersonRef<'a> { 31 | type Owned = Person; 32 | 33 | fn into_owned(self) -> Self::Owned { 34 | Person { 35 | name: self.name.into_owned(), 36 | age: self.age, 37 | hobbies: self.hobbies.into_owned(), 38 | } 39 | } 40 | 41 | fn clone_onto(self, other: &mut Self::Owned) { 42 | self.name.clone_onto(&mut other.name); 43 | other.age = self.age; 44 | self.hobbies.clone_onto(&mut other.hobbies); 45 | } 46 | 47 | fn borrow_as(owned: &'a Self::Owned) -> Self { 48 | Self { 49 | name: IntoOwned::borrow_as(&owned.name), 50 | age: owned.age, 51 | hobbies: IntoOwned::borrow_as(&owned.hobbies), 52 | } 53 | } 54 | } 55 | 56 | impl Region for PersonRegion { 57 | type Owned = Person; 58 | type ReadItem<'a> = PersonRef<'a> where Self: 'a; 59 | type Index = ( 60 | <::Region as Region>::Index, 61 | <::Region as Region>::Index, 62 | < as RegionPreference>::Region as Region>::Index, 63 | ); 64 | 65 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 66 | where 67 | Self: 'a, 68 | { 69 | Self { 70 | name_container: ::Region::merge_regions( 71 | regions.clone().map(|r| &r.name_container), 72 | ), 73 | age_container: ::Region::merge_regions( 74 | regions.clone().map(|r| &r.age_container), 75 | ), 76 | hobbies: as RegionPreference>::Region::merge_regions( 77 | regions.map(|r| &r.hobbies), 78 | ), 79 | } 80 | } 81 | 82 | fn index(&self, (name, age, hobbies): Self::Index) -> Self::ReadItem<'_> { 83 | PersonRef { 84 | name: self.name_container.index(name), 85 | age: self.age_container.index(age), 86 | hobbies: self.hobbies.index(hobbies), 87 | } 88 | } 89 | 90 | fn reserve_regions<'a, I>(&mut self, regions: I) 91 | where 92 | Self: 'a, 93 | I: Iterator + Clone, 94 | { 95 | self.name_container 96 | .reserve_regions(regions.clone().map(|r| &r.name_container)); 97 | self.age_container 98 | .reserve_regions(regions.clone().map(|r| &r.age_container)); 99 | self.hobbies 100 | .reserve_regions(regions.clone().map(|r| &r.hobbies)); 101 | } 102 | 103 | fn clear(&mut self) { 104 | self.name_container.clear(); 105 | self.age_container.clear(); 106 | self.hobbies.clear(); 107 | } 108 | 109 | fn heap_size(&self, mut callback: F) { 110 | self.name_container.heap_size(&mut callback); 111 | self.age_container.heap_size(&mut callback); 112 | self.hobbies.heap_size(callback); 113 | } 114 | 115 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 116 | where 117 | Self: 'a, 118 | { 119 | PersonRef { 120 | name: ::Region::reborrow(item.name), 121 | age: ::Region::reborrow(item.age), 122 | hobbies: as RegionPreference>::Region::reborrow(item.hobbies), 123 | } 124 | } 125 | } 126 | 127 | impl Push<&Person> for PersonRegion { 128 | fn push(&mut self, item: &Person) -> ::Index { 129 | let name = self.name_container.push(&item.name); 130 | let age = self.age_container.push(item.age); 131 | let hobbies = self.hobbies.push(&item.hobbies); 132 | (name, age, hobbies) 133 | } 134 | } 135 | 136 | impl<'a> ReserveItems<&'a Person> for PersonRegion { 137 | fn reserve_items(&mut self, items: I) 138 | where 139 | I: Iterator + Clone, 140 | { 141 | self.name_container 142 | .reserve_items(items.clone().map(|i| &i.name)); 143 | self.age_container 144 | .reserve_items(items.clone().map(|i| &i.age)); 145 | self.hobbies.reserve_items(items.map(|i| &i.hobbies)); 146 | } 147 | } 148 | 149 | impl Push> for PersonRegion { 150 | fn push(&mut self, item: PersonRef<'_>) -> ::Index { 151 | let name = self.name_container.push(item.name); 152 | let age = self.age_container.push(item.age); 153 | let hobbies = self.hobbies.push(item.hobbies); 154 | (name, age, hobbies) 155 | } 156 | } 157 | 158 | #[test] 159 | fn test_person() { 160 | let hobbies = ["Computers", "Guitar"]; 161 | let p = Person { 162 | name: "Moritz".to_string(), 163 | age: 123, 164 | hobbies: hobbies.iter().map(ToString::to_string).collect(), 165 | }; 166 | 167 | let mut c = FlatStack::default_impl::(); 168 | c.copy(&p); 169 | let person_ref = c.get(0); 170 | assert_eq!("Moritz", person_ref.name); 171 | assert_eq!(123, person_ref.age); 172 | assert_eq!(2, person_ref.hobbies.len()); 173 | for (copied_hobby, hobby) in person_ref.hobbies.iter().zip(hobbies) { 174 | assert_eq!(copied_hobby, hobby); 175 | } 176 | 177 | let mut cc = FlatStack::default_impl::(); 178 | 179 | cc.copy(c.get(0)); 180 | 181 | let person_ref = cc.get(0); 182 | assert_eq!("Moritz", person_ref.name); 183 | assert_eq!(123, person_ref.age); 184 | assert_eq!(2, person_ref.hobbies.len()); 185 | for (copied_hobby, hobby) in person_ref.hobbies.iter().zip(hobbies) { 186 | assert_eq!(copied_hobby, hobby); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /tests/recursive.rs: -------------------------------------------------------------------------------- 1 | //! Demonstration of how to encode recursive data structures. 2 | 3 | use flatcontainer::impls::deduplicate::ConsecutiveIndexPairs; 4 | use flatcontainer::{IntoOwned, Push, Region, StringRegion}; 5 | 6 | #[derive(Clone)] 7 | struct List(T, Option>>); 8 | 9 | struct ListRef<'a, C: Region>( 10 | Result<(&'a ListRegion, ::Index, Option), &'a List>, 11 | ); 12 | 13 | impl<'a, C: Region> ListRef<'a, C> 14 | where 15 | C::Owned: Clone, 16 | { 17 | fn inner(&self) -> C::ReadItem<'_> { 18 | match &self.0 { 19 | Ok((region, index, _continuation)) => region.inner.index(*index), 20 | Err(list) => IntoOwned::borrow_as(&list.0), 21 | } 22 | } 23 | 24 | fn next(&self) -> Option { 25 | match &self.0 { 26 | Ok((region, _index, continuation)) => continuation.map(|index| region.index(index)), 27 | Err(list) => list 28 | .1 29 | .as_ref() 30 | .map(|next| Self(Err(IntoOwned::borrow_as(next.as_ref())))), 31 | } 32 | } 33 | } 34 | 35 | impl<'a, C: Region> IntoOwned<'a> for ListRef<'a, C> 36 | where 37 | C::Owned: Clone, 38 | { 39 | type Owned = List; 40 | 41 | fn into_owned(self) -> Self::Owned { 42 | List( 43 | self.inner().into_owned(), 44 | self.next().map(|next| Box::new(next.into_owned())), 45 | ) 46 | } 47 | 48 | fn clone_onto(self, other: &mut Self::Owned) { 49 | *other = self.into_owned(); 50 | } 51 | 52 | fn borrow_as(owned: &'a Self::Owned) -> Self { 53 | Self(Err(owned)) 54 | } 55 | } 56 | 57 | #[derive(Debug)] 58 | struct ListRegion { 59 | indexes: Vec<(C::Index, Option)>, 60 | inner: C, 61 | } 62 | 63 | impl Default for ListRegion { 64 | fn default() -> Self { 65 | Self { 66 | indexes: Vec::default(), 67 | inner: C::default(), 68 | } 69 | } 70 | } 71 | 72 | impl Region for ListRegion 73 | where 74 | C::Owned: Clone, 75 | { 76 | type Owned = List; 77 | type ReadItem<'a> = ListRef<'a, C> where C: 'a; 78 | type Index = usize; 79 | 80 | fn merge_regions<'a>(regions: impl Iterator + Clone) -> Self 81 | where 82 | Self: 'a, 83 | { 84 | Self { 85 | indexes: Vec::with_capacity(regions.clone().map(|r| r.indexes.len()).sum()), 86 | inner: C::merge_regions(regions.map(|r| &r.inner)), 87 | } 88 | } 89 | 90 | fn index(&self, index: Self::Index) -> Self::ReadItem<'_> { 91 | let (inner_index, continuation) = self.indexes[index]; 92 | ListRef(Ok((self, inner_index, continuation))) 93 | } 94 | 95 | fn reserve_regions<'a, I>(&mut self, regions: I) 96 | where 97 | Self: 'a, 98 | I: Iterator + Clone, 99 | { 100 | self.indexes 101 | .reserve(regions.clone().map(|r| r.indexes.len()).sum()); 102 | self.inner.reserve_regions(regions.map(|r| &r.inner)); 103 | } 104 | 105 | fn clear(&mut self) { 106 | self.indexes.clear(); 107 | self.inner.clear(); 108 | } 109 | 110 | fn heap_size(&self, mut callback: F) { 111 | self.indexes.heap_size(&mut callback); 112 | self.inner.heap_size(callback); 113 | } 114 | 115 | fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> 116 | where 117 | Self: 'a, 118 | { 119 | item 120 | } 121 | } 122 | 123 | impl Push<&List> for ListRegion 124 | where 125 | for<'a> C: Region + Push<&'a T>, 126 | C::Owned: Clone, 127 | { 128 | fn push(&mut self, item: &List) -> as Region>::Index { 129 | let inner_index = self.inner.push(&item.0); 130 | let continuation = item.1.as_deref().map(|next| self.push(next)); 131 | self.indexes.push((inner_index, continuation)); 132 | self.indexes.len() - 1 133 | } 134 | } 135 | 136 | #[test] 137 | fn recursive() { 138 | let mut region = >>::default(); 139 | let r = List("abc", Some(Box::new(List("def", None)))); 140 | let index = region.push(&r); 141 | 142 | let rref = region.index(index); 143 | assert_eq!(rref.inner(), "abc"); 144 | let next = rref.next(); 145 | assert!(next.is_some()); 146 | let next = next.unwrap(); 147 | assert_eq!(next.inner(), "def"); 148 | assert!(next.next().is_none()); 149 | 150 | println!("{region:?}"); 151 | } 152 | --------------------------------------------------------------------------------