├── .clippy.toml ├── .github ├── FUNDING.yml └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README-zh_CN.md ├── README.md ├── benchmarks ├── Cargo.toml └── src │ └── lib.rs ├── fmmap-dart ├── Cargo.toml └── src │ └── lib.rs ├── fmmap-js ├── Cargo.toml └── src │ └── lib.rs ├── fmmap-py ├── Cargo.toml └── src │ └── lib.rs ├── fmmap-rs ├── Cargo.toml ├── README-zh_CN.md ├── README.md └── src │ ├── disk.rs │ ├── disk │ ├── async_std_impl.rs │ ├── smol_impl.rs │ ├── sync_impl.rs │ └── tokio_impl.rs │ ├── empty.rs │ ├── empty │ ├── async_std_impl.rs │ ├── smol_impl.rs │ ├── sync_impl.rs │ └── tokio_impl.rs │ ├── error.rs │ ├── lib.rs │ ├── memory.rs │ ├── memory │ ├── async_std_impl.rs │ ├── smol_impl.rs │ ├── sync_impl.rs │ └── tokio_impl.rs │ ├── metadata.rs │ ├── metadata │ ├── unix.rs │ └── windows.rs │ ├── mmap_file.rs │ ├── mmap_file │ ├── async_std_impl.rs │ ├── smol_impl.rs │ ├── sync_impl.rs │ └── tokio_impl.rs │ ├── options.rs │ ├── options │ ├── async_std_impl.rs │ ├── smol_impl.rs │ ├── sync_impl.rs │ └── tokio_impl.rs │ ├── reader.rs │ ├── reader │ ├── async_std_impl.rs │ ├── smol_impl.rs │ ├── sync_impl.rs │ └── tokio_impl.rs │ ├── tests.rs │ ├── utils.rs │ ├── writer.rs │ └── writer │ ├── async_std_impl.rs │ ├── smol_impl.rs │ ├── sync_impl.rs │ └── tokio_impl.rs ├── fmmap-uniffi ├── Cargo.toml └── src │ └── lib.rs └── scripts └── .gitkeep /.clippy.toml: -------------------------------------------------------------------------------- 1 | msrv = "1.72" 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: al8n 2 | ko_fi: al8n9434 3 | patreon: al8n 4 | liberapay: al8n 5 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: rust 2 | 3 | on: 4 | push: 5 | # Ignore bors branches, since they are covered by `clippy_bors.yml` 6 | branches: 7 | - main 8 | # Don't run Clippy tests, when only textfiles were modified 9 | paths-ignore: 10 | - 'README' 11 | - 'COPYRIGHT' 12 | - 'LICENSE-*' 13 | - '**.md' 14 | - '**.txt' 15 | pull_request: 16 | # Don't run Clippy tests, when only textfiles were modified 17 | paths-ignore: 18 | - 'README' 19 | - 'COPYRIGHT' 20 | - 'LICENSE-*' 21 | - '**.md' 22 | - '**.txt' 23 | 24 | env: 25 | CARGO_TERM_COLOR: always 26 | RUSTFLAGS: -Dwarnings 27 | RUST_BACKTRACE: 1 28 | nightly: nightly 29 | minrust: 1.65.0 30 | 31 | jobs: 32 | check: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v3 37 | 38 | - name: Install toolchain 39 | uses: dtolnay/rust-toolchain@stable 40 | with: 41 | targets: x86_64-pc-windows-gnu 42 | components: clippy, rustfmt 43 | 44 | - name: Run checks 45 | env: 46 | CLIPPY_OPTS: --all-targets 47 | run: | 48 | cargo fmt --check 49 | cargo clippy $CLIPPY_OPTS 50 | cargo clippy --target x86_64-pc-windows-gnu $CLIPPY_OPTS 51 | 52 | unit-tests: 53 | name: unit tests 54 | runs-on: ${{ matrix.os }} 55 | strategy: 56 | matrix: 57 | rust: 58 | - nightly 59 | os: 60 | - macos-latest 61 | - ubuntu-latest 62 | # see https://github.com/al8n/fmmap/issues/1 63 | # - windows-latest 64 | steps: 65 | - uses: actions/checkout@v2 66 | - name: Install latest nightly 67 | uses: actions-rs/toolchain@v1 68 | with: 69 | toolchain: ${{ matrix.rust }} 70 | override: true 71 | components: rustfmt, clippy 72 | 73 | - uses: Swatinem/rust-cache@v1 74 | - uses: actions/cache@v2 75 | with: 76 | path: | 77 | ~/.cargo/registry 78 | ~/.cargo/git 79 | target 80 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 81 | 82 | - name: test fmmap all features 83 | run: cargo test --all-features 84 | working-directory: fmmap-rs 85 | 86 | coverage: 87 | name: cargo tarpaulin 88 | runs-on: ubuntu-latest 89 | needs: 90 | - check 91 | - unit-tests 92 | steps: 93 | - uses: actions/checkout@v3 94 | - name: Install latest nightly 95 | uses: actions-rs/toolchain@v1 96 | with: 97 | toolchain: nightly 98 | override: true 99 | - uses: actions-rs/install@v0.1 100 | with: 101 | crate: cargo-tarpaulin 102 | version: latest 103 | - name: Cache ~/.cargo 104 | uses: actions/cache@v3 105 | with: 106 | path: ~/.cargo 107 | key: ${{ runner.os }}-coverage-dotcargo 108 | - name: Cache cargo build 109 | uses: actions/cache@v3 110 | with: 111 | path: target 112 | key: ${{ runner.os }}-coverage-cargo-build-target 113 | - name: Change dir 114 | run: cd fmmap-rs 115 | - name: Run tarpaulin 116 | uses: actions-rs/cargo@v1 117 | with: 118 | command: tarpaulin 119 | args: --run-types AllTargets --workspace --out xml 120 | 121 | - name: Upload to codecov.io 122 | uses: codecov/codecov-action@v5 123 | with: 124 | token: ${{ secrets.CODECOV_TOKEN }} 125 | fail_ci_if_error: true 126 | slug: ${{ github.repository }} 127 | 128 | docs: 129 | name: docs 130 | runs-on: ubuntu-latest 131 | steps: 132 | - uses: actions/checkout@v2 133 | - uses: actions-rs/toolchain@v1 134 | with: 135 | toolchain: ${{ env.nightly }} 136 | override: true 137 | - uses: Swatinem/rust-cache@v1 138 | - name: "doc --lib --all-features" 139 | run: cargo doc --lib --no-deps --all-features --document-private-items 140 | env: 141 | RUSTFLAGS: --cfg docsrs 142 | RUSTDOCFLAGS: --cfg docsrs 143 | working-directory: fmmap-rs 144 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **.idea/ 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | **target/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "fmmap-dart", 4 | "fmmap-js", 5 | "fmmap-py", 6 | "fmmap-rs", 7 | "fmmap-uniffi", 8 | 9 | # internal 10 | "benchmarks" 11 | ] 12 | resolver = "2" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README-zh_CN.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al8n/fmmap/1775bec5a0a32f7a0916f20d4faca066f8d41665/README-zh_CN.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

fmmap

3 |
4 |
5 | 6 | A flexible and convenient high-level mmap for zero-copy file I/O. 7 | 8 | English | [简体中文](README-zh_CN.md) 9 | 10 | [github][Github-url] 11 | license 12 | 13 |
14 | 15 | ## Languages Support 16 | | language | CI | code coverage | package | documents | code | status | 17 | |:-------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------:|:------:| 18 | | Rust | [Build][rust-ci-url] | [codecov][codecov-url] | [crates.io][crates-url] | [docs.rs][doc-url] | [fmmap-rs](https://github.com/al8n/fmmap/tree/main/fmmap-rs) | stable | 19 | | Python | | | | | [fmmap-py](https://github.com/al8n/fmmap/tree/main/fmmap-py) | dev | 20 | | Node | | | | | [fmmap-js](https://github.com/al8n/fmmap/tree/main/fmmap-js) | dev | 21 | | Dart/Flutter | | | | | [fmmap-dart](https://github.com/al8n/fmmap/tree/main/fmmap-dart) | dev | 22 | | UniFFI | | | | | [fmmap-uniffi](https://github.com/al8n/fmmap/tree/main/fmmap-uniffi) | dev | 23 | 24 | **Notes:** empty table cell means currently does not support 25 | 26 | ## TODO 27 | - [ ] Python binding 28 | - [ ] Node binding 29 | - [ ] Dart/Flutter binding 30 | - [ ] UniFFI binding 31 | 32 | #### License 33 | 34 | 35 | Licensed under either of Apache License, Version 36 | 2.0 or MIT license at your option. 37 | 38 | 39 |
40 | 41 | 42 | Unless you explicitly state otherwise, any contribution intentionally submitted 43 | for inclusion in this project by you, as defined in the Apache-2.0 license, 44 | shall be dual licensed as above, without any additional terms or conditions. 45 | 46 | 47 | 48 | [Github-url]: https://github.com/al8n/fmmap/ 49 | [rust-ci-url]: https://github.com/al8n/fmmap/actions/workflows/rust.yml 50 | [doc-url]: https://docs.rs/fmmap 51 | [crates-url]: https://crates.io/crates/fmmap 52 | [codecov-url]: https://app.codecov.io/gh/al8n/fmmap/ 53 | 54 | -------------------------------------------------------------------------------- /benchmarks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "benches" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | 7 | [dependencies] 8 | criterion = "0.5" 9 | fmmap = { path = "../fmmap-rs", features = ["sync", "tokio"] } 10 | tokio = { version = "1", features = ["full"] } 11 | -------------------------------------------------------------------------------- /benchmarks/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fmmap-dart/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fmmap-dart" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /fmmap-dart/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fmmap-js/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fmmap-js" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /fmmap-js/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fmmap-py/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fmmap-py" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | fmmap = { path = "../fmmap-rs", features = ["sync", "tokio"] } -------------------------------------------------------------------------------- /fmmap-py/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fmmap-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fmmap" 3 | version = "0.4.0" 4 | edition = "2021" 5 | repository = "https://github.com/al8n/fmmap/tree/main/fmmap-rs" 6 | description = "A flexible and convenient high-level mmap for zero-copy file I/O." 7 | license = "MIT OR Apache-2.0" 8 | authors = ["Al Liu "] 9 | documentation = "https://docs.rs/fmmap" 10 | keywords = ["file", "memmap2", "mmap", "memory-map", "io"] 11 | categories = ["filesystem", "external-ffi-bindings", "asynchronous"] 12 | rust-version = "1.65.0" 13 | 14 | [lib] 15 | doctest = true 16 | 17 | [features] 18 | default = ["sync"] 19 | nightly = [] 20 | sync = ["dep:fs4", "fs4?/sync"] 21 | tokio = ["dep:tokio", "fs4/tokio", "dep:pin-project-lite", "tokio?/io-std", "tokio?/io-util", "tokio?/fs"] 22 | smol = ["dep:smol", "fs4/smol", "dep:pin-project-lite"] 23 | async-std = ["dep:async-std", "fs4/async-std", "dep:pin-project-lite", "async-std?/async-io", "async-std?/futures-lite", "dep:futures-util", "futures-util?/io"] 24 | 25 | 26 | [dependencies] 27 | async-std = { version = "1", optional = true } 28 | bytes = "1" 29 | byteorder = "1" 30 | enum_dispatch = "0.3" 31 | fs4 = { version = "0.12", optional = true } 32 | futures-util = { version = "0.3", optional = true } 33 | memmap2 = "0.9" 34 | pin-project-lite = { version = "0.2", optional = true } 35 | parse-display = "0.10" 36 | smol = { version = "2", optional = true } 37 | tokio = { version = "1", optional = true } 38 | 39 | [dev-dependencies] 40 | async-std = { version = "1", features = ["attributes"] } 41 | ctor = "0.3" 42 | criterion = "0.5" 43 | rand = "0.9" 44 | smol-potat = "1" 45 | scopeguard = "1.2" 46 | tempdir = "0.3" 47 | tokio = { version = "1", features = ["full"] } 48 | tokio-test = "0.4" 49 | 50 | [package.metadata.docs.rs] 51 | all-features = true 52 | rustdoc-args = ["--cfg", "docsrs"] 53 | -------------------------------------------------------------------------------- /fmmap-rs/README-zh_CN.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al8n/fmmap/1775bec5a0a32f7a0916f20d4faca066f8d41665/fmmap-rs/README-zh_CN.md -------------------------------------------------------------------------------- /fmmap-rs/README.md: -------------------------------------------------------------------------------- 1 |
2 |

fmmap

3 |
4 |
5 | 6 | A flexible and convenient high-level mmap for zero-copy file I/O. 7 | 8 | English | [简体中文](README-zh_CN.md) 9 | 10 | [github][Github-url] 11 | [Build][CI-url] 12 | [codecov][codecov-url] 13 | 14 | [docs.rs][doc-url] 15 | [crates.io][crates-url] 16 | [rustc][rustc-url] 17 | 18 | [license-apache][license-apache-url] 19 | [license-mit][license-mit-url] 20 | 21 |
22 | 23 | ## Design 24 | The design of this crate is inspired by Dgraph's mmap file implementation in [ristretto](https://github.com/hypermodeinc/ristretto). 25 | 26 | All of file-backed memory map has the potential for Undefined Behavior (UB) if the underlying file is subsequently modified (e.g. the file is deleted by another process), in or out of process, this crate tries to avoid this situation by provide file lock APIs. 27 | 28 | This crate supports std and popular async runtime(tokio, async-std, smol), and thanks to `macro` in Rust, it is super easy to support any new async runtime. For details, please see the implementation for tokio, async-std, smol of the source code. 29 | 30 | ## Features 31 | - [x] dozens of file I/O util functions 32 | - [x] file-backed memory maps 33 | - [x] synchronous and asynchronous flushing 34 | - [x] copy-on-write memory maps 35 | - [x] read-only memory maps 36 | - [x] stack support (`MAP_STACK` on unix) 37 | - [x] executable memory maps 38 | - [x] file locks. 39 | - [x] [tokio][tokio] 40 | - [x] [smol][smol] 41 | - [x] [async-std][async-std] 42 | 43 | ## Installation 44 | - std 45 | ```toml 46 | [dependencies] 47 | fmmap = "0.4" 48 | ``` 49 | 50 | - [tokio][tokio] 51 | ```toml 52 | [dependencies] 53 | fmmap = { version = "0.4", features = ["tokio-async"] } 54 | ``` 55 | 56 | - [async-std][async-std] 57 | ```toml 58 | [dependencies] 59 | fmmap = { version = "0.4", features = ["std-async"] } 60 | ``` 61 | 62 | - [smol][smol] 63 | ```toml 64 | [dependencies] 65 | fmmap = { version = "0.4", features = ["smol-async"] } 66 | ``` 67 | 68 | ## Examples 69 | This crate is 100% documented, see [documents][doc-url] for examples. 70 | 71 | ## TODO 72 | - [ ] add benchmarks 73 | 74 | #### License 75 | 76 | 77 | Licensed under either of Apache License, Version 78 | 2.0 or MIT license at your option. 79 | 80 | 81 |
82 | 83 | 84 | Unless you explicitly state otherwise, any contribution intentionally submitted 85 | for inclusion in this project by you, as defined in the Apache-2.0 license, 86 | shall be dual licensed as above, without any additional terms or conditions. 87 | 88 | 89 | 90 | [Github-url]: https://github.com/al8n/fmmap/ 91 | [CI-url]: https://github.com/al8n/fmmap/actions/workflows/rust.yml 92 | [doc-url]: https://docs.rs/fmmap 93 | [crates-url]: https://crates.io/crates/fmmap 94 | [codecov-url]: https://app.codecov.io/gh/al8n/fmmap/ 95 | [license-url]: https://opensource.org/licenses/Apache-2.0 96 | [rustc-url]: https://github.com/rust-lang/rust/blob/master/RELEASES.md 97 | [license-apache-url]: https://opensource.org/licenses/Apache-2.0 98 | [license-mit-url]: https://opensource.org/licenses/MIT 99 | [tokio]: https://crates.io/crates/tokio 100 | [smol]: https://crates.io/crates/smol 101 | [async-std]: https://crates.io/crates/async-std 102 | -------------------------------------------------------------------------------- /fmmap-rs/src/disk/async_std_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::async_std::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions}; 2 | use crate::disk::MmapFileMutType; 3 | use crate::error::{Error, ErrorKind}; 4 | use crate::utils::async_std::{ 5 | create_file_async, open_exist_file_with_append_async, open_or_create_file_async, 6 | open_read_only_file_async, sync_parent_async, 7 | }; 8 | use crate::MetaData; 9 | use async_std::fs::{remove_file, File}; 10 | use async_std::path::{Path, PathBuf}; 11 | 12 | use fs4::async_std::AsyncFileExt; 13 | use memmap2::{Mmap, MmapAsRawDesc, MmapMut, MmapOptions}; 14 | #[cfg(not(target_os = "linux"))] 15 | use std::ptr::{drop_in_place, write}; 16 | 17 | remmap!(Path); 18 | 19 | declare_and_impl_async_fmmap_file!("async_std_async", "async_std::task", "async_std", File); 20 | 21 | declare_and_impl_async_fmmap_file_mut!( 22 | "async_std_async", 23 | "async_std::task", 24 | "async_std", 25 | File, 26 | AsyncDiskMmapFile 27 | ); 28 | 29 | impl_async_fmmap_file_mut_private!(AsyncDiskMmapFileMut); 30 | 31 | impl_async_tests!( 32 | "std_async_disk", 33 | async_std::test, 34 | async_std, 35 | AsyncDiskMmapFile, 36 | AsyncDiskMmapFileMut 37 | ); 38 | 39 | #[cfg(test)] 40 | mod test { 41 | use super::*; 42 | use scopeguard::defer; 43 | 44 | #[async_std::test] 45 | async fn test_close_with_truncate_on_empty_file() { 46 | let file = 47 | AsyncDiskMmapFileMut::create("async_std_async_disk_close_with_truncate_test.txt") 48 | .await 49 | .unwrap(); 50 | defer!(std::fs::remove_file("async_std_async_disk_close_with_truncate_test.txt").unwrap()); 51 | file.close_with_truncate(10).await.unwrap(); 52 | 53 | assert_eq!( 54 | 10, 55 | File::open("async_std_async_disk_close_with_truncate_test.txt") 56 | .await 57 | .unwrap() 58 | .metadata() 59 | .await 60 | .unwrap() 61 | .len() 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /fmmap-rs/src/disk/smol_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::disk::MmapFileMutType; 2 | use crate::error::{Error, ErrorKind}; 3 | use crate::smol::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions}; 4 | use crate::utils::smol::{ 5 | create_file_async, open_exist_file_with_append_async, open_or_create_file_async, 6 | open_read_only_file_async, sync_parent_async, 7 | }; 8 | use crate::MetaData; 9 | 10 | use fs4::smol::AsyncFileExt; 11 | use memmap2::{Mmap, MmapAsRawDesc, MmapMut, MmapOptions}; 12 | use smol::fs::{remove_file, File}; 13 | use std::path::{Path, PathBuf}; 14 | #[cfg(not(target_os = "linux"))] 15 | use std::ptr::{drop_in_place, write}; 16 | 17 | remmap!(Path); 18 | 19 | declare_and_impl_async_fmmap_file!("smol_async", "smol", "smol", File); 20 | 21 | declare_and_impl_async_fmmap_file_mut!("smol_async", "smol", "smol", File, AsyncDiskMmapFile); 22 | 23 | impl_async_fmmap_file_mut_private!(AsyncDiskMmapFileMut); 24 | 25 | impl_async_tests!( 26 | "smol_async_disk", 27 | smol_potat::test, 28 | smol, 29 | AsyncDiskMmapFile, 30 | AsyncDiskMmapFileMut 31 | ); 32 | 33 | #[cfg(test)] 34 | mod test { 35 | use super::*; 36 | use scopeguard::defer; 37 | 38 | #[smol_potat::test] 39 | async fn test_close_with_truncate_on_empty_file() { 40 | let file = AsyncDiskMmapFileMut::create("smol_async_disk_close_with_truncate_test.txt") 41 | .await 42 | .unwrap(); 43 | defer!(std::fs::remove_file("smol_async_disk_close_with_truncate_test.txt").unwrap()); 44 | file.close_with_truncate(10).await.unwrap(); 45 | 46 | assert_eq!( 47 | 10, 48 | File::open("smol_async_disk_close_with_truncate_test.txt") 49 | .await 50 | .unwrap() 51 | .metadata() 52 | .await 53 | .unwrap() 54 | .len() 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /fmmap-rs/src/disk/tokio_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::disk::MmapFileMutType; 2 | use crate::error::{Error, ErrorKind}; 3 | use crate::tokio::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions}; 4 | use crate::utils::tokio::{ 5 | create_file_async, open_exist_file_with_append_async, open_or_create_file_async, 6 | open_read_only_file_async, sync_parent_async, 7 | }; 8 | use crate::MetaData; 9 | 10 | use fs4::tokio::AsyncFileExt; 11 | use memmap2::{Mmap, MmapAsRawDesc, MmapMut, MmapOptions}; 12 | use std::path::{Path, PathBuf}; 13 | #[cfg(not(target_os = "linux"))] 14 | use std::ptr::{drop_in_place, write}; 15 | use tokio::fs::{remove_file, File}; 16 | 17 | remmap!(Path); 18 | 19 | declare_and_impl_async_fmmap_file!("tokio_async", "tokio_test", "tokio", File); 20 | 21 | declare_and_impl_async_fmmap_file_mut!( 22 | "tokio_async", 23 | "tokio_test", 24 | "tokio", 25 | File, 26 | AsyncDiskMmapFile 27 | ); 28 | 29 | impl_async_fmmap_file_mut_private!(AsyncDiskMmapFileMut); 30 | 31 | impl_async_tests!( 32 | "tokio_async_disk", 33 | tokio::test, 34 | tokio, 35 | AsyncDiskMmapFile, 36 | AsyncDiskMmapFileMut 37 | ); 38 | 39 | #[cfg(test)] 40 | mod test { 41 | use super::*; 42 | use scopeguard::defer; 43 | 44 | #[tokio::test] 45 | async fn test_close_with_truncate_on_empty_file() { 46 | let file = AsyncDiskMmapFileMut::create("tokio_async_disk_close_with_truncate_test.txt") 47 | .await 48 | .unwrap(); 49 | defer!(std::fs::remove_file("tokio_async_disk_close_with_truncate_test.txt").unwrap()); 50 | file.close_with_truncate(10).await.unwrap(); 51 | 52 | assert_eq!( 53 | 10, 54 | File::open("tokio_async_disk_close_with_truncate_test.txt") 55 | .await 56 | .unwrap() 57 | .metadata() 58 | .await 59 | .unwrap() 60 | .len() 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /fmmap-rs/src/empty.rs: -------------------------------------------------------------------------------- 1 | cfg_sync!( 2 | mod sync_impl; 3 | pub use sync_impl::EmptyMmapFile; 4 | ); 5 | 6 | cfg_async! { 7 | macro_rules! declare_and_impl_async_empty_mmap_file { 8 | () => { 9 | #[derive(Default, Clone)] 10 | pub struct AsyncEmptyMmapFile { 11 | inner: [u8; 0], 12 | path: PathBuf, 13 | } 14 | 15 | 16 | impl AsyncMmapFileExt for AsyncEmptyMmapFile { 17 | #[inline] 18 | fn len(&self) -> usize { 19 | 0 20 | } 21 | 22 | #[inline] 23 | fn as_slice(&self) -> &[u8] { 24 | &self.inner 25 | } 26 | 27 | #[inline] 28 | fn bytes(&self, _offset: usize, _sz: usize) -> Result<&[u8]> { 29 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 30 | } 31 | 32 | #[inline] 33 | fn path(&self) -> &Path { 34 | self.path.as_path() 35 | } 36 | 37 | #[inline] 38 | fn is_exec(&self) -> bool { 39 | false 40 | } 41 | 42 | #[inline] 43 | async fn metadata(&self) -> Result { 44 | Ok(MetaData::empty(EmptyMetaData)) 45 | } 46 | 47 | #[inline] 48 | fn copy_all_to_vec(&self) -> Vec { 49 | self.inner.to_vec() 50 | } 51 | 52 | #[inline] 53 | fn copy_range_to_vec(&self, _offset: usize, _len: usize) -> Vec { 54 | self.inner.to_vec() 55 | } 56 | 57 | #[inline] 58 | async fn write_all_to_new_file + Send>(&self, _new_file_path: P) -> Result<()> { 59 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 60 | } 61 | 62 | #[inline] 63 | async fn write_range_to_new_file + Send>(&self, _new_file_path: P, _offset: usize, _sz: usize) -> Result<()> { 64 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 65 | } 66 | 67 | #[inline] 68 | fn reader(&self, _offset: usize) -> Result { 69 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 70 | } 71 | 72 | #[inline] 73 | fn range_reader(&self, _offset: usize, _len: usize) -> Result { 74 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 75 | } 76 | 77 | noop_file_lock!(); 78 | 79 | #[inline] 80 | fn read_exact(&self, _dst: &mut [u8], _offset: usize) -> Result<()> { 81 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 82 | } 83 | 84 | #[inline] 85 | fn read_i8(&self, _offset: usize) -> Result { 86 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 87 | } 88 | 89 | #[inline] 90 | fn read_u8(&self, _offset: usize) -> Result { 91 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 92 | } 93 | } 94 | 95 | 96 | impl AsyncMmapFileMutExt for AsyncEmptyMmapFile { 97 | #[inline] 98 | fn as_mut_slice(&mut self) -> &mut [u8] { 99 | &mut self.inner 100 | } 101 | 102 | #[inline] 103 | fn is_cow(&self) -> bool { 104 | false 105 | } 106 | 107 | #[inline] 108 | fn bytes_mut(&mut self, _offset: usize, _len: usize) -> Result<&mut [u8]> { 109 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 110 | } 111 | 112 | #[inline] 113 | fn zero_range(&mut self, _start: usize, _end: usize) {} 114 | 115 | noop_flush!(); 116 | 117 | #[inline] 118 | async fn truncate(&mut self, _max_sz: u64) -> Result<()> { 119 | Ok(()) 120 | } 121 | 122 | #[inline] 123 | async fn drop_remove(self) -> Result<()> { 124 | Ok(()) 125 | } 126 | 127 | #[inline] 128 | async fn close_with_truncate(self, _max_sz: i64) -> Result<()> { 129 | Ok(()) 130 | } 131 | 132 | #[inline] 133 | fn writer(&mut self, _offset: usize) -> Result { 134 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 135 | } 136 | 137 | #[inline] 138 | fn range_writer(&mut self, _offset: usize, _len: usize) -> Result { 139 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 140 | } 141 | 142 | #[inline] 143 | fn write(&mut self, _src: &[u8], _offset: usize) -> usize { 0 } 144 | 145 | #[inline] 146 | fn write_all(&mut self, _src: &[u8], _offset: usize) -> Result<()> { 147 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 148 | } 149 | } 150 | }; 151 | } 152 | 153 | macro_rules! test_empty_mmap_file { 154 | ($attr: meta) => { 155 | #[cfg(test)] 156 | mod tests { 157 | use super::*; 158 | 159 | #[$attr] 160 | async fn test_async_empty() { 161 | let mut file = AsyncEmptyMmapFile::default(); 162 | file.slice(0, 0); 163 | file.as_slice(); 164 | file.as_mut_slice(); 165 | file.bytes(0,0).unwrap_err(); 166 | file.bytes_mut(0,0).unwrap_err(); 167 | file.metadata().await.unwrap(); 168 | file.copy_range_to_vec(0,0); 169 | file.copy_all_to_vec(); 170 | file.write_all_to_new_file("test").await.unwrap_err(); 171 | file.write_range_to_new_file("test", 0, 0).await.unwrap_err(); 172 | assert!(!file.is_exec()); 173 | assert!(!file.is_cow()); 174 | assert_eq!(file.len(), 0); 175 | file.path(); 176 | file.path_lossy(); 177 | file.path_string(); 178 | file.flush().unwrap(); 179 | file.flush_async().unwrap(); 180 | file.flush_range(0, 0).unwrap(); 181 | file.flush_async_range(0, 0).unwrap(); 182 | let mut buf = [0; 10]; 183 | file.reader(0).unwrap_err(); 184 | file.range_reader(0, 0).unwrap_err(); 185 | file.read_i8(0).unwrap_err(); 186 | file.read_u8(0).unwrap_err(); 187 | file.read_exact(&mut buf, 0).unwrap_err(); 188 | file.write(&buf, 0); 189 | file.write_all(&[0], 0).unwrap_err(); 190 | file.writer(0).unwrap_err(); 191 | file.range_writer(0, 0).unwrap_err(); 192 | file.zero_range(0, 0); 193 | file.clone().close_with_truncate(0).await.unwrap(); 194 | file.truncate(0).await.unwrap(); 195 | file.clone().drop_remove().await.unwrap(); 196 | } 197 | } 198 | }; 199 | } 200 | } 201 | 202 | cfg_async_std!( 203 | pub(crate) mod async_std_impl; 204 | ); 205 | 206 | cfg_smol!( 207 | pub(crate) mod smol_impl; 208 | ); 209 | 210 | cfg_tokio!( 211 | pub(crate) mod tokio_impl; 212 | ); 213 | -------------------------------------------------------------------------------- /fmmap-rs/src/empty/async_std_impl.rs: -------------------------------------------------------------------------------- 1 | use async_std::path::{Path, PathBuf}; 2 | use crate::async_std::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncMmapFileReader, AsyncMmapFileWriter}; 3 | use crate::MetaData; 4 | use crate::error::{Error, ErrorKind, Result}; 5 | use crate::metadata::EmptyMetaData; 6 | 7 | declare_and_impl_async_empty_mmap_file!(); 8 | 9 | test_empty_mmap_file!(async_std::test); -------------------------------------------------------------------------------- /fmmap-rs/src/empty/smol_impl.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use crate::smol::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncMmapFileReader, AsyncMmapFileWriter}; 3 | use crate::MetaData; 4 | use crate::error::{Error, ErrorKind, Result}; 5 | use crate::metadata::EmptyMetaData; 6 | 7 | declare_and_impl_async_empty_mmap_file!(); 8 | 9 | test_empty_mmap_file!(smol_potat::test); -------------------------------------------------------------------------------- /fmmap-rs/src/empty/sync_impl.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use crate::error::{Error, ErrorKind, Result}; 3 | use crate::metadata::{EmptyMetaData, MetaData}; 4 | use crate::mmap_file::{MmapFileExt, MmapFileMutExt}; 5 | use crate::{MmapFileReader, MmapFileWriter}; 6 | 7 | #[derive(Default, Clone)] 8 | pub struct EmptyMmapFile { 9 | inner: [u8; 0], 10 | path: PathBuf, 11 | } 12 | 13 | impl MmapFileExt for EmptyMmapFile { 14 | fn len(&self) -> usize { 15 | 0 16 | } 17 | 18 | fn as_slice(&self) -> &[u8] { 19 | &self.inner 20 | } 21 | 22 | fn bytes(&self, _offset: usize, _sz: usize) -> Result<&[u8]> { 23 | Ok(&self.inner) 24 | } 25 | 26 | fn path(&self) -> &Path { 27 | self.path.as_path() 28 | } 29 | 30 | fn metadata(&self) -> Result { 31 | Ok(MetaData::empty(EmptyMetaData)) 32 | } 33 | 34 | fn is_exec(&self) -> bool { 35 | false 36 | } 37 | 38 | fn copy_all_to_vec(&self) -> Vec { 39 | self.inner.to_vec() 40 | } 41 | 42 | fn copy_range_to_vec(&self, _offset: usize, _len: usize) -> Vec { 43 | self.inner.to_vec() 44 | } 45 | 46 | fn write_all_to_new_file>(&self, _new_file_path: P) -> Result<()> { 47 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 48 | } 49 | 50 | fn write_range_to_new_file>(&self, _new_file_path: P, _offset: usize, _len: usize) -> Result<()> { 51 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 52 | } 53 | 54 | fn reader(&self, _offset: usize) -> Result { 55 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 56 | } 57 | 58 | fn range_reader(&self, _offset: usize, _len: usize) -> Result { 59 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 60 | } 61 | 62 | noop_file_lock!(); 63 | 64 | fn read_exact(&self, _dst: &mut [u8], _offset: usize) -> Result<()> { 65 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 66 | } 67 | 68 | fn read_i8(&self, _offset: usize) -> Result { 69 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 70 | } 71 | 72 | fn read_u8(&self, _offset: usize) -> Result { 73 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 74 | } 75 | } 76 | 77 | impl MmapFileMutExt for EmptyMmapFile { 78 | #[inline] 79 | fn as_mut_slice(&mut self) -> &mut [u8] { 80 | &mut self.inner 81 | } 82 | 83 | #[inline] 84 | fn is_cow(&self) -> bool { 85 | false 86 | } 87 | 88 | #[inline] 89 | fn bytes_mut(&mut self, _offset: usize, _sz: usize) -> Result<&mut [u8]> { 90 | Ok(&mut self.inner) 91 | } 92 | 93 | #[inline] 94 | fn zero_range(&mut self, _start: usize, _end: usize) {} 95 | 96 | noop_flush!(); 97 | 98 | #[inline] 99 | fn truncate(&mut self, _max_sz: u64) -> Result<()> { 100 | Ok(()) 101 | } 102 | 103 | #[inline] 104 | fn drop_remove(self) -> Result<()> { 105 | Ok(()) 106 | } 107 | 108 | #[inline] 109 | fn close_with_truncate(self, _max_sz: i64) -> Result<()> { 110 | Ok(()) 111 | } 112 | 113 | #[inline] 114 | fn writer(&mut self, _offset: usize) -> Result { 115 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 116 | } 117 | 118 | #[inline] 119 | fn range_writer(&mut self, _offset: usize, _len: usize) -> Result { 120 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 121 | } 122 | 123 | #[inline] 124 | fn write(&mut self, _src: &[u8], _offset: usize) -> usize { 0 } 125 | 126 | #[inline] 127 | fn write_all(&mut self, _src: &[u8], _offset: usize) -> Result<()> { 128 | Err(Error::from(ErrorKind::InvokeEmptyMmap)) 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod tests { 134 | use super::*; 135 | 136 | #[test] 137 | fn test_empty() { 138 | let mut file = EmptyMmapFile::default(); 139 | file.slice(0, 0); 140 | file.as_slice(); 141 | file.as_mut_slice(); 142 | file.bytes(0,0).unwrap(); 143 | file.bytes_mut(0,0).unwrap(); 144 | file.metadata().unwrap(); 145 | file.copy_range_to_vec(0,0); 146 | file.copy_all_to_vec(); 147 | file.write_all_to_new_file("test").unwrap_err(); 148 | file.write_range_to_new_file("test", 0, 0).unwrap_err(); 149 | assert!(!file.is_exec()); 150 | assert!(!file.is_cow()); 151 | assert_eq!(file.len(), 0); 152 | file.path(); 153 | file.path_lossy(); 154 | file.path_string(); 155 | file.flush().unwrap(); 156 | file.flush_async().unwrap(); 157 | file.flush_range(0, 0).unwrap(); 158 | file.flush_async_range(0, 0).unwrap(); 159 | let mut buf = [0; 10]; 160 | file.reader(0).unwrap_err(); 161 | file.range_reader(0, 0).unwrap_err(); 162 | file.read_i8(0).unwrap_err(); 163 | file.read_u8(0).unwrap_err(); 164 | file.read_exact(&mut buf, 0).unwrap_err(); 165 | file.write(&buf, 0); 166 | file.write_all(&[0], 0).unwrap_err(); 167 | file.writer(0).unwrap_err(); 168 | file.range_writer(0, 0).unwrap_err(); 169 | file.zero_range(0, 0); 170 | file.clone().close_with_truncate(0).unwrap(); 171 | file.truncate(0).unwrap(); 172 | file.clone().drop_remove().unwrap(); 173 | } 174 | } -------------------------------------------------------------------------------- /fmmap-rs/src/empty/tokio_impl.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use crate::tokio::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncMmapFileReader, AsyncMmapFileWriter}; 3 | use crate::MetaData; 4 | use crate::error::{Error, ErrorKind, Result}; 5 | use crate::metadata::EmptyMetaData; 6 | 7 | declare_and_impl_async_empty_mmap_file!(); 8 | 9 | test_empty_mmap_file!(tokio::test); -------------------------------------------------------------------------------- /fmmap-rs/src/error.rs: -------------------------------------------------------------------------------- 1 | use parse_display::Display; 2 | use std::io; 3 | 4 | /// alias for [`Result`] 5 | /// 6 | /// [`Result`]: struct.Error.html 7 | pub type Result = std::result::Result; 8 | 9 | /// ErrorKind in this crate. 10 | #[derive(Copy, Clone, Eq, PartialEq, Display, Debug)] 11 | pub enum ErrorKind { 12 | /// unexpected EOF 13 | #[display("unexpected EOF")] 14 | EOF, 15 | 16 | /// IO error 17 | #[display("IO error")] 18 | IO, 19 | 20 | /// Truncation failed 21 | #[display("truncation failed")] 22 | TruncationFailed, 23 | 24 | /// unable to open file 25 | #[display("unable to open file")] 26 | OpenFailed, 27 | 28 | /// unable to open dir 29 | #[display("unable to open dir")] 30 | OpenDirFailed, 31 | 32 | /// flush file failed 33 | #[display("flush file failed")] 34 | FlushFailed, 35 | 36 | /// sync dir failed 37 | #[display("sync file failed")] 38 | SyncFileFailed, 39 | 40 | /// sync dir failed 41 | #[display("sync dir failed")] 42 | SyncDirFailed, 43 | 44 | /// mmap failed 45 | #[display("mmap failed")] 46 | MmapFailed, 47 | 48 | /// remmap failed 49 | #[display("remmap failed")] 50 | RemmapFailed, 51 | 52 | /// invalid range 53 | #[display("range start must not be greater than end: {0} <= {1}")] 54 | InvalidBound(usize, usize), 55 | 56 | /// out of range 57 | #[display("range end out of bounds: {0} <= {1}")] 58 | OutOfBound(usize, usize), 59 | 60 | /// call on an empty mmap file 61 | #[display("call on an empty mmap file")] 62 | InvokeEmptyMmap, 63 | 64 | /// not a directory 65 | #[cfg(not(feature = "nightly"))] 66 | #[display("not a directory")] 67 | NotADirectory, 68 | } 69 | 70 | enum Repr { 71 | Simple(ErrorKind), 72 | Message { kd: ErrorKind, msg: String }, 73 | Source(Box), 74 | SourceMessage { msg: String, src: Box }, 75 | } 76 | 77 | struct Source { 78 | kind: ErrorKind, 79 | error: Box, 80 | } 81 | 82 | /// Error in this crate 83 | pub struct Error { 84 | repr: Repr, 85 | } 86 | 87 | impl Error { 88 | pub(crate) fn new(kd: ErrorKind, src: E) -> Self 89 | where 90 | E: Into>, 91 | { 92 | Self::_new(kd, src.into()) 93 | } 94 | 95 | fn _new(kind: ErrorKind, error: Box) -> Self { 96 | Error { 97 | repr: Repr::Source(Box::new(Source { kind, error })), 98 | } 99 | } 100 | 101 | pub(crate) fn new_with_message(kd: ErrorKind, msg: M) -> Self 102 | where 103 | M: Into, 104 | { 105 | Self { 106 | repr: Repr::Message { 107 | kd, 108 | msg: msg.into(), 109 | }, 110 | } 111 | } 112 | 113 | pub(crate) fn new_source_msg(kd: ErrorKind, msg: M, src: E) -> Self 114 | where 115 | E: Into>, 116 | M: Into, 117 | { 118 | Self { 119 | repr: Repr::SourceMessage { 120 | msg: msg.into(), 121 | src: Box::new(Source { 122 | kind: kd, 123 | error: src.into(), 124 | }), 125 | }, 126 | } 127 | } 128 | 129 | pub(crate) fn f(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 130 | match &self.repr { 131 | Repr::Simple(kd) => write!(formatter, "{}", kd), 132 | Repr::Source(src) => write!(formatter, "{}: {}", src.error, src.kind), 133 | Repr::Message { kd, msg } => write!(formatter, "{}: {}", msg, kd), 134 | Repr::SourceMessage { msg, src } => { 135 | write!(formatter, "{}: {}: {}", msg, src.kind, src.error) 136 | } 137 | } 138 | } 139 | 140 | /// Return the [`ErrorKind`] of this error 141 | /// 142 | /// [`ErrorKind`]: struct.ErrorKind.html 143 | pub fn kind(&self) -> ErrorKind { 144 | match &self.repr { 145 | Repr::Simple(kd) => *kd, 146 | Repr::Message { kd, msg: _ } => *kd, 147 | Repr::Source(src) => src.kind, 148 | Repr::SourceMessage { msg: _, src } => src.kind, 149 | } 150 | } 151 | } 152 | 153 | impl From for Error { 154 | fn from(kd: ErrorKind) -> Self { 155 | Self { 156 | repr: Repr::Simple(kd), 157 | } 158 | } 159 | } 160 | 161 | impl std::fmt::Debug for Error { 162 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 163 | self.f(f) 164 | } 165 | } 166 | 167 | impl std::fmt::Display for Error { 168 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 169 | self.f(f) 170 | } 171 | } 172 | 173 | impl std::error::Error for Error { 174 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 175 | match self.repr { 176 | Repr::Simple(_) => None, 177 | Repr::Source(ref c) => Some(c.error.as_ref()), 178 | Repr::Message { .. } => None, 179 | Repr::SourceMessage { msg: _, ref src } => Some(src.error.as_ref()), 180 | } 181 | } 182 | } 183 | 184 | impl From for Error { 185 | fn from(err: io::Error) -> Self { 186 | match err.kind() { 187 | io::ErrorKind::UnexpectedEof => Error::new(ErrorKind::EOF, err), 188 | _ => Error::new(ErrorKind::IO, err), 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /fmmap-rs/src/memory.rs: -------------------------------------------------------------------------------- 1 | macro_rules! define_impl_constructor_for_mmap_file { 2 | ($name: ident, $name_str: literal, $path_str: literal) => { 3 | /// Use [`Bytes`] to mock a mmap, which is useful for test and in-memory storage engine. 4 | /// 5 | /// [`Bytes`]: https://docs.rs/bytes/1.1.0/bytes/struct.Bytes.html 6 | #[derive(Clone, PartialEq, Eq)] 7 | pub struct $name { 8 | mmap: Bytes, 9 | path: PathBuf, 10 | create_at: SystemTime, 11 | } 12 | 13 | impl $name { 14 | #[doc = concat!("Create a ", $name_str)] 15 | #[doc = "# Examples"] 16 | #[doc = "```rust"] 17 | #[doc = "use bytes::{BufMut, BytesMut};"] 18 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 19 | #[doc = ""] 20 | #[doc = "let mut data = BytesMut::with_capacity(100);"] 21 | #[doc = "data.put_slice(\"some data...\".as_bytes());"] 22 | #[doc = concat!($name_str, "::new(\"foo.mem\", data.freeze());")] 23 | #[doc = "```"] 24 | pub fn new>(path: P, data: Bytes) -> Self { 25 | Self { 26 | mmap: data, 27 | path: path.as_ref().to_path_buf(), 28 | create_at: SystemTime::now(), 29 | } 30 | } 31 | 32 | #[doc = concat!("Create a ", $name_str, " from Vec")] 33 | #[doc = "# Examples"] 34 | #[doc = "```rust"] 35 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 36 | #[doc = ""] 37 | #[doc = "let data = (0..=255u8).collect::>();"] 38 | #[doc = concat!($name_str, "::from_vec(\"foo.mem\", data);")] 39 | #[doc = "```"] 40 | pub fn from_vec>(path: P, src: Vec) -> Self { 41 | Self { 42 | mmap: Bytes::from(src), 43 | path: path.as_ref().to_path_buf(), 44 | create_at: SystemTime::now(), 45 | } 46 | } 47 | 48 | #[doc = concat!("Create a ", $name_str, " from String")] 49 | #[doc = "# Examples"] 50 | #[doc = "```rust"] 51 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 52 | #[doc = ""] 53 | #[doc = "let data: &'static str = \"some data...\";"] 54 | #[doc = concat!($name_str, "::from_string(\"foo.mem\", data.to_string());")] 55 | #[doc = "```"] 56 | pub fn from_string>(path: P, src: String) -> Self { 57 | Self { 58 | mmap: Bytes::from(src), 59 | path: path.as_ref().to_path_buf(), 60 | create_at: SystemTime::now() 61 | } 62 | } 63 | 64 | #[doc = concat!("Create a ", $name_str, " from static slice")] 65 | #[doc = "# Examples"] 66 | #[doc = "```rust"] 67 | #[doc = "use bytes::Bytes;"] 68 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 69 | #[doc = ""] 70 | #[doc = "let data: &'static [u8] = \"some data...\".as_bytes();"] 71 | #[doc = concat!($name_str, "::from_slice(\"foo.mem\", data);")] 72 | #[doc = "```"] 73 | pub fn from_slice>(path: P, src: &'static [u8]) -> Self { 74 | Self { 75 | mmap: Bytes::from(src), 76 | path: path.as_ref().to_path_buf(), 77 | create_at: SystemTime::now() 78 | } 79 | } 80 | 81 | #[doc = concat!("Create a ", $name_str, " from static str")] 82 | #[doc = "# Examples"] 83 | #[doc = "```rust"] 84 | #[doc = "use bytes::Bytes;"] 85 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 86 | #[doc = ""] 87 | #[doc = "let data: &'static str = \"some data...\";"] 88 | #[doc = concat!($name_str, "::from_str(\"foo.mem\", data);")] 89 | #[doc = "```"] 90 | pub fn from_str>(path: P, src: &'static str) -> Self { 91 | Self { 92 | mmap: Bytes::from(src), 93 | path: path.as_ref().to_path_buf(), 94 | create_at: SystemTime::now() 95 | } 96 | } 97 | 98 | #[doc = concat!("Create a ", $name_str, " by copy from slice")] 99 | #[doc = "# Examples"] 100 | #[doc = "```rust"] 101 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 102 | #[doc = ""] 103 | #[doc = concat!($name_str, "::copy_from_slice(\"foo.mem\", \"some data...\".as_bytes());")] 104 | #[doc = "```"] 105 | pub fn copy_from_slice>(path: P, src: &[u8]) -> Self { 106 | Self { 107 | mmap: Bytes::copy_from_slice(src), 108 | path: path.as_ref().to_path_buf(), 109 | create_at: SystemTime::now(), 110 | } 111 | } 112 | 113 | #[doc = "Returns the inner bytes"] 114 | #[doc = "# Examples"] 115 | #[doc = "```rust"] 116 | #[doc = "use bytes::Bytes;"] 117 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 118 | #[doc = ""] 119 | #[doc = concat!("let b1 = ", $name_str, "::copy_from_slice(\"foo.mem\", \"some data...\".as_bytes()).into_bytes();")] 120 | #[doc = "assert_eq!(b1, Bytes::copy_from_slice(\"some data...\".as_bytes()));"] 121 | #[doc = "```"] 122 | pub fn into_bytes(self) -> Bytes { 123 | self.mmap 124 | } 125 | } 126 | }; 127 | } 128 | 129 | macro_rules! define_and_impl_constructor_for_mmap_file_mut { 130 | ($name: ident, $name_str: literal, $immutable: ident, $immutable_str: literal, $trait_str: literal, $path_str: literal) => { 131 | #[doc = "Use [`BytesMut`] to mock a mmap, which is useful for test and in-memory storage engine."] 132 | #[doc = ""] 133 | #[doc = "# Notes"] 134 | #[doc = concat!($name_str, " mocks a mmap behaviour, which means when writing to it,")] 135 | #[doc = "it will not auto-grow its size, so if you want to grow the size of the MemoryMmapFileMut,"] 136 | #[doc = "you need to [`truncate`] it first."] 137 | #[doc = ""] 138 | #[doc = "If you want the auto-grow functionality, please use [`BytesMut`]."] 139 | #[doc = ""] 140 | #[doc = "[`truncate`]: structs.MemoryMmapFileMut.html#methods.truncate"] 141 | #[doc = "[`BytesMut`]: https://docs.rs/bytes/1.1.0/bytes/struct.BytesMut.html"] 142 | #[derive(Clone, PartialEq, Eq)] 143 | pub struct $name { 144 | mmap: BytesMut, 145 | path: PathBuf, 146 | create_at: SystemTime, 147 | } 148 | 149 | impl $name { 150 | #[doc = concat!("Create a ", $name_str)] 151 | #[doc = "# Examples"] 152 | #[doc = "```rust"] 153 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 154 | #[doc = ""] 155 | #[doc = concat!($name_str, "::new(\"foo.mem\");")] 156 | #[doc = "```"] 157 | pub fn new>(path: P) -> Self { 158 | Self { 159 | mmap: BytesMut::new(), 160 | path: path.as_ref().to_path_buf(), 161 | create_at: SystemTime::now(), 162 | } 163 | } 164 | 165 | #[doc = concat!("Create a ", $name_str, "with capacity")] 166 | #[doc = "# Examples"] 167 | #[doc = "```rust"] 168 | #[doc = concat!("use fmmap::raw::",$path_str, $name_str, ";")] 169 | #[doc = ""] 170 | #[doc = concat!($name_str, "::with_capacity(\"foo.mem\", 1000);")] 171 | #[doc = "```"] 172 | pub fn with_capacity>(path: P, cap: usize) -> Self { 173 | Self { 174 | mmap: BytesMut::with_capacity(cap), 175 | path: path.as_ref().to_path_buf(), 176 | create_at: SystemTime::now(), 177 | } 178 | } 179 | 180 | #[doc = concat!("Create a ", $name_str, " from Vec")] 181 | #[doc = "# Examples"] 182 | #[doc = "```rust"] 183 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 184 | #[doc = ""] 185 | #[doc = "let data = (0..=255u8).collect::>();"] 186 | #[doc = concat!($name_str, "::from_vec(\"foo.mem\", data);")] 187 | #[doc = "```"] 188 | pub fn from_vec>(path: P, src: Vec) -> Self { 189 | Self { 190 | mmap: BytesMut::from_iter(src), 191 | path: path.as_ref().to_path_buf(), 192 | create_at: SystemTime::now(), 193 | } 194 | } 195 | 196 | #[doc = concat!("Create a ", $name_str, " from String")] 197 | #[doc = "# Examples"] 198 | #[doc = "```rust"] 199 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 200 | #[doc = ""] 201 | #[doc = "let data: &'static str = \"some data...\";"] 202 | #[doc = concat!($name_str, "::from_string(\"foo.mem\", data.to_string());")] 203 | #[doc = "```"] 204 | pub fn from_string>(path: P, src: String) -> Self { 205 | Self { 206 | mmap: BytesMut::from(src.as_bytes()), 207 | path: path.as_ref().to_path_buf(), 208 | create_at: SystemTime::now() 209 | } 210 | } 211 | 212 | #[doc = concat!("Create a ", $name_str, " from static str")] 213 | #[doc = "# Examples"] 214 | #[doc = "```rust"] 215 | #[doc = "use bytes::Bytes;"] 216 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 217 | #[doc = ""] 218 | #[doc = "let data: &'static str = \"some data...\";"] 219 | #[doc = concat!($name_str, "::from_str(\"foo.mem\", data);")] 220 | #[doc = "```"] 221 | pub fn from_str>(path: P, src: &'static str) -> Self { 222 | Self { 223 | mmap: BytesMut::from(src), 224 | path: path.as_ref().to_path_buf(), 225 | create_at: SystemTime::now() 226 | } 227 | } 228 | 229 | #[doc = concat!("Create a ", $name_str, " by from slice")] 230 | #[doc = "# Examples"] 231 | #[doc = "```rust"] 232 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 233 | #[doc = ""] 234 | #[doc = concat!($name_str, "::from_slice(\"foo.mem\", \"some data...\".as_bytes());")] 235 | #[doc = "```"] 236 | pub fn from_slice>(path: P, src: &[u8]) -> Self { 237 | Self { 238 | mmap: BytesMut::from(src), 239 | path: path.as_ref().to_path_buf(), 240 | create_at: SystemTime::now() 241 | } 242 | } 243 | 244 | #[doc = "Returns the inner mutable bytes"] 245 | #[doc = "# Examples"] 246 | #[doc = "```rust"] 247 | #[doc = "use bytes::BytesMut;"] 248 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 249 | #[doc = ""] 250 | #[doc = concat!("let b1 = ", $name_str, "::from_slice(\"foo.mem\", \"some data...\".as_bytes()).into_bytes_mut();")] 251 | #[doc = "assert_eq!(b1, BytesMut::from(\"some data...\".as_bytes()));"] 252 | #[doc = "```"] 253 | pub fn into_bytes_mut(self) -> BytesMut { 254 | self.mmap 255 | } 256 | 257 | #[doc = "Returns the inner bytes"] 258 | #[doc = "# Examples"] 259 | #[doc = "```rust"] 260 | #[doc = "use bytes::Bytes;"] 261 | #[doc = concat!("use fmmap::raw::", $path_str, $name_str, ";")] 262 | #[doc = ""] 263 | #[doc = concat!("let b1 = ", $name_str, "::from_slice(\"foo.mem\", \"some data...\".as_bytes()).into_bytes();")] 264 | #[doc = "assert_eq!(b1, Bytes::copy_from_slice(\"some data...\".as_bytes()));"] 265 | #[doc = "```"] 266 | pub fn into_bytes(self) -> Bytes { 267 | self.mmap.freeze() 268 | } 269 | 270 | 271 | #[doc = "Make the memory mmap file immutable"] 272 | #[doc = "# Examples"] 273 | #[doc = "```rust"] 274 | #[doc = "use bytes::Bytes;"] 275 | #[doc = concat!("use fmmap::", $path_str, $trait_str, ";")] 276 | #[doc = concat!("use fmmap::raw::", $path_str, "{", $name_str, ",", $immutable_str, "};")] 277 | #[doc = ""] 278 | #[doc = concat!("let b1 = ", $name_str, "::from_string(\"foo.mem\", \"some data...\".to_string()).freeze();")] 279 | #[doc = concat!("let b2 = ", $immutable_str, "::from_string(\"foo.mem\", \"some data...\".to_string());")] 280 | #[doc = "assert!(!b1.is_exec());"] 281 | #[doc = "assert_eq!(b1.len(), b2.len());"] 282 | #[doc = "assert_eq!(b1.as_slice(), b2.as_slice());"] 283 | #[doc = "assert_eq!(b1.path_string(), b2.path_string());"] 284 | #[doc = "```"] 285 | #[inline] 286 | pub fn freeze(self) -> $immutable { 287 | $immutable { 288 | mmap: self.mmap.freeze(), 289 | path: self.path, 290 | create_at: self.create_at, 291 | } 292 | } 293 | } 294 | }; 295 | } 296 | 297 | cfg_sync! { 298 | macro_rules! impl_mmap_file_ext { 299 | ($name: ident) => { 300 | impl MmapFileExt for $name { 301 | fn len(&self) -> usize { 302 | self.mmap.len() 303 | } 304 | 305 | fn as_slice(&self) -> &[u8] { 306 | self.mmap.as_ref() 307 | } 308 | 309 | fn path(&self) -> &Path { 310 | self.path.as_path() 311 | } 312 | 313 | fn is_exec(&self) -> bool { 314 | false 315 | } 316 | 317 | fn metadata(&self) -> crate::error::Result { 318 | Ok(MetaData::memory(MemoryMetaData::new( 319 | self.mmap.len() as u64, 320 | self.create_at, 321 | ))) 322 | } 323 | 324 | noop_file_lock!(); 325 | } 326 | }; 327 | } 328 | mod sync_impl; 329 | pub use sync_impl::{MemoryMmapFile, MemoryMmapFileMut}; 330 | } 331 | 332 | cfg_async! { 333 | macro_rules! impl_async_mmap_file_ext { 334 | ($name: ident) => { 335 | 336 | impl AsyncMmapFileExt for $name { 337 | fn len(&self) -> usize { 338 | self.mmap.len() 339 | } 340 | 341 | fn as_slice(&self) -> &[u8] { 342 | self.mmap.as_ref() 343 | } 344 | 345 | fn path(&self) -> &Path { 346 | self.path.as_path() 347 | } 348 | 349 | fn is_exec(&self) -> bool { 350 | false 351 | } 352 | 353 | noop_file_lock!(); 354 | 355 | async fn metadata(&self) -> crate::error::Result { 356 | Ok(MetaData::memory(MemoryMetaData::new( 357 | self.mmap.len() as u64, 358 | self.create_at, 359 | ))) 360 | } 361 | } 362 | }; 363 | } 364 | 365 | macro_rules! impl_async_mmap_file_mut_ext { 366 | () => { 367 | 368 | impl AsyncMmapFileMutExt for AsyncMemoryMmapFileMut { 369 | #[inline] 370 | fn as_mut_slice(&mut self) -> &mut [u8] { 371 | self.mmap.as_mut() 372 | } 373 | 374 | #[inline] 375 | fn is_cow(&self) -> bool { 376 | false 377 | } 378 | 379 | noop_flush!(); 380 | 381 | #[inline] 382 | async fn truncate(&mut self, max_sz: u64) -> crate::error::Result<()> { 383 | self.mmap.resize(max_sz as usize, 0); 384 | Ok(()) 385 | } 386 | 387 | #[inline] 388 | async fn drop_remove(self) -> crate::error::Result<()> { 389 | Ok(()) 390 | } 391 | 392 | #[inline] 393 | async fn close_with_truncate(self, _max_sz: i64) -> crate::error::Result<()> { 394 | Ok(()) 395 | } 396 | } 397 | }; 398 | } 399 | } 400 | 401 | cfg_async_std!( 402 | pub(crate) mod async_std_impl; 403 | ); 404 | 405 | cfg_smol!( 406 | pub(crate) mod smol_impl; 407 | ); 408 | 409 | cfg_tokio!( 410 | pub(crate) mod tokio_impl; 411 | ); 412 | -------------------------------------------------------------------------------- /fmmap-rs/src/memory/async_std_impl.rs: -------------------------------------------------------------------------------- 1 | use async_std::path::{Path, PathBuf}; 2 | use std::time::SystemTime; 3 | 4 | use bytes::{Bytes, BytesMut}; 5 | use crate::async_std::{AsyncMmapFileExt, AsyncMmapFileMutExt}; 6 | use crate::MetaData; 7 | use crate::metadata::MemoryMetaData; 8 | 9 | define_impl_constructor_for_mmap_file!(AsyncMemoryMmapFile, "AsyncMemoryMmapFile", "async_std::"); 10 | 11 | impl_async_mmap_file_ext!(AsyncMemoryMmapFile); 12 | 13 | define_and_impl_constructor_for_mmap_file_mut!(AsyncMemoryMmapFileMut, "AsyncMemoryMmapFileMut", AsyncMemoryMmapFile, "AsyncMemoryMmapFile", "AsyncMmapFileExt", "async_std::"); 14 | 15 | impl_async_mmap_file_ext!(AsyncMemoryMmapFileMut); 16 | impl_async_mmap_file_mut_ext!(); -------------------------------------------------------------------------------- /fmmap-rs/src/memory/smol_impl.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use std::time::SystemTime; 3 | 4 | use bytes::{Bytes, BytesMut}; 5 | use crate::smol::{AsyncMmapFileExt, AsyncMmapFileMutExt}; 6 | use crate::MetaData; 7 | use crate::metadata::MemoryMetaData; 8 | 9 | define_impl_constructor_for_mmap_file!(AsyncMemoryMmapFile, "AsyncMemoryMmapFile", "smol::"); 10 | 11 | impl_async_mmap_file_ext!(AsyncMemoryMmapFile); 12 | 13 | define_and_impl_constructor_for_mmap_file_mut!(AsyncMemoryMmapFileMut, "AsyncMemoryMmapFileMut", AsyncMemoryMmapFile, "AsyncMemoryMmapFile", "AsyncMmapFileExt", "smol::"); 14 | 15 | impl_async_mmap_file_ext!(AsyncMemoryMmapFileMut); 16 | impl_async_mmap_file_mut_ext!(); -------------------------------------------------------------------------------- /fmmap-rs/src/memory/sync_impl.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use std::time::SystemTime; 3 | use bytes::{Bytes, BytesMut}; 4 | use crate::{MmapFileExt, MmapFileMutExt, MetaData}; 5 | use crate::metadata::MemoryMetaData; 6 | 7 | define_impl_constructor_for_mmap_file!(MemoryMmapFile, "MemoryMmapFile", ""); 8 | 9 | impl_mmap_file_ext!(MemoryMmapFile); 10 | 11 | define_and_impl_constructor_for_mmap_file_mut!(MemoryMmapFileMut, "MemoryMmapFileMut", MemoryMmapFile, "MemoryMmapFile", "MmapFileExt", ""); 12 | 13 | impl_mmap_file_ext!(MemoryMmapFileMut); 14 | 15 | impl MmapFileMutExt for MemoryMmapFileMut { 16 | #[inline] 17 | fn as_mut_slice(&mut self) -> &mut [u8] { 18 | self.mmap.as_mut() 19 | } 20 | 21 | #[inline] 22 | fn is_cow(&self) -> bool { 23 | false 24 | } 25 | 26 | noop_flush!(); 27 | 28 | #[inline] 29 | fn truncate(&mut self, max_sz: u64) -> crate::error::Result<()> { 30 | self.mmap.resize(max_sz as usize, 0); 31 | Ok(()) 32 | } 33 | 34 | #[inline] 35 | fn drop_remove(self) -> crate::error::Result<()> { 36 | Ok(()) 37 | } 38 | 39 | #[inline] 40 | fn close_with_truncate(self, _max_sz: i64) -> crate::error::Result<()> { 41 | Ok(()) 42 | } 43 | } -------------------------------------------------------------------------------- /fmmap-rs/src/memory/tokio_impl.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use std::time::SystemTime; 3 | 4 | use bytes::{Bytes, BytesMut}; 5 | use crate::tokio::{AsyncMmapFileExt, AsyncMmapFileMutExt}; 6 | use crate::MetaData; 7 | use crate::metadata::MemoryMetaData; 8 | 9 | define_impl_constructor_for_mmap_file!(AsyncMemoryMmapFile, "AsyncMemoryMmapFile", "tokio::"); 10 | 11 | impl_async_mmap_file_ext!(AsyncMemoryMmapFile); 12 | 13 | define_and_impl_constructor_for_mmap_file_mut!(AsyncMemoryMmapFileMut, "AsyncMemoryMmapFileMut", AsyncMemoryMmapFile, "AsyncMemoryMmapFile", "AsyncMmapFileExt", "tokio::"); 14 | 15 | impl_async_mmap_file_ext!(AsyncMemoryMmapFileMut); 16 | impl_async_mmap_file_mut_ext!(); -------------------------------------------------------------------------------- /fmmap-rs/src/metadata.rs: -------------------------------------------------------------------------------- 1 | cfg_windows!( 2 | mod windows; 3 | pub use windows::MetaDataExt; 4 | ); 5 | 6 | cfg_unix!( 7 | mod unix; 8 | pub use unix::MetaDataExt; 9 | ); 10 | 11 | use crate::error::Error; 12 | use std::fs::Metadata; 13 | use std::ops::{Deref, DerefMut}; 14 | use std::time::SystemTime; 15 | 16 | /// Empty MetaData 17 | #[derive(Default, Copy, Clone)] 18 | pub struct EmptyMetaData; 19 | 20 | /// MetaData for [`MemoryMmapFile`]/[`MemoryMmapFileMut`] 21 | /// 22 | /// [`MemoryMmapFile`]: structs.MemoryMmapFile.html 23 | /// [`MemoryMmapFileMut`]: structs.MemoryMmapFileMut.html 24 | #[derive(Copy, Clone)] 25 | pub struct MemoryMetaData { 26 | size: u64, 27 | create_at: SystemTime, 28 | } 29 | 30 | impl MemoryMetaData { 31 | pub(crate) fn new(size: u64, create_at: SystemTime) -> Self { 32 | Self { size, create_at } 33 | } 34 | } 35 | 36 | /// MetaData for [`DiskMmapFile`]/[`DiskMmapFileMut`] 37 | /// 38 | /// [`DiskMmapFile`]: structs.DiskMmapFile.html 39 | /// [`DiskMmapFileMut`]: structs.DiskMmapFileMut.html 40 | #[derive(Clone)] 41 | #[repr(transparent)] 42 | pub struct DiskMetaData { 43 | inner: Metadata, 44 | } 45 | 46 | impl DiskMetaData { 47 | pub(crate) fn new(meta: Metadata) -> Self { 48 | Self { inner: meta } 49 | } 50 | } 51 | 52 | impl Deref for DiskMetaData { 53 | type Target = Metadata; 54 | 55 | fn deref(&self) -> &Self::Target { 56 | &self.inner 57 | } 58 | } 59 | 60 | impl DerefMut for DiskMetaData { 61 | fn deref_mut(&mut self) -> &mut Self::Target { 62 | &mut self.inner 63 | } 64 | } 65 | 66 | /// Metadata information about a file. 67 | /// This structure is returned from the metadata or 68 | /// symlink_metadata function or method and represents 69 | /// known metadata about a file such as its permissions, size, modification times, etc 70 | #[repr(transparent)] 71 | pub struct MetaData { 72 | inner: MetaDataInner, 73 | } 74 | 75 | #[enum_dispatch(MetaDataExt)] 76 | enum MetaDataInner { 77 | Empty(EmptyMetaData), 78 | Memory(MemoryMetaData), 79 | Disk(DiskMetaData), 80 | } 81 | 82 | impl MetaData { 83 | pub(crate) fn empty(meta: EmptyMetaData) -> Self { 84 | Self { 85 | inner: MetaDataInner::Empty(meta), 86 | } 87 | } 88 | 89 | pub(crate) fn memory(meta: MemoryMetaData) -> Self { 90 | Self { 91 | inner: MetaDataInner::Memory(meta), 92 | } 93 | } 94 | 95 | pub(crate) fn disk(meta: Metadata) -> Self { 96 | Self { 97 | inner: MetaDataInner::Disk(DiskMetaData::new(meta)), 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /fmmap-rs/src/metadata/unix.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::fs::MetadataExt; 2 | use std::time::{SystemTime, UNIX_EPOCH}; 3 | use crate::error::{Error, ErrorKind, Result}; 4 | use crate::MetaData; 5 | use crate::metadata::{DiskMetaData, EmptyMetaData, MemoryMetaData}; 6 | 7 | /// Utility methods to MetaData 8 | #[enum_dispatch] 9 | pub trait MetaDataExt { 10 | /// Returns the last access time of this metadata. 11 | /// 12 | /// The returned value corresponds to the atime field of stat on Unix platforms and the ftLastAccessTime field on Windows platforms. 13 | /// 14 | /// Note that not all platforms will keep this field update in a file’s metadata, 15 | /// for example Windows has an option to disable updating 16 | /// this time when files are accessed and Linux similarly has noatime. 17 | fn accessed(&self) -> std::result::Result; 18 | 19 | /// Returns the creation time listed in this metadata. 20 | /// 21 | /// The returned value corresponds to the `btime` field of `statx` on Linux kernel starting from to 4.11, 22 | /// the `birthtime` field of stat on other Unix platforms, 23 | /// and the `ftCreationTime` field on Windows platforms. 24 | fn created(&self) -> std::result::Result; 25 | 26 | /// Returns true if this metadata is for a regular file. 27 | /// 28 | /// It will be false for symlink metadata obtained from [`symlink_metadata`]. 29 | /// 30 | /// When the goal is simply to read from (or write to) the source, 31 | /// the most reliable way to test the source can be read (or written to) is to open it. 32 | /// Only using is_file can break workflows like diff <( prog_a ) on a Unix-like system for example. 33 | /// 34 | /// [`symlink_metadata`]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html 35 | fn is_file(&self) -> bool; 36 | 37 | /// Returns `true` if this metadata is for a symbolic link. 38 | #[cfg(feature = "nightly")] 39 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 40 | fn is_symlink(&self) -> bool; 41 | 42 | /// Returns the size of the file, in bytes, this metadata is for. 43 | fn len(&self) -> u64; 44 | 45 | /// Returns the last modification time listed in this metadata. 46 | /// 47 | /// The returned value corresponds to the `mtime` field of `stat` on Unix platforms 48 | /// and the `ftLastWriteTime` field on Windows platforms. 49 | /// 50 | /// # Errors 51 | /// This field might not be available on all platforms, and 52 | /// will return an `Err` on platforms where it is not available. 53 | fn modified(&self) -> std::result::Result; 54 | 55 | /// Returns the ID of the device containing the file. 56 | fn dev(&self) -> u64; 57 | 58 | /// Returns the inode number. 59 | fn ino(&self) -> u64; 60 | 61 | /// Returns the rights applied to this file. 62 | fn mode(&self) -> u32; 63 | 64 | /// Returns the number of hard links pointing to this file. 65 | fn nlink(&self) -> u64; 66 | 67 | /// Returns the user ID of the owner of this file. 68 | fn uid(&self) -> u32; 69 | 70 | /// Returns the group ID of the owner of this file. 71 | fn gid(&self) -> u32; 72 | 73 | /// Returns the device ID of this file (if it is a special one). 74 | fn rdev(&self) -> u64; 75 | 76 | /// Returns the total size of this file in bytes. 77 | fn size(&self) -> u64; 78 | 79 | /// Returns the last access time of the file, in seconds since Unix Epoch. 80 | fn atime(&self) -> i64; 81 | 82 | /// Returns the last access time of the file, in nanoseconds since atime. 83 | fn atime_nsec(&self) -> i64; 84 | 85 | /// Returns the last modification time of the file, in seconds since Unix Epoch. 86 | fn mtime(&self) -> i64; 87 | 88 | /// Returns the last modification time of the file, in nanoseconds since mtime. 89 | fn mtime_nsec(&self) -> i64; 90 | 91 | /// Returns the last status change time of the file, in seconds since Unix Epoch. 92 | fn ctime(&self) -> i64; 93 | 94 | /// Returns the last status change time of the file, in nanoseconds since ctime. 95 | fn ctime_nsec(&self) -> i64; 96 | 97 | /// Returns the block size for filesystem I/O. 98 | fn blksize(&self) -> u64; 99 | 100 | /// Returns the number of blocks allocated to the file, in 512-byte units. 101 | /// 102 | /// Please note that this may be smaller than st_size / 512 when the file has holes. 103 | fn blocks(&self) -> u64; 104 | } 105 | 106 | impl MetaDataExt for MemoryMetaData { 107 | fn accessed(&self) -> Result { 108 | Ok(self.create_at) 109 | } 110 | 111 | #[inline] 112 | fn created(&self) -> Result { 113 | Ok(self.create_at) 114 | } 115 | 116 | fn is_file(&self) -> bool { 117 | false 118 | } 119 | 120 | #[cfg(feature = "nightly")] 121 | fn is_symlink(&self) -> bool { 122 | false 123 | } 124 | 125 | #[inline] 126 | fn len(&self) -> u64 { 127 | self.size 128 | } 129 | 130 | fn modified(&self) -> Result { 131 | Ok(self.create_at) 132 | } 133 | 134 | fn dev(&self) -> u64 { 135 | 0 136 | } 137 | 138 | fn ino(&self) -> u64 { 139 | 0 140 | } 141 | 142 | fn mode(&self) -> u32 { 143 | 0 144 | } 145 | 146 | fn nlink(&self) -> u64 { 147 | 0 148 | } 149 | 150 | fn uid(&self) -> u32 { 151 | 0 152 | } 153 | 154 | fn gid(&self) -> u32 { 155 | 0 156 | } 157 | 158 | fn rdev(&self) -> u64 { 159 | 0 160 | } 161 | 162 | fn size(&self) -> u64 { 163 | self.size 164 | } 165 | 166 | fn atime(&self) -> i64 { 167 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64 168 | } 169 | 170 | fn atime_nsec(&self) -> i64 { 171 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_nanos() as i64 172 | } 173 | 174 | fn mtime(&self) -> i64 { 175 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64 176 | } 177 | 178 | fn mtime_nsec(&self) -> i64 { 179 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_nanos() as i64 180 | } 181 | 182 | fn ctime(&self) -> i64 { 183 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64 184 | } 185 | 186 | fn ctime_nsec(&self) -> i64 { 187 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_nanos() as i64 188 | } 189 | 190 | fn blksize(&self) -> u64 { 191 | 0 192 | } 193 | 194 | fn blocks(&self) -> u64 { 195 | 0 196 | } 197 | } 198 | 199 | impl MetaDataExt for DiskMetaData { 200 | fn accessed(&self) -> Result { 201 | self.inner.accessed().map_err(|e| Error::new(ErrorKind::IO, e)) 202 | } 203 | 204 | fn created(&self) -> Result { 205 | self.inner.created().map_err(|e| Error::new(ErrorKind::IO, e)) 206 | } 207 | 208 | fn is_file(&self) -> bool { 209 | self.inner.is_file() 210 | } 211 | 212 | #[cfg(feature = "nightly")] 213 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 214 | fn is_symlink(&self) -> bool { 215 | self.inner.is_symlink() 216 | } 217 | 218 | fn len(&self) -> u64 { 219 | self.inner.len() 220 | } 221 | 222 | fn modified(&self) -> Result { 223 | self.inner.modified().map_err(|e| Error::new(ErrorKind::IO, e)) 224 | } 225 | 226 | fn dev(&self) -> u64 { 227 | self.inner.dev() 228 | } 229 | 230 | fn ino(&self) -> u64 { 231 | self.inner.ino() 232 | } 233 | 234 | fn mode(&self) -> u32 { 235 | self.inner.mode() 236 | } 237 | 238 | fn nlink(&self) -> u64 { 239 | self.inner.nlink() 240 | } 241 | 242 | fn uid(&self) -> u32 { 243 | self.inner.uid() 244 | } 245 | 246 | fn gid(&self) -> u32 { 247 | self.inner.gid() 248 | } 249 | 250 | fn rdev(&self) -> u64 { 251 | self.inner.rdev() 252 | } 253 | 254 | fn size(&self) -> u64 { 255 | self.inner.size() 256 | } 257 | 258 | fn atime(&self) -> i64 { 259 | self.inner.atime() 260 | } 261 | 262 | fn atime_nsec(&self) -> i64 { 263 | self.inner.atime_nsec() 264 | } 265 | 266 | fn mtime(&self) -> i64 { 267 | self.inner.mtime() 268 | } 269 | 270 | fn mtime_nsec(&self) -> i64 { 271 | self.inner.mtime_nsec() 272 | } 273 | 274 | fn ctime(&self) -> i64 { 275 | self.inner.ctime() 276 | } 277 | 278 | fn ctime_nsec(&self) -> i64 { 279 | self.inner.ctime_nsec() 280 | } 281 | 282 | fn blksize(&self) -> u64 { 283 | self.inner.blksize() 284 | } 285 | 286 | fn blocks(&self) -> u64 { 287 | self.inner.blocks() 288 | } 289 | } 290 | 291 | impl MetaDataExt for EmptyMetaData { 292 | fn accessed(&self) -> Result { 293 | Ok(UNIX_EPOCH) 294 | } 295 | 296 | fn created(&self) -> Result { 297 | Ok(UNIX_EPOCH) 298 | } 299 | 300 | fn is_file(&self) -> bool { 301 | false 302 | } 303 | 304 | #[cfg(feature = "nightly")] 305 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 306 | fn is_symlink(&self) -> bool { 307 | false 308 | } 309 | 310 | fn len(&self) -> u64 { 311 | 0 312 | } 313 | 314 | fn modified(&self) -> Result { 315 | Ok(UNIX_EPOCH) 316 | } 317 | 318 | fn dev(&self) -> u64 { 319 | 0 320 | } 321 | 322 | fn ino(&self) -> u64 { 323 | 0 324 | } 325 | 326 | fn mode(&self) -> u32 { 327 | 0 328 | } 329 | 330 | fn nlink(&self) -> u64 { 331 | 0 332 | } 333 | 334 | fn uid(&self) -> u32 { 335 | 0 336 | } 337 | 338 | fn gid(&self) -> u32 { 339 | 0 340 | } 341 | 342 | fn rdev(&self) -> u64 { 343 | 0 344 | } 345 | 346 | fn size(&self) -> u64 { 347 | 0 348 | } 349 | 350 | fn atime(&self) -> i64 { 351 | 0 352 | } 353 | 354 | fn atime_nsec(&self) -> i64 { 355 | 0 356 | } 357 | 358 | fn mtime(&self) -> i64 { 359 | 0 360 | } 361 | 362 | fn mtime_nsec(&self) -> i64 { 363 | 0 364 | } 365 | 366 | fn ctime(&self) -> i64 { 367 | 0 368 | } 369 | 370 | fn ctime_nsec(&self) -> i64 { 371 | 0 372 | } 373 | 374 | fn blksize(&self) -> u64 { 375 | 0 376 | } 377 | 378 | fn blocks(&self) -> u64 { 379 | 0 380 | } 381 | } 382 | 383 | impl MetaDataExt for MetaData { 384 | fn accessed(&self) -> std::result::Result { 385 | self.inner.accessed() 386 | } 387 | 388 | fn created(&self) -> std::result::Result { 389 | self.inner.created() 390 | } 391 | 392 | fn is_file(&self) -> bool { 393 | self.inner.is_file() 394 | } 395 | 396 | #[cfg(feature = "nightly")] 397 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 398 | fn is_symlink(&self) -> bool { 399 | self.inner.is_symlink() 400 | } 401 | 402 | fn len(&self) -> u64 { 403 | self.inner.len() 404 | } 405 | 406 | fn modified(&self) -> std::result::Result { 407 | self.inner.modified() 408 | } 409 | 410 | fn dev(&self) -> u64 { 411 | self.inner.dev() 412 | } 413 | 414 | fn ino(&self) -> u64 { 415 | self.inner.ino() 416 | } 417 | 418 | fn mode(&self) -> u32 { 419 | self.inner.mode() 420 | } 421 | 422 | fn nlink(&self) -> u64 { 423 | self.inner.nlink() 424 | } 425 | 426 | fn uid(&self) -> u32 { 427 | self.inner.uid() 428 | } 429 | 430 | fn gid(&self) -> u32 { 431 | self.inner.gid() 432 | } 433 | 434 | fn rdev(&self) -> u64 { 435 | self.inner.rdev() 436 | } 437 | 438 | fn size(&self) -> u64 { 439 | self.inner.size() 440 | } 441 | 442 | fn atime(&self) -> i64 { 443 | self.inner.atime() 444 | } 445 | 446 | fn atime_nsec(&self) -> i64 { 447 | self.inner.atime_nsec() 448 | } 449 | 450 | fn mtime(&self) -> i64 { 451 | self.inner.mtime() 452 | } 453 | 454 | fn mtime_nsec(&self) -> i64 { 455 | self.inner.mtime_nsec() 456 | } 457 | 458 | fn ctime(&self) -> i64 { 459 | self.inner.ctime() 460 | } 461 | 462 | fn ctime_nsec(&self) -> i64 { 463 | self.inner.ctime_nsec() 464 | } 465 | 466 | fn blksize(&self) -> u64 { 467 | self.inner.blksize() 468 | } 469 | 470 | fn blocks(&self) -> u64 { 471 | self.inner.blocks() 472 | } 473 | } 474 | 475 | 476 | #[cfg(test)] 477 | mod tests { 478 | use bytes::Bytes; 479 | use crate::empty::EmptyMmapFile; 480 | use crate::{MmapFileExt, MmapFileMutExt, Options}; 481 | use crate::raw::MemoryMmapFile; 482 | use crate::tests::get_random_filename; 483 | use super::*; 484 | 485 | macro_rules! metadata_test { 486 | ($expr: expr) => { 487 | let meta = $expr; 488 | meta.accessed().unwrap(); 489 | meta.created().unwrap(); 490 | assert!(meta.is_file()); 491 | #[cfg(feature = "nightly")] 492 | assert!(!meta.is_symlink()); 493 | assert_eq!(meta.len(), "Hello, fmmap!".len() as u64); 494 | assert_eq!(meta.size(), "Hello, fmmap!".len() as u64); 495 | meta.modified().unwrap(); 496 | meta.dev(); 497 | meta.ino(); 498 | meta.mode(); 499 | meta.nlink(); 500 | meta.uid(); 501 | meta.gid(); 502 | meta.rdev(); 503 | meta.size(); 504 | meta.atime(); 505 | meta.atime_nsec(); 506 | meta.mtime(); 507 | meta.mtime_nsec(); 508 | meta.ctime(); 509 | meta.ctime_nsec(); 510 | meta.blocks(); 511 | meta.blksize(); 512 | }; 513 | } 514 | 515 | #[test] 516 | fn test_metadata() { 517 | let mut file = Options::new() 518 | .max_size("Hello, fmmap!".len() as u64) 519 | .create_mmap_file_mut(get_random_filename()) 520 | .unwrap(); 521 | file.set_remove_on_drop(true); 522 | file.write_all("Hello, fmmap!".as_bytes(), 0).unwrap(); 523 | metadata_test!(file.metadata().unwrap()); 524 | 525 | // let meta = file.metadata().unwrap(); 526 | // meta.accessed().unwrap(); 527 | // meta.created().unwrap(); 528 | // assert!(meta.is_file()); 529 | // #[cfg(feature = "nightly")] 530 | // assert!(!meta.is_symlink()); 531 | // assert_eq!(meta.len(), "Hello, fmmap!".len() as u64); 532 | // assert_eq!(meta.size(), "Hello, fmmap!".len() as u64); 533 | // meta.modified().unwrap(); 534 | // meta.dev(); 535 | // meta.ino(); 536 | // meta.mode(); 537 | // meta.nlink(); 538 | // meta.uid(); 539 | // meta.gid(); 540 | // meta.rdev(); 541 | // meta.size(); 542 | // meta.atime(); 543 | // meta.atime_nsec(); 544 | // meta.mtime(); 545 | // meta.mtime_nsec(); 546 | // meta.ctime(); 547 | // meta.ctime_nsec(); 548 | // meta.blocks(); 549 | // meta.blksize(); 550 | } 551 | 552 | #[cfg(feature = "tokio")] 553 | #[tokio::test] 554 | async fn test_async_metadata() { 555 | use crate::tokio::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions}; 556 | let mut file = AsyncOptions::new() 557 | .max_size("Hello, fmmap!".len() as u64) 558 | .create_mmap_file_mut(get_random_filename()) 559 | .await 560 | .unwrap(); 561 | file.set_remove_on_drop(true); 562 | file.write_all("Hello, fmmap!".as_bytes(), 0).unwrap(); 563 | metadata_test!(file.metadata().await.unwrap()); 564 | 565 | // let meta = file.metadata().await.unwrap(); 566 | // meta.accessed().unwrap(); 567 | // meta.created().unwrap(); 568 | // assert!(meta.is_file()); 569 | // #[cfg(feature = "nightly")] 570 | // assert!(!meta.is_symlink()); 571 | // assert_eq!(meta.len(), "Hello, fmmap!".len() as u64); 572 | // assert_eq!(meta.size(), "Hello, fmmap!".len() as u64); 573 | // meta.modified().unwrap(); 574 | // meta.dev(); 575 | // meta.ino(); 576 | // meta.mode(); 577 | // meta.nlink(); 578 | // meta.uid(); 579 | // meta.gid(); 580 | // meta.rdev(); 581 | // meta.size(); 582 | // meta.atime(); 583 | // meta.atime_nsec(); 584 | // meta.mtime(); 585 | // meta.mtime_nsec(); 586 | // meta.ctime(); 587 | // meta.ctime_nsec(); 588 | // meta.blocks(); 589 | // meta.blksize(); 590 | } 591 | 592 | #[test] 593 | fn test_memory_metadata() { 594 | let file = MemoryMmapFile::new("test.mem", Bytes::from("Hello, fmmap!")); 595 | let meta = file.metadata().unwrap(); 596 | 597 | assert!(!meta.is_file()); 598 | #[cfg(feature = "nightly")] 599 | assert!(!meta.is_symlink()); 600 | assert_eq!(meta.len(), "Hello, fmmap!".len() as u64); 601 | assert_eq!(meta.size(), "Hello, fmmap!".len() as u64); 602 | assert!(meta.modified().unwrap() == meta.created().unwrap() && meta.created().unwrap() == meta.accessed().unwrap()); 603 | assert!(meta.atime() == meta.mtime() && meta.mtime() == meta.ctime()); 604 | assert!(meta.atime_nsec() == meta.mtime_nsec() && meta.mtime_nsec() == meta.ctime_nsec()); 605 | assert_eq!(meta.dev(), 0); 606 | assert_eq!(meta.ino(), 0); 607 | assert_eq!(meta.mode(), 0); 608 | assert_eq!(meta.nlink(), 0); 609 | assert_eq!(meta.uid(), 0); 610 | assert_eq!(meta.gid(), 0); 611 | assert_eq!(meta.rdev(), 0); 612 | assert_eq!(meta.blocks(), 0); 613 | assert_eq!(meta.blksize(), 0); 614 | } 615 | 616 | #[test] 617 | fn test_empty_metadata() { 618 | let file = EmptyMmapFile::default(); 619 | let meta = file.metadata().unwrap(); 620 | 621 | assert_eq!(meta.accessed().unwrap(), UNIX_EPOCH); 622 | assert_eq!(meta.created().unwrap(), UNIX_EPOCH); 623 | assert!(!meta.is_file()); 624 | #[cfg(feature = "nightly")] 625 | assert!(!meta.is_symlink()); 626 | assert_eq!(meta.len(), 0); 627 | assert_eq!(meta.modified().unwrap(), UNIX_EPOCH); 628 | assert_eq!(meta.dev(), 0); 629 | assert_eq!(meta.ino(), 0); 630 | assert_eq!(meta.mode(), 0); 631 | assert_eq!(meta.nlink(), 0); 632 | assert_eq!(meta.uid(), 0); 633 | assert_eq!(meta.gid(), 0); 634 | assert_eq!(meta.rdev(), 0); 635 | assert_eq!(meta.size(), 0); 636 | assert_eq!(meta.atime(), 0); 637 | assert_eq!(meta.atime_nsec(), 0); 638 | assert_eq!(meta.mtime(), 0); 639 | assert_eq!(meta.mtime_nsec(), 0); 640 | assert_eq!(meta.ctime(), 0); 641 | assert_eq!(meta.ctime_nsec(), 0); 642 | assert_eq!(meta.blocks(), 0); 643 | assert_eq!(meta.blksize(), 0); 644 | } 645 | } -------------------------------------------------------------------------------- /fmmap-rs/src/metadata/windows.rs: -------------------------------------------------------------------------------- 1 | use std::os::windows::fs::MetadataExt; 2 | use std::time::{SystemTime, UNIX_EPOCH}; 3 | use crate::error::{Error, ErrorKind, Result}; 4 | use crate::MetaData; 5 | use crate::metadata::{DiskMetaData, EmptyMetaData, MemoryMetaData}; 6 | 7 | /// Utility methods to MetaData 8 | #[enum_dispatch] 9 | pub trait MetaDataExt { 10 | /// Returns the last access time of this metadata. 11 | /// 12 | /// The returned value corresponds to the atime field of stat on Unix platforms and the ftLastAccessTime field on Windows platforms. 13 | /// 14 | /// Note that not all platforms will keep this field update in a file’s metadata, 15 | /// for example Windows has an option to disable updating 16 | /// this time when files are accessed and Linux similarly has noatime. 17 | fn accessed(&self) -> std::result::Result; 18 | 19 | /// Returns the creation time listed in this metadata. 20 | /// 21 | /// The returned value corresponds to the `btime` field of `statx` on Linux kernel starting from to 4.11, 22 | /// the `birthtime` field of stat on other Unix platforms, 23 | /// and the `ftCreationTime` field on Windows platforms. 24 | fn created(&self) -> std::result::Result; 25 | 26 | /// Returns true if this metadata is for a regular file. 27 | /// 28 | /// It will be false for symlink metadata obtained from [`symlink_metadata`]. 29 | /// 30 | /// When the goal is simply to read from (or write to) the source, 31 | /// the most reliable way to test the source can be read (or written to) is to open it. 32 | /// Only using is_file can break workflows like diff <( prog_a ) on a Unix-like system for example. 33 | /// 34 | /// [`symlink_metadata`]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html 35 | fn is_file(&self) -> bool; 36 | 37 | /// Returns `true` if this metadata is for a symbolic link. 38 | #[cfg(feature = "nightly")] 39 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 40 | fn is_symlink(&self) -> bool; 41 | 42 | /// Returns the size of the file, in bytes, this metadata is for. 43 | fn len(&self) -> u64; 44 | 45 | /// Returns the last modification time listed in this metadata. 46 | /// 47 | /// The returned value corresponds to the `mtime` field of `stat` on Unix platforms 48 | /// and the `ftLastWriteTime` field on Windows platforms. 49 | /// 50 | /// # Errors 51 | /// This field might not be available on all platforms, and 52 | /// will return an `Err` on platforms where it is not available. 53 | fn modified(&self) -> std::result::Result; 54 | 55 | /// Returns the value of the `dwFileAttributes` field of this metadata. 56 | fn file_attributes(&self) -> u32; 57 | 58 | /// Returns the value of the `ftCreationTime` field of this metadata. 59 | fn creation_time(&self) -> u64; 60 | 61 | /// Returns the value of the `ftLastAccessTime` field of this metadata. 62 | fn last_access_time(&self) -> u64; 63 | 64 | /// Returns the value of the `ftLastWriteTime` field of this metadata. 65 | fn last_write_time(&self) -> u64; 66 | 67 | /// Returns the value of the `nFileSize{High,Low}` fields of this metadata. 68 | fn file_size(&self) -> u64; 69 | 70 | /// Returns the value of the `dwVolumeSerialNumber` field of this metadata. 71 | #[cfg(feature = "nightly")] 72 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 73 | fn volume_serial_number(&self) -> Option; 74 | 75 | /// Returns the value of the `nNumberOfLinks` field of this metadata. 76 | #[cfg(feature = "nightly")] 77 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 78 | fn number_of_links(&self) -> Option; 79 | 80 | /// Returns the value of the `nFileIndex{Low,High}` fields of this metadata. 81 | #[cfg(feature = "nightly")] 82 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 83 | fn file_index(&self) -> Option; 84 | } 85 | 86 | #[cfg(windows)] 87 | impl MetaDataExt for MemoryMetaData { 88 | fn accessed(&self) -> Result { 89 | Ok(self.create_at) 90 | } 91 | 92 | #[inline] 93 | fn created(&self) -> Result { 94 | Ok(self.create_at) 95 | } 96 | 97 | fn is_file(&self) -> bool { 98 | false 99 | } 100 | 101 | #[cfg(feature = "nightly")] 102 | fn is_symlink(&self) -> bool { 103 | false 104 | } 105 | 106 | #[inline] 107 | fn len(&self) -> u64 { 108 | self.size 109 | } 110 | 111 | fn modified(&self) -> Result { 112 | Ok(self.create_at) 113 | } 114 | 115 | fn file_attributes(&self) -> u32 { 116 | 0 117 | } 118 | 119 | fn creation_time(&self) -> u64 { 120 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() 121 | } 122 | 123 | fn last_access_time(&self) -> u64 { 124 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() 125 | } 126 | 127 | fn last_write_time(&self) -> u64 { 128 | self.create_at.duration_since(UNIX_EPOCH).unwrap().as_secs() 129 | } 130 | 131 | fn file_size(&self) -> u64 { 132 | self.size 133 | } 134 | 135 | #[cfg(feature = "nightly")] 136 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 137 | fn volume_serial_number(&self) -> Option { 138 | None 139 | } 140 | 141 | #[cfg(feature = "nightly")] 142 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 143 | fn number_of_links(&self) -> Option { 144 | None 145 | } 146 | 147 | #[cfg(feature = "nightly")] 148 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 149 | fn file_index(&self) -> Option { 150 | None 151 | } 152 | } 153 | 154 | #[cfg(windows)] 155 | impl MetaDataExt for DiskMetaData { 156 | fn accessed(&self) -> Result { 157 | self.inner.accessed().map_err(|e| Error::new(ErrorKind::IO, e)) 158 | } 159 | 160 | fn created(&self) -> Result { 161 | self.inner.created().map_err(|e| Error::new(ErrorKind::IO, e)) 162 | } 163 | 164 | fn is_file(&self) -> bool { 165 | self.inner.is_file() 166 | } 167 | 168 | #[cfg(feature = "nightly")] 169 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 170 | fn is_symlink(&self) -> bool { 171 | self.inner.is_symlink() 172 | } 173 | 174 | fn len(&self) -> u64 { 175 | self.inner.len() 176 | } 177 | 178 | fn modified(&self) -> Result { 179 | self.inner.modified().map_err(|e| Error::new(ErrorKind::IO, e)) 180 | } 181 | 182 | fn file_attributes(&self) -> u32 { 183 | self.inner.file_attributes() 184 | } 185 | 186 | fn creation_time(&self) -> u64 { 187 | self.inner.creation_time() 188 | } 189 | 190 | fn last_access_time(&self) -> u64 { 191 | self.inner.last_access_time() 192 | } 193 | 194 | fn last_write_time(&self) -> u64 { 195 | self.inner.last_write_time() 196 | } 197 | 198 | fn file_size(&self) -> u64 { 199 | self.inner.file_size() 200 | } 201 | 202 | #[cfg(feature = "nightly")] 203 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 204 | fn volume_serial_number(&self) -> Option { 205 | self.inner.volume_serial_number() 206 | } 207 | 208 | #[cfg(feature = "nightly")] 209 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 210 | fn number_of_links(&self) -> Option { 211 | self.inner.number_of_links() 212 | } 213 | 214 | #[cfg(feature = "nightly")] 215 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 216 | fn file_index(&self) -> Option { 217 | self.inner.file_index() 218 | } 219 | } 220 | 221 | #[cfg(windows)] 222 | impl MetaDataExt for EmptyMetaData { 223 | fn accessed(&self) -> Result { 224 | Ok(UNIX_EPOCH) 225 | } 226 | 227 | fn created(&self) -> Result { 228 | Ok(UNIX_EPOCH) 229 | } 230 | 231 | fn is_file(&self) -> bool { 232 | false 233 | } 234 | 235 | #[cfg(feature = "nightly")] 236 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 237 | fn is_symlink(&self) -> bool { 238 | false 239 | } 240 | 241 | fn len(&self) -> u64 { 242 | 0 243 | } 244 | 245 | fn modified(&self) -> Result { 246 | Ok(UNIX_EPOCH) 247 | } 248 | 249 | fn file_attributes(&self) -> u32 { 250 | 0 251 | } 252 | 253 | fn creation_time(&self) -> u64 { 254 | 0 255 | } 256 | 257 | fn last_access_time(&self) -> u64 { 258 | 0 259 | } 260 | 261 | fn last_write_time(&self) -> u64 { 262 | 0 263 | } 264 | 265 | fn file_size(&self) -> u64 { 266 | 0 267 | } 268 | 269 | #[cfg(feature = "nightly")] 270 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 271 | fn volume_serial_number(&self) -> Option { 272 | None 273 | } 274 | 275 | #[cfg(feature = "nightly")] 276 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 277 | fn number_of_links(&self) -> Option { 278 | None 279 | } 280 | 281 | #[cfg(feature = "nightly")] 282 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 283 | fn file_index(&self) -> Option { 284 | None 285 | } 286 | } 287 | 288 | #[cfg(windows)] 289 | impl MetaDataExt for MetaData { 290 | fn accessed(&self) -> std::result::Result { 291 | self.inner.accessed() 292 | } 293 | 294 | fn created(&self) -> std::result::Result { 295 | self.inner.created() 296 | } 297 | 298 | fn is_file(&self) -> bool { 299 | self.inner.is_file() 300 | } 301 | 302 | #[cfg(feature = "nightly")] 303 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 304 | fn is_symlink(&self) -> bool { 305 | self.inner.is_symlink() 306 | } 307 | 308 | fn len(&self) -> u64 { 309 | self.inner.len() 310 | } 311 | 312 | fn modified(&self) -> std::result::Result { 313 | self.inner.modified() 314 | } 315 | 316 | fn file_attributes(&self) -> u32 { 317 | self.inner.file_attributes() 318 | } 319 | 320 | fn creation_time(&self) -> u64 { 321 | self.inner.creation_time() 322 | } 323 | 324 | fn last_access_time(&self) -> u64 { 325 | self.inner.last_access_time() 326 | } 327 | 328 | fn last_write_time(&self) -> u64 { 329 | self.inner.last_write_time() 330 | } 331 | 332 | fn file_size(&self) -> u64 { 333 | self.inner.file_size() 334 | } 335 | 336 | #[cfg(feature = "nightly")] 337 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 338 | fn volume_serial_number(&self) -> Option { 339 | self.inner.volume_serial_number() 340 | } 341 | 342 | #[cfg(feature = "nightly")] 343 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 344 | fn number_of_links(&self) -> Option { 345 | self.inner.number_of_links() 346 | } 347 | 348 | #[cfg(feature = "nightly")] 349 | #[cfg_attr(docsrs, doc(cfg(feature = "nightly")))] 350 | fn file_index(&self) -> Option { 351 | self.inner.file_index() 352 | } 353 | } 354 | 355 | #[cfg(test)] 356 | mod tests { 357 | use std::time::UNIX_EPOCH; 358 | use bytes::Bytes; 359 | use crate::empty::EmptyMmapFile; 360 | use crate::{MetaDataExt, MmapFileExt, MmapFileMutExt, Options}; 361 | use crate::raw::MemoryMmapFile; 362 | use crate::tests::get_random_filename; 363 | 364 | #[test] 365 | fn test_metadata() { 366 | let mut file = Options::new() 367 | .max_size("Hello, fmmap!".len() as u64) 368 | .create_mmap_file_mut(get_random_filename()) 369 | .unwrap(); 370 | file.set_remove_on_drop(true); 371 | file.write_all("Hello, fmmap!".as_bytes(), 0).unwrap(); 372 | 373 | let meta = file.metadata().unwrap(); 374 | meta.accessed().unwrap(); 375 | meta.created().unwrap(); 376 | assert!(meta.is_file()); 377 | #[cfg(feature = "nightly")] 378 | assert!(!meta.is_symlink()); 379 | assert_eq!(meta.len(), "Hello, fmmap!".len() as u64); 380 | assert_eq!(meta.file_size(), "Hello, fmmap!".len() as u64); 381 | meta.file_attributes(); 382 | meta.creation_time(); 383 | meta.last_access_time(); 384 | meta.last_write_time(); 385 | #[cfg(feature = "nightly")] 386 | assert!(meta.volume_serial_number().is_some()); 387 | #[cfg(feature = "nightly")] 388 | assert!(meta.number_of_links().is_some()); 389 | #[cfg(feature = "nightly")] 390 | assert!(meta.file_index().is_some()); 391 | } 392 | 393 | #[test] 394 | fn test_memory_metadata() { 395 | let file = MemoryMmapFile::new("test.mem", Bytes::from("Hello, fmmap!")); 396 | let meta = file.metadata().unwrap(); 397 | 398 | assert!(!meta.is_file()); 399 | #[cfg(feature = "nightly")] 400 | assert!(!meta.is_symlink()); 401 | assert_eq!(meta.len(), "Hello, fmmap!".len() as u64); 402 | assert_eq!(meta.file_size(), "Hello, fmmap!".len() as u64); 403 | assert_eq!(meta.file_attributes(), 0); 404 | assert!(meta.modified().unwrap() == meta.created().unwrap() && meta.created().unwrap() == meta.accessed().unwrap()); 405 | assert!(meta.creation_time() == meta.last_access_time() && meta.last_access_time() == meta.last_write_time()); 406 | #[cfg(feature = "nightly")] 407 | assert_eq!(meta.volume_serial_number(), None); 408 | #[cfg(feature = "nightly")] 409 | assert_eq!(meta.number_of_links(), None); 410 | #[cfg(feature = "nightly")] 411 | assert_eq!(meta.file_index(), None); 412 | } 413 | 414 | #[test] 415 | fn test_empty_metadata() { 416 | let file = EmptyMmapFile::default(); 417 | let meta = file.metadata().unwrap(); 418 | 419 | assert_eq!(meta.accessed().unwrap(), UNIX_EPOCH); 420 | assert_eq!(meta.created().unwrap(), UNIX_EPOCH); 421 | assert!(!meta.is_file()); 422 | #[cfg(feature = "nightly")] 423 | assert!(!meta.is_symlink()); 424 | assert_eq!(meta.len(), 0); 425 | assert_eq!(meta.modified().unwrap(), UNIX_EPOCH); 426 | assert_eq!(meta.file_attributes(), 0); 427 | assert_eq!(meta.creation_time(), 0); 428 | assert_eq!(meta.last_access_time(), 0); 429 | assert_eq!(meta.last_write_time(), 0); 430 | assert_eq!(meta.file_size(), 0); 431 | #[cfg(feature = "nightly")] 432 | assert_eq!(meta.volume_serial_number(), None); 433 | #[cfg(feature = "nightly")] 434 | assert_eq!(meta.number_of_links(), None); 435 | #[cfg(feature = "nightly")] 436 | assert_eq!(meta.file_index(), None); 437 | } 438 | } -------------------------------------------------------------------------------- /fmmap-rs/src/mmap_file/async_std_impl.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::mem; 3 | use async_std::path::{Path, PathBuf}; 4 | 5 | use async_std::fs::remove_file; 6 | use async_std::io::{WriteExt as AsyncWriteExt, Cursor}; 7 | use crate::async_std::{AsyncMmapFileReader, AsyncMmapFileWriter, AsyncOptions}; 8 | use crate::disk::async_std_impl::{AsyncDiskMmapFile, AsyncDiskMmapFileMut}; 9 | use crate::empty::async_std_impl::AsyncEmptyMmapFile; 10 | use crate::error::{Error, ErrorKind, Result}; 11 | use crate::memory::async_std_impl::{AsyncMemoryMmapFile, AsyncMemoryMmapFileMut}; 12 | use crate::metadata::MetaData; 13 | 14 | declare_async_mmap_file_ext!(AsyncDiskMmapFileMut, AsyncOptions, AsyncMmapFileReader); 15 | 16 | declare_async_mmap_file_mut_ext!(AsyncMmapFileWriter); 17 | 18 | declare_and_impl_inners!(); 19 | 20 | declare_and_impl_async_mmap_file!("async_std_async", "async_std::task", "async_std"); 21 | 22 | delcare_and_impl_async_mmap_file_mut!("async_std_async", "async_std::task", "async_std"); 23 | 24 | impl_async_tests!("std_async", async_std::test, async_std, AsyncMmapFile, AsyncMmapFileMut); -------------------------------------------------------------------------------- /fmmap-rs/src/mmap_file/smol_impl.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::mem; 3 | use std::path::{Path, PathBuf}; 4 | 5 | use smol::fs::remove_file; 6 | use smol::io::{Cursor, AsyncWriteExt}; 7 | use crate::smol::{AsyncMmapFileReader, AsyncMmapFileWriter, AsyncOptions}; 8 | use crate::disk::smol_impl::{AsyncDiskMmapFile, AsyncDiskMmapFileMut}; 9 | use crate::empty::smol_impl::AsyncEmptyMmapFile; 10 | use crate::error::{Error, ErrorKind, Result}; 11 | use crate::memory::smol_impl::{AsyncMemoryMmapFile, AsyncMemoryMmapFileMut}; 12 | use crate::metadata::MetaData; 13 | 14 | declare_async_mmap_file_ext!(AsyncDiskMmapFileMut, AsyncOptions, AsyncMmapFileReader); 15 | 16 | declare_async_mmap_file_mut_ext!(AsyncMmapFileWriter); 17 | 18 | declare_and_impl_inners!(); 19 | 20 | declare_and_impl_async_mmap_file!("smol_async", "smol", "smol"); 21 | 22 | delcare_and_impl_async_mmap_file_mut!("smol_async", "smol", "smol"); 23 | 24 | impl_async_tests!("smol_async", smol_potat::test, smol, AsyncMmapFile, AsyncMmapFileMut); -------------------------------------------------------------------------------- /fmmap-rs/src/mmap_file/tokio_impl.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::mem; 3 | use std::io::Cursor; 4 | use std::path::{Path, PathBuf}; 5 | use tokio::io::AsyncWriteExt; 6 | use tokio::fs::remove_file; 7 | use crate::tokio::{AsyncMmapFileReader, AsyncMmapFileWriter, AsyncOptions}; 8 | use crate::disk::tokio_impl::{AsyncDiskMmapFile, AsyncDiskMmapFileMut}; 9 | use crate::empty::tokio_impl::AsyncEmptyMmapFile; 10 | use crate::error::{Error, ErrorKind, Result}; 11 | use crate::memory::tokio_impl::{AsyncMemoryMmapFile, AsyncMemoryMmapFileMut}; 12 | use crate::metadata::MetaData; 13 | 14 | declare_async_mmap_file_ext!(AsyncDiskMmapFileMut, AsyncOptions, AsyncMmapFileReader); 15 | 16 | declare_async_mmap_file_mut_ext!(AsyncMmapFileWriter); 17 | 18 | declare_and_impl_inners!(); 19 | 20 | declare_and_impl_async_mmap_file!("tokio_async", "tokio_test", "tokio"); 21 | 22 | delcare_and_impl_async_mmap_file_mut!("tokio_async", "tokio_test", "tokio"); 23 | 24 | impl_async_tests!("tokio_async", tokio::test, tokio, AsyncMmapFile, AsyncMmapFileMut); -------------------------------------------------------------------------------- /fmmap-rs/src/options/async_std_impl.rs: -------------------------------------------------------------------------------- 1 | use async_std::fs::OpenOptions; 2 | #[cfg(unix)] 3 | use async_std::os::unix::fs::OpenOptionsExt; 4 | use async_std::path::Path; 5 | 6 | use crate::async_std::{AsyncMmapFile, AsyncMmapFileMut}; 7 | use crate::error::Error; 8 | use crate::raw::async_std::{AsyncDiskMmapFile, AsyncDiskMmapFileMut}; 9 | use memmap2::MmapOptions; 10 | 11 | declare_and_impl_async_options!("async_std_async", "tokio_test", "async_std"); 12 | 13 | impl_async_options_tests!("std_async", async_std::test, async_std); 14 | 15 | #[cfg(unix)] 16 | impl_options_unix_ext!(AsyncOptions); 17 | -------------------------------------------------------------------------------- /fmmap-rs/src/options/smol_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::raw::smol::{AsyncDiskMmapFile, AsyncDiskMmapFileMut}; 3 | use crate::smol::{AsyncMmapFile, AsyncMmapFileMut}; 4 | use memmap2::MmapOptions; 5 | #[cfg(unix)] 6 | use smol::fs::unix::OpenOptionsExt; 7 | #[cfg(windows)] 8 | use smol::fs::windows::OpenOptionsExt; 9 | use smol::fs::OpenOptions; 10 | use std::path::Path; 11 | 12 | declare_and_impl_async_options!("smol_async", "smol", "smol"); 13 | 14 | impl_async_options_tests!("smol_async", smol_potat::test, smol); 15 | 16 | #[cfg(unix)] 17 | impl_options_unix_ext!(AsyncOptions); 18 | 19 | #[cfg(windows)] 20 | impl_options_windows_ext!(AsyncOptions); 21 | -------------------------------------------------------------------------------- /fmmap-rs/src/options/sync_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::raw::{DiskMmapFile, DiskMmapFileMut}; 3 | use crate::{MmapFile, MmapFileMut}; 4 | use memmap2::MmapOptions; 5 | use std::fs::OpenOptions; 6 | #[cfg(unix)] 7 | use std::os::unix::fs::OpenOptionsExt; 8 | #[cfg(windows)] 9 | use std::os::windows::fs::OpenOptionsExt; 10 | use std::path::Path; 11 | 12 | declare_and_impl_options!(Options, OpenOptions); 13 | 14 | #[cfg(unix)] 15 | impl_options_unix_ext!(Options); 16 | 17 | #[cfg(windows)] 18 | impl_options_windows_ext!(Options); 19 | 20 | impl Options { 21 | /// Create a new file and mmap this file with [`Options`] 22 | /// 23 | /// # Examples 24 | /// 25 | /// ```ignore 26 | /// use fmmap::{Options, MmapFileMut, MmapFileMutExt, MmapFileExt}; 27 | /// # use scopeguard::defer; 28 | /// 29 | /// let mut file = Options::new().max_size(100).create_mmap_file_mut("create_with_options_test.txt").unwrap(); 30 | /// # defer!(std::fs::remove_file("create_with_options_test.txt").unwrap()); 31 | /// assert!(!file.is_empty()); 32 | /// 33 | /// file.write_all("some data...".as_bytes(), 0).unwrap(); 34 | /// file.flush().unwrap(); 35 | /// ``` 36 | /// 37 | /// [`Options`]: struct.Options.html 38 | pub fn create_mmap_file_mut>(self, path: P) -> Result { 39 | Ok(MmapFileMut::from(DiskMmapFileMut::create_with_options( 40 | path, self, 41 | )?)) 42 | } 43 | 44 | /// Open a readable memory map backed by a file with [`Options`] 45 | /// 46 | /// # Examples 47 | /// 48 | /// ```ignore 49 | /// use fmmap::{Options, MmapFile, MmapFileExt}; 50 | /// # use scopeguard::defer; 51 | /// 52 | /// # let mut file = std::fs::File::create("open_test_with_options.txt").unwrap(); 53 | /// # defer!(std::fs::remove_file("open_test_with_options.txt").unwrap()); 54 | /// # std::io::Write::write_all(&mut file, "sanity text".as_bytes()).unwrap(); 55 | /// # std::io::Write::write_all(&mut file, "some data...".as_bytes()).unwrap(); 56 | /// # drop(file); 57 | /// 58 | /// // open and mmap the file 59 | /// let file = Options::new() 60 | /// // allow read 61 | /// .read(true) 62 | /// // allow write 63 | /// .write(true) 64 | /// // allow append 65 | /// .append(true) 66 | /// // truncate to 100 67 | /// .max_size(100) 68 | /// // mmap content after the sanity text 69 | /// .offset("sanity text".as_bytes().len() as u64) 70 | /// .open_mmap_file("open_test_with_options.txt") 71 | /// .unwrap(); 72 | /// 73 | /// let mut buf = vec![0; "some data...".len()]; 74 | /// file.read_exact(buf.as_mut_slice(), 0); 75 | /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); 76 | /// ``` 77 | /// 78 | /// [`Options`]: struct.Options.html 79 | pub fn open_mmap_file>(self, path: P) -> Result { 80 | Ok(MmapFile::from(DiskMmapFile::open_with_options(path, self)?)) 81 | } 82 | 83 | /// Open a readable and executable memory map backed by a file with [`Options`]. 84 | /// 85 | /// # Examples 86 | /// 87 | /// ```ignore 88 | /// use fmmap::{Options, MmapFile, MmapFileExt}; 89 | /// # use scopeguard::defer; 90 | /// 91 | /// # let mut file = std::fs::File::create("exec_mmap_file.txt").unwrap(); 92 | /// # defer!(std::fs::remove_file("exec_mmap_file.txt").unwrap()); 93 | /// # std::io::Write::write_all(&mut file, "sanity text".as_bytes()).unwrap(); 94 | /// # std::io::Write::write_all(&mut file, "some data...".as_bytes()).unwrap(); 95 | /// # drop(file); 96 | /// 97 | /// // open and mmap the file 98 | /// let file = Options::new() 99 | /// // allow read 100 | /// .read(true) 101 | /// // mmap content after the sanity text 102 | /// .offset("sanity text".as_bytes().len() as u64) 103 | /// .open_exec_mmap_file("exec_mmap_file.txt") 104 | /// .unwrap(); 105 | /// let mut buf = vec![0; "some data...".len()]; 106 | /// file.read_exact(buf.as_mut_slice(), 0); 107 | /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); 108 | /// ``` 109 | /// 110 | /// [`Options`]: struct.Options.html 111 | pub fn open_exec_mmap_file>(self, path: P) -> Result { 112 | Ok(MmapFile::from(DiskMmapFile::open_exec_with_options( 113 | path, self, 114 | )?)) 115 | } 116 | 117 | /// Open or Create(if not exists) a file and mmap this file with [`Options`]. 118 | /// 119 | /// # Examples 120 | /// 121 | /// File already exists 122 | /// 123 | /// ```ignore 124 | /// use fmmap::{MmapFileMut, MmapFileExt, MmapFileMutExt, Options}; 125 | /// use std::fs::File; 126 | /// use std::io::{Read, Seek, SeekFrom, Write}; 127 | /// # use scopeguard::defer; 128 | /// 129 | /// # let mut file = File::create("mmap_file_mut.txt").unwrap(); 130 | /// # defer!(std::fs::remove_file("mmap_file_mut.txt").unwrap()); 131 | /// # file.write_all("sanity text".as_bytes()).unwrap(); 132 | /// # file.write_all("some data...".as_bytes()).unwrap(); 133 | /// # drop(file); 134 | /// 135 | /// // mmap the file with options 136 | /// let mut file = Options::new() 137 | /// // allow read 138 | /// .read(true) 139 | /// // allow write 140 | /// .write(true) 141 | /// // allow append 142 | /// .append(true) 143 | /// // truncate to 100 144 | /// .max_size(100) 145 | /// // mmap content after the sanity text 146 | /// .offset("sanity text".as_bytes().len() as u64) 147 | /// .open_mmap_file_mut("mmap_file_mut.txt") 148 | /// .unwrap(); 149 | /// let mut buf = vec![0; "some data...".len()]; 150 | /// file.read_exact(buf.as_mut_slice(), 0); 151 | /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); 152 | /// 153 | /// // modify the file data 154 | /// file.truncate(("some modified data...".len() + "sanity text".len()) as u64).unwrap(); 155 | /// file.write_all("some modified data...".as_bytes(), 0).unwrap(); 156 | /// file.flush().unwrap(); 157 | /// drop(file); 158 | /// 159 | /// // reopen to check content 160 | /// let mut buf = vec![0; "some modified data...".len()]; 161 | /// let mut file = File::open("mmap_file_mut.txt").unwrap(); 162 | /// file.seek(SeekFrom::Start("sanity text".as_bytes().len() as u64)).unwrap(); 163 | /// file.read_exact(buf.as_mut_slice()).unwrap(); 164 | /// assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); 165 | /// ``` 166 | /// 167 | /// File does not exists 168 | /// 169 | /// ```ignore 170 | /// use fmmap::{MmapFileMut, MmapFileExt, MmapFileMutExt, Options}; 171 | /// use std::fs::File; 172 | /// use std::io::{Read, Write}; 173 | /// # use scopeguard::defer; 174 | /// 175 | /// // mmap the file with options 176 | /// let mut file = Options::new() 177 | /// // allow read 178 | /// .read(true) 179 | /// // allow write 180 | /// .write(true) 181 | /// // allow append 182 | /// .append(true) 183 | /// // truncate to 100 184 | /// .max_size(100) 185 | /// .open_mmap_file_mut("mmap_file_mut.txt") 186 | /// .unwrap(); 187 | /// 188 | /// # defer!(std::fs::remove_file("mmap_file_mut.txt").unwrap()); 189 | /// file.write_all("some data...".as_bytes(), 0).unwrap(); 190 | /// 191 | /// let mut buf = vec![0; "some data...".len()]; 192 | /// file.read_exact(buf.as_mut_slice(), 0); 193 | /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); 194 | /// 195 | /// // modify the file data 196 | /// file.truncate("some modified data...".len() as u64).unwrap(); 197 | /// file.write_all("some modified data...".as_bytes(), 0).unwrap(); 198 | /// file.flush().unwrap(); 199 | /// drop(file); 200 | /// 201 | /// // reopen to check content 202 | /// let mut buf = vec![0; "some modified data...".len()]; 203 | /// let mut file = File::open("mmap_file_mut.txt").unwrap(); 204 | /// file.read_exact(buf.as_mut_slice()).unwrap(); 205 | /// assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); 206 | /// ``` 207 | /// 208 | /// [`Options`]: struct.Options.html 209 | pub fn open_mmap_file_mut>(self, path: P) -> Result { 210 | Ok(MmapFileMut::from(DiskMmapFileMut::open_with_options( 211 | path, self, 212 | )?)) 213 | } 214 | 215 | /// Open an existing file and mmap this file with [`Options`] 216 | /// 217 | /// # Examples 218 | /// ```ignore 219 | /// use fmmap::{MmapFileMut, MmapFileExt, MmapFileMutExt, Options}; 220 | /// use std::fs::File; 221 | /// use std::io::{Read, Seek, SeekFrom, Write}; 222 | /// # use scopeguard::defer; 223 | /// 224 | /// // create a temp file 225 | /// let mut file = File::create("exist_mmap_file_mut.txt").unwrap(); 226 | /// # defer!(std::fs::remove_file("exist_mmap_file_mut.txt").unwrap()); 227 | /// file.write_all("sanity text".as_bytes()).unwrap(); 228 | /// file.write_all("some data...".as_bytes()).unwrap(); 229 | /// drop(file); 230 | /// 231 | /// // mmap the file with options 232 | /// let mut file = Options::new() 233 | /// // truncate to 100 234 | /// .max_size(100) 235 | /// // mmap content after the sanity text 236 | /// .offset("sanity text".as_bytes().len() as u64) 237 | /// .open_exist_mmap_file_mut("exist_mmap_file_mut.txt") 238 | /// .unwrap(); 239 | /// let mut buf = vec![0; "some data...".len()]; 240 | /// file.read_exact(buf.as_mut_slice(), 0); 241 | /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); 242 | /// 243 | /// // modify the file data 244 | /// file.truncate(("some modified data...".len() + "sanity text".len()) as u64).unwrap(); 245 | /// file.write_all("some modified data...".as_bytes(), 0).unwrap(); 246 | /// file.flush().unwrap(); 247 | /// drop(file); 248 | /// 249 | /// // reopen to check content 250 | /// let mut buf = vec![0; "some modified data...".len()]; 251 | /// let mut file = File::open("exist_mmap_file_mut.txt").unwrap(); 252 | /// file.seek(SeekFrom::Start("sanity text".as_bytes().len() as u64)).unwrap(); 253 | /// file.read_exact(buf.as_mut_slice()).unwrap(); 254 | /// assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); 255 | /// ``` 256 | /// 257 | /// [`Options`]: struct.Options.html 258 | pub fn open_exist_mmap_file_mut>(self, path: P) -> Result { 259 | Ok(MmapFileMut::from(DiskMmapFileMut::open_exist_with_options( 260 | path, self, 261 | )?)) 262 | } 263 | 264 | /// Open and mmap an existing file in copy-on-write mode(copy-on-write memory map backed by a file) with [`Options`]. 265 | /// Data written to the memory map will not be visible by other processes, and will not be carried through to the underlying file. 266 | /// 267 | /// # Examples 268 | /// 269 | /// ```ignore 270 | /// use fmmap::{MmapFileMut, MmapFileExt, MmapFileMutExt, Options}; 271 | /// use std::fs::File; 272 | /// use std::io::{Read, Seek, Write, SeekFrom}; 273 | /// # use scopeguard::defer; 274 | /// 275 | /// // create a temp file 276 | /// let mut file = File::create("cow_mmap_file_mut.txt").unwrap(); 277 | /// # defer!(std::fs::remove_file("cow_mmap_file_mut.txt").unwrap()); 278 | /// file.write_all("sanity text".as_bytes()).unwrap(); 279 | /// file.write_all("some data...".as_bytes()).unwrap(); 280 | /// drop(file); 281 | /// 282 | /// // mmap the file with options 283 | /// let mut file = Options::new() 284 | /// // mmap content after the sanity text 285 | /// .offset("sanity text".as_bytes().len() as u64) 286 | /// .open_cow_mmap_file_mut("cow_mmap_file_mut.txt") 287 | /// .unwrap(); 288 | /// assert!(file.is_cow()); 289 | /// 290 | /// let mut buf = vec![0; "some data...".len()]; 291 | /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); 292 | /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); 293 | /// 294 | /// // modify the file data 295 | /// file.write_all("some data!!!".as_bytes(), 0).unwrap(); 296 | /// file.flush().unwrap(); 297 | /// 298 | /// // cow, change will only be seen in current caller 299 | /// assert_eq!(file.as_slice(), "some data!!!".as_bytes()); 300 | /// drop(file); 301 | /// 302 | /// // reopen to check content, cow will not change the content. 303 | /// let mut file = File::open("cow_mmap_file_mut.txt").unwrap(); 304 | /// let mut buf = vec![0; "some data...".len()]; 305 | /// // skip the sanity text 306 | /// file.seek(SeekFrom::Start("sanity text".as_bytes().len() as u64)).unwrap(); 307 | /// file.read_exact(buf.as_mut_slice()).unwrap(); 308 | /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); 309 | /// ``` 310 | /// 311 | /// [`Options`]: struct.Options.html 312 | pub fn open_cow_mmap_file_mut>(self, path: P) -> Result { 313 | Ok(MmapFileMut::from(DiskMmapFileMut::open_cow_with_options( 314 | path, self, 315 | )?)) 316 | } 317 | } 318 | 319 | #[cfg(test)] 320 | mod tests { 321 | use crate::sync::{MmapFileExt, MmapFileMut, MmapFileMutExt, Options}; 322 | use scopeguard::defer; 323 | 324 | #[test] 325 | fn test_create_mmap_file_mut() { 326 | let path = concat!("sync", "_options_create_mmap_file_mut.txt"); 327 | defer!(std::fs::remove_file(path).unwrap()); 328 | let mut file = Options::new() 329 | // truncate to 100 330 | .max_size(100) 331 | .create_mmap_file_mut(path) 332 | .unwrap(); 333 | 334 | assert!(!file.is_empty()); 335 | file.write_all("some data...".as_bytes(), 0).unwrap(); 336 | file.flush().unwrap(); 337 | } 338 | 339 | #[test] 340 | fn test_open_mmap_file() { 341 | let path = concat!("sync", "_options_open_mmap_file.txt"); 342 | defer!(std::fs::remove_file(path).unwrap()); 343 | let mut file = MmapFileMut::create(path).unwrap(); 344 | file.truncate(23).unwrap(); 345 | file.write_all("sanity text".as_bytes(), 0).unwrap(); 346 | file.write_all("some data...".as_bytes(), "sanity text".len()) 347 | .unwrap(); 348 | file.flush().unwrap(); 349 | drop(file); 350 | 351 | // mmap the file 352 | let file = Options::new() 353 | // mmap content after the sanity text 354 | .offset("sanity text".len() as u64) 355 | .open_mmap_file(path) 356 | .unwrap(); 357 | let mut buf = vec![0; "some data...".len()]; 358 | file.read_exact(buf.as_mut_slice(), 0).unwrap(); 359 | assert_eq!(buf.as_slice(), "some data...".as_bytes()); 360 | } 361 | 362 | #[test] 363 | fn test_open_mmap_file_exec() { 364 | let path = concat!("sync", "_options_open_exec_mmap_file.txt"); 365 | defer!(std::fs::remove_file(path).unwrap()); 366 | let mut file = MmapFileMut::create(path).unwrap(); 367 | file.truncate(23).unwrap(); 368 | file.write_all("sanity text".as_bytes(), 0).unwrap(); 369 | file.write_all("some data...".as_bytes(), "sanity text".len()) 370 | .unwrap(); 371 | file.flush().unwrap(); 372 | drop(file); 373 | 374 | // mmap the file 375 | let file = Options::new() 376 | // mmap content after the sanity text 377 | .offset("sanity text".len() as u64) 378 | .open_exec_mmap_file(path) 379 | .unwrap(); 380 | let mut buf = vec![0; "some data...".len()]; 381 | file.read_exact(buf.as_mut_slice(), 0).unwrap(); 382 | assert_eq!(buf.as_slice(), "some data...".as_bytes()); 383 | } 384 | 385 | #[test] 386 | fn test_open_mmap_file_mut() { 387 | let path = concat!("sync", "_options_open_mmap_file_mut.txt"); 388 | defer!(std::fs::remove_file(path).unwrap()); 389 | let mut file = MmapFileMut::create(path).unwrap(); 390 | file.truncate(23).unwrap(); 391 | file.write_all("sanity text".as_bytes(), 0).unwrap(); 392 | file.write_all("some data...".as_bytes(), "sanity text".len()) 393 | .unwrap(); 394 | file.flush().unwrap(); 395 | drop(file); 396 | 397 | let mut file = Options::new() 398 | // allow read 399 | .read(true) 400 | // allow write 401 | .write(true) 402 | // allow append 403 | .append(true) 404 | // truncate to 100 405 | .max_size(100) 406 | // mmap content after the sanity text 407 | .offset("sanity text".len() as u64) 408 | .open_mmap_file_mut(path) 409 | .unwrap(); 410 | let mut buf = vec![0; "some data...".len()]; 411 | file.read_exact(buf.as_mut_slice(), 0).unwrap(); 412 | assert_eq!(buf.as_slice(), "some data...".as_bytes()); 413 | 414 | // modify the file data 415 | file.truncate(("some modified data...".len() + "sanity text".len()) as u64) 416 | .unwrap(); 417 | file.write_all("some modified data...".as_bytes(), 0) 418 | .unwrap(); 419 | file.flush().unwrap(); 420 | drop(file); 421 | 422 | // reopen to check content 423 | let mut buf = vec![0; "some modified data...".len()]; 424 | let file = MmapFileMut::open(path).unwrap(); 425 | // skip the sanity text 426 | file.read_exact(buf.as_mut_slice(), "sanity text".len()) 427 | .unwrap(); 428 | assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); 429 | } 430 | 431 | #[test] 432 | fn open_exist_mmap_file_mut() { 433 | let path = concat!("sync", "_options_open_exist_mmap_file_mut.txt"); 434 | defer!(std::fs::remove_file(path).unwrap()); 435 | let mut file = MmapFileMut::create(path).unwrap(); 436 | file.truncate(23).unwrap(); 437 | file.write_all("sanity text".as_bytes(), 0).unwrap(); 438 | file.write_all("some data...".as_bytes(), "sanity text".len()) 439 | .unwrap(); 440 | file.flush().unwrap(); 441 | drop(file); 442 | 443 | // mmap the file 444 | let mut file = Options::new() 445 | // truncate to 100 446 | .max_size(100) 447 | // mmap content after the sanity text 448 | .offset("sanity text".len() as u64) 449 | .open_exist_mmap_file_mut(path) 450 | .unwrap(); 451 | 452 | let mut buf = vec![0; "some data...".len()]; 453 | file.read_exact(buf.as_mut_slice(), 0).unwrap(); 454 | assert_eq!(buf.as_slice(), "some data...".as_bytes()); 455 | 456 | // modify the file data 457 | file.truncate(("some modified data...".len() + "sanity text".len()) as u64) 458 | .unwrap(); 459 | file.write_all("some modified data...".as_bytes(), 0) 460 | .unwrap(); 461 | file.flush().unwrap(); 462 | 463 | // reopen to check content, cow will not change the content. 464 | let file = MmapFileMut::open(path).unwrap(); 465 | let mut buf = vec![0; "some modified data...".len()]; 466 | // skip the sanity text 467 | file.read_exact(buf.as_mut_slice(), "sanity text".len()) 468 | .unwrap(); 469 | assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); 470 | } 471 | 472 | #[test] 473 | fn open_cow_mmap_file_mut() { 474 | let path = concat!("sync", "_options_open_cow_mmap_file_mut.txt"); 475 | defer!(std::fs::remove_file(path).unwrap()); 476 | let mut file = MmapFileMut::create(path).unwrap(); 477 | file.truncate(23).unwrap(); 478 | file.write_all("sanity text".as_bytes(), 0).unwrap(); 479 | file.write_all("some data...".as_bytes(), "sanity text".len()) 480 | .unwrap(); 481 | file.flush().unwrap(); 482 | drop(file); 483 | 484 | // mmap the file 485 | let mut file = Options::new() 486 | // mmap content after the sanity text 487 | .offset("sanity text".len() as u64) 488 | .open_cow_mmap_file_mut(path) 489 | .unwrap(); 490 | assert!(file.is_cow()); 491 | 492 | let mut buf = vec![0; "some data...".len()]; 493 | file.read_exact(buf.as_mut_slice(), 0).unwrap(); 494 | assert_eq!(buf.as_slice(), "some data...".as_bytes()); 495 | 496 | // modify the file data 497 | file.write_all("some data!!!".as_bytes(), 0).unwrap(); 498 | file.flush().unwrap(); 499 | 500 | // cow, change will only be seen in current caller 501 | assert_eq!(file.as_slice(), "some data!!!".as_bytes()); 502 | drop(file); 503 | 504 | // reopen to check content, cow will not change the content. 505 | let file = MmapFileMut::open(path).unwrap(); 506 | let mut buf = vec![0; "some data...".len()]; 507 | // skip the sanity text 508 | file.read_exact(buf.as_mut_slice(), "sanity text".len()) 509 | .unwrap(); 510 | assert_eq!(buf.as_slice(), "some data...".as_bytes()); 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /fmmap-rs/src/options/tokio_impl.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::raw::tokio::{AsyncDiskMmapFile, AsyncDiskMmapFileMut}; 3 | use crate::tokio::{AsyncMmapFile, AsyncMmapFileMut}; 4 | use memmap2::MmapOptions; 5 | use std::path::Path; 6 | use tokio::fs::OpenOptions; 7 | 8 | declare_and_impl_async_options!("tokio_async", "tokio_test", "tokio"); 9 | 10 | impl_async_options_tests!("tokio_async", tokio::test, tokio); 11 | 12 | #[cfg(unix)] 13 | impl_options_unix_ext!(AsyncOptions); 14 | 15 | #[cfg(windows)] 16 | impl_options_windows_ext!(AsyncOptions); 17 | -------------------------------------------------------------------------------- /fmmap-rs/src/reader.rs: -------------------------------------------------------------------------------- 1 | cfg_sync!( 2 | mod sync_impl; 3 | pub use sync_impl::{MmapFileReader, MmapFileReaderExt}; 4 | ); 5 | 6 | cfg_async! { 7 | macro_rules! declare_and_impl_basic_reader { 8 | () => { 9 | pin_project! { 10 | /// AsyncMmapFileReader helps read data from mmap file 11 | /// like a normal file. 12 | pub struct AsyncMmapFileReader<'a> { 13 | #[pin] 14 | r: Cursor<&'a [u8]>, 15 | offset: usize, 16 | len: usize, 17 | } 18 | } 19 | 20 | 21 | impl<'a> AsyncMmapFileReader<'a> { 22 | pub(crate) fn new(r: Cursor<&'a [u8]>, offset: usize, len: usize) -> Self { 23 | Self { 24 | r, 25 | offset, 26 | len 27 | } 28 | } 29 | 30 | /// Returns the start offset(related to the mmap) of the reader 31 | #[inline] 32 | pub fn offset(&self) -> usize { 33 | self.offset 34 | } 35 | 36 | /// Returns the length of the reader 37 | #[inline] 38 | pub fn len(&self) -> usize { 39 | self.len 40 | } 41 | } 42 | 43 | impl Debug for AsyncMmapFileReader<'_> { 44 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 45 | f.debug_struct("AsyncMmapFileReader") 46 | .field("offset", &self.offset) 47 | .field("len", &self.len) 48 | .field("reader", &self.r) 49 | .finish() 50 | } 51 | } 52 | }; 53 | } 54 | } 55 | 56 | cfg_async_std!( 57 | pub(crate) mod async_std_impl; 58 | ); 59 | 60 | cfg_smol!( 61 | pub(crate) mod smol_impl; 62 | ); 63 | 64 | cfg_tokio!( 65 | pub(crate) mod tokio_impl; 66 | ); 67 | -------------------------------------------------------------------------------- /fmmap-rs/src/reader/async_std_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use pin_project_lite::pin_project; 5 | use async_std::io::{Cursor, SeekFrom, Read, BufRead, Seek}; 6 | 7 | declare_and_impl_basic_reader!(); 8 | 9 | impl Read for AsyncMmapFileReader<'_> { 10 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { 11 | self.project().r.poll_read(cx, buf) 12 | } 13 | } 14 | 15 | impl BufRead for AsyncMmapFileReader<'_> { 16 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 17 | self.project().r.poll_fill_buf(cx) 18 | } 19 | 20 | fn consume(self: Pin<&mut Self>, amt: usize) { 21 | self.project().r.consume(amt) 22 | } 23 | } 24 | 25 | impl Seek for AsyncMmapFileReader<'_> { 26 | fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll> { 27 | self.project().r.poll_seek(cx, pos) 28 | } 29 | } 30 | 31 | #[cfg(test)] 32 | mod tests { 33 | use futures_util::{AsyncBufReadExt, AsyncReadExt}; 34 | use crate::async_std::AsyncMmapFileExt; 35 | use crate::raw::async_std::AsyncMemoryMmapFileMut; 36 | 37 | #[async_std::test] 38 | async fn test_reader() { 39 | let file = AsyncMemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 40 | let mut w = file.reader(0).unwrap(); 41 | let _ = format!("{:?}", w); 42 | assert_eq!(w.len(), 8096); 43 | assert_eq!(w.offset(), 0); 44 | let mut buf = [0; 10]; 45 | let n = w.read(&mut buf).await.unwrap(); 46 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 47 | w.fill_buf().await.unwrap(); 48 | } 49 | } -------------------------------------------------------------------------------- /fmmap-rs/src/reader/smol_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use pin_project_lite::pin_project; 5 | use smol::io::{AsyncBufRead, AsyncRead, AsyncSeek, Cursor, SeekFrom}; 6 | 7 | declare_and_impl_basic_reader!(); 8 | 9 | impl AsyncRead for AsyncMmapFileReader<'_> { 10 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { 11 | self.project().r.poll_read(cx, buf) 12 | } 13 | } 14 | 15 | impl AsyncSeek for AsyncMmapFileReader<'_> { 16 | fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll> { 17 | self.project().r.poll_seek(cx, pos) 18 | } 19 | } 20 | 21 | impl AsyncBufRead for AsyncMmapFileReader<'_> { 22 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 23 | self.project().r.poll_fill_buf(cx) 24 | } 25 | 26 | fn consume(self: Pin<&mut Self>, amt: usize) { 27 | self.project().r.consume(amt) 28 | } 29 | } 30 | 31 | #[cfg(test)] 32 | mod tests { 33 | use smol::io::{AsyncBufReadExt, AsyncReadExt}; 34 | use crate::smol::AsyncMmapFileExt; 35 | use crate::raw::smol::AsyncMemoryMmapFileMut; 36 | 37 | #[smol_potat::test] 38 | async fn test_reader() { 39 | let file = AsyncMemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 40 | let mut w = file.reader(0).unwrap(); 41 | let _ = format!("{:?}", w); 42 | assert_eq!(w.len(), 8096); 43 | assert_eq!(w.offset(), 0); 44 | let mut buf = [0; 10]; 45 | let n = w.read(&mut buf).await.unwrap(); 46 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 47 | w.fill_buf().await.unwrap(); 48 | w.consume(8096); 49 | } 50 | } -------------------------------------------------------------------------------- /fmmap-rs/src/reader/sync_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::io; 3 | use std::io::Read; 4 | use std::mem; 5 | use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; 6 | use bytes::Buf; 7 | 8 | /// MmapFileReader helps read data from mmap file 9 | /// like a normal file. 10 | pub struct MmapFileReader<'a> { 11 | r: io::Cursor<&'a [u8]>, 12 | offset: usize, 13 | len: usize, 14 | } 15 | 16 | impl<'a> MmapFileReader<'a> { 17 | pub(crate) fn new(r: io::Cursor<&'a [u8]>, offset: usize, len: usize) -> Self { 18 | Self { 19 | r, 20 | offset, 21 | len 22 | } 23 | } 24 | 25 | /// Returns the start offset(related to the mmap) of the reader 26 | #[inline] 27 | pub fn offset(&self) -> usize { 28 | self.offset 29 | } 30 | 31 | /// Returns the length of the reader 32 | #[inline] 33 | pub fn len(&self) -> usize { 34 | self.len 35 | } 36 | } 37 | 38 | 39 | impl Debug for MmapFileReader<'_> { 40 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 41 | f.debug_struct("MmapFileReader") 42 | .field("offset", &self.offset) 43 | .field("len", &self.len) 44 | .field("reader", &self.r) 45 | .finish() 46 | } 47 | } 48 | 49 | impl io::Seek for MmapFileReader<'_> { 50 | fn seek(&mut self, pos: io::SeekFrom) -> io::Result { 51 | self.r.seek(pos) 52 | } 53 | } 54 | 55 | impl io::BufRead for MmapFileReader<'_> { 56 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 57 | self.r.fill_buf() 58 | } 59 | 60 | fn consume(&mut self, amt: usize) { 61 | self.r.consume(amt) 62 | } 63 | } 64 | 65 | impl io::Read for MmapFileReader<'_> { 66 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 67 | self.r.read(buf) 68 | } 69 | } 70 | 71 | impl Buf for MmapFileReader<'_> { 72 | fn remaining(&self) -> usize { 73 | self.r.remaining() 74 | } 75 | 76 | fn chunk(&self) -> &[u8] { 77 | self.r.chunk() 78 | } 79 | 80 | fn advance(&mut self, cnt: usize) { 81 | self.r.advance(cnt) 82 | } 83 | } 84 | 85 | /// Extends MmapFileReader with methods for reading numbers. 86 | pub trait MmapFileReaderExt { 87 | /// Reads a signed 8 bit integer from the underlying reader. 88 | /// Note that since this reads a single byte, no byte order conversions are used. It is included for completeness. 89 | fn read_i8(&mut self) -> io::Result; 90 | /// Reads a signed 16 bit integer(big endian) from the underlying reader. 91 | fn read_i16(&mut self) -> io::Result; 92 | /// Reads a signed 16 bit integer(little endian) from the underlying reader. 93 | fn read_i16_le(&mut self) -> io::Result; 94 | /// Reads a signed 16 bit integer(big endian) from the underlying reader. 95 | fn read_i32(&mut self) -> io::Result; 96 | /// Reads a signed 32 bit integer(little endian) from the underlying reader. 97 | fn read_i32_le(&mut self) -> io::Result; 98 | /// Reads a signed 64 bit integer(big endian) from the underlying reader. 99 | fn read_i64(&mut self) -> io::Result; 100 | /// Reads a signed 64 bit integer(little endian) from the underlying reader. 101 | fn read_i64_le(&mut self) -> io::Result; 102 | /// Reads a signed integer(big endian) from the underlying reader. 103 | fn read_isize(&mut self) -> io::Result; 104 | /// Reads a signed integer(little endian) from the underlying reader. 105 | fn read_isize_le(&mut self) -> io::Result; 106 | /// Reads a signed 128 bit integer(big endian) from the underlying reader. 107 | fn read_i128(&mut self) -> io::Result; 108 | /// Reads a signed 128 bit integer(little endian) from the underlying reader. 109 | fn read_i128_le(&mut self) -> io::Result; 110 | 111 | /// Reads an unsigned 8 bit integer from the underlying reader. 112 | /// Note that since this reads a single byte, no byte order conversions are used. It is included for completeness. 113 | fn read_u8(&mut self) -> io::Result; 114 | /// Reads an unsigned 16 bit integer(big endian) from the underlying reader. 115 | fn read_u16(&mut self) -> io::Result; 116 | /// Reads an unsigned 16 bit integer(little endian) from the underlying reader. 117 | fn read_u16_le(&mut self) -> io::Result; 118 | /// Reads an unsigned 32 bit integer(big endian) from the underlying reader. 119 | fn read_u32(&mut self) -> io::Result; 120 | /// Reads an unsigned 32 bit integer(little endian) from the underlying reader. 121 | fn read_u32_le(&mut self) -> io::Result; 122 | /// Reads an unsigned 64 bit integer(big endian) from the underlying reader. 123 | fn read_u64(&mut self) -> io::Result; 124 | /// Reads an unsigned 64 bit integer(little endian) from the underlying reader. 125 | fn read_u64_le(&mut self) -> io::Result; 126 | /// Reads an unsigned integer(big endian) from the underlying reader. 127 | fn read_usize(&mut self) -> io::Result; 128 | /// Reads an unsigned integer(little endian) from the underlying reader. 129 | fn read_usize_le(&mut self) -> io::Result; 130 | /// Reads an unsigned 128 bit integer(big endian) from the underlying reader. 131 | fn read_u128(&mut self) -> io::Result; 132 | /// Reads an unsigned 128 bit integer(little endian) from the underlying reader. 133 | fn read_u128_le(&mut self) -> io::Result; 134 | 135 | /// Reads a IEEE754 single-precision (4 bytes, big endian) floating point number from the underlying reader. 136 | fn read_f32(&mut self) -> io::Result; 137 | /// Reads a IEEE754 single-precision (4 bytes, little endian) floating point number from the underlying reader. 138 | fn read_f32_le(&mut self) -> io::Result; 139 | /// Reads a IEEE754 single-precision (8 bytes, big endian) floating point number from the underlying reader. 140 | fn read_f64(&mut self) -> io::Result; 141 | /// Reads a IEEE754 single-precision (8 bytes, little endian) floating point number from the underlying reader. 142 | fn read_f64_le(&mut self) -> io::Result; 143 | } 144 | 145 | impl MmapFileReaderExt for MmapFileReader<'_> { 146 | #[inline] 147 | fn read_i8(&mut self) -> io::Result { 148 | self.r.read_i8() 149 | } 150 | 151 | #[inline] 152 | fn read_i16(&mut self) -> io::Result { 153 | self.r.read_i16::() 154 | } 155 | 156 | #[inline] 157 | fn read_i16_le(&mut self) -> io::Result { 158 | self.r.read_i16::() 159 | } 160 | 161 | #[inline] 162 | fn read_i32(&mut self) -> io::Result { 163 | self.r.read_i32::() 164 | } 165 | 166 | #[inline] 167 | fn read_i32_le(&mut self) -> io::Result { 168 | self.r.read_i32::() 169 | } 170 | 171 | #[inline] 172 | fn read_i64(&mut self) -> io::Result { 173 | self.r.read_i64::() 174 | } 175 | 176 | #[inline] 177 | fn read_i64_le(&mut self) -> io::Result { 178 | self.r.read_i64::() 179 | } 180 | 181 | #[inline] 182 | fn read_isize(&mut self) -> io::Result { 183 | const ISIZE_SIZE: usize = mem::size_of::(); 184 | let mut buf: [u8; ISIZE_SIZE] = [0; ISIZE_SIZE]; 185 | self.r.read_exact(&mut buf)?; 186 | Ok(isize::from_be_bytes(buf)) 187 | } 188 | 189 | #[inline] 190 | fn read_isize_le(&mut self) -> io::Result { 191 | const ISIZE_SIZE: usize = mem::size_of::(); 192 | let mut buf: [u8; ISIZE_SIZE] = [0; ISIZE_SIZE]; 193 | self.r.read_exact(&mut buf)?; 194 | Ok(isize::from_le_bytes(buf)) 195 | } 196 | 197 | #[inline] 198 | fn read_i128(&mut self) -> io::Result { 199 | const I128_SIZE: usize = mem::size_of::(); 200 | let mut buf: [u8; I128_SIZE] = [0; I128_SIZE]; 201 | self.r.read_exact(&mut buf)?; 202 | Ok(i128::from_be_bytes(buf)) 203 | } 204 | 205 | #[inline] 206 | fn read_i128_le(&mut self) -> io::Result { 207 | const I128_SIZE: usize = mem::size_of::(); 208 | let mut buf: [u8; I128_SIZE] = [0; I128_SIZE]; 209 | self.r.read_exact(&mut buf)?; 210 | Ok(i128::from_le_bytes(buf)) 211 | } 212 | 213 | #[inline] 214 | fn read_u8(&mut self) -> io::Result { 215 | self.r.read_u8() 216 | } 217 | 218 | #[inline] 219 | fn read_u16(&mut self) -> io::Result { 220 | self.r.read_u16::() 221 | } 222 | 223 | #[inline] 224 | fn read_u16_le(&mut self) -> io::Result { 225 | self.r.read_u16::() 226 | } 227 | 228 | #[inline] 229 | fn read_u32(&mut self) -> io::Result { 230 | self.r.read_u32::() 231 | } 232 | 233 | #[inline] 234 | fn read_u32_le(&mut self) -> io::Result { 235 | self.r.read_u32::() 236 | } 237 | 238 | #[inline] 239 | fn read_u64(&mut self) -> io::Result { 240 | self.r.read_u64::() 241 | } 242 | 243 | #[inline] 244 | fn read_u64_le(&mut self) -> io::Result { 245 | self.r.read_u64::() 246 | } 247 | 248 | #[inline] 249 | fn read_usize(&mut self) -> io::Result { 250 | const USIZE_SIZE: usize = mem::size_of::(); 251 | let mut buf: [u8; USIZE_SIZE] = [0; USIZE_SIZE]; 252 | self.r.read_exact(&mut buf)?; 253 | Ok(usize::from_be_bytes(buf)) 254 | } 255 | 256 | #[inline] 257 | fn read_usize_le(&mut self) -> io::Result { 258 | const USIZE_SIZE: usize = mem::size_of::(); 259 | let mut buf: [u8; USIZE_SIZE] = [0; USIZE_SIZE]; 260 | self.r.read_exact(&mut buf)?; 261 | Ok(usize::from_le_bytes(buf)) 262 | } 263 | 264 | #[inline] 265 | fn read_u128(&mut self) -> io::Result { 266 | const U128_SIZE: usize = mem::size_of::(); 267 | let mut buf: [u8; U128_SIZE] = [0; U128_SIZE]; 268 | self.r.read_exact(&mut buf)?; 269 | Ok(u128::from_be_bytes(buf)) 270 | } 271 | 272 | #[inline] 273 | fn read_u128_le(&mut self) -> io::Result { 274 | const U128_SIZE: usize = mem::size_of::(); 275 | let mut buf: [u8; U128_SIZE] = [0; U128_SIZE]; 276 | self.r.read_exact(&mut buf)?; 277 | Ok(u128::from_le_bytes(buf)) 278 | } 279 | 280 | #[inline] 281 | fn read_f32(&mut self) -> io::Result { 282 | self.r.read_f32::() 283 | } 284 | 285 | #[inline] 286 | fn read_f32_le(&mut self) -> io::Result { 287 | self.r.read_f32::() 288 | } 289 | 290 | #[inline] 291 | fn read_f64(&mut self) -> io::Result { 292 | self.r.read_f64::() 293 | } 294 | 295 | #[inline] 296 | fn read_f64_le(&mut self) -> io::Result { 297 | self.r.read_f64::() 298 | } 299 | } 300 | 301 | #[cfg(test)] 302 | mod tests { 303 | use std::io::{BufRead, Read}; 304 | use bytes::Buf; 305 | use crate::MmapFileExt; 306 | use crate::raw::MemoryMmapFileMut; 307 | 308 | #[test] 309 | fn test_reader() { 310 | let file = MemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 311 | let mut w = file.reader(0).unwrap(); 312 | let _ = format!("{:?}", w); 313 | assert_eq!(w.len(), 8096); 314 | assert_eq!(w.offset(), 0); 315 | let mut buf = [0; 10]; 316 | let n = w.read(&mut buf).unwrap(); 317 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 318 | w.fill_buf().unwrap(); 319 | w.consume(8096); 320 | 321 | let mut w = file.range_reader(100, 100).unwrap(); 322 | assert_eq!(w.remaining(), 100); 323 | w.advance(10); 324 | assert_eq!(w.remaining(), 90); 325 | let buf = w.chunk(); 326 | assert_eq!(buf.len(), 90); 327 | } 328 | } -------------------------------------------------------------------------------- /fmmap-rs/src/reader/tokio_impl.rs: -------------------------------------------------------------------------------- 1 | use bytes::Buf; 2 | use std::fmt::{Debug, Formatter}; 3 | use std::io::{Cursor, SeekFrom}; 4 | use std::pin::Pin; 5 | use std::task::{Context, Poll}; 6 | use pin_project_lite::pin_project; 7 | use tokio::io::{AsyncBufRead, AsyncRead, AsyncSeek, ReadBuf}; 8 | 9 | declare_and_impl_basic_reader!(); 10 | 11 | impl AsyncRead for AsyncMmapFileReader<'_> { 12 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll> { 13 | self.project().r.poll_read(cx, buf) 14 | } 15 | } 16 | 17 | impl AsyncSeek for AsyncMmapFileReader<'_> { 18 | fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> { 19 | self.project().r.start_seek(position) 20 | } 21 | 22 | fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 23 | self.project().r.poll_complete(cx) 24 | } 25 | } 26 | 27 | impl AsyncBufRead for AsyncMmapFileReader<'_> { 28 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 29 | self.project().r.poll_fill_buf(cx) 30 | } 31 | 32 | fn consume(self: Pin<&mut Self>, amt: usize) { 33 | self.project().r.consume(amt) 34 | } 35 | } 36 | 37 | impl Buf for AsyncMmapFileReader<'_> { 38 | fn remaining(&self) -> usize { 39 | self.r.remaining() 40 | } 41 | 42 | fn chunk(&self) -> &[u8] { 43 | self.r.chunk() 44 | } 45 | 46 | fn advance(&mut self, cnt: usize) { 47 | self.r.advance(cnt) 48 | } 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use bytes::Buf; 54 | use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt}; 55 | use crate::tokio::{AsyncMmapFileExt, AsyncMmapFileMutExt}; 56 | use crate::raw::tokio::AsyncMemoryMmapFileMut; 57 | 58 | #[tokio::test] 59 | async fn test_reader() { 60 | let mut file = AsyncMemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 61 | let mut w = file.writer(0).unwrap(); 62 | let _ = format!("{:?}", w); 63 | assert_eq!(w.len(), 8096); 64 | assert_eq!(w.offset(), 0); 65 | let mut buf = [0; 10]; 66 | let n = w.read(&mut buf).await.unwrap(); 67 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 68 | w.fill_buf().await.unwrap(); 69 | w.consume(8096); 70 | w.shutdown().await.unwrap(); 71 | 72 | let mut w = file.range_reader(100, 100).unwrap(); 73 | assert_eq!(w.remaining(), 100); 74 | w.advance(10); 75 | assert_eq!(w.remaining(), 90); 76 | let buf = w.chunk(); 77 | assert_eq!(buf.len(), 90); 78 | } 79 | } -------------------------------------------------------------------------------- /fmmap-rs/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, ErrorKind, Result}; 2 | #[cfg(feature = "nightly")] 3 | use std::io; 4 | // use std::ops::{Bound, RangeBounds}; 5 | use std::path::Path; 6 | 7 | cfg_sync! { 8 | use std::fs::{File, OpenOptions}; 9 | 10 | /// Sync directory 11 | pub fn sync_dir>(path: P) -> Result<()> { 12 | let path = path.as_ref(); 13 | if !path.is_dir() { 14 | #[cfg(feature = "nightly")] 15 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 16 | 17 | #[cfg(not(feature = "nightly"))] 18 | return Err(Error::from(ErrorKind::NotADirectory)); 19 | } 20 | 21 | File::open(path) 22 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 23 | .sync_all() 24 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 25 | } 26 | 27 | /// Sync parent 28 | pub fn sync_parent>(path: P) -> Result<()> { 29 | let path = path.as_ref().canonicalize().map_err(|e| Error::new(ErrorKind::IO, e))?; 30 | let path = path.parent().unwrap(); 31 | if !path.is_dir() { 32 | #[cfg(feature = "nightly")] 33 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 34 | #[cfg(not(feature = "nightly"))] 35 | return Err(Error::from(ErrorKind::NotADirectory)); 36 | } 37 | File::open(path) 38 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 39 | .sync_all() 40 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 41 | } 42 | 43 | /// Open a read-only file 44 | pub fn open_read_only_file>(path: P) -> Result { 45 | OpenOptions::new() 46 | .read(true) 47 | .open(path) 48 | .map_err(|e| Error::new(ErrorKind::IO, e)) 49 | } 50 | 51 | /// Open an existing file in write mode, all writes will overwrite the original file 52 | pub fn open_exist_file>(path: P) -> Result { 53 | OpenOptions::new() 54 | .read(true) 55 | .write(true) 56 | .append(false) 57 | .open(path) 58 | .map_err(|e| Error::new(ErrorKind::IO, e)) 59 | } 60 | 61 | /// Open an existing file in write mode, all writes will append to the file 62 | pub fn open_exist_file_with_append>(path: P) -> Result { 63 | OpenOptions::new() 64 | .read(true) 65 | .append(true) 66 | .open(path) 67 | .map_err(|e| Error::new(ErrorKind::IO, e)) 68 | } 69 | 70 | /// Open an existing file and truncate it 71 | pub fn open_file_with_truncate>(path: P) -> Result { 72 | OpenOptions::new() 73 | .read(true) 74 | .write(true) 75 | .truncate(true) 76 | .open(path) 77 | .map_err(|e| Error::new(ErrorKind::IO, e)) 78 | } 79 | 80 | /// Open or create a file 81 | pub fn open_or_create_file>(path: P) -> Result { 82 | OpenOptions::new() 83 | .create(true) 84 | .truncate(false) 85 | .read(true) 86 | .write(true) 87 | .open(path) 88 | .map_err(|e| Error::new(ErrorKind::IO, e)) 89 | } 90 | 91 | /// Create a new file 92 | pub fn create_file>(path: P) -> Result { 93 | OpenOptions::new() 94 | .create_new(true) 95 | .read(true) 96 | .append(true) 97 | .open(path) 98 | .map_err(|e| Error::new(ErrorKind::IO, e)) 99 | } 100 | } 101 | 102 | cfg_async! { 103 | macro_rules! impl_async_file_utils { 104 | ($file: ident, $open_options: ident) => { 105 | /// Open a read-only file 106 | pub async fn open_read_only_file_async>(path: P) -> Result<$file> { 107 | <$open_options>::new() 108 | .read(true) 109 | .open(path) 110 | .await 111 | .map_err(|e| Error::new(ErrorKind::IO, e)) 112 | } 113 | 114 | /// Open an existing file in write mode, all writes will overwrite the original file 115 | pub async fn open_exist_file_async>(path: P) -> Result<$file> { 116 | <$open_options>::new() 117 | .read(true) 118 | .write(true) 119 | .append(false) 120 | .open(path) 121 | .await 122 | .map_err(|e| Error::new(ErrorKind::IO, e)) 123 | } 124 | 125 | /// Open an existing file in write mode, all writes will append to the file 126 | pub async fn open_exist_file_with_append_async>(path: P) -> Result<$file> { 127 | <$open_options>::new() 128 | .read(true) 129 | .write(true) 130 | .append(true) 131 | .open(path) 132 | .await 133 | .map_err(|e| Error::new(ErrorKind::IO, e)) 134 | } 135 | 136 | /// Open an existing file and truncate it 137 | pub async fn open_file_with_truncate_async>(path: P) -> Result<$file> { 138 | <$open_options>::new() 139 | .read(true) 140 | .write(true) 141 | .truncate(true) 142 | .open(path) 143 | .await 144 | .map_err(|e| Error::new(ErrorKind::IO, e)) 145 | } 146 | 147 | /// Open or create a file 148 | pub async fn open_or_create_file_async>(path: P) -> Result<$file> { 149 | <$open_options>::new() 150 | .create(true) 151 | .read(true) 152 | .write(true) 153 | .open(path) 154 | .await 155 | .map_err(|e| Error::new(ErrorKind::IO, e)) 156 | } 157 | 158 | /// Create a new file 159 | pub async fn create_file_async>(path: P) -> Result<$file> { 160 | <$open_options>::new() 161 | .create_new(true) 162 | .read(true) 163 | .write(true) 164 | .append(true) 165 | .open(path) 166 | .await 167 | .map_err(|e| Error::new(ErrorKind::IO, e)) 168 | } 169 | }; 170 | } 171 | } 172 | 173 | cfg_smol! { 174 | /// file open utils for smol 175 | pub mod smol { 176 | use smol::fs::{File, OpenOptions}; 177 | use crate::error::{Error, ErrorKind, Result}; 178 | #[cfg(feature = "nightly")] 179 | use std::io; 180 | use std::path::Path; 181 | 182 | /// Sync directory 183 | pub async fn sync_dir_async>(path: P) -> Result<()> { 184 | let path = path.as_ref(); 185 | if !path.is_dir() { 186 | #[cfg(feature = "nightly")] 187 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 188 | 189 | #[cfg(not(feature = "nightly"))] 190 | return Err(Error::from(ErrorKind::NotADirectory)); 191 | } 192 | 193 | File::open(path) 194 | .await 195 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 196 | .sync_all() 197 | .await 198 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 199 | } 200 | 201 | /// Sync parent directory 202 | pub async fn sync_parent_async>(path: P) -> Result<()> { 203 | let path = path.as_ref().canonicalize().map_err(|e| Error::new(ErrorKind::IO, e))?; 204 | let path = path.parent().unwrap(); 205 | if !path.is_dir() { 206 | #[cfg(feature = "nightly")] 207 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 208 | 209 | #[cfg(not(feature = "nightly"))] 210 | return Err(Error::from(ErrorKind::NotADirectory)); 211 | } 212 | 213 | File::open(path) 214 | .await 215 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 216 | .sync_all() 217 | .await 218 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 219 | } 220 | 221 | impl_async_file_utils!(File, OpenOptions); 222 | } 223 | } 224 | 225 | cfg_tokio! { 226 | /// file open utils for tokio 227 | pub mod tokio { 228 | use tokio::fs::{File, OpenOptions}; 229 | use crate::error::{Error, ErrorKind, Result}; 230 | #[cfg(feature = "nightly")] 231 | use std::io; 232 | use std::path::Path; 233 | 234 | /// Sync directory 235 | pub async fn sync_dir_async>(path: P) -> Result<()> { 236 | let path = path.as_ref(); 237 | if !path.is_dir() { 238 | #[cfg(feature = "nightly")] 239 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 240 | 241 | #[cfg(not(feature = "nightly"))] 242 | return Err(Error::from(ErrorKind::NotADirectory)); 243 | } 244 | 245 | File::open(path) 246 | .await 247 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 248 | .sync_all() 249 | .await 250 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 251 | } 252 | 253 | /// Sync parent directory 254 | pub async fn sync_parent_async>(path: P) -> Result<()> { 255 | let path = path.as_ref().canonicalize().map_err(|e| Error::new(ErrorKind::IO, e))?; 256 | let path = path.parent().unwrap(); 257 | if !path.is_dir() { 258 | #[cfg(feature = "nightly")] 259 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 260 | 261 | #[cfg(not(feature = "nightly"))] 262 | return Err(Error::from(ErrorKind::NotADirectory)); 263 | } 264 | 265 | File::open(path) 266 | .await 267 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 268 | .sync_all() 269 | .await 270 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 271 | } 272 | 273 | impl_async_file_utils!(File, OpenOptions); 274 | } 275 | } 276 | 277 | cfg_async_std! { 278 | /// file open utils for async-std 279 | pub mod async_std { 280 | use async_std::fs::{File, OpenOptions}; 281 | use crate::error::{Error, ErrorKind, Result}; 282 | #[cfg(feature = "nightly")] 283 | use std::io; 284 | use async_std::path::Path; 285 | 286 | /// Sync directory 287 | pub async fn sync_dir_async>(path: P) -> Result<()> { 288 | let path = path.as_ref(); 289 | if !path.is_dir().await { 290 | #[cfg(feature = "nightly")] 291 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 292 | 293 | #[cfg(not(feature = "nightly"))] 294 | return Err(Error::from(ErrorKind::NotADirectory)); 295 | } 296 | 297 | File::open(path) 298 | .await 299 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 300 | .sync_all() 301 | .await 302 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 303 | } 304 | 305 | /// Sync parent directory 306 | pub async fn sync_parent_async>(path: P) -> Result<()> { 307 | let path = path.as_ref().canonicalize().await.map_err(|e| Error::new(ErrorKind::IO, e))?; 308 | let path = path.parent().unwrap(); 309 | if !path.is_dir().await { 310 | #[cfg(feature = "nightly")] 311 | return Err(Error::new(ErrorKind::IO, io::Error::from(io::ErrorKind::NotADirectory))); 312 | 313 | #[cfg(not(feature = "nightly"))] 314 | return Err(Error::from(ErrorKind::NotADirectory)); 315 | } 316 | 317 | File::open(path) 318 | .await 319 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e))? 320 | .sync_all() 321 | .await 322 | .map_err(|e| Error::new_source_msg(ErrorKind::OpenFailed, path.to_string_lossy(), e)) 323 | } 324 | 325 | impl_async_file_utils!(File, OpenOptions); 326 | } 327 | } 328 | 329 | // // TODO: enum_dispatch does not support impl as parameter 330 | // #[allow(dead_code)] 331 | // pub(crate) fn handle_range_unchecked( 332 | // range: impl RangeBounds, 333 | // upper_bound: usize, 334 | // ) -> (usize, usize) { 335 | // let begin = match range.start_bound() { 336 | // Bound::Included(&n) => n, 337 | // Bound::Excluded(&n) => n + 1, 338 | // Bound::Unbounded => 0, 339 | // }; 340 | // 341 | // let end = match range.end_bound() { 342 | // Bound::Included(&n) => n.checked_add(1).expect("out of range"), 343 | // Bound::Excluded(&n) => n, 344 | // Bound::Unbounded => upper_bound, 345 | // }; 346 | // 347 | // assert!( 348 | // begin < end, 349 | // "range start must less than end: {:?} <= {:?}", 350 | // begin, 351 | // end, 352 | // ); 353 | // assert!( 354 | // end <= upper_bound, 355 | // "range end out of bounds: {:?} <= {:?}", 356 | // end, 357 | // upper_bound, 358 | // ); 359 | // (begin, end) 360 | // } 361 | // 362 | // // TODO: enum_dispatch does not support impl as parameter 363 | // #[allow(dead_code)] 364 | // pub(crate) fn handle_range( 365 | // range: impl RangeBounds, 366 | // upper_bound: usize, 367 | // ) -> Result<(usize, usize)> { 368 | // let begin = match range.start_bound() { 369 | // Bound::Included(&n) => n, 370 | // Bound::Excluded(&n) => n + 1, 371 | // Bound::Unbounded => 0, 372 | // }; 373 | // 374 | // let end = match range.end_bound() { 375 | // Bound::Included(&n) => n.checked_add(1), 376 | // Bound::Excluded(&n) => Some(n), 377 | // Bound::Unbounded => Some(upper_bound), 378 | // } 379 | // .ok_or(Error::OutOfBound(usize::MAX, upper_bound))?; 380 | // 381 | // if begin >= end { 382 | // return Err(Error::InvalidBound(begin, end)); 383 | // } 384 | // 385 | // if end > upper_bound { 386 | // return Err(Error::OutOfBound(end, upper_bound)); 387 | // } 388 | // 389 | // Ok((begin, end)) 390 | // } 391 | -------------------------------------------------------------------------------- /fmmap-rs/src/writer.rs: -------------------------------------------------------------------------------- 1 | cfg_sync!( 2 | mod sync_impl; 3 | pub use sync_impl::{MmapFileWriter, MmapFileWriterExt}; 4 | ); 5 | 6 | cfg_async! { 7 | macro_rules! declare_and_impl_basic_writer { 8 | () => { 9 | pin_project! { 10 | /// AsyncMmapFileWriter helps read or write data from mmap file 11 | /// like a normal file. 12 | /// 13 | /// # Notes 14 | /// If you use a writer to write data to mmap, there is no guarantee all 15 | /// data will be durably stored. So you need to call [`flush`]/[`flush_range`]/[`flush_async`]/[`flush_async_range`] in [`AsyncMmapFileMutExt`] 16 | /// to guarantee all data will be durably stored. 17 | /// 18 | /// [`flush`]: trait.AsyncMmapFileMutExt.html#methods.flush 19 | /// [`flush_range`]: trait.AsyncMmapFileMutExt.html#methods.flush_range 20 | /// [`flush_async`]: trait.AsyncMmapFileMutExt.html#methods.flush_async 21 | /// [`flush_async_range`]: trait.AsyncMmapFileMutExt.html#methods.flush_async_range 22 | /// [`AsyncMmapFileMutExt`]: trait.AsyncMmapFileMutExt.html 23 | pub struct AsyncMmapFileWriter<'a> { 24 | #[pin] 25 | w: Cursor<&'a mut [u8]>, 26 | offset: usize, 27 | len: usize, 28 | } 29 | } 30 | 31 | impl<'a> AsyncMmapFileWriter<'a> { 32 | pub(crate) fn new(w: Cursor<&'a mut [u8]>, offset: usize, len: usize) -> Self { 33 | Self { 34 | w, 35 | offset, 36 | len 37 | } 38 | } 39 | 40 | /// Returns the start offset(related to the mmap) of the writer 41 | #[inline] 42 | pub fn offset(&self) -> usize { 43 | self.offset 44 | } 45 | 46 | /// Returns the length of the writer 47 | #[inline] 48 | pub fn len(&self) -> usize { 49 | self.len 50 | } 51 | } 52 | 53 | impl Debug for AsyncMmapFileWriter<'_> { 54 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 55 | f.debug_struct("AsyncMmapFileWriter") 56 | .field("offset", &self.offset) 57 | .field("len", &self.len) 58 | .field("writer", &self.w) 59 | .finish() 60 | } 61 | } 62 | }; 63 | } 64 | } 65 | 66 | cfg_async_std!( 67 | pub mod async_std_impl; 68 | ); 69 | cfg_smol!( 70 | pub mod smol_impl; 71 | ); 72 | cfg_tokio!( 73 | pub mod tokio_impl; 74 | ); 75 | -------------------------------------------------------------------------------- /fmmap-rs/src/writer/async_std_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use async_std::io::{Cursor, Read, BufRead, Seek, SeekFrom, Write}; 5 | use pin_project_lite::pin_project; 6 | 7 | declare_and_impl_basic_writer!(); 8 | 9 | impl Read for AsyncMmapFileWriter<'_> { 10 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { 11 | self.project().w.poll_read(cx, buf) 12 | } 13 | } 14 | 15 | impl BufRead for AsyncMmapFileWriter<'_> { 16 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 17 | self.project().w.poll_fill_buf(cx) 18 | } 19 | 20 | fn consume(self: Pin<&mut Self>, amt: usize) { 21 | self.project().w.consume(amt) 22 | } 23 | } 24 | 25 | impl Seek for AsyncMmapFileWriter<'_> { 26 | fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll> { 27 | self.project().w.poll_seek(cx, pos) 28 | } 29 | } 30 | 31 | impl Write for AsyncMmapFileWriter<'_> { 32 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { 33 | self.project().w.poll_write(cx, buf) 34 | } 35 | 36 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 37 | self.project().w.poll_flush(cx) 38 | } 39 | 40 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 41 | self.project().w.poll_close(cx) 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use futures_util::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt}; 48 | use crate::async_std::AsyncMmapFileMutExt; 49 | use crate::raw::async_std::AsyncMemoryMmapFileMut; 50 | 51 | #[async_std::test] 52 | async fn test_writer() { 53 | let mut file = AsyncMemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 54 | let mut w = file.writer(0).unwrap(); 55 | let _ = format!("{:?}", w); 56 | assert_eq!(w.len(), 8096); 57 | assert_eq!(w.offset(), 0); 58 | let mut buf = [0; 10]; 59 | let n = w.read(&mut buf).await.unwrap(); 60 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 61 | w.fill_buf().await.unwrap(); 62 | w.close().await.unwrap(); 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /fmmap-rs/src/writer/smol_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use smol::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, Cursor, SeekFrom}; 5 | use pin_project_lite::pin_project; 6 | 7 | declare_and_impl_basic_writer!(); 8 | 9 | impl AsyncRead for AsyncMmapFileWriter<'_> { 10 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { 11 | self.project().w.poll_read(cx, buf) 12 | } 13 | } 14 | 15 | impl AsyncBufRead for AsyncMmapFileWriter<'_> { 16 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 17 | self.project().w.poll_fill_buf(cx) 18 | } 19 | 20 | fn consume(self: Pin<&mut Self>, amt: usize) { 21 | self.project().w.consume(amt) 22 | } 23 | } 24 | 25 | impl AsyncSeek for AsyncMmapFileWriter<'_> { 26 | fn poll_seek(self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom) -> Poll> { 27 | self.project().w.poll_seek(cx, pos) 28 | } 29 | } 30 | 31 | impl AsyncWrite for AsyncMmapFileWriter<'_> { 32 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { 33 | self.project().w.poll_write(cx, buf) 34 | } 35 | 36 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 37 | self.project().w.poll_flush(cx) 38 | } 39 | 40 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 41 | self.project().w.poll_close(cx) 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use smol::io::{AsyncBufReadExt, AsyncWriteExt, AsyncReadExt}; 48 | use crate::smol::AsyncMmapFileMutExt; 49 | use crate::raw::smol::AsyncMemoryMmapFileMut; 50 | 51 | #[smol_potat::test] 52 | async fn test_writer() { 53 | let mut file = AsyncMemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 54 | let mut w = file.writer(0).unwrap(); 55 | let _ = format!("{:?}", w); 56 | assert_eq!(w.len(), 8096); 57 | assert_eq!(w.offset(), 0); 58 | let mut buf = [0; 10]; 59 | let n = w.read(&mut buf).await.unwrap(); 60 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 61 | w.fill_buf().await.unwrap(); 62 | w.consume(8096); 63 | w.close().await.unwrap(); 64 | } 65 | } -------------------------------------------------------------------------------- /fmmap-rs/src/writer/sync_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::io; 3 | use std::io::Write; 4 | use byteorder::{BigEndian, LittleEndian, WriteBytesExt}; 5 | use bytes::Buf; 6 | 7 | 8 | /// MmapFileWriter helps read or write data from mmap file 9 | /// like a normal file. 10 | /// 11 | /// # Notes 12 | /// If you use a writer to write data to mmap, there is no guarantee all 13 | /// data will be durably stored. So you need to call [`flush`]/[`flush_range`]/[`flush_async`]/[`flush_async_range`] in [`MmapFileMutExt`] 14 | /// to guarantee all data will be durably stored. 15 | /// 16 | /// [`flush`]: trait.MmapFileMutExt.html#methods.flush 17 | /// [`flush_range`]: trait.MmapFileMutExt.html#methods.flush_range 18 | /// [`flush_async`]: trait.MmapFileMutExt.html#methods.flush_async 19 | /// [`flush_async_range`]: trait.MmapFileMutExt.html#methods.flush_async_range 20 | /// [`MmapFileMutExt`]: trait.MmapFileMutExt.html 21 | pub struct MmapFileWriter<'a> { 22 | w: io::Cursor<&'a mut [u8]>, 23 | offset: usize, 24 | len: usize, 25 | } 26 | 27 | impl<'a> MmapFileWriter<'a> { 28 | pub(crate) fn new(w: io::Cursor<&'a mut [u8]>, offset: usize, len: usize) -> Self { 29 | Self { 30 | w, 31 | offset, 32 | len 33 | } 34 | } 35 | 36 | /// Returns the start offset(related to the mmap) of the writer 37 | #[inline] 38 | pub fn offset(&self) -> usize { 39 | self.offset 40 | } 41 | 42 | /// Returns the length of the writer 43 | #[inline] 44 | pub fn len(&self) -> usize { 45 | self.len 46 | } 47 | } 48 | 49 | impl Debug for MmapFileWriter<'_> { 50 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 51 | f.debug_struct("MmapFileWriter") 52 | .field("offset", &self.offset) 53 | .field("len", &self.len) 54 | .field("writer", &self.w) 55 | .finish() 56 | } 57 | } 58 | 59 | impl io::Read for MmapFileWriter<'_> { 60 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 61 | self.w.read(buf) 62 | } 63 | } 64 | 65 | impl io::BufRead for MmapFileWriter<'_> { 66 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 67 | self.w.fill_buf() 68 | } 69 | 70 | fn consume(&mut self, amt: usize) { 71 | self.w.consume(amt) 72 | } 73 | } 74 | 75 | impl io::Write for MmapFileWriter<'_> { 76 | fn write(&mut self, buf: &[u8]) -> io::Result { 77 | self.w.write(buf) 78 | } 79 | 80 | fn flush(&mut self) -> io::Result<()> { 81 | self.w.flush() 82 | } 83 | } 84 | 85 | impl io::Seek for MmapFileWriter<'_> { 86 | fn seek(&mut self, pos: io::SeekFrom) -> io::Result { 87 | self.w.seek(pos) 88 | } 89 | } 90 | 91 | impl Buf for MmapFileWriter<'_> { 92 | fn remaining(&self) -> usize { 93 | self.w.remaining() 94 | } 95 | 96 | fn chunk(&self) -> &[u8] { 97 | self.w.chunk() 98 | } 99 | 100 | fn advance(&mut self, cnt: usize) { 101 | self.w.advance(cnt) 102 | } 103 | } 104 | 105 | /// Extends MmapFileWriter with methods for writing numbers. 106 | pub trait MmapFileWriterExt { 107 | /// Writes a signed 8 bit integer to the underlying writer. 108 | /// Note that since this writes a single byte, no byte order conversions are used. It is included for completeness. 109 | fn write_i8(&mut self, n: i8) -> io::Result<()>; 110 | /// Writes a signed 16 bit integer(big endian) to the underlying writer. 111 | fn write_i16(&mut self, n: i16) -> io::Result<()>; 112 | /// Writes a signed 16 bit integer(little endian) to the underlying writer. 113 | fn write_i16_le(&mut self, n: i16) -> io::Result<()>; 114 | /// Writes a signed 32 bit integer(big endian) to the underlying writer. 115 | fn write_i32(&mut self, n: i32) -> io::Result<()>; 116 | /// Writes a signed 32 bit integer(little endian) to the underlying writer. 117 | fn write_i32_le(&mut self, n: i32) -> io::Result<()>; 118 | /// Writes a signed 64 bit integer(big endian) to the underlying writer. 119 | fn write_i64(&mut self, n: i64) -> io::Result<()>; 120 | /// Writes a signed 64 bit integer(little endian) to the underlying writer. 121 | fn write_i64_le(&mut self, n: i64) -> io::Result<()>; 122 | /// Writes a signed integer(big endian) to the underlying writer. 123 | fn write_isize(&mut self, n: isize) -> io::Result<()>; 124 | /// Writes a signed integer(little endian) to the underlying writer. 125 | fn write_isize_le(&mut self, n: isize) -> io::Result<()>; 126 | /// Writes a signed 128 bit integer(big endian) to the underlying writer. 127 | fn write_i128(&mut self, n: i128) -> io::Result<()>; 128 | /// Writes a signed 128 bit integer(little endian) to the underlying writer. 129 | fn write_i128_le(&mut self, n: i128) -> io::Result<()>; 130 | 131 | /// Writes an unsigned 8 bit integer to the underlying writer. 132 | /// Note that since this writes a single byte, no byte order conversions are used. It is included for completeness. 133 | fn write_u8(&mut self, n: u8) -> io::Result<()>; 134 | /// Writes an unsigned 16 bit integer(big endian) to the underlying writer. 135 | fn write_u16(&mut self, n: u16) -> io::Result<()>; 136 | /// Writes an unsigned 16 bit integer(little endian) to the underlying writer. 137 | fn write_u16_le(&mut self, n: u16) -> io::Result<()>; 138 | /// Writes an unsigned 32 bit integer(big endian) to the underlying writer. 139 | fn write_u32(&mut self, n: u32) -> io::Result<()>; 140 | /// Writes an unsigned 32 bit integer(little endian) to the underlying writer. 141 | fn write_u32_le(&mut self, n: u32) -> io::Result<()>; 142 | /// Writes an unsigned 64 bit integer(big endian) to the underlying writer. 143 | fn write_u64(&mut self, n: u64) -> io::Result<()>; 144 | /// Writes an unsigned 64 bit integer(little endian) to the underlying writer. 145 | fn write_u64_le(&mut self, n: u64) -> io::Result<()>; 146 | /// Writes an unsigned integer(big endian) to the underlying writer. 147 | fn write_usize(&mut self, n: usize) -> io::Result<()>; 148 | /// Writes an unsigned integer(little endian) to the underlying writer. 149 | fn write_usize_le(&mut self, n: usize) -> io::Result<()>; 150 | /// Writes an unsigned 128 bit integer(big endian) to the underlying writer. 151 | fn write_u128(&mut self, n: u128) -> io::Result<()>; 152 | /// Writes an unsigned 128 bit integer(little endian) to the underlying writer. 153 | fn write_u128_le(&mut self, n: u128) -> io::Result<()>; 154 | 155 | /// Writes a IEEE754 single-precision (4 bytes, big endian) floating point number to the underlying writer. 156 | fn write_f32(&mut self, n: f32) -> io::Result<()>; 157 | /// Writes a IEEE754 single-precision (4 bytes, little endian) floating point number to the underlying writer. 158 | fn write_f32_le(&mut self, n: f32) -> io::Result<()>; 159 | /// Writes a IEEE754 single-precision (8 bytes, big endian) floating point number to the underlying writer 160 | fn write_f64(&mut self, n: f64) -> io::Result<()>; 161 | /// Writes a IEEE754 single-precision (8 bytes, little endian) floating point number to the underlying writer 162 | fn write_f64_le(&mut self, n: f64) -> io::Result<()>; 163 | } 164 | 165 | impl MmapFileWriterExt for MmapFileWriter<'_> { 166 | #[inline] 167 | fn write_i8(&mut self, n: i8) -> io::Result<()> { 168 | self.w.write_i8(n) 169 | } 170 | 171 | #[inline] 172 | fn write_i16(&mut self, n: i16) -> io::Result<()> { 173 | self.w.write_i16::(n) 174 | } 175 | 176 | #[inline] 177 | fn write_i16_le(&mut self, n: i16) -> io::Result<()> { 178 | self.w.write_i16::(n) 179 | } 180 | 181 | #[inline] 182 | fn write_i32(&mut self, n: i32) -> io::Result<()> { 183 | self.w.write_i32::(n) 184 | } 185 | 186 | #[inline] 187 | fn write_i32_le(&mut self, n: i32) -> io::Result<()> { 188 | self.w.write_i32::(n) 189 | } 190 | 191 | #[inline] 192 | fn write_i64(&mut self, n: i64) -> io::Result<()> { 193 | self.w.write_i64::(n) 194 | } 195 | 196 | #[inline] 197 | fn write_i64_le(&mut self, n: i64) -> io::Result<()> { 198 | self.w.write_i64::(n) 199 | } 200 | 201 | #[inline] 202 | fn write_isize(&mut self, n: isize) -> io::Result<()> { 203 | self.w.write_all(n.to_be_bytes().as_ref()) 204 | } 205 | 206 | #[inline] 207 | fn write_isize_le(&mut self, n: isize) -> io::Result<()> { 208 | self.w.write_all(n.to_le_bytes().as_ref()) 209 | } 210 | 211 | #[inline] 212 | fn write_i128(&mut self, n: i128) -> io::Result<()> { 213 | self.w.write_all(n.to_be_bytes().as_ref()) 214 | } 215 | 216 | #[inline] 217 | fn write_i128_le(&mut self, n: i128) -> io::Result<()> { 218 | self.w.write_all(n.to_le_bytes().as_ref()) 219 | } 220 | 221 | #[inline] 222 | fn write_u8(&mut self, n: u8) -> io::Result<()> { 223 | self.w.write_u8(n) 224 | } 225 | 226 | #[inline] 227 | fn write_u16(&mut self, n: u16) -> io::Result<()> { 228 | self.w.write_u16::(n) 229 | } 230 | 231 | #[inline] 232 | fn write_u16_le(&mut self, n: u16) -> io::Result<()> { 233 | self.w.write_u16::(n) 234 | } 235 | 236 | #[inline] 237 | fn write_u32(&mut self, n: u32) -> io::Result<()> { 238 | self.w.write_u32::(n) 239 | } 240 | 241 | #[inline] 242 | fn write_u32_le(&mut self, n: u32) -> io::Result<()> { 243 | self.w.write_u32::(n) 244 | } 245 | 246 | #[inline] 247 | fn write_u64(&mut self, n: u64) -> io::Result<()> { 248 | self.w.write_u64::(n) 249 | } 250 | 251 | #[inline] 252 | fn write_u64_le(&mut self, n: u64) -> io::Result<()> { 253 | self.w.write_u64::(n) 254 | } 255 | 256 | #[inline] 257 | fn write_usize(&mut self, n: usize) -> io::Result<()> { 258 | self.w.write_all(n.to_be_bytes().as_ref()) 259 | } 260 | 261 | #[inline] 262 | fn write_usize_le(&mut self, n: usize) -> io::Result<()> { 263 | self.w.write_all(n.to_le_bytes().as_ref()) 264 | } 265 | 266 | #[inline] 267 | fn write_u128(&mut self, n: u128) -> io::Result<()> { 268 | self.w.write_all(n.to_be_bytes().as_ref()) 269 | } 270 | 271 | #[inline] 272 | fn write_u128_le(&mut self, n: u128) -> io::Result<()> { 273 | self.w.write_all(n.to_le_bytes().as_ref()) 274 | } 275 | 276 | #[inline] 277 | fn write_f32(&mut self, n: f32) -> io::Result<()> { 278 | self.w.write_f32::(n) 279 | } 280 | 281 | #[inline] 282 | fn write_f32_le(&mut self, n: f32) -> io::Result<()> { 283 | self.w.write_f32::(n) 284 | } 285 | 286 | #[inline] 287 | fn write_f64(&mut self, n: f64) -> io::Result<()> { 288 | self.w.write_f64::(n) 289 | } 290 | 291 | #[inline] 292 | fn write_f64_le(&mut self, n: f64) -> io::Result<()> { 293 | self.w.write_f64::(n) 294 | } 295 | } 296 | 297 | #[cfg(test)] 298 | mod tests { 299 | use std::io::{BufRead, Read}; 300 | use bytes::Buf; 301 | use crate::MmapFileMutExt; 302 | use crate::raw::MemoryMmapFileMut; 303 | 304 | #[test] 305 | fn test_writer() { 306 | let mut file = MemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 307 | let mut w = file.writer(0).unwrap(); 308 | let _ = format!("{:?}", w); 309 | assert_eq!(w.len(), 8096); 310 | assert_eq!(w.offset(), 0); 311 | let mut buf = [0; 10]; 312 | let n = w.read(&mut buf).unwrap(); 313 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 314 | w.fill_buf().unwrap(); 315 | w.consume(8096); 316 | 317 | let mut w = file.range_writer(100, 100).unwrap(); 318 | assert_eq!(w.remaining(), 100); 319 | w.advance(10); 320 | assert_eq!(w.remaining(), 90); 321 | let buf = w.chunk(); 322 | assert_eq!(buf.len(), 90); 323 | } 324 | } -------------------------------------------------------------------------------- /fmmap-rs/src/writer/tokio_impl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Formatter}; 2 | use std::io::{Error, SeekFrom, Cursor}; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | use bytes::Buf; 6 | use tokio::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf}; 7 | use pin_project_lite::pin_project; 8 | 9 | declare_and_impl_basic_writer!(); 10 | 11 | impl AsyncRead for AsyncMmapFileWriter<'_> { 12 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll> { 13 | self.project().w.poll_read(cx, buf) 14 | } 15 | } 16 | 17 | impl AsyncBufRead for AsyncMmapFileWriter<'_> { 18 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 19 | self.project().w.poll_fill_buf(cx) 20 | } 21 | 22 | fn consume(self: Pin<&mut Self>, amt: usize) { 23 | self.project().w.consume(amt) 24 | } 25 | } 26 | 27 | impl AsyncSeek for AsyncMmapFileWriter<'_> { 28 | fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> { 29 | self.project().w.start_seek(position) 30 | } 31 | 32 | fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 33 | self.project().w.poll_complete(cx) 34 | } 35 | } 36 | 37 | impl AsyncWrite for AsyncMmapFileWriter<'_> { 38 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { 39 | self.project().w.poll_write(cx, buf) 40 | } 41 | 42 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 43 | self.project().w.poll_flush(cx) 44 | } 45 | 46 | fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 47 | self.project().w.poll_shutdown(cx) 48 | 49 | } 50 | } 51 | 52 | impl Buf for AsyncMmapFileWriter<'_> { 53 | fn remaining(&self) -> usize { 54 | self.w.remaining() 55 | } 56 | 57 | fn chunk(&self) -> &[u8] { 58 | self.w.chunk() 59 | } 60 | 61 | fn advance(&mut self, cnt: usize) { 62 | self.w.advance(cnt) 63 | } 64 | } 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use bytes::Buf; 69 | use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt}; 70 | use crate::tokio::AsyncMmapFileMutExt; 71 | use crate::raw::tokio::AsyncMemoryMmapFileMut; 72 | 73 | #[tokio::test] 74 | async fn test_writer() { 75 | let mut file = AsyncMemoryMmapFileMut::from_vec("test.mem", vec![1; 8096]); 76 | let mut w = file.writer(0).unwrap(); 77 | let _ = format!("{:?}", w); 78 | assert_eq!(w.len(), 8096); 79 | assert_eq!(w.offset(), 0); 80 | let mut buf = [0; 10]; 81 | let n = w.read(&mut buf).await.unwrap(); 82 | assert!(buf[0..n].eq(vec![1; n].as_slice())); 83 | w.fill_buf().await.unwrap(); 84 | w.consume(8096); 85 | w.shutdown().await.unwrap(); 86 | 87 | let mut w = file.range_writer(100, 100).unwrap(); 88 | assert_eq!(w.remaining(), 100); 89 | w.advance(10); 90 | assert_eq!(w.remaining(), 90); 91 | let buf = w.chunk(); 92 | assert_eq!(buf.len(), 90); 93 | } 94 | } -------------------------------------------------------------------------------- /fmmap-uniffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fmmap-uniffi" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /fmmap-uniffi/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /scripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al8n/fmmap/1775bec5a0a32f7a0916f20d4faca066f8d41665/scripts/.gitkeep --------------------------------------------------------------------------------