├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .mailmap ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-Apache-2.0 ├── LICENSE-LGPL-2.1-or-later ├── README.md ├── mem_dbg-derive ├── Cargo.toml ├── README.md └── src │ └── lib.rs └── mem_dbg ├── Cargo.toml ├── README.md ├── examples ├── bench_hash_map │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── example.rs └── readme.rs ├── src ├── impl_mem_dbg.rs ├── impl_mem_size.rs ├── lib.rs └── utils.rs └── tests ├── test_mem_size.rs └── test_mem_size_no_import.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Check formatting 14 | run: cargo fmt -- --check 15 | - name: Build 16 | run: cargo build --verbose 17 | - name: Run tests 18 | run: cargo test --verbose 19 | - name: Run examples 20 | working-directory: ./mem_dbg 21 | run: for example in examples/*.rs ; do cargo run --example "$(basename "${example%.rs}")" ; done 22 | - name: Run clippy 23 | run: cargo clippy #-- -Dclippy::all -Dclippy::cargo 24 | 25 | coverage: 26 | needs: build 27 | name: coverage 28 | runs-on: ubuntu-latest 29 | container: 30 | image: xd009642/tarpaulin:develop-nightly 31 | options: --security-opt seccomp=unconfined 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v2 35 | 36 | - name: Generate code coverage 37 | run: | 38 | cargo +nightly tarpaulin --verbose --engine llvm --all-features --workspace --out Lcov 39 | 40 | - name: Coveralls 41 | uses: coverallsapp/github-action@v2 42 | with: 43 | github-token: ${{ secrets.GITHUB_TOKEN }} 44 | path-to-lcov: "lcov.info" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | .vscode 17 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Tommaso Fontana 2 | Tommaso Fontana 3 | Tommaso Fontana 4 | Tommaso Fontana -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.3.0] 2024-02-09 4 | 5 | ### Fixed 6 | 7 | * The `offset_of_nested` feature has been stabilized, so the references to it 8 | have been removed. 9 | 10 | * The check for the `offset_of_enum` feature was partly in the generated 11 | code, which was problematic as it had to be set from the user crate 12 | by adding an `offset_of_enum` feature. Now all checks are in the 13 | code of the derive macro. 14 | 15 | ## [0.2.5] 2024-02-06 16 | 17 | ### New 18 | 19 | * `COLOR` flag for colorized output. 20 | 21 | * We now print to standard error. 22 | 23 | ## [0.2.4] - 2024-08-09 24 | 25 | ### Fixed 26 | 27 | * The size of slices was off by 8 bytes. 28 | 29 | ## [0.2.3] - 2024-08-09 30 | 31 | ### Fixed 32 | 33 | * Mutable and non-mutable slices were returning different sizes within 34 | structures. At the top level, this is however unavoidable due to 35 | different autodeferentiation policies in the compiler, and this behavior 36 | has been documented. The same problem was affecting `MemDbg`. 37 | 38 | ## [0.2.2] - 2024-06-03 39 | 40 | ### Fixed 41 | 42 | * Fixed a bug where the compiler was issuing the error `usize cannot be 43 | dereferenced`. Invoking `id_sizes.iter()` in the generated code was 44 | returning sometimes `&(usize, usize)` and sometimes `(usize, usize)`. 45 | Using `into_iter` gives us a consistent behavior. 46 | 47 | ## [0.2.1] - 2024-05-28 48 | 49 | ### New 50 | 51 | * Support for network and time structures. 52 | 53 | ## [0.2.0] - 2024-04-10 54 | 55 | ### New 56 | 57 | * Support for displaying padding in structures and tuples using the new 58 | stable macro `offset_of`, with an optional feature `offset_of_enum` for 59 | enums (the latter requires nightly). 60 | 61 | * `MemDbgFlags::RUST_LAYOUT` flag displays structure in compiler layout; 62 | needs feature `offset_of_enum` for enums (the latter requires nightly). 63 | 64 | * Support for other crates such as `maligned` and `rand`. 65 | 66 | ### Fixed 67 | 68 | * Fixed cfgs for mmap-rs because it could not be enabled. 69 | 70 | * Fixed size for tuples (it was not considering padding). 71 | 72 | ## [0.1.8] - 2024-04-07 73 | 74 | ### Improved 75 | 76 | * Fixed cfgs for mmap-rs because it could not be enabled. 77 | 78 | ## [0.1.7] - 2024-04-07 79 | 80 | ### Improved 81 | 82 | * Added missing CopyTypes for maligned types. 83 | 84 | ## [0.1.6] - 2024-04-07 85 | 86 | ### Improved 87 | 88 | * Many new implementations for standard types such as RefCell. 89 | 90 | ## [0.1.4] - 2024-03-18 91 | 92 | ### Changed 93 | 94 | * Removed `.unwrap()` from the examples. 95 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | If you want to contribute to this project, please follow the Rust development 2 | guidelines at 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "mem_dbg-derive", 6 | "mem_dbg", 7 | "mem_dbg/examples/bench_hash_map" 8 | ] 9 | -------------------------------------------------------------------------------- /LICENSE-Apache-2.0: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /LICENSE-LGPL-2.1-or-later: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mem_dbg 2 | 3 | [![downloads](https://img.shields.io/crates/d/mem_dbg)](https://crates.io/crates/mem_dbg) 4 | [![dependents](https://img.shields.io/librariesio/dependents/cargo/mem_dbg)](https://crates.io/crates/mem_dbg/reverse_dependencies) 5 | ![GitHub CI](https://github.com/zommiommy/mem_dbg-rs/actions/workflows/rust.yml/badge.svg) 6 | ![license](https://img.shields.io/crates/l/mem_dbg) 7 | [![](https://tokei.rs/b1/github/zommiommy/mem_dbg-rs?type=Rust,Python)](https://github.com/zommiommy/mem_dbg-rs) 8 | [![Latest version](https://img.shields.io/crates/v/mem_dbg.svg)](https://crates.io/crates/mem_dbg) 9 | [![Documentation](https://docs.rs/mem_dbg/badge.svg)](https://docs.rs/mem_dbg) 10 | [![Coverage Status](https://coveralls.io/repos/github/zommiommy/mem_dbg-rs/badge.svg?branch=main)](https://coveralls.io/github/zommiommy/mem_dbg-rs?branch=main) 11 | 12 | Traits and associated procedural macros to inspect recursively the memory usage 13 | and layout of a value. 14 | 15 | The trait [`MemSize`] can be used to compute the overall memory usage of a value 16 | in bytes; the standard library function [`std::mem::size_of`] returns the 17 | *stack* size of a type in bytes, but it does not take into consideration heap 18 | memory. We provide implementations for most basic types, a derive macro for 19 | structs and enums whose fields implement [`MemSize`], and support for a few other 20 | crates. 21 | 22 | The trait [`MemDbg`], which depends on [`MemSize`], can be used to display the 23 | recursive layout of a value, together with the size of each part and the 24 | associated padding bytes. Also in this case we provide implementations for most 25 | basic types, a derive macro for structs and enums whose fields implement 26 | [`MemDbg`], and support for a few other crates. 27 | 28 | ## Why `MemSize` 29 | 30 | Other traits partially provide the functionality of [`MemSize`], but either they 31 | require implementing manually a trait, which is prone to error, or they do not 32 | provide the flexibility necessary for [`MemDbg`]. Most importantly, [`MemSize`] 33 | uses the type system to avoid iterating over the content of a container (a 34 | vector, etc.) when it is not necessary, making it possible to compute instantly 35 | the size of values occupying hundreds of gigabytes of heap memory. 36 | 37 | This is the result of the benchmark `bench_hash_map` contained in the `examples` 38 | directory. It builds a hash map with a hundred million entries and then measures 39 | its heap size: 40 | 41 | ```test 42 | Allocated: 2281701509 43 | get_size: 1879048240 152477833 ns 44 | deep_size_of: 1879048240 152482000 ns 45 | size_of: 2281701432 152261958 ns 46 | mem_size: 2281701424 209 ns 47 | ``` 48 | 49 | The first line is the number of bytes allocated by the program as returned by 50 | [`cap`]. Then, we display the result of [`get-size`], [`deepsize`], [`size-of`], 51 | and our own [`MemSize`]. Note that the first two crates are just measuring the 52 | space used by the items, and not by the data structure (i.e., they are not 53 | taking into account the load factor and the power-of-two size constraint of the 54 | hash map). Moreover, all other crates are about six orders of magnitude slower 55 | than our implementation, due to the necessity to iterate over all elements. 56 | 57 | ## Padding 58 | 59 | The trait [`MemDbg`] is useful to display the layout of a value and understand 60 | how much memory is used by each part. In particular, it exploits the new stable 61 | macro [`std::mem::offset_of`] to display the padding of each field in square 62 | brackets; moreover, the flag [`DbgFlags::RUST_LAYOUT`] makes it possible to 63 | display structures in the layout used by the Rust compiler, rather than 64 | that given by declaration order. 65 | 66 | These features are also available for enums using the feature `offset_of_enum`, 67 | which however needs the nightly compiler, as it enables the unstable feature 68 | `offset_of_enum`. 69 | 70 | ## Features 71 | 72 | - `offset_of_enum`: support for padding and for the `DbgFlags::RUST_LAYOUT` flag 73 | for enums. Requires the nightly compiler as it enables the unstable feature 74 | `offset_of_enum`. Calling `mem_dbg` with the flag `DbgFlags::RUST_LAYOUT` 75 | without this feature enabled will result in a panic. 76 | 77 | - `half`: support for the [`half`] crate. 78 | 79 | - `maligned`: support for the [`maligned`] crate. 80 | 81 | - `mmap-rs`: support for the [`mmap-rs`] crate. 82 | 83 | - `rand`: support for the [`rand`] crate. 84 | 85 | ## Examples 86 | 87 | This is an example program using [`MemSize`] and [`MemDbg`]. Note that we cannot 88 | visualize the effect of the useful [`DbgFlags::COLOR`] flag, which colorizes 89 | sizes depending on their magnitude. 90 | 91 | ```rust 92 | # #![cfg_attr(feature = "offset_of_enum", feature(offset_of_enum))] 93 | # fn main() -> Result<(), Box> { 94 | use mem_dbg::*; 95 | 96 | #[derive(MemSize, MemDbg)] 97 | struct Struct { 98 | a: A, 99 | b: B, 100 | test: isize, 101 | } 102 | 103 | #[derive(MemSize, MemDbg)] 104 | struct Data { 105 | a: A, 106 | b: Vec, 107 | c: (u8, String), 108 | } 109 | 110 | #[derive(MemSize, MemDbg)] 111 | union SingletonUnion { 112 | a: A 113 | } 114 | 115 | #[derive(MemSize, MemDbg)] 116 | enum TestEnum { 117 | Unit, 118 | Unit2(), 119 | Unit3 {}, 120 | Union(SingletonUnion), 121 | Unnamed(usize, u8), 122 | Named { first: usize, second: u8 }, 123 | } 124 | 125 | let b = Vec::with_capacity(100); 126 | 127 | let s = Struct { 128 | a: TestEnum::Unnamed(0, 16), 129 | b: Data { 130 | a: vec![0x42_u8; 700], 131 | b, 132 | c: (1, "foo".to_owned()), 133 | }, 134 | test: -0xbadf00d, 135 | }; 136 | 137 | println!("size: {}", s.mem_size(SizeFlags::default())); 138 | println!("capacity: {}", s.mem_size(SizeFlags::CAPACITY)); 139 | println!(); 140 | 141 | s.mem_dbg(DbgFlags::empty())?; 142 | 143 | println!(); 144 | 145 | println!("size: {}", s.mem_size(SizeFlags::default())); 146 | println!("capacity: {}", s.mem_size(SizeFlags::CAPACITY)); 147 | println!(); 148 | 149 | s.mem_dbg(DbgFlags::default() | DbgFlags::CAPACITY | DbgFlags::HUMANIZE)?; 150 | 151 | #[cfg(feature = "offset_of_enum")] 152 | { 153 | println!(); 154 | 155 | println!("size: {}", s.mem_size(SizeFlags::default())); 156 | println!("capacity: {}", s.mem_size(SizeFlags::CAPACITY)); 157 | println!(); 158 | 159 | s.mem_dbg(DbgFlags::empty() | DbgFlags::RUST_LAYOUT)?; 160 | } 161 | # Ok(()) 162 | # } 163 | ``` 164 | 165 | The previous program prints: 166 | 167 | ```test 168 | size: 807 169 | capacity: 1207 170 | 171 | 807 B ⏺ 172 | 16 B ├╴a 173 | │ ├╴Variant: Unnamed 174 | 8 B │ ├╴0 175 | 1 B │ ╰╴1 176 | 783 B ├╴b 177 | 724 B │ ├╴a 178 | 24 B │ ├╴b 179 | 35 B │ ╰╴c 180 | 1 B │ ├╴0 [7B] 181 | 27 B │ ╰╴1 182 | 8 B ╰╴test 183 | 184 | size: 807 185 | capacity: 1207 186 | 187 | 1.207 kB 100.00% ⏺: readme::main::Struct>> 188 | 16 B 1.33% ├╴a: readme::main::TestEnum 189 | │ ├╴Variant: Unnamed 190 | 8 B 0.66% │ ├╴0: usize 191 | 1 B 0.08% │ ╰╴1: u8 192 | 1.183 kB 98.01% ├╴b: readme::main::Data> 193 | 724 B 59.98% │ ├╴a: alloc::vec::Vec 194 | 424 B 35.13% │ ├╴b: alloc::vec::Vec 195 | 35 B 2.90% │ ╰╴c: (u8, alloc::string::String) 196 | 1 B 0.08% │ ├╴0: u8 [7B] 197 | 27 B 2.24% │ ╰╴1: alloc::string::String 198 | 8 B 0.66% ╰╴test: isize 199 | ``` 200 | 201 | If run with the feature `offset_of_enum`, it prints: 202 | 203 | ```text 204 | size: 807 205 | capacity: 1207 206 | 207 | 807 B ⏺ 208 | 16 B ├╴a 209 | │ ├╴Variant: Unnamed 210 | 8 B │ ├╴0 211 | 1 B │ ╰╴1 [6B] 212 | 783 B ├╴b 213 | 724 B │ ├╴a 214 | 24 B │ ├╴b 215 | 35 B │ ╰╴c 216 | 1 B │ ├╴0 [7B] 217 | 27 B │ ╰╴1 218 | 8 B ╰╴test 219 | 220 | size: 807 221 | capacity: 1207 222 | 223 | 1.207 kB 100.00% ⏺: readme::main::Struct>> 224 | 16 B 1.33% ├╴a: readme::main::TestEnum 225 | │ ├╴Variant: Unnamed 226 | 8 B 0.66% │ ├╴0: usize 227 | 1 B 0.08% │ ╰╴1: u8 [6B] 228 | 1.183 kB 98.01% ├╴b: readme::main::Data> 229 | 724 B 59.98% │ ├╴a: alloc::vec::Vec 230 | 424 B 35.13% │ ├╴b: alloc::vec::Vec 231 | 35 B 2.90% │ ╰╴c: (u8, alloc::string::String) 232 | 1 B 0.08% │ ├╴0: u8 [7B] 233 | 27 B 2.24% │ ╰╴1: alloc::string::String 234 | 8 B 0.66% ╰╴test: isize 235 | 236 | size: 807 237 | capacity: 1207 238 | 239 | 807 B ⏺ 240 | 783 B ├╴b 241 | 724 B │ ├╴a 242 | 24 B │ ├╴b 243 | 35 B │ ╰╴c 244 | 1 B │ ├╴0 [7B] 245 | 27 B │ ╰╴1 246 | 16 B ├╴a 247 | │ ├╴Variant: Unnamed 248 | 1 B │ ├╴1 [6B] 249 | 8 B │ ╰╴0 250 | 8 B ╰╴test 251 | ``` 252 | 253 | ## Caveats 254 | 255 | - We support out-of-the-box most basic types, and tuples up to size ten. The 256 | derive macros `MemSize`/`MemDbg` will generate implementations for structs and 257 | enums whose fields implement the associated interface: if this is not the case 258 | (e.g., because of the orphan rule) one can implement the traits manually. 259 | 260 | - If you invoke the methods of this crate on a shared reference, the compiler 261 | will automatically dereference it, and the method will be invoked on the 262 | referenced type: 263 | 264 | ```rust 265 | # fn main() -> Result<(), Box> { 266 | use mem_dbg::*; 267 | 268 | let mut x: [i32; 4] = [0, 0, 0, 0]; 269 | 270 | assert_eq!( 271 | (&x).mem_size(SizeFlags::default()), 272 | std::mem::size_of::<[i32; 4]>() 273 | ); 274 | 275 | assert_eq!( 276 | (&mut x).mem_size(SizeFlags::default()), 277 | std::mem::size_of::<&mut [i32; 4]>() 278 | ); 279 | 280 | assert_eq!( 281 | <&[i32; 4] as MemSize>::mem_size(&&x, SizeFlags::default()), 282 | std::mem::size_of::<&[i32; 4]>() 283 | ); 284 | # Ok(()) 285 | # } 286 | ``` 287 | 288 | - Computation of the size of arrays, slices, and vectors will be performed by 289 | iterating over their elements unless the type is a copy type that does not 290 | contain non-`'static` references and it is declared as such using the attribute 291 | `#[copy_type]`. See [`CopyType`] for more details. 292 | 293 | - The content of vectors and slices is not expanded recursively as the output 294 | might be too complex; this might change in the future (e.g., via a flag) 295 | should interesting use cases arise. 296 | 297 | - `BTreeMap`/`BTreeSet` are not currently supported as we still have to 298 | figure out a way to precisely measure their memory size and capacity. 299 | 300 | - Regarding `union`s, we only support completely the special case of the single 301 | field `union`, for which we implement both the derive macros `MemSize`/`MemDbg`. 302 | For the more complex cases of unions with multiple fields, we only provide the 303 | `MemSize` derive macro with partial support, excluding support for the 304 | `SizeFlags::FOLLOW_REFS` flag. If full support for derive macros `MemSize`/`MemDbg` 305 | in the case of an union with multiple fields, one can implement the traits manually. 306 | 307 | [`MemDbg`]: 308 | [`MemSize`]: 309 | [`std::mem::size_of`]: 310 | [`DbgFlags::RUST_LAYOUT`]: 311 | [`DbgFlags::COLOR`]: 312 | [`CopyType`]: 313 | [`cap`]: 314 | [`get-size`]: 315 | [`deepsize`]: 316 | [`size-of`]: 317 | [`maligned`]: 318 | [`mmap-rs`]: 319 | [`half`]: 320 | [`rand`]: 321 | -------------------------------------------------------------------------------- /mem_dbg-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mem_dbg-derive" 3 | version = "0.2.0" 4 | edition = "2021" 5 | description = "Procedural macros for mem_dbg" 6 | repository = "https://github.com/zommiommy/mem_dbg/" 7 | license = "Apache-2.0 OR LGPL-2.1-or-later" 8 | readme = "README.md" 9 | keywords = ["allocation", "debug", "memory"] 10 | authors = [ 11 | "Tommaso Fontana ", 12 | "Sebastiano Vigna ", 13 | ] 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | quote = "1" 20 | proc-macro2 = "1.0" 21 | syn = "2.0" 22 | 23 | [features] 24 | offset_of_enum = [] 25 | -------------------------------------------------------------------------------- /mem_dbg-derive/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /mem_dbg-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 3 | * SPDX-FileCopyrightText: 2023 Inria 4 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 7 | */ 8 | 9 | //! Derive procedural macros for the [`mem_dbg`](https://crates.io/crates/mem_dbg) crate. 10 | 11 | use proc_macro::TokenStream; 12 | use quote::{quote, ToTokens}; 13 | use syn::{ 14 | parse_macro_input, parse_quote, parse_quote_spanned, spanned::Spanned, Data, DeriveInput, 15 | }; 16 | 17 | /** 18 | 19 | Generate a `mem_dbg::MemSize` implementation for custom types. 20 | 21 | Presently we do not support unions. 22 | 23 | The attribute `copy_type` can be used on [`Copy`] types that do not contain non-`'static` references 24 | to make `MemSize::mem_size` faster on arrays, vectors and slices. Note that specifying 25 | `copy_type` will add the bound that the type is `Copy + 'static`. 26 | 27 | See `mem_dbg::CopyType` for more details. 28 | 29 | */ 30 | #[proc_macro_derive(MemSize, attributes(copy_type))] 31 | pub fn mem_dbg_mem_size(input: TokenStream) -> TokenStream { 32 | let mut input = parse_macro_input!(input as DeriveInput); 33 | 34 | let input_ident = input.ident; 35 | input.generics.make_where_clause(); 36 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 37 | let mut where_clause = where_clause.unwrap().clone(); // We just created it 38 | 39 | let is_copy_type = input 40 | .attrs 41 | .iter() 42 | .any(|x| x.meta.path().is_ident("copy_type")); 43 | 44 | // If copy_type, add the Copy + 'static bound 45 | let copy_type: syn::Expr = if is_copy_type { 46 | where_clause 47 | .predicates 48 | .push(parse_quote_spanned!(input_ident.span()=> Self: Copy + 'static)); 49 | parse_quote!(mem_dbg::True) 50 | } else { 51 | parse_quote!(mem_dbg::False) 52 | }; 53 | 54 | match input.data { 55 | Data::Struct(s) => { 56 | let mut fields_ident = vec![]; 57 | let mut fields_ty = vec![]; 58 | 59 | for (field_idx, field) in s.fields.iter().enumerate() { 60 | fields_ident.push( 61 | field 62 | .ident 63 | .to_owned() 64 | .map(|t| t.to_token_stream()) 65 | .unwrap_or(syn::Index::from(field_idx).to_token_stream()), 66 | ); 67 | fields_ty.push(field.ty.to_token_stream()); 68 | let field_ty = &field.ty; 69 | // Add MemSize bound to all fields 70 | where_clause 71 | .predicates 72 | .push(parse_quote_spanned!(field.span()=> #field_ty: mem_dbg::MemSize)); 73 | } 74 | quote! { 75 | #[automatically_derived] 76 | impl #impl_generics mem_dbg::CopyType for #input_ident #ty_generics #where_clause 77 | { 78 | type Copy = #copy_type; 79 | } 80 | 81 | #[automatically_derived] 82 | impl #impl_generics mem_dbg::MemSize for #input_ident #ty_generics #where_clause { 83 | fn mem_size(&self, _memsize_flags: mem_dbg::SizeFlags) -> usize { 84 | let mut bytes = core::mem::size_of::(); 85 | #(bytes += <#fields_ty as mem_dbg::MemSize>::mem_size(&self.#fields_ident, _memsize_flags) - core::mem::size_of::<#fields_ty>();)* 86 | bytes 87 | } 88 | } 89 | } 90 | } 91 | 92 | Data::Enum(e) => { 93 | let mut variants = Vec::new(); 94 | let mut variants_size = Vec::new(); 95 | 96 | for variant in e.variants { 97 | let mut res = variant.ident.to_owned().to_token_stream(); 98 | let mut var_args_size = quote! {core::mem::size_of::()}; 99 | match &variant.fields { 100 | syn::Fields::Unit => {} 101 | syn::Fields::Named(fields) => { 102 | let mut args = proc_macro2::TokenStream::new(); 103 | for field in &fields.named { 104 | let field_ty = &field.ty; 105 | where_clause 106 | .predicates 107 | .push(parse_quote_spanned!(field.span() => #field_ty: mem_dbg::MemSize)); 108 | let field_ident = &field.ident; 109 | let field_ty = field.ty.to_token_stream(); 110 | var_args_size.extend([quote! { 111 | + <#field_ty as mem_dbg::MemSize>::mem_size(#field_ident, _memsize_flags) - core::mem::size_of::<#field_ty>() 112 | }]); 113 | args.extend([field_ident.to_token_stream()]); 114 | args.extend([quote! {,}]); 115 | } 116 | // extend res with the args sourrounded by curly braces 117 | res.extend(quote! { 118 | { #args } 119 | }); 120 | } 121 | syn::Fields::Unnamed(fields) => { 122 | let mut args = proc_macro2::TokenStream::new(); 123 | 124 | for (field_idx, field) in fields.unnamed.iter().enumerate() { 125 | let ident = syn::Ident::new( 126 | &format!("v{}", field_idx), 127 | proc_macro2::Span::call_site(), 128 | ) 129 | .to_token_stream(); 130 | let field_ty = field.ty.to_token_stream(); 131 | var_args_size.extend([quote! { 132 | + <#field_ty as mem_dbg::MemSize>::mem_size(#ident, _memsize_flags) - core::mem::size_of::<#field_ty>() 133 | }]); 134 | args.extend([ident]); 135 | args.extend([quote! {,}]); 136 | 137 | where_clause 138 | .predicates 139 | .push(parse_quote_spanned!(field.span()=> #field_ty: mem_dbg::MemSize)); 140 | } 141 | // extend res with the args sourrounded by curly braces 142 | res.extend(quote! { 143 | ( #args ) 144 | }); 145 | } 146 | } 147 | variants.push(res); 148 | variants_size.push(var_args_size); 149 | } 150 | 151 | quote! { 152 | #[automatically_derived] 153 | impl #impl_generics mem_dbg::CopyType for #input_ident #ty_generics #where_clause 154 | { 155 | type Copy = #copy_type; 156 | } 157 | 158 | #[automatically_derived] 159 | impl #impl_generics mem_dbg::MemSize for #input_ident #ty_generics #where_clause { 160 | fn mem_size(&self, _memsize_flags: mem_dbg::SizeFlags) -> usize { 161 | match self { 162 | #( 163 | #input_ident::#variants => #variants_size, 164 | )* 165 | } 166 | } 167 | } 168 | } 169 | } 170 | 171 | Data::Union(u) => { 172 | // We only support single-field unions completely or call to mem_size on unions 173 | // without the FOLLOW_REFS flag, as we cannot know programmatically 174 | // which field is initialized. 175 | 176 | let fields = u.fields.named.iter().collect::>(); 177 | 178 | match fields.len() { 179 | 0 => unreachable!("Empty unions are not supported by the Rust programming language."), 180 | 1 => { 181 | let field = fields[0]; 182 | let field_ty = &field.ty; 183 | let ident = field.ident.as_ref().unwrap(); 184 | where_clause 185 | .predicates 186 | .push(parse_quote_spanned!(field.span() => #field_ty: mem_dbg::MemSize)); 187 | quote! { 188 | #[automatically_derived] 189 | impl #impl_generics mem_dbg::CopyType for #input_ident #ty_generics #where_clause 190 | { 191 | type Copy = #copy_type; 192 | } 193 | 194 | #[automatically_derived] 195 | impl #impl_generics mem_dbg::MemSize for #input_ident #ty_generics #where_clause { 196 | fn mem_size(&self, _memsize_flags: mem_dbg::SizeFlags) -> usize { 197 | unsafe{<#field_ty as mem_dbg::MemSize>::mem_size(&self.#ident, _memsize_flags)} 198 | } 199 | } 200 | } 201 | } 202 | number_of_fields => unimplemented!( 203 | "mem_dbg::MemSize for unions with more than one field ({}) is not supported.", number_of_fields 204 | ) 205 | } 206 | } 207 | }.into() 208 | } 209 | 210 | /** 211 | 212 | Generate a `mem_dbg::MemDbg` implementation for custom types. 213 | 214 | Presently we do not support unions. 215 | 216 | */ 217 | #[proc_macro_derive(MemDbg)] 218 | pub fn mem_dbg_mem_dbg(input: TokenStream) -> TokenStream { 219 | let mut input = parse_macro_input!(input as DeriveInput); 220 | 221 | let input_ident = input.ident; 222 | input.generics.make_where_clause(); 223 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 224 | let mut where_clause = where_clause.unwrap().clone(); // We just created it 225 | 226 | match input.data { 227 | Data::Struct(s) => { 228 | let mut id_offset_pushes = vec![]; 229 | let mut match_code = vec![]; 230 | 231 | for (field_idx, field) in s.fields.iter().enumerate() { 232 | // Use the field name for named structures, and the index 233 | // for tuple structures 234 | let field_ident = field 235 | .ident 236 | .to_owned() 237 | .map(|t| t.to_token_stream()) 238 | .unwrap_or_else(|| syn::Index::from(field_idx).to_token_stream()); 239 | 240 | let field_ident_str = field 241 | .ident 242 | .to_owned() 243 | .map(|t| t.to_string().to_token_stream()) 244 | .unwrap_or_else(|| field_idx.to_string().to_token_stream()); 245 | 246 | let field_ty = &field.ty; 247 | where_clause 248 | .predicates 249 | .push(parse_quote_spanned!(field.span() => #field_ty: mem_dbg::MemDbgImpl)); 250 | 251 | // We push the field index and its offset 252 | id_offset_pushes.push(quote!{ 253 | id_sizes.push((#field_idx, core::mem::offset_of!(#input_ident #ty_generics, #field_ident))); 254 | }); 255 | // This is the arm of the match statement that invokes 256 | // _mem_dbg_depth_on on the field. 257 | match_code.push(quote!{ 258 | #field_idx => <#field_ty as mem_dbg::MemDbgImpl>::_mem_dbg_depth_on(&self.#field_ident, _memdbg_writer, _memdbg_total_size, _memdbg_max_depth, _memdbg_prefix, Some(#field_ident_str), i == n - 1, padded_size, _memdbg_flags)?, 259 | }); 260 | } 261 | 262 | quote! { 263 | #[automatically_derived] 264 | impl #impl_generics mem_dbg::MemDbgImpl for #input_ident #ty_generics #where_clause { 265 | #[inline(always)] 266 | fn _mem_dbg_rec_on( 267 | &self, 268 | _memdbg_writer: &mut impl core::fmt::Write, 269 | _memdbg_total_size: usize, 270 | _memdbg_max_depth: usize, 271 | _memdbg_prefix: &mut String, 272 | _memdbg_is_last: bool, 273 | _memdbg_flags: mem_dbg::DbgFlags, 274 | ) -> core::fmt::Result { 275 | let mut id_sizes: Vec<(usize, usize)> = vec![]; 276 | #(#id_offset_pushes)* 277 | let n = id_sizes.len(); 278 | id_sizes.push((n, core::mem::size_of::())); 279 | // Sort by offset 280 | id_sizes.sort_by_key(|x| x.1); 281 | // Compute padded sizes 282 | for i in 0..n { 283 | id_sizes[i].1 = id_sizes[i + 1].1 - id_sizes[i].1; 284 | }; 285 | // Put the candle back unless the user requested otherwise 286 | if ! _memdbg_flags.contains(mem_dbg::DbgFlags::RUST_LAYOUT) { 287 | id_sizes.sort_by_key(|x| x.0); 288 | } 289 | 290 | for (i, (field_idx, padded_size)) in id_sizes.into_iter().enumerate().take(n) { 291 | match field_idx { 292 | #(#match_code)* 293 | _ => unreachable!(), 294 | } 295 | } 296 | Ok(()) 297 | } 298 | } 299 | } 300 | } 301 | 302 | Data::Enum(e) => { 303 | let mut variants = Vec::new(); 304 | let mut variants_code = Vec::new(); 305 | 306 | for variant in &e.variants { 307 | let variant_ident = &variant.ident; 308 | let mut res = variant.ident.to_owned().to_token_stream(); 309 | // Depending on the presence of the feature offset_of_enum, this 310 | // will contains field indices and offset_of or field indices 311 | // and size_of; in the latter case, we will assume size_of to be 312 | // the padded size, resulting in no padding. 313 | let mut id_offset_pushes = vec![]; 314 | let mut match_code = vec![]; 315 | let mut arrow = '╰'; 316 | match &variant.fields { 317 | syn::Fields::Unit => {}, 318 | syn::Fields::Named(fields) => { 319 | let mut args = proc_macro2::TokenStream::new(); 320 | if !fields.named.is_empty() { 321 | arrow = '├'; 322 | } 323 | for (field_idx, field) in fields.named.iter().enumerate() { 324 | let field_ty = &field.ty; 325 | let field_ident = field.ident.as_ref().unwrap(); 326 | let field_ident_str = format!("{}", field_ident); 327 | 328 | #[cfg(feature = "offset_of_enum")] 329 | id_offset_pushes.push(quote!{ 330 | // We push the offset of the field, which will 331 | // be used to compute the padded size. 332 | id_sizes.push((#field_idx, core::mem::offset_of!(#input_ident #ty_generics, #variant_ident . #field_ident))); 333 | }); 334 | #[cfg(not(feature = "offset_of_enum"))] 335 | id_offset_pushes.push(quote!{ 336 | id_sizes.push((#field_idx, core::mem::offset_of!(#input_ident #ty_generics, #variant_ident . #field_ident))); 337 | // We push the size of the field, which will be 338 | // used as a surrogate of the padded size. 339 | id_sizes.push((#field_idx, std::mem::size_of_val(#field_ident))); 340 | }); 341 | 342 | // This is the arm of the match statement that 343 | // invokes _mem_dbg_depth_on on the field. 344 | match_code.push(quote! { 345 | #field_idx => <#field_ty as mem_dbg::MemDbgImpl>::_mem_dbg_depth_on(#field_ident, _memdbg_writer, _memdbg_total_size, _memdbg_max_depth, _memdbg_prefix, Some(#field_ident_str), i == n - 1, padded_size, _memdbg_flags)?, 346 | }); 347 | args.extend([field_ident.to_token_stream()]); 348 | args.extend([quote! {,}]); 349 | 350 | let field_ty = &field.ty; 351 | where_clause 352 | .predicates 353 | .push(parse_quote_spanned!(field.span()=> #field_ty: mem_dbg::MemDbgImpl)); 354 | } 355 | // extend res with the args sourrounded by curly braces 356 | res.extend(quote! { 357 | // TODO: sanitize somehow the names or it'll be 358 | // havoc. 359 | { #args } 360 | }); 361 | } 362 | syn::Fields::Unnamed(fields) => { 363 | let mut args = proc_macro2::TokenStream::new(); 364 | if !fields.unnamed.is_empty() { 365 | arrow = '├'; 366 | } 367 | for (field_idx, field) in fields.unnamed.iter().enumerate() { 368 | let field_ident = syn::Ident::new( 369 | &format!("v{}", field_idx), 370 | proc_macro2::Span::call_site(), 371 | ) 372 | .to_token_stream(); 373 | let field_ty = &field.ty; 374 | let field_ident_str = format!("{}", field_idx); 375 | let _field_tuple_idx = syn::Index::from(field_idx); 376 | 377 | #[cfg(feature = "offset_of_enum")] 378 | id_offset_pushes.push(quote!{ 379 | // We push the offset of the field, which will 380 | // be used to compute the padded size. 381 | id_sizes.push((#field_idx, core::mem::offset_of!(#input_ident #ty_generics, #variant_ident . #_field_tuple_idx))); 382 | }); 383 | 384 | #[cfg(not(feature = "offset_of_enum"))] 385 | id_offset_pushes.push(quote!{ 386 | // We push the size of the field, which will be 387 | // used as a surrogate of the padded size. 388 | id_sizes.push((#field_idx, std::mem::size_of_val(#field_ident))); 389 | }); 390 | 391 | // This is the arm of the match statement that 392 | // invokes _mem_dbg_depth_on on the field. 393 | match_code.push(quote! { 394 | #field_idx => <#field_ty as mem_dbg::MemDbgImpl>::_mem_dbg_depth_on(#field_ident, _memdbg_writer, _memdbg_total_size, _memdbg_max_depth, _memdbg_prefix, Some(#field_ident_str), i == n - 1, padded_size, _memdbg_flags)?, 395 | }); 396 | 397 | args.extend([field_ident]); 398 | args.extend([quote! {,}]); 399 | 400 | let field_ty = &field.ty; 401 | where_clause 402 | .predicates 403 | .push(parse_quote_spanned!(field.span()=> #field_ty: mem_dbg::MemDbgImpl)); 404 | } 405 | // extend res with the args sourrounded by curly braces 406 | res.extend(quote! { 407 | ( #args ) 408 | }); 409 | } 410 | } 411 | variants.push(res); 412 | let variant_name = format!("Variant: {}\n", variant.ident); 413 | variants_code.push(quote!{{ 414 | _memdbg_writer.write_char(#arrow)?; 415 | _memdbg_writer.write_char('╴')?; 416 | _memdbg_writer.write_str(#variant_name)?; 417 | }}); 418 | 419 | // There's some abundant code duplication here, but we need to 420 | // keep the #[cfg] attributes outside of the quote! macro. 421 | 422 | #[cfg(feature = "offset_of_enum")] 423 | variants_code.push(quote!{{ 424 | let mut id_sizes: Vec<(usize, usize)> = vec![]; 425 | #(#id_offset_pushes)* 426 | let n = id_sizes.len(); 427 | 428 | // We use the offset_of information to build the real 429 | // space occupied by a field. 430 | id_sizes.push((n, core::mem::size_of::())); 431 | // Sort by offset 432 | id_sizes.sort_by_key(|x| x.1); 433 | // Compute padded sizes 434 | for i in 0..n { 435 | id_sizes[i].1 = id_sizes[i + 1].1 - id_sizes[i].1; 436 | }; 437 | // Put the candle back unless the user requested otherwise 438 | if ! _memdbg_flags.contains(mem_dbg::DbgFlags::RUST_LAYOUT) { 439 | id_sizes.sort_by_key(|x| x.0); 440 | } 441 | 442 | for (i, (field_idx, padded_size)) in id_sizes.into_iter().enumerate().take(n) { 443 | match field_idx { 444 | #(#match_code)* 445 | _ => unreachable!(), 446 | } 447 | } 448 | }}); 449 | 450 | #[cfg(not(feature = "offset_of_enum"))] 451 | variants_code.push(quote!{{ 452 | let mut id_sizes: Vec<(usize, usize)> = vec![]; 453 | #(#id_offset_pushes)* 454 | let n = id_sizes.len(); 455 | 456 | // Lacking offset_of for enums, id_sizes contains the 457 | // size_of of each field which we use as a surrogate of 458 | // the padded size. 459 | assert!(!_memdbg_flags.contains(mem_dbg::DbgFlags::RUST_LAYOUT), "DbgFlags::RUST_LAYOUT for enums requires the offset_of_enum feature"); 460 | 461 | for (i, (field_idx, padded_size)) in id_sizes.into_iter().enumerate().take(n) { 462 | match field_idx { 463 | #(#match_code)* 464 | _ => unreachable!(), 465 | } 466 | } 467 | }}); 468 | } 469 | 470 | quote! { 471 | #[automatically_derived] 472 | impl #impl_generics mem_dbg::MemDbgImpl for #input_ident #ty_generics #where_clause { 473 | #[inline(always)] 474 | fn _mem_dbg_rec_on( 475 | &self, 476 | _memdbg_writer: &mut impl core::fmt::Write, 477 | _memdbg_total_size: usize, 478 | _memdbg_max_depth: usize, 479 | _memdbg_prefix: &mut String, 480 | _memdbg_is_last: bool, 481 | _memdbg_flags: mem_dbg::DbgFlags, 482 | ) -> core::fmt::Result { 483 | let mut _memdbg_digits_number = mem_dbg::n_of_digits(_memdbg_total_size); 484 | if _memdbg_flags.contains(mem_dbg::DbgFlags::SEPARATOR) { 485 | _memdbg_digits_number += _memdbg_digits_number / 3; 486 | } 487 | if _memdbg_flags.contains(mem_dbg::DbgFlags::HUMANIZE) { 488 | _memdbg_digits_number = 6; 489 | } 490 | 491 | if _memdbg_flags.contains(mem_dbg::DbgFlags::PERCENTAGE) { 492 | _memdbg_digits_number += 8; 493 | } 494 | 495 | for _ in 0.._memdbg_digits_number + 3 { 496 | _memdbg_writer.write_char(' ')?; 497 | } 498 | if !_memdbg_prefix.is_empty() { 499 | _memdbg_writer.write_str(&_memdbg_prefix[2..])?; 500 | } 501 | match self { 502 | #( 503 | #input_ident::#variants => #variants_code, 504 | )* 505 | } 506 | Ok(()) 507 | } 508 | } 509 | } 510 | } 511 | 512 | Data::Union(u) => { 513 | // We only support single-field unions for the MemDbg. 514 | 515 | let fields = u.fields.named.iter().collect::>(); 516 | 517 | match fields.len() { 518 | 0 => unreachable!("Empty unions are not supported by the Rust programming language."), 519 | 1 => { 520 | let field = fields[0]; 521 | let field_ty = &field.ty; 522 | let ident = field.ident.as_ref().unwrap(); 523 | where_clause 524 | .predicates 525 | .push(parse_quote_spanned!(field.span() => #field_ty: mem_dbg::MemDbgImpl)); 526 | quote! { 527 | #[automatically_derived] 528 | impl #impl_generics mem_dbg::MemDbgImpl for #input_ident #ty_generics #where_clause { 529 | #[inline(always)] 530 | fn _mem_dbg_rec_on( 531 | &self, 532 | _memdbg_writer: &mut impl core::fmt::Write, 533 | _memdbg_total_size: usize, 534 | _memdbg_max_depth: usize, 535 | _memdbg_prefix: &mut String, 536 | _memdbg_is_last: bool, 537 | _memdbg_flags: mem_dbg::DbgFlags, 538 | ) -> core::fmt::Result { 539 | unsafe{<#field_ty as mem_dbg::MemDbgImpl>::_mem_dbg_depth_on(&self.#ident, _memdbg_writer, _memdbg_total_size, _memdbg_max_depth, _memdbg_prefix, None, _memdbg_is_last, core::mem::size_of::<#field_ty>(), _memdbg_flags)} 540 | } 541 | } 542 | } 543 | } 544 | _ => unimplemented!( 545 | "mem_dbg::MemDbg for unions with more than one field is not supported." 546 | ) 547 | } 548 | } 549 | }.into() 550 | } 551 | -------------------------------------------------------------------------------- /mem_dbg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mem_dbg" 3 | version = "0.3.0" 4 | edition = "2021" 5 | description = "Traits and associated procedural macros to display recursively the layout and memory usage of a value" 6 | repository = "https://github.com/zommiommy/mem_dbg/" 7 | license = "Apache-2.0 OR LGPL-2.1-or-later" 8 | readme = "README.md" 9 | keywords = ["allocation", "debug", "memory"] 10 | authors = [ 11 | "Tommaso Fontana ", 12 | "Sebastiano Vigna ", 13 | ] 14 | 15 | [dependencies] 16 | #mem_dbg-derive = { path = "../mem_dbg-derive", optional = true } 17 | mem_dbg-derive = { version = "=0.2.0", optional = true } 18 | mmap-rs = { version = "0.6.0", optional = true } 19 | half = { version = "2.0.4", optional = true } 20 | bitflags = "2.4.1" 21 | rand = { version = "0.9.0", optional = true, features = ["small_rng"] } 22 | maligned = { version = "0.2.1", optional = true } 23 | 24 | [dev-dependencies] 25 | paste = "1.0.15" 26 | 27 | [features] 28 | default = ["std", "derive"] 29 | std = ["alloc"] 30 | derive = ["mem_dbg-derive"] 31 | offset_of_enum = ["mem_dbg-derive/offset_of_enum"] 32 | alloc = [] 33 | -------------------------------------------------------------------------------- /mem_dbg/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /mem_dbg/examples/bench_hash_map/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = ["-C", "target-cpu=native"] 3 | -------------------------------------------------------------------------------- /mem_dbg/examples/bench_hash_map/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bench_hash_map" 3 | description = "A simple benchmark showing the output and performance of a few crates tracking heap usage of a value" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | [dependencies] 8 | mem_dbg = { path = "../.." } 9 | cap = "0.1.2" 10 | deepsize = "0.2.0" 11 | get-size = "0.1.4" 12 | size-of = "0.1.5" 13 | 14 | -------------------------------------------------------------------------------- /mem_dbg/examples/bench_hash_map/src/main.rs: -------------------------------------------------------------------------------- 1 | use cap::Cap; 2 | use deepsize::*; 3 | use get_size::*; 4 | use mem_dbg::*; 5 | use size_of::*; 6 | use std::alloc; 7 | use std::collections::HashMap; 8 | use std::time::Instant; 9 | 10 | #[global_allocator] 11 | static ALLOCATOR: Cap = Cap::new(alloc::System, usize::MAX); 12 | 13 | fn main() { 14 | const N: usize = 100_000_000; 15 | let mut m = HashMap::with_capacity(N); 16 | for i in 0..N { 17 | m.insert(i, i); 18 | } 19 | 20 | println!("Allocated: {}", ALLOCATOR.allocated()); 21 | 22 | let start = Instant::now(); 23 | let size = m.get_size(); 24 | println!("get_size: {} {:?} ns", size, start.elapsed().as_nanos()); 25 | 26 | let start = Instant::now(); 27 | let size = m.deep_size_of(); 28 | println!("deep_size_of: {} {:?} ns", size, start.elapsed().as_nanos()); 29 | 30 | let start = Instant::now(); 31 | let size = m.size_of().total_bytes(); 32 | println!("size_of: {} {:?} ns", size, start.elapsed().as_nanos()); 33 | 34 | let start = Instant::now(); 35 | let size = m.mem_size(SizeFlags::default()); 36 | println!("mem_size: {} {:?} ns", size, start.elapsed().as_nanos()); 37 | } 38 | -------------------------------------------------------------------------------- /mem_dbg/examples/example.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | #![cfg_attr(feature = "offset_of_enum", feature(offset_of_enum))] 9 | #![allow(dead_code)] 10 | 11 | use std::collections::HashSet; 12 | 13 | use mem_dbg::*; 14 | 15 | #[derive(Clone, Copy, MemSize, MemDbg)] 16 | #[copy_type] 17 | enum TestEnum { 18 | Unit, 19 | Unit2(), 20 | Unit3 {}, 21 | Unnamed(usize, u8), 22 | Named { first: usize, second: u8 }, 23 | } 24 | 25 | #[derive(Clone, Copy, MemSize, MemDbg)] 26 | struct TestMarker; 27 | 28 | #[derive(Clone, Copy, MemSize, MemDbg)] 29 | struct TestTuple(usize, u8); 30 | 31 | #[derive(MemSize, MemDbg)] 32 | struct Struct { 33 | a: A, 34 | b: B, 35 | test: isize, 36 | h: HashSet, 37 | } 38 | 39 | #[derive(MemSize, MemDbg)] 40 | struct Data { 41 | a: A, 42 | b: Vec, 43 | c: (u8, String), 44 | } 45 | 46 | fn main() -> Result<(), Box> { 47 | let mut b = Vec::with_capacity(100); 48 | b.extend(0..10); 49 | let mut h = HashSet::with_capacity(100); 50 | h.extend(0..10); 51 | 52 | let s = Struct { 53 | a: TestEnum::Unnamed(0, 16), 54 | b: Data { 55 | a: vec![0x42_u8; 700], 56 | b: b.clone(), 57 | c: (1, "foo".to_owned()), 58 | }, 59 | test: -0xbadf00d, 60 | h: h.clone(), 61 | }; 62 | 63 | // print the size in bytes of the value 64 | println!("size: {}", s.mem_size(SizeFlags::default())); 65 | println!("capacity: {}", s.mem_size(SizeFlags::CAPACITY)); 66 | 67 | println!(); 68 | 69 | println!("DbgFlags::default():"); 70 | println!(); 71 | s.mem_dbg(DbgFlags::default())?; 72 | 73 | println!(); 74 | 75 | println!("DbgFlags::default() | DbgFlags::CAPACITY:"); 76 | println!(); 77 | s.mem_dbg(DbgFlags::default() | DbgFlags::CAPACITY)?; 78 | 79 | println!(); 80 | 81 | println!("DbgFlags::default() | DbgFlags::CAPACITY | DbgFlags::HUMANIZE:"); 82 | println!(); 83 | s.mem_dbg(DbgFlags::default() | DbgFlags::HUMANIZE)?; 84 | 85 | println!(); 86 | 87 | println!("DbgFlags::default() | DbgFlags::CAPACITY | DbgFlags::HUMANIZE:"); 88 | println!(); 89 | s.mem_dbg(DbgFlags::default() | DbgFlags::CAPACITY | DbgFlags::HUMANIZE)?; 90 | 91 | println!(); 92 | 93 | let s = Struct { 94 | a: TestEnum::Named { 95 | first: 0, 96 | second: 1, 97 | }, 98 | b: Data { 99 | a: vec![0x42_u8; 700], 100 | b, 101 | c: (1, "foo".to_owned()), 102 | }, 103 | test: -0xbadf00d, 104 | h, 105 | }; 106 | 107 | println!("DbgFlags::empty():"); 108 | println!(); 109 | s.mem_dbg(DbgFlags::empty())?; 110 | 111 | #[cfg(feature = "offset_of_enum")] 112 | { 113 | println!(); 114 | println!("DbgFlags::HUMANIZE | DbgFlags::RUST_LAYOUT:"); 115 | println!(); 116 | s.mem_dbg(DbgFlags::HUMANIZE | DbgFlags::RUST_LAYOUT)?; 117 | } 118 | let s = Struct { 119 | a: 0_u8, 120 | b: 0_u8, 121 | test: 1, 122 | h: HashSet::new(), 123 | }; 124 | 125 | println!(); 126 | 127 | println!("DbgFlags::empty()"); 128 | println!(); 129 | s.mem_dbg(DbgFlags::empty())?; 130 | 131 | println!(); 132 | 133 | println!("DbgFlags::RUST_LAYOUT"); 134 | println!(); 135 | s.mem_dbg(DbgFlags::RUST_LAYOUT)?; 136 | 137 | Ok(()) 138 | } 139 | -------------------------------------------------------------------------------- /mem_dbg/examples/readme.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | #![cfg_attr(feature = "offset_of_enum", feature(offset_of_enum))] 9 | #![allow(dead_code)] 10 | 11 | use mem_dbg::*; 12 | 13 | fn main() -> Result<(), Box> { 14 | use mem_dbg::*; 15 | 16 | #[derive(MemSize, MemDbg)] 17 | struct Struct { 18 | a: A, 19 | b: B, 20 | test: isize, 21 | } 22 | 23 | #[derive(MemSize, MemDbg)] 24 | struct Data { 25 | a: A, 26 | b: Vec, 27 | c: (u8, String), 28 | } 29 | 30 | #[derive(MemSize, MemDbg)] 31 | enum TestEnum { 32 | Unit, 33 | Unit2(), 34 | Unit3 {}, 35 | Unnamed(usize, usize), 36 | Named { first: usize, second: u8 }, 37 | } 38 | 39 | let b = Vec::with_capacity(100); 40 | 41 | let s = Struct { 42 | a: TestEnum::Unnamed(0, 16), 43 | b: Data { 44 | a: vec![0x42_u8; 700], 45 | b, 46 | c: (1, "foo".to_owned()), 47 | }, 48 | test: -0xbadf00d, 49 | }; 50 | 51 | println!("size: {}", s.mem_size(SizeFlags::default())); 52 | println!("capacity: {}", s.mem_size(SizeFlags::CAPACITY)); 53 | println!(); 54 | 55 | s.mem_dbg(DbgFlags::empty())?; 56 | 57 | println!(); 58 | 59 | println!("size: {}", s.mem_size(SizeFlags::default())); 60 | println!("capacity: {}", s.mem_size(SizeFlags::CAPACITY)); 61 | println!(); 62 | 63 | s.mem_dbg(DbgFlags::default() | DbgFlags::CAPACITY | DbgFlags::HUMANIZE)?; 64 | 65 | #[cfg(feature = "offset_of_enum")] 66 | { 67 | println!(); 68 | 69 | println!("size: {}", s.mem_size(SizeFlags::default())); 70 | println!("capacity: {}", s.mem_size(SizeFlags::CAPACITY)); 71 | println!(); 72 | 73 | s.mem_dbg(DbgFlags::empty() | DbgFlags::RUST_LAYOUT)?; 74 | } 75 | Ok(()) 76 | } 77 | -------------------------------------------------------------------------------- /mem_dbg/src/impl_mem_dbg.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 3 | * SPDX-FileCopyrightText: 2023 Inria 4 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 7 | */ 8 | 9 | use core::marker::PhantomPinned; 10 | use core::num::*; 11 | use core::ops::Deref; 12 | use core::{marker::PhantomData, sync::atomic::*}; 13 | use std::collections::{HashMap, HashSet}; 14 | 15 | use crate::impl_mem_size::MemSizeHelper2; 16 | use crate::{impl_mem_size::MemSizeHelper, CopyType, DbgFlags, MemDbgImpl}; 17 | 18 | /// Implements [`MemDbg`] using the default implementation of [`MemDbgImpl`]. 19 | macro_rules! impl_mem_dbg { 20 | ($($ty:ty),*) => {$( 21 | impl MemDbgImpl for $ty {} 22 | )*}; 23 | } 24 | 25 | impl_mem_dbg! { 26 | (), bool, char, f32, f64, 27 | u8, u16, u32, u64, u128, usize, 28 | i8, i16, i32, i64, i128, isize, 29 | AtomicBool, 30 | AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, 31 | AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, 32 | NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, 33 | NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, 34 | PhantomPinned, str, String 35 | } 36 | 37 | impl MemDbgImpl for PhantomData {} 38 | 39 | // References: we recurse only if FOLLOW_REFS is set 40 | 41 | impl MemDbgImpl for &'_ T { 42 | fn _mem_dbg_rec_on( 43 | &self, 44 | writer: &mut impl core::fmt::Write, 45 | total_size: usize, 46 | max_depth: usize, 47 | prefix: &mut String, 48 | is_last: bool, 49 | flags: DbgFlags, 50 | ) -> core::fmt::Result { 51 | if flags.contains(DbgFlags::FOLLOW_REFS) { 52 | (**self)._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 53 | } else { 54 | Ok(()) 55 | } 56 | } 57 | } 58 | 59 | impl MemDbgImpl for &'_ mut T { 60 | fn _mem_dbg_rec_on( 61 | &self, 62 | writer: &mut impl core::fmt::Write, 63 | total_size: usize, 64 | max_depth: usize, 65 | prefix: &mut String, 66 | is_last: bool, 67 | flags: DbgFlags, 68 | ) -> core::fmt::Result { 69 | if flags.contains(DbgFlags::FOLLOW_REFS) { 70 | (**self)._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 71 | } else { 72 | Ok(()) 73 | } 74 | } 75 | } 76 | 77 | // Option 78 | 79 | impl MemDbgImpl for Option {} 80 | 81 | // Box 82 | 83 | #[cfg(feature = "alloc")] 84 | impl MemDbgImpl for Box { 85 | fn _mem_dbg_rec_on( 86 | &self, 87 | writer: &mut impl core::fmt::Write, 88 | total_size: usize, 89 | max_depth: usize, 90 | prefix: &mut String, 91 | is_last: bool, 92 | flags: DbgFlags, 93 | ) -> core::fmt::Result { 94 | self.as_ref() 95 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 96 | } 97 | } 98 | 99 | #[cfg(all(feature = "alloc", not(feature = "std")))] 100 | use alloc::sync::Arc; 101 | #[cfg(feature = "std")] 102 | use std::sync::Arc; 103 | #[cfg(feature = "alloc")] 104 | impl MemDbgImpl for Arc { 105 | fn _mem_dbg_rec_on( 106 | &self, 107 | writer: &mut impl core::fmt::Write, 108 | total_size: usize, 109 | max_depth: usize, 110 | prefix: &mut String, 111 | is_last: bool, 112 | flags: DbgFlags, 113 | ) -> core::fmt::Result { 114 | self.as_ref() 115 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 116 | } 117 | } 118 | 119 | // Slices 120 | 121 | impl MemDbgImpl for [T] where [T]: MemSizeHelper<::Copy> {} 122 | 123 | // Arrays 124 | 125 | impl MemDbgImpl for [T; N] where 126 | [T; N]: MemSizeHelper<::Copy> 127 | { 128 | } 129 | 130 | // Vectors 131 | 132 | #[cfg(feature = "alloc")] 133 | impl MemDbgImpl for Vec where 134 | Vec: MemSizeHelper<::Copy> 135 | { 136 | } 137 | 138 | // Tuples 139 | 140 | macro_rules! impl_tuples_muncher { 141 | () => {}; 142 | 143 | (($idx:tt => $ty:ident), $(($i:tt => $t:ident),)*) => { 144 | // Pass to list reversal 145 | impl_tuples_muncher!([($idx => $ty);] $(($i => $t),)*); 146 | // Recurse on tail 147 | impl_tuples_muncher!($(($i => $t),)*); 148 | }; 149 | 150 | // List reversal 151 | ([$(($accIdx: tt => $accTyp: ident);)+] ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => { 152 | impl_tuples_muncher!([($idx => $typ); $(($accIdx => $accTyp); )*] $( ($nidx => $ntyp), ) *); 153 | }; 154 | 155 | // Implement on reversed list, building the tuple type as we cannot expand 156 | // recursively 157 | ([($idx:tt => $ty:ident); $( ($nidx:tt => $nty:ident); )*]) => { 158 | impl_tuples_muncher!([($idx => $ty); $(($nidx => $nty);)* ], ($ty, $($nty,)*)); 159 | }; 160 | 161 | // Implement on reversed list and tuple type 162 | ([($idx:tt => $ty:ident); $( ($nidx:tt => $nty:ident); )*], $tty:ty) => { 163 | impl<$ty: crate::MemSize + MemDbgImpl, $($nty: crate::MemSize + MemDbgImpl,)*> MemDbgImpl for ($ty, $($nty,)*) { 164 | fn _mem_dbg_rec_on( 165 | &self, 166 | writer: &mut impl core::fmt::Write, 167 | total_size: usize, 168 | max_depth: usize, 169 | prefix: &mut String, 170 | _is_last: bool, 171 | flags: DbgFlags, 172 | ) -> core::fmt::Result { 173 | // Compute size of tuple minus one for last-field check. 174 | let mut _max_idx = $idx; 175 | $(_max_idx = _max_idx.max($nidx);)* 176 | 177 | let mut id_sizes: Vec<(usize, usize)> = vec![]; 178 | let n; 179 | 180 | { 181 | // We use the offset_of information to build the real 182 | // space occupied by a field. 183 | id_sizes.push(($idx, core::mem::offset_of!($tty, $idx))); 184 | $(id_sizes.push(($nidx, core::mem::offset_of!($tty, $nidx)));)* 185 | n = id_sizes.len(); 186 | id_sizes.push((n, core::mem::size_of::())); 187 | // Sort by offset 188 | id_sizes.sort_by_key(|x| x.1); 189 | // Compute actual sizes 190 | for i in 0..n { 191 | id_sizes[i].1 = id_sizes[i + 1].1 - id_sizes[i].1; 192 | }; 193 | // Put the candle back 194 | id_sizes.sort_by_key(|x| x.0); 195 | } 196 | 197 | self.$idx._mem_dbg_depth_on(writer, total_size, max_depth, prefix, Some(stringify!($idx)), $idx == _max_idx, id_sizes[$idx].1, flags)?; 198 | $( 199 | self.$nidx._mem_dbg_depth_on(writer, total_size, max_depth, prefix, Some(stringify!($nidx)), $nidx == _max_idx, id_sizes[$nidx].1, flags)?; 200 | )* 201 | Ok(()) 202 | } 203 | } 204 | }; 205 | } 206 | 207 | impl_tuples_muncher!( 208 | (9 => T9), 209 | (8 => T8), 210 | (7 => T7), 211 | (6 => T6), 212 | (5 => T5), 213 | (4 => T4), 214 | (3 => T3), 215 | (2 => T2), 216 | (1 => T1), 217 | (0 => T0), 218 | ); 219 | 220 | // Function pointers cannot recurse 221 | 222 | impl MemDbgImpl for fn() -> R {} 223 | impl MemDbgImpl for fn(A) -> R {} 224 | impl MemDbgImpl for fn(A, B) -> R {} 225 | impl MemDbgImpl for fn(A, B, C) -> R {} 226 | impl MemDbgImpl for fn(A, B, C, D) -> R {} 227 | 228 | // Hash-based containers from the standard library 229 | 230 | impl MemDbgImpl for HashSet where HashSet: MemSizeHelper<::Copy> {} 231 | impl MemDbgImpl for HashMap where 232 | HashMap: MemSizeHelper2<::Copy, ::Copy> 233 | { 234 | } 235 | 236 | // Hash stuff 237 | 238 | #[cfg(feature = "mmap-rs")] 239 | impl_mem_dbg!(mmap_rs::Mmap, mmap_rs::MmapMut); 240 | 241 | impl MemDbgImpl for core::hash::BuildHasherDefault { 242 | // it's a phantom data so no recursion 243 | } 244 | 245 | #[cfg(feature = "std")] 246 | impl MemDbgImpl for std::collections::hash_map::RandomState { 247 | // it's two u64s, but they are private so can't recurse 248 | } 249 | 250 | // alloc 251 | 252 | #[cfg(feature = "std")] 253 | impl MemDbgImpl for core::alloc::Layout { 254 | // Layout is size + align, but align is unstable so we can't recurse 255 | // on that, nor implement memdbg or memsize for that :) 256 | } 257 | 258 | // Ranges 259 | 260 | impl MemDbgImpl for core::ops::Range { 261 | fn _mem_dbg_rec_on( 262 | &self, 263 | writer: &mut impl core::fmt::Write, 264 | total_size: usize, 265 | max_depth: usize, 266 | prefix: &mut String, 267 | is_last: bool, 268 | flags: DbgFlags, 269 | ) -> core::fmt::Result { 270 | self.start 271 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags)?; 272 | self.end 273 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 274 | } 275 | } 276 | 277 | impl MemDbgImpl for core::ops::RangeFrom { 278 | fn _mem_dbg_rec_on( 279 | &self, 280 | writer: &mut impl core::fmt::Write, 281 | total_size: usize, 282 | max_depth: usize, 283 | prefix: &mut String, 284 | is_last: bool, 285 | flags: DbgFlags, 286 | ) -> core::fmt::Result { 287 | self.start 288 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 289 | } 290 | } 291 | 292 | impl MemDbgImpl for core::ops::RangeInclusive { 293 | fn _mem_dbg_rec_on( 294 | &self, 295 | writer: &mut impl core::fmt::Write, 296 | total_size: usize, 297 | max_depth: usize, 298 | prefix: &mut String, 299 | is_last: bool, 300 | flags: DbgFlags, 301 | ) -> core::fmt::Result { 302 | self.start() 303 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags)?; 304 | self.end() 305 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 306 | } 307 | } 308 | 309 | impl MemDbgImpl for core::ops::RangeTo { 310 | fn _mem_dbg_rec_on( 311 | &self, 312 | writer: &mut impl core::fmt::Write, 313 | total_size: usize, 314 | max_depth: usize, 315 | prefix: &mut String, 316 | is_last: bool, 317 | flags: DbgFlags, 318 | ) -> core::fmt::Result { 319 | self.end 320 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 321 | } 322 | } 323 | 324 | impl MemDbgImpl for core::ops::RangeToInclusive { 325 | fn _mem_dbg_rec_on( 326 | &self, 327 | writer: &mut impl core::fmt::Write, 328 | total_size: usize, 329 | max_depth: usize, 330 | prefix: &mut String, 331 | is_last: bool, 332 | flags: DbgFlags, 333 | ) -> core::fmt::Result { 334 | self.end 335 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 336 | } 337 | } 338 | 339 | impl MemDbgImpl for core::ptr::NonNull { 340 | // no recursion because we don't follow pointers 341 | } 342 | 343 | // Rand crate 344 | 345 | #[cfg(feature = "rand")] 346 | impl_mem_dbg!( 347 | rand::rngs::SmallRng, 348 | rand::rngs::StdRng, 349 | rand::rngs::ThreadRng 350 | ); 351 | 352 | // Cells 353 | 354 | impl MemDbgImpl for core::cell::RefCell { 355 | fn _mem_dbg_rec_on( 356 | &self, 357 | writer: &mut impl core::fmt::Write, 358 | total_size: usize, 359 | max_depth: usize, 360 | prefix: &mut String, 361 | is_last: bool, 362 | flags: DbgFlags, 363 | ) -> core::fmt::Result { 364 | self.borrow() 365 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 366 | } 367 | } 368 | 369 | impl MemDbgImpl for core::cell::Cell { 370 | fn _mem_dbg_rec_on( 371 | &self, 372 | writer: &mut impl core::fmt::Write, 373 | total_size: usize, 374 | max_depth: usize, 375 | prefix: &mut String, 376 | is_last: bool, 377 | flags: DbgFlags, 378 | ) -> core::fmt::Result { 379 | unsafe { 380 | (*self.as_ptr())._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 381 | } 382 | } 383 | } 384 | 385 | impl MemDbgImpl for core::cell::UnsafeCell { 386 | fn _mem_dbg_rec_on( 387 | &self, 388 | writer: &mut impl core::fmt::Write, 389 | total_size: usize, 390 | max_depth: usize, 391 | prefix: &mut String, 392 | is_last: bool, 393 | flags: DbgFlags, 394 | ) -> core::fmt::Result { 395 | unsafe { 396 | (*self.get())._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 397 | } 398 | } 399 | } 400 | 401 | // Mutexes 402 | 403 | #[cfg(feature = "std")] 404 | impl MemDbgImpl for std::sync::Mutex { 405 | fn _mem_dbg_rec_on( 406 | &self, 407 | writer: &mut impl core::fmt::Write, 408 | total_size: usize, 409 | max_depth: usize, 410 | prefix: &mut String, 411 | is_last: bool, 412 | flags: DbgFlags, 413 | ) -> core::fmt::Result { 414 | self.lock() 415 | .unwrap() 416 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 417 | } 418 | } 419 | 420 | #[cfg(feature = "std")] 421 | impl MemDbgImpl for std::sync::RwLock { 422 | fn _mem_dbg_rec_on( 423 | &self, 424 | writer: &mut impl core::fmt::Write, 425 | total_size: usize, 426 | max_depth: usize, 427 | prefix: &mut String, 428 | is_last: bool, 429 | flags: DbgFlags, 430 | ) -> core::fmt::Result { 431 | self.read() 432 | .unwrap() 433 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 434 | } 435 | } 436 | 437 | #[cfg(feature = "std")] 438 | impl MemDbgImpl for std::cell::OnceCell { 439 | fn _mem_dbg_rec_on( 440 | &self, 441 | writer: &mut impl core::fmt::Write, 442 | total_size: usize, 443 | max_depth: usize, 444 | prefix: &mut String, 445 | is_last: bool, 446 | flags: DbgFlags, 447 | ) -> core::fmt::Result { 448 | self.get() 449 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 450 | } 451 | } 452 | 453 | #[cfg(feature = "std")] 454 | impl MemDbgImpl for std::sync::MutexGuard<'_, T> { 455 | fn _mem_dbg_rec_on( 456 | &self, 457 | writer: &mut impl core::fmt::Write, 458 | total_size: usize, 459 | max_depth: usize, 460 | prefix: &mut String, 461 | is_last: bool, 462 | flags: DbgFlags, 463 | ) -> core::fmt::Result { 464 | if flags.contains(DbgFlags::FOLLOW_REFS) { 465 | self.deref() 466 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 467 | } else { 468 | Ok(()) 469 | } 470 | } 471 | } 472 | 473 | #[cfg(feature = "std")] 474 | impl MemDbgImpl for std::sync::RwLockReadGuard<'_, T> { 475 | fn _mem_dbg_rec_on( 476 | &self, 477 | writer: &mut impl core::fmt::Write, 478 | total_size: usize, 479 | max_depth: usize, 480 | prefix: &mut String, 481 | is_last: bool, 482 | flags: DbgFlags, 483 | ) -> core::fmt::Result { 484 | if flags.contains(DbgFlags::FOLLOW_REFS) { 485 | self.deref() 486 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 487 | } else { 488 | Ok(()) 489 | } 490 | } 491 | } 492 | 493 | #[cfg(feature = "std")] 494 | impl MemDbgImpl for std::sync::RwLockWriteGuard<'_, T> { 495 | fn _mem_dbg_rec_on( 496 | &self, 497 | writer: &mut impl core::fmt::Write, 498 | total_size: usize, 499 | max_depth: usize, 500 | prefix: &mut String, 501 | is_last: bool, 502 | flags: DbgFlags, 503 | ) -> core::fmt::Result { 504 | if flags.contains(DbgFlags::FOLLOW_REFS) { 505 | self.deref() 506 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 507 | } else { 508 | Ok(()) 509 | } 510 | } 511 | } 512 | 513 | // Os stuff 514 | 515 | #[cfg(feature = "std")] 516 | impl_mem_dbg!( 517 | std::path::Path, 518 | std::path::PathBuf, 519 | std::ffi::OsStr, 520 | std::ffi::OsString, 521 | std::fs::File, 522 | std::fs::OpenOptions, 523 | std::fs::Metadata, 524 | std::fs::FileTimes, 525 | std::fs::FileType, 526 | std::fs::Permissions 527 | ); 528 | 529 | // I/O 530 | 531 | #[cfg(feature = "std")] 532 | impl MemDbgImpl for std::io::BufReader { 533 | fn _mem_dbg_rec_on( 534 | &self, 535 | writer: &mut impl core::fmt::Write, 536 | total_size: usize, 537 | max_depth: usize, 538 | prefix: &mut String, 539 | is_last: bool, 540 | flags: DbgFlags, 541 | ) -> core::fmt::Result { 542 | self.get_ref() 543 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 544 | } 545 | } 546 | 547 | #[cfg(feature = "std")] 548 | impl MemDbgImpl for std::io::BufWriter { 549 | fn _mem_dbg_rec_on( 550 | &self, 551 | writer: &mut impl core::fmt::Write, 552 | total_size: usize, 553 | max_depth: usize, 554 | prefix: &mut String, 555 | is_last: bool, 556 | flags: DbgFlags, 557 | ) -> core::fmt::Result { 558 | self.get_ref() 559 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 560 | } 561 | } 562 | 563 | #[cfg(feature = "std")] 564 | impl MemDbgImpl for std::io::Cursor { 565 | fn _mem_dbg_rec_on( 566 | &self, 567 | writer: &mut impl core::fmt::Write, 568 | total_size: usize, 569 | max_depth: usize, 570 | prefix: &mut String, 571 | is_last: bool, 572 | flags: DbgFlags, 573 | ) -> core::fmt::Result { 574 | self.get_ref() 575 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 576 | } 577 | } 578 | 579 | // maligned crate 580 | 581 | #[cfg(feature = "maligned")] 582 | impl_mem_dbg!( 583 | maligned::A2, 584 | maligned::A4, 585 | maligned::A8, 586 | maligned::A16, 587 | maligned::A32, 588 | maligned::A64, 589 | maligned::A128, 590 | maligned::A256, 591 | maligned::A512 592 | ); 593 | 594 | #[cfg(feature = "maligned")] 595 | impl MemDbgImpl for maligned::Aligned { 596 | fn _mem_dbg_rec_on( 597 | &self, 598 | writer: &mut impl core::fmt::Write, 599 | total_size: usize, 600 | max_depth: usize, 601 | prefix: &mut String, 602 | is_last: bool, 603 | flags: DbgFlags, 604 | ) -> core::fmt::Result { 605 | self.deref() 606 | ._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags) 607 | } 608 | } 609 | 610 | // half crate 611 | 612 | #[cfg(feature = "half")] 613 | impl_mem_dbg!(half::f16, half::bf16); 614 | -------------------------------------------------------------------------------- /mem_dbg/src/impl_mem_size.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 3 | * SPDX-FileCopyrightText: 2023 Inria 4 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 7 | */ 8 | 9 | use core::marker::{PhantomData, PhantomPinned}; 10 | use core::num::*; 11 | use core::ops::Deref; 12 | use core::sync::atomic::*; 13 | use std::collections::{HashMap, HashSet}; 14 | 15 | use crate::{Boolean, CopyType, False, MemSize, SizeFlags, True}; 16 | 17 | /// A basic implementation using [`core::mem::size_of`] for non-[`Copy`] types, 18 | /// setting [`CopyType::Copy`] to [`False`]. 19 | macro_rules! impl_size_of { 20 | ($($ty:ty),*) => {$( 21 | impl CopyType for $ty { 22 | type Copy = False; 23 | } 24 | 25 | impl MemSize for $ty { 26 | #[inline(always)] 27 | fn mem_size(&self, _flags: SizeFlags) -> usize { 28 | core::mem::size_of::() 29 | } 30 | } 31 | )*}; 32 | } 33 | 34 | /// A basic implementation using [`core::mem::size_of`] for [`Copy`] types, 35 | /// setting [`CopyType::Copy`] to [`True`]. 36 | macro_rules! impl_copy_size_of { 37 | ($($ty:ty),*) => {$( 38 | impl CopyType for $ty { 39 | type Copy = True; 40 | } 41 | 42 | impl MemSize for $ty { 43 | #[inline(always)] 44 | fn mem_size(&self, _flags: SizeFlags) -> usize { 45 | core::mem::size_of::() 46 | } 47 | } 48 | )*}; 49 | } 50 | 51 | impl_copy_size_of! { 52 | (), bool, char, f32, f64, 53 | u8, u16, u32, u64, u128, usize, 54 | i8, i16, i32, i64, i128, isize, 55 | AtomicBool, 56 | AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, 57 | AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, 58 | NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, 59 | NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, 60 | PhantomPinned 61 | } 62 | 63 | // Strings 64 | 65 | impl CopyType for str { 66 | type Copy = False; 67 | } 68 | 69 | impl MemSize for str { 70 | #[inline(always)] 71 | fn mem_size(&self, _flags: SizeFlags) -> usize { 72 | core::mem::size_of::() + self.len() 73 | } 74 | } 75 | 76 | impl CopyType for String { 77 | type Copy = False; 78 | } 79 | 80 | impl MemSize for String { 81 | #[inline(always)] 82 | fn mem_size(&self, flags: SizeFlags) -> usize { 83 | if flags.contains(SizeFlags::CAPACITY) { 84 | core::mem::size_of::() + self.capacity() 85 | } else { 86 | core::mem::size_of::() + self.len() 87 | } 88 | } 89 | } 90 | 91 | // PhantomData 92 | 93 | impl CopyType for PhantomData { 94 | type Copy = True; 95 | } 96 | 97 | impl MemSize for PhantomData { 98 | #[inline(always)] 99 | fn mem_size(&self, _flags: SizeFlags) -> usize { 100 | 0 101 | } 102 | } 103 | 104 | // References: we recurse only if FOLLOW_REFS is set 105 | 106 | impl CopyType for &'_ T { 107 | type Copy = False; 108 | } 109 | 110 | impl MemSize for &'_ T { 111 | #[inline(always)] 112 | fn mem_size(&self, flags: SizeFlags) -> usize { 113 | if flags.contains(SizeFlags::FOLLOW_REFS) { 114 | core::mem::size_of::() + ::mem_size(*self, flags) 115 | } else { 116 | core::mem::size_of::() 117 | } 118 | } 119 | } 120 | 121 | impl CopyType for &'_ mut T { 122 | type Copy = False; 123 | } 124 | 125 | impl MemSize for &'_ mut T { 126 | #[inline(always)] 127 | fn mem_size(&self, flags: SizeFlags) -> usize { 128 | <&'_ T as MemSize>::mem_size(&&**self, flags) 129 | } 130 | } 131 | 132 | // Option 133 | 134 | impl CopyType for Option { 135 | type Copy = T::Copy; 136 | } 137 | 138 | impl MemSize for Option { 139 | #[inline(always)] 140 | fn mem_size(&self, flags: SizeFlags) -> usize { 141 | core::mem::size_of::() 142 | + self.as_ref().map_or(0, |x| { 143 | ::mem_size(x, flags) - core::mem::size_of::() 144 | }) 145 | } 146 | } 147 | 148 | // Box 149 | 150 | #[cfg(all(feature = "alloc", not(feature = "std")))] 151 | use alloc::boxed::Box; 152 | #[cfg(feature = "alloc")] 153 | impl MemSize for Box { 154 | #[inline(always)] 155 | fn mem_size(&self, flags: SizeFlags) -> usize { 156 | core::mem::size_of::() + ::mem_size(self.as_ref(), flags) 157 | } 158 | } 159 | 160 | #[cfg(all(feature = "alloc", not(feature = "std")))] 161 | use alloc::sync::Arc; 162 | #[cfg(feature = "std")] 163 | use std::sync::Arc; 164 | #[cfg(feature = "alloc")] 165 | impl MemSize for Arc { 166 | #[inline(always)] 167 | fn mem_size(&self, flags: SizeFlags) -> usize { 168 | core::mem::size_of::() - core::mem::size_of::() 169 | + ::mem_size(self.as_ref(), flags) 170 | } 171 | } 172 | 173 | /// A helper trait that makes it possible to implement differently 174 | /// the size computation for arrays, vectors, and slices of 175 | /// [`Copy`] types. 176 | /// 177 | /// See [`crate::CopyType`] for more information. 178 | pub trait MemSizeHelper { 179 | fn mem_size_impl(&self, flags: SizeFlags) -> usize; 180 | } 181 | 182 | // Slices 183 | 184 | impl MemSize for [T] 185 | where 186 | [T]: MemSizeHelper<::Copy>, 187 | { 188 | #[inline(always)] 189 | fn mem_size(&self, flags: SizeFlags) -> usize { 190 | <[T] as MemSizeHelper<::Copy>>::mem_size_impl(self, flags) 191 | } 192 | } 193 | 194 | #[cfg(all(feature = "alloc", not(feature = "std")))] 195 | use alloc::vec::Vec; 196 | #[cfg(feature = "alloc")] 197 | impl MemSizeHelper for [T] { 198 | #[inline(always)] 199 | fn mem_size_impl(&self, _flags: SizeFlags) -> usize { 200 | std::mem::size_of_val(self) 201 | } 202 | } 203 | 204 | #[cfg(all(feature = "alloc", not(feature = "std")))] 205 | use alloc::vec::Vec; 206 | #[cfg(feature = "alloc")] 207 | impl MemSizeHelper for [T] { 208 | #[inline(always)] 209 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 210 | self.iter() 211 | .map(|x| ::mem_size(x, flags)) 212 | .sum::() 213 | } 214 | } 215 | 216 | // Arrays 217 | 218 | impl CopyType for [T; N] { 219 | type Copy = T::Copy; 220 | } 221 | 222 | impl MemSize for [T; N] 223 | where 224 | [T; N]: MemSizeHelper<::Copy>, 225 | { 226 | #[inline(always)] 227 | fn mem_size(&self, flags: SizeFlags) -> usize { 228 | <[T; N] as MemSizeHelper<::Copy>>::mem_size_impl(self, flags) 229 | } 230 | } 231 | 232 | impl MemSizeHelper for [T; N] { 233 | #[inline(always)] 234 | fn mem_size_impl(&self, _flags: SizeFlags) -> usize { 235 | core::mem::size_of::() 236 | } 237 | } 238 | 239 | impl MemSizeHelper for [T; N] { 240 | #[inline(always)] 241 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 242 | core::mem::size_of::() 243 | + self 244 | .iter() 245 | .map(|x| ::mem_size(x, flags) - core::mem::size_of::()) 246 | .sum::() 247 | } 248 | } 249 | 250 | // Vectors 251 | 252 | impl CopyType for Vec { 253 | type Copy = False; 254 | } 255 | 256 | impl MemSize for Vec 257 | where 258 | Vec: MemSizeHelper<::Copy>, 259 | { 260 | #[inline(always)] 261 | fn mem_size(&self, flags: SizeFlags) -> usize { 262 | as MemSizeHelper<::Copy>>::mem_size_impl(self, flags) 263 | } 264 | } 265 | 266 | #[cfg(all(feature = "alloc", not(feature = "std")))] 267 | use alloc::vec::Vec; 268 | #[cfg(feature = "alloc")] 269 | impl MemSizeHelper for Vec { 270 | #[inline(always)] 271 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 272 | if flags.contains(SizeFlags::CAPACITY) { 273 | core::mem::size_of::() + self.capacity() * core::mem::size_of::() 274 | } else { 275 | core::mem::size_of::() + self.len() * core::mem::size_of::() 276 | } 277 | } 278 | } 279 | 280 | #[cfg(all(feature = "alloc", not(feature = "std")))] 281 | use alloc::vec::Vec; 282 | #[cfg(feature = "alloc")] 283 | impl MemSizeHelper for Vec { 284 | #[inline(always)] 285 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 286 | if flags.contains(SizeFlags::CAPACITY) { 287 | core::mem::size_of::() 288 | + self 289 | .iter() 290 | .map(|x| ::mem_size(x, flags)) 291 | .sum::() 292 | + (self.capacity() - self.len()) * core::mem::size_of::() 293 | } else { 294 | core::mem::size_of::() 295 | + self 296 | .iter() 297 | .map(|x| ::mem_size(x, flags)) 298 | .sum::() 299 | } 300 | } 301 | } 302 | 303 | // Tuples 304 | 305 | macro_rules! impl_tuples_muncher { 306 | () => {}; 307 | 308 | (($idx:tt => $ty:ident), $(($i:tt => $t:ident),)*) => { 309 | // Pass to list reversal 310 | impl_tuples_muncher!([($idx => $ty);] $(($i => $t),)*); 311 | // Recurse on tail 312 | impl_tuples_muncher!($(($i => $t),)*); 313 | }; 314 | 315 | // List reversal 316 | ([$(($accIdx: tt => $accTyp: ident);)+] ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => { 317 | impl_tuples_muncher!([($idx => $typ); $(($accIdx => $accTyp); )*] $( ($nidx => $ntyp), ) *); 318 | }; 319 | 320 | // Implement on reversed list 321 | ([($idx:tt => $ty:ident); $( ($nidx:tt => $nty:ident); )*]) => { 322 | impl<$ty, $($nty,)*> CopyType for ($ty, $($nty,)*) { 323 | type Copy = False; 324 | } 325 | 326 | impl<$ty: MemSize, $($nty: MemSize,)*> MemSize for ($ty, $($nty,)*) 327 | { 328 | #[inline(always)] 329 | fn mem_size(&self, flags: SizeFlags) -> usize { 330 | let mut bytes = core::mem::size_of::(); 331 | bytes += <$ty as MemSize>::mem_size(&self.$idx, flags) - core::mem::size_of::<$ty>(); 332 | $( bytes += <$nty as MemSize>::mem_size(&self.$nidx, flags) - core::mem::size_of::<$nty>(); )* 333 | bytes 334 | } 335 | } 336 | } 337 | 338 | } 339 | 340 | impl_tuples_muncher!( 341 | (9 => T9), 342 | (8 => T8), 343 | (7 => T7), 344 | (6 => T6), 345 | (5 => T5), 346 | (4 => T4), 347 | (3 => T3), 348 | (2 => T2), 349 | (1 => T1), 350 | (0 => T0), 351 | ); 352 | 353 | // Functions 354 | 355 | impl CopyType for fn() -> R { 356 | type Copy = True; 357 | } 358 | 359 | impl MemSize for fn() -> R { 360 | #[inline(always)] 361 | fn mem_size(&self, _flags: SizeFlags) -> usize { 362 | core::mem::size_of::() 363 | } 364 | } 365 | 366 | impl CopyType for fn(A) -> R { 367 | type Copy = True; 368 | } 369 | 370 | impl MemSize for fn(A) -> R { 371 | #[inline(always)] 372 | fn mem_size(&self, _flags: SizeFlags) -> usize { 373 | core::mem::size_of::() 374 | } 375 | } 376 | 377 | impl CopyType for fn(A, B) -> R { 378 | type Copy = True; 379 | } 380 | 381 | impl MemSize for fn(A, B) -> R { 382 | #[inline(always)] 383 | fn mem_size(&self, _flags: SizeFlags) -> usize { 384 | core::mem::size_of::() 385 | } 386 | } 387 | 388 | impl CopyType for fn(A, B, C) -> R { 389 | type Copy = True; 390 | } 391 | 392 | impl MemSize for fn(A, B, C) -> R { 393 | #[inline(always)] 394 | fn mem_size(&self, _flags: SizeFlags) -> usize { 395 | core::mem::size_of::() 396 | } 397 | } 398 | 399 | impl CopyType for fn(A, B, C, D) -> R { 400 | type Copy = True; 401 | } 402 | 403 | impl MemSize for fn(A, B, C, D) -> R { 404 | #[inline(always)] 405 | fn mem_size(&self, _flags: SizeFlags) -> usize { 406 | core::mem::size_of::() 407 | } 408 | } 409 | 410 | // Ranges 411 | 412 | impl CopyType for core::ops::Range { 413 | type Copy = True; 414 | } 415 | 416 | impl MemSize for core::ops::Range { 417 | #[inline(always)] 418 | fn mem_size(&self, flags: SizeFlags) -> usize { 419 | core::mem::size_of::() 420 | + ::mem_size(&self.start, flags) 421 | + ::mem_size(&self.end, flags) 422 | - 2 * core::mem::size_of::() 423 | } 424 | } 425 | 426 | impl CopyType for core::ops::RangeFrom { 427 | type Copy = True; 428 | } 429 | 430 | impl MemSize for core::ops::RangeFrom { 431 | #[inline(always)] 432 | fn mem_size(&self, flags: SizeFlags) -> usize { 433 | core::mem::size_of::() + ::mem_size(&self.start, flags) 434 | - core::mem::size_of::() 435 | } 436 | } 437 | 438 | impl CopyType for core::ops::RangeInclusive { 439 | type Copy = True; 440 | } 441 | 442 | impl MemSize for core::ops::RangeInclusive { 443 | #[inline(always)] 444 | fn mem_size(&self, flags: SizeFlags) -> usize { 445 | core::mem::size_of::() 446 | + ::mem_size(self.start(), flags) 447 | + ::mem_size(self.end(), flags) 448 | - 2 * core::mem::size_of::() 449 | } 450 | } 451 | 452 | impl CopyType for core::ops::RangeTo { 453 | type Copy = True; 454 | } 455 | 456 | impl MemSize for core::ops::RangeTo { 457 | #[inline(always)] 458 | fn mem_size(&self, flags: SizeFlags) -> usize { 459 | core::mem::size_of::() + ::mem_size(&self.end, flags) 460 | - core::mem::size_of::() 461 | } 462 | } 463 | 464 | impl CopyType for core::ops::RangeToInclusive { 465 | type Copy = True; 466 | } 467 | 468 | impl MemSize for core::ops::RangeToInclusive { 469 | #[inline(always)] 470 | fn mem_size(&self, flags: SizeFlags) -> usize { 471 | core::mem::size_of::() + ::mem_size(&self.end, flags) 472 | - core::mem::size_of::() 473 | } 474 | } 475 | 476 | // Rand crate 477 | 478 | #[cfg(feature = "rand")] 479 | impl_copy_size_of!( 480 | rand::rngs::SmallRng, 481 | rand::rngs::ThreadRng, 482 | rand::rngs::StdRng 483 | ); 484 | 485 | // Cells 486 | 487 | impl CopyType for core::cell::RefCell { 488 | type Copy = T::Copy; 489 | } 490 | 491 | impl MemSize for core::cell::RefCell { 492 | fn mem_size(&self, flags: SizeFlags) -> usize { 493 | core::mem::size_of::() - core::mem::size_of::() 494 | + ::mem_size(&self.borrow(), flags) 495 | } 496 | } 497 | 498 | impl CopyType for core::cell::Cell { 499 | type Copy = T::Copy; 500 | } 501 | 502 | impl MemSize for core::cell::Cell { 503 | fn mem_size(&self, flags: SizeFlags) -> usize { 504 | core::mem::size_of::() - core::mem::size_of::() 505 | + unsafe { ::mem_size(&*self.as_ptr(), flags) } 506 | } 507 | } 508 | 509 | impl CopyType for core::cell::OnceCell { 510 | type Copy = T::Copy; 511 | } 512 | 513 | impl MemSize for core::cell::OnceCell { 514 | fn mem_size(&self, flags: SizeFlags) -> usize { 515 | core::mem::size_of::() - core::mem::size_of::() 516 | + as MemSize>::mem_size(&self.get(), flags) 517 | } 518 | } 519 | 520 | impl CopyType for core::cell::UnsafeCell { 521 | type Copy = T::Copy; 522 | } 523 | 524 | impl MemSize for core::cell::UnsafeCell { 525 | fn mem_size(&self, flags: SizeFlags) -> usize { 526 | core::mem::size_of::() - core::mem::size_of::() 527 | + unsafe { ::mem_size(&*self.get(), flags) } 528 | } 529 | } 530 | 531 | // Mutexes 532 | 533 | #[cfg(feature = "std")] 534 | impl CopyType for std::sync::Mutex { 535 | type Copy = False; 536 | } 537 | 538 | #[cfg(feature = "std")] 539 | impl MemSize for std::sync::Mutex { 540 | fn mem_size(&self, flags: SizeFlags) -> usize { 541 | core::mem::size_of::() - core::mem::size_of::() 542 | + ::mem_size(&self.lock().unwrap(), flags) 543 | } 544 | } 545 | 546 | #[cfg(feature = "std")] 547 | impl CopyType for std::sync::RwLock { 548 | type Copy = False; 549 | } 550 | 551 | #[cfg(feature = "std")] 552 | impl MemSize for std::sync::RwLock { 553 | fn mem_size(&self, flags: SizeFlags) -> usize { 554 | core::mem::size_of::() - core::mem::size_of::() 555 | + ::mem_size(&self.read().unwrap(), flags) 556 | } 557 | } 558 | 559 | #[cfg(feature = "std")] 560 | impl CopyType for std::sync::MutexGuard<'_, T> { 561 | type Copy = False; 562 | } 563 | 564 | #[cfg(feature = "std")] 565 | impl MemSize for std::sync::MutexGuard<'_, T> { 566 | fn mem_size(&self, flags: SizeFlags) -> usize { 567 | if flags.contains(SizeFlags::FOLLOW_REFS) { 568 | core::mem::size_of::() - core::mem::size_of::() 569 | + ::mem_size(self.deref(), flags) 570 | } else { 571 | 0 572 | } 573 | } 574 | } 575 | 576 | #[cfg(feature = "std")] 577 | impl CopyType for std::sync::RwLockReadGuard<'_, T> { 578 | type Copy = False; 579 | } 580 | 581 | #[cfg(feature = "std")] 582 | impl MemSize for std::sync::RwLockReadGuard<'_, T> { 583 | fn mem_size(&self, flags: SizeFlags) -> usize { 584 | if flags.contains(SizeFlags::FOLLOW_REFS) { 585 | core::mem::size_of::() - core::mem::size_of::() 586 | + ::mem_size(self.deref(), flags) 587 | } else { 588 | 0 589 | } 590 | } 591 | } 592 | 593 | #[cfg(feature = "std")] 594 | impl CopyType for std::sync::RwLockWriteGuard<'_, T> { 595 | type Copy = False; 596 | } 597 | 598 | #[cfg(feature = "std")] 599 | impl MemSize for std::sync::RwLockWriteGuard<'_, T> { 600 | fn mem_size(&self, flags: SizeFlags) -> usize { 601 | if flags.contains(SizeFlags::FOLLOW_REFS) { 602 | core::mem::size_of::() - core::mem::size_of::() 603 | + ::mem_size(self.deref(), flags) 604 | } else { 605 | 0 606 | } 607 | } 608 | } 609 | 610 | // OS stuff 611 | 612 | #[cfg(feature = "std")] 613 | impl CopyType for std::path::Path { 614 | type Copy = False; 615 | } 616 | 617 | #[cfg(feature = "std")] 618 | impl MemSize for std::path::Path { 619 | fn mem_size(&self, flags: SizeFlags) -> usize { 620 | ::mem_size(self.as_os_str(), flags) 621 | } 622 | } 623 | 624 | #[cfg(feature = "std")] 625 | impl CopyType for std::path::PathBuf { 626 | type Copy = False; 627 | } 628 | 629 | #[cfg(feature = "std")] 630 | impl MemSize for std::path::PathBuf { 631 | fn mem_size(&self, flags: SizeFlags) -> usize { 632 | if flags.contains(SizeFlags::CAPACITY) { 633 | core::mem::size_of::() + core::mem::size_of::() 634 | } else { 635 | ::mem_size(self.as_os_str(), flags) 636 | } 637 | } 638 | } 639 | 640 | #[cfg(feature = "std")] 641 | impl CopyType for std::ffi::OsStr { 642 | type Copy = False; 643 | } 644 | 645 | #[cfg(feature = "std")] 646 | impl MemSize for std::ffi::OsStr { 647 | fn mem_size(&self, flags: SizeFlags) -> usize { 648 | if flags.contains(SizeFlags::FOLLOW_REFS) { 649 | self.as_encoded_bytes().len() 650 | } else { 651 | 0 652 | } 653 | } 654 | } 655 | 656 | #[cfg(feature = "std")] 657 | impl CopyType for std::ffi::OsString { 658 | type Copy = False; 659 | } 660 | 661 | #[cfg(feature = "std")] 662 | impl MemSize for std::ffi::OsString { 663 | fn mem_size(&self, flags: SizeFlags) -> usize { 664 | core::mem::size_of::() 665 | + if flags.contains(SizeFlags::CAPACITY) { 666 | // Capacity is an usize 667 | core::mem::size_of::() 668 | } else { 669 | // Len is an usize 670 | core::mem::size_of::() 671 | } 672 | } 673 | } 674 | 675 | #[cfg(feature = "std")] 676 | impl_size_of!( 677 | std::fs::File, 678 | std::fs::OpenOptions, 679 | std::fs::Metadata, 680 | std::fs::FileType, 681 | std::fs::FileTimes, 682 | std::fs::Permissions 683 | ); 684 | 685 | // I/O 686 | 687 | #[cfg(feature = "std")] 688 | impl CopyType for std::io::BufReader { 689 | type Copy = False; 690 | } 691 | 692 | #[cfg(feature = "std")] 693 | impl MemSize for std::io::BufReader { 694 | fn mem_size(&self, flags: SizeFlags) -> usize { 695 | core::mem::size_of::() - core::mem::size_of::() 696 | + ::mem_size(self.get_ref(), flags) 697 | } 698 | } 699 | 700 | #[cfg(feature = "std")] 701 | impl CopyType for std::io::BufWriter { 702 | type Copy = False; 703 | } 704 | 705 | #[cfg(feature = "std")] 706 | impl MemSize for std::io::BufWriter { 707 | fn mem_size(&self, flags: SizeFlags) -> usize { 708 | core::mem::size_of::() - core::mem::size_of::() 709 | + ::mem_size(self.get_ref(), flags) 710 | } 711 | } 712 | 713 | #[cfg(feature = "std")] 714 | impl CopyType for std::io::Cursor { 715 | type Copy = False; 716 | } 717 | 718 | #[cfg(feature = "std")] 719 | impl MemSize for std::io::Cursor { 720 | fn mem_size(&self, flags: SizeFlags) -> usize { 721 | core::mem::size_of::() - core::mem::size_of::() 722 | + ::mem_size(self.get_ref(), flags) 723 | } 724 | } 725 | 726 | // IpAddr 727 | #[cfg(feature = "std")] 728 | impl_copy_size_of!( 729 | std::net::Ipv4Addr, 730 | std::net::Ipv6Addr, 731 | std::net::IpAddr, 732 | std::net::SocketAddrV4, 733 | std::net::SocketAddrV6, 734 | std::net::SocketAddr 735 | ); 736 | 737 | // Time 738 | #[cfg(feature = "std")] 739 | impl_copy_size_of!( 740 | std::time::Duration, 741 | std::time::Instant, 742 | std::time::SystemTime, 743 | std::time::SystemTimeError 744 | ); 745 | 746 | // mmap-rs crate 747 | 748 | #[cfg(feature = "mmap-rs")] 749 | impl CopyType for mmap_rs::Mmap { 750 | type Copy = False; 751 | } 752 | 753 | #[cfg(feature = "mmap-rs")] 754 | impl MemSize for mmap_rs::Mmap { 755 | #[inline(always)] 756 | fn mem_size(&self, flags: SizeFlags) -> usize { 757 | core::mem::size_of::() 758 | + if flags.contains(SizeFlags::FOLLOW_REFS) { 759 | self.len() 760 | } else { 761 | 0 762 | } 763 | } 764 | } 765 | 766 | #[cfg(feature = "mmap-rs")] 767 | impl CopyType for mmap_rs::MmapMut { 768 | type Copy = False; 769 | } 770 | 771 | #[cfg(feature = "mmap-rs")] 772 | impl MemSize for mmap_rs::MmapMut { 773 | #[inline(always)] 774 | fn mem_size(&self, flags: SizeFlags) -> usize { 775 | core::mem::size_of::() 776 | + if flags.contains(SizeFlags::FOLLOW_REFS) { 777 | self.len() 778 | } else { 779 | 0 780 | } 781 | } 782 | } 783 | 784 | // Hash-based containers from the standard library 785 | // 786 | // If the standard library changes load factor, this code will have to change 787 | // accordingly. 788 | 789 | // Straight from hashbrown 790 | fn capacity_to_buckets(cap: usize) -> Option { 791 | // TODO: check that cap == 0 is handled correctly (we presently return 4) 792 | 793 | // For small tables we require at least 1 empty bucket so that lookups are 794 | // guaranteed to terminate if an element doesn't exist in the table. 795 | if cap < 8 { 796 | // We don't bother with a table size of 2 buckets since that can only 797 | // hold a single element. Instead we skip directly to a 4 bucket table 798 | // which can hold 3 elements. 799 | return Some(if cap < 4 { 4 } else { 8 }); 800 | } 801 | 802 | // Otherwise require 1/8 buckets to be empty (87.5% load) 803 | // 804 | // Be careful when modifying this, calculate_layout relies on the 805 | // overflow check here. 806 | let adjusted_cap = cap.checked_mul(8)? / 7; 807 | 808 | // Any overflows will have been caught by the checked_mul. Also, any 809 | // rounding errors from the division above will be cleaned up by 810 | // next_power_of_two (which can't overflow because of the previous division). 811 | Some(adjusted_cap.next_power_of_two()) 812 | } 813 | 814 | impl MemSize for HashSet 815 | where 816 | HashSet: MemSizeHelper<::Copy>, 817 | { 818 | #[inline(always)] 819 | fn mem_size(&self, flags: SizeFlags) -> usize { 820 | as MemSizeHelper<::Copy>>::mem_size_impl(self, flags) 821 | } 822 | } 823 | 824 | // Add to the given size the space occupied on the stack by the hash set, by the unused 825 | // but unavoidable buckets, by the speedup bytes of Swiss Tables, and if `flags` contains 826 | // `SizeFlags::CAPACITY`, by empty buckets. 827 | fn fix_set_for_capacity(hash_set: &HashSet, size: usize, flags: SizeFlags) -> usize { 828 | core::mem::size_of::>() 829 | + size 830 | + if flags.contains(SizeFlags::CAPACITY) { 831 | (capacity_to_buckets(hash_set.capacity()).unwrap_or(usize::MAX) - hash_set.len()) 832 | * std::mem::size_of::() 833 | + capacity_to_buckets(hash_set.capacity()).unwrap_or(usize::MAX) 834 | * std::mem::size_of::() 835 | } else { 836 | (capacity_to_buckets(hash_set.len()).unwrap_or(usize::MAX) - hash_set.len()) 837 | * std::mem::size_of::() 838 | + capacity_to_buckets(hash_set.len()).unwrap_or(usize::MAX) 839 | * std::mem::size_of::() 840 | } 841 | } 842 | 843 | #[cfg(feature = "alloc")] 844 | impl MemSizeHelper for HashSet { 845 | #[inline(always)] 846 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 847 | fix_set_for_capacity(self, std::mem::size_of::() * self.len(), flags) 848 | } 849 | } 850 | 851 | #[cfg(feature = "alloc")] 852 | impl MemSizeHelper for HashSet { 853 | #[inline(always)] 854 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 855 | fix_set_for_capacity( 856 | self, 857 | self.iter() 858 | .map(|x| ::mem_size(x, flags)) 859 | .sum::(), 860 | flags, 861 | ) 862 | } 863 | } 864 | 865 | /// A helper trait that makes it possible to implement differently 866 | /// the size computation for maps in which keys or values are 867 | /// [`Copy`] types. 868 | /// 869 | /// See [`crate::CopyType`] for more information. 870 | pub trait MemSizeHelper2 { 871 | fn mem_size_impl(&self, flags: SizeFlags) -> usize; 872 | } 873 | 874 | impl MemSize for HashMap 875 | where 876 | HashMap: MemSizeHelper2<::Copy, ::Copy>, 877 | { 878 | #[inline(always)] 879 | fn mem_size(&self, flags: SizeFlags) -> usize { 880 | as MemSizeHelper2<::Copy, ::Copy>>::mem_size_impl(self, flags) 881 | } 882 | } 883 | 884 | // Add to the given size the space occupied on the stack by the hash map, by the unused 885 | // but unavoidable buckets, by the speedup bytes of Swiss Tables, and if `flags` contains 886 | // `SizeFlags::CAPACITY`, by empty buckets. 887 | fn fix_map_for_capacity(hash_map: &HashMap, size: usize, flags: SizeFlags) -> usize { 888 | core::mem::size_of::>() 889 | + size 890 | + if flags.contains(SizeFlags::CAPACITY) { 891 | (capacity_to_buckets(hash_map.capacity()).unwrap_or(usize::MAX) - hash_map.len()) 892 | * (std::mem::size_of::() + std::mem::size_of::()) 893 | + capacity_to_buckets(hash_map.capacity()).unwrap_or(usize::MAX) 894 | * std::mem::size_of::() 895 | } else { 896 | (capacity_to_buckets(hash_map.len()).unwrap_or(usize::MAX) - hash_map.len()) 897 | * (std::mem::size_of::() + std::mem::size_of::()) 898 | + capacity_to_buckets(hash_map.len()).unwrap_or(usize::MAX) 899 | * std::mem::size_of::() 900 | } 901 | } 902 | 903 | #[cfg(feature = "alloc")] 904 | impl MemSizeHelper2 for HashMap { 905 | #[inline(always)] 906 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 907 | fix_map_for_capacity( 908 | self, 909 | (std::mem::size_of::() + std::mem::size_of::()) * self.len(), 910 | flags, 911 | ) 912 | } 913 | } 914 | 915 | #[cfg(feature = "alloc")] 916 | impl MemSizeHelper2 for HashMap { 917 | #[inline(always)] 918 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 919 | fix_map_for_capacity( 920 | self, 921 | (std::mem::size_of::()) * self.len() 922 | + self 923 | .values() 924 | .map(|v| ::mem_size(v, flags)) 925 | .sum::(), 926 | flags, 927 | ) 928 | } 929 | } 930 | 931 | #[cfg(feature = "alloc")] 932 | impl MemSizeHelper2 for HashMap { 933 | #[inline(always)] 934 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 935 | fix_map_for_capacity( 936 | self, 937 | self.keys() 938 | .map(|k| ::mem_size(k, flags)) 939 | .sum::() 940 | + (std::mem::size_of::()) * self.len(), 941 | flags, 942 | ) 943 | } 944 | } 945 | 946 | #[cfg(feature = "alloc")] 947 | impl MemSizeHelper2 for HashMap { 948 | #[inline(always)] 949 | fn mem_size_impl(&self, flags: SizeFlags) -> usize { 950 | fix_map_for_capacity( 951 | self, 952 | self.iter() 953 | .map(|(k, v)| { 954 | ::mem_size(k, flags) + ::mem_size(v, flags) 955 | }) 956 | .sum::(), 957 | flags, 958 | ) 959 | } 960 | } 961 | 962 | // Hash 963 | 964 | impl CopyType for core::hash::BuildHasherDefault { 965 | type Copy = True; 966 | } 967 | impl MemSize for core::hash::BuildHasherDefault { 968 | #[inline(always)] 969 | fn mem_size(&self, _flags: SizeFlags) -> usize { 970 | // it's a phantom hash 971 | debug_assert_eq!(core::mem::size_of::(), 0); 972 | 0 973 | } 974 | } 975 | 976 | #[cfg(feature = "std")] 977 | impl CopyType for std::collections::hash_map::RandomState { 978 | type Copy = True; 979 | } 980 | 981 | #[cfg(feature = "std")] 982 | impl MemSize for std::collections::hash_map::RandomState { 983 | #[inline(always)] 984 | fn mem_size(&self, _flags: SizeFlags) -> usize { 985 | core::mem::size_of::() 986 | } 987 | } 988 | 989 | // Memory stuff 990 | 991 | impl_copy_size_of!(core::alloc::Layout); 992 | 993 | impl CopyType for core::ptr::NonNull { 994 | type Copy = True; 995 | } 996 | 997 | impl MemSize for core::ptr::NonNull { 998 | #[inline(always)] 999 | fn mem_size(&self, _flags: SizeFlags) -> usize { 1000 | core::mem::size_of::() 1001 | } 1002 | } 1003 | 1004 | // maligned crate 1005 | 1006 | #[cfg(feature = "maligned")] 1007 | impl_copy_size_of!( 1008 | maligned::A2, 1009 | maligned::A4, 1010 | maligned::A8, 1011 | maligned::A16, 1012 | maligned::A32, 1013 | maligned::A64, 1014 | maligned::A128, 1015 | maligned::A256, 1016 | maligned::A512 1017 | ); 1018 | 1019 | #[cfg(feature = "maligned")] 1020 | impl CopyType for maligned::Aligned { 1021 | type Copy = True; 1022 | } 1023 | 1024 | #[cfg(feature = "maligned")] 1025 | impl MemSize for maligned::Aligned { 1026 | fn mem_size(&self, flags: SizeFlags) -> usize { 1027 | core::mem::size_of::() - core::mem::size_of::() 1028 | + ::mem_size(self.deref(), flags) 1029 | } 1030 | } 1031 | 1032 | // half crate 1033 | 1034 | #[cfg(feature = "half")] 1035 | impl_copy_size_of!(half::f16, half::bf16); 1036 | -------------------------------------------------------------------------------- /mem_dbg/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 3 | * SPDX-FileCopyrightText: 2023 Inria 4 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 7 | */ 8 | #![cfg_attr(feature = "offset_of_enum", feature(offset_of_enum))] 9 | #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] 10 | #![deny(unconditional_recursion)] 11 | #![cfg_attr(not(feature = "std"), no_std)] 12 | #[cfg(all(feature = "alloc", not(feature = "std")))] 13 | extern crate alloc; 14 | 15 | #[cfg(feature = "derive")] 16 | pub use mem_dbg_derive::{MemDbg, MemSize}; 17 | 18 | mod impl_mem_dbg; 19 | mod impl_mem_size; 20 | 21 | mod utils; 22 | pub use utils::*; 23 | 24 | /** 25 | 26 | Internal trait used within [`CopyType`] to implement [`MemSize`] depending 27 | on whether a type is [`Copy`] or not. 28 | 29 | It has only two implementations, [`True`] and [`False`]. 30 | 31 | */ 32 | pub trait Boolean {} 33 | /// One of the two possible implementations of [`Boolean`]. 34 | pub struct True {} 35 | impl Boolean for True {} 36 | /// One of the two possible implementations of [`Boolean`]. 37 | pub struct False {} 38 | impl Boolean for False {} 39 | 40 | /** 41 | 42 | Marker trait for copy types. 43 | 44 | The trait comes in two flavors: `CopyType` and 45 | `CopyType`. In the first case, [`MemSize::mem_size`] can be computed on 46 | arrays, vectors, and slices by multiplying the length or capacity 47 | by the size of the element type; in the second case, it 48 | is necessary to iterate on each element. 49 | 50 | The trait is made necessary by the impossibility of checking that a type 51 | implements [`Copy`] from a procedural macro. 52 | 53 | Since we cannot use negative trait bounds, every type that is used as a parameter of 54 | an array, vector, or slice must implement either `CopyType` or 55 | `CopyType`. If you do not implement either of these traits, 56 | you will not be able to compute the size of arrays, vectors, and slices but error 57 | messages will be very unhelpful due to the contrived way we have to implement 58 | mutually exclusive types [working around the bug that prevents the compiler 59 | from understanding that implementations for the two flavors of `CopyType` are mutually 60 | exclusive](https://github.com/rust-lang/rfcs/pull/1672#issuecomment-1405377983). 61 | 62 | If you use the provided derive macros all this logic will be hidden from you. You'll 63 | just have to add the attribute `#[copy_type]` to your structures if they 64 | are [`Copy`] types and they do not contain non-`'static` references. We enforce this property by 65 | adding a bound `Copy + 'static` to the type in the procedural macro. 66 | 67 | Note that this approach forces us to compute the size of [`Copy`] types that contain 68 | references by iteration _even if you do not specify_ [`SizeFlags::FOLLOW_REFS`]. 69 | 70 | */ 71 | pub trait CopyType { 72 | type Copy: Boolean; 73 | } 74 | 75 | bitflags::bitflags! { 76 | /// Flags for [`MemDbg`]. 77 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 78 | pub struct SizeFlags: u32 { 79 | /// Follow references. 80 | /// 81 | /// By default [`MemSize::mem_size`] does not follow references and 82 | /// computes only the size of the reference itself. 83 | /// 84 | /// # Warning 85 | /// 86 | /// Note that all references are followed independently. If the same 87 | /// region of memory is reachable by two different paths, it will be 88 | /// counted twice. 89 | const FOLLOW_REFS = 1 << 0; 90 | /// Return capacity instead of size. 91 | /// 92 | /// Size does not include memory allocated but not used: for example, in 93 | /// the case of a vector [`MemSize::mem_size`] calls [`Vec::len`] rather 94 | /// than [`Vec::capacity`]. 95 | /// 96 | /// However, when this flag is specified [`MemSize::mem_size`] will 97 | /// return the size of all memory allocated, even if it is not used: for 98 | /// example, in the case of a vector this option makes 99 | /// [`MemSize::mem_size`] call [`Vec::capacity`] rather than 100 | /// [`Vec::len`]. 101 | const CAPACITY = 1 << 1; 102 | } 103 | } 104 | 105 | impl Default for SizeFlags { 106 | /// The default set of flags is the empty set. 107 | #[inline(always)] 108 | fn default() -> Self { 109 | Self::empty() 110 | } 111 | } 112 | 113 | /// A trait to compute recursively the overall size or capacity of a structure, 114 | /// as opposed to the stack size returned by [`core::mem::size_of()`]. 115 | /// 116 | /// You can derive this trait with `#[derive(MemSize)]` if all the fields of 117 | /// your type implement [`MemSize`]. 118 | pub trait MemSize { 119 | /// Returns the (recursively computed) overall 120 | /// memory size of the structure in bytes. 121 | fn mem_size(&self, flags: SizeFlags) -> usize; 122 | } 123 | 124 | bitflags::bitflags! { 125 | /// Flags for [`MemDbg`]. 126 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 127 | pub struct DbgFlags: u32 { 128 | /// Follow references. See [`SizeFlags::FOLLOW_REFS`]. 129 | const FOLLOW_REFS = 1 << 0; 130 | /// Print memory usage in human readable format. 131 | const HUMANIZE = 1 << 1; 132 | /// Print memory usage as a percentage. 133 | const PERCENTAGE = 1 << 2; 134 | /// Print the type name. 135 | const TYPE_NAME = 1 << 3; 136 | /// Display capacity instead of size. See [`SizeFlags::CAPACITY`]. 137 | const CAPACITY = 1 << 4; 138 | /// Add an underscore every 3 digits, when `HUMANIZE` is not set. 139 | const SEPARATOR = 1 << 5; 140 | /// Print fields in memory order (i.e., using the layout chosen by the 141 | /// compiler), rather than in declaration order. 142 | const RUST_LAYOUT = 1 << 6; 143 | /// Use colors to distinguish sizes. 144 | const COLOR = 1 << 7; 145 | } 146 | } 147 | 148 | impl DbgFlags { 149 | /// Translates flags that are in common with [`MemSize`] into [`SizeFlags`]. 150 | pub fn to_size_flags(&self) -> SizeFlags { 151 | let mut flags = SizeFlags::empty(); 152 | if self.contains(DbgFlags::FOLLOW_REFS) { 153 | flags |= SizeFlags::FOLLOW_REFS; 154 | } 155 | if self.contains(DbgFlags::CAPACITY) { 156 | flags |= SizeFlags::CAPACITY; 157 | } 158 | flags 159 | } 160 | } 161 | 162 | impl Default for DbgFlags { 163 | /// The default set of flags contains [`DbgFlags::TYPE_NAME`], 164 | /// [`DbgFlags::SEPARATOR`], and [`DbgFlags::PERCENTAGE`]. 165 | #[inline(always)] 166 | fn default() -> Self { 167 | Self::TYPE_NAME | Self::SEPARATOR | Self::PERCENTAGE 168 | } 169 | } 170 | 171 | /// A trait providing methods to display recursively the content and size of a 172 | /// structure. 173 | /// 174 | /// You can derive this trait with `#[derive(MemDbg)]` if all the fields of your 175 | /// type implement [`MemDbg`]. Note that you will also need to derive 176 | /// [`MemSize`]. 177 | pub trait MemDbg: MemDbgImpl { 178 | /// Writes to stderr debug infos about the structure memory usage, expanding 179 | /// all levels of nested structures. 180 | #[cfg(feature = "std")] 181 | #[inline(always)] 182 | fn mem_dbg(&self, flags: DbgFlags) -> core::fmt::Result { 183 | // TODO: fix padding 184 | self._mem_dbg_depth( 185 | ::mem_size(self, flags.to_size_flags()), 186 | usize::MAX, 187 | std::mem::size_of_val(self), 188 | flags, 189 | ) 190 | } 191 | 192 | /// Writes to a [`core::fmt::Write`] debug infos about the structure memory 193 | /// usage, expanding all levels of nested structures. 194 | #[inline(always)] 195 | fn mem_dbg_on(&self, writer: &mut impl core::fmt::Write, flags: DbgFlags) -> core::fmt::Result { 196 | // TODO: fix padding 197 | self._mem_dbg_depth_on( 198 | writer, 199 | ::mem_size(self, flags.to_size_flags()), 200 | usize::MAX, 201 | &mut String::new(), 202 | Some("⏺"), 203 | true, 204 | std::mem::size_of_val(self), 205 | flags, 206 | ) 207 | } 208 | 209 | /// Writes to stderr debug infos about the structure memory usage as 210 | /// [`mem_dbg`](MemDbg::mem_dbg), but expanding only up to `max_depth` 211 | /// levels of nested structures. 212 | fn mem_dbg_depth(&self, max_depth: usize, flags: DbgFlags) -> core::fmt::Result { 213 | self._mem_dbg_depth( 214 | ::mem_size(self, flags.to_size_flags()), 215 | max_depth, 216 | std::mem::size_of_val(self), 217 | flags, 218 | ) 219 | } 220 | 221 | /// Writes to a [`core::fmt::Write`] debug infos about the structure memory 222 | /// usage as [`mem_dbg_on`](MemDbg::mem_dbg_on), but expanding only up to 223 | /// `max_depth` levels of nested structures. 224 | fn mem_dbg_depth_on( 225 | &self, 226 | writer: &mut impl core::fmt::Write, 227 | max_depth: usize, 228 | flags: DbgFlags, 229 | ) -> core::fmt::Result { 230 | self._mem_dbg_depth_on( 231 | writer, 232 | ::mem_size(self, flags.to_size_flags()), 233 | max_depth, 234 | &mut String::new(), 235 | None, 236 | false, 237 | std::mem::size_of_val(self), 238 | flags, 239 | ) 240 | } 241 | } 242 | 243 | /// Implemens [`MemDbg`] for all types that implement [`MemDbgImpl`]. 244 | /// 245 | /// This is done so that no one can change the implementation of [`MemDbg`], 246 | /// which ensures consistency in printing. 247 | impl MemDbg for T {} 248 | 249 | /// Inner trait used to implement [`MemDbg`]. 250 | /// 251 | /// This trait should not be implemented by users, which should use the 252 | /// [`MemDbg`](mem_dbg_derive::MemDbg) derive macro instead. 253 | /// 254 | /// The default no-op implementation is used by all types in which it does not 255 | /// make sense, or it is impossible, to recurse. 256 | pub trait MemDbgImpl: MemSize { 257 | #[inline(always)] 258 | fn _mem_dbg_rec_on( 259 | &self, 260 | _writer: &mut impl core::fmt::Write, 261 | _total_size: usize, 262 | _max_depth: usize, 263 | _prefix: &mut String, 264 | _is_last: bool, 265 | _flags: DbgFlags, 266 | ) -> core::fmt::Result { 267 | Ok(()) 268 | } 269 | 270 | #[cfg(feature = "std")] 271 | #[doc(hidden)] 272 | #[inline(always)] 273 | fn _mem_dbg_depth( 274 | &self, 275 | total_size: usize, 276 | max_depth: usize, 277 | padded_size: usize, 278 | flags: DbgFlags, 279 | ) -> core::fmt::Result { 280 | struct Wrapper(std::io::Stderr); 281 | impl core::fmt::Write for Wrapper { 282 | #[inline(always)] 283 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 284 | use std::io::Write; 285 | self.0 286 | .lock() 287 | .write(s.as_bytes()) 288 | .map_err(|_| core::fmt::Error) 289 | .map(|_| ()) 290 | } 291 | } 292 | self._mem_dbg_depth_on( 293 | &mut Wrapper(std::io::stderr()), 294 | total_size, 295 | max_depth, 296 | &mut String::new(), 297 | Some("⏺"), 298 | true, 299 | padded_size, 300 | flags, 301 | ) 302 | } 303 | 304 | #[inline(always)] 305 | #[allow(clippy::too_many_arguments)] 306 | fn _mem_dbg_depth_on( 307 | &self, 308 | writer: &mut impl core::fmt::Write, 309 | total_size: usize, 310 | max_depth: usize, 311 | prefix: &mut String, 312 | field_name: Option<&str>, 313 | is_last: bool, 314 | padded_size: usize, 315 | flags: DbgFlags, 316 | ) -> core::fmt::Result { 317 | if prefix.len() > max_depth { 318 | return Ok(()); 319 | } 320 | let real_size = ::mem_size(self, flags.to_size_flags()); 321 | if flags.contains(DbgFlags::COLOR) { 322 | let color = utils::color(real_size); 323 | writer.write_fmt(format_args!("{color}"))?; 324 | }; 325 | if flags.contains(DbgFlags::HUMANIZE) { 326 | let (value, uom) = crate::utils::humanize_float(real_size as f64); 327 | if uom == " B" { 328 | writer.write_fmt(format_args!("{:>5} B ", real_size))?; 329 | } else { 330 | let mut precision = 4; 331 | let a = value.abs(); 332 | if a >= 100.0 { 333 | precision = 1; 334 | } else if a >= 10.0 { 335 | precision = 2; 336 | } else if a >= 1.0 { 337 | precision = 3; 338 | } 339 | writer.write_fmt(format_args!("{0:>4.1$} {2} ", value, precision, uom))?; 340 | } 341 | } else if flags.contains(DbgFlags::SEPARATOR) { 342 | let mut align = crate::utils::n_of_digits(total_size); 343 | let mut real_size = real_size; 344 | align += align / 3; 345 | let mut digits = crate::utils::n_of_digits(real_size); 346 | let digit_align = digits + digits / 3; 347 | for _ in digit_align..align { 348 | writer.write_char(' ')?; 349 | } 350 | 351 | let first_digits = digits % 3; 352 | let mut multiplier = 10_usize.pow((digits - first_digits) as u32); 353 | if first_digits != 0 { 354 | writer.write_fmt(format_args!("{}", real_size / multiplier))?; 355 | } else { 356 | multiplier /= 1000; 357 | digits -= 3; 358 | writer.write_fmt(format_args!(" {}", real_size / multiplier))?; 359 | } 360 | 361 | while digits >= 3 { 362 | real_size %= multiplier; 363 | multiplier /= 1000; 364 | writer.write_fmt(format_args!("_{:03}", real_size / multiplier))?; 365 | digits -= 3; 366 | } 367 | 368 | writer.write_str(" B ")?; 369 | } else { 370 | let align = crate::utils::n_of_digits(total_size); 371 | writer.write_fmt(format_args!("{:>align$} B ", real_size, align = align))?; 372 | } 373 | 374 | if flags.contains(DbgFlags::PERCENTAGE) { 375 | writer.write_fmt(format_args!( 376 | "{:>6.2}% ", 377 | if total_size == 0 { 378 | 100.0 379 | } else { 380 | 100.0 * real_size as f64 / total_size as f64 381 | } 382 | ))?; 383 | } 384 | if flags.contains(DbgFlags::COLOR) { 385 | let reset_color = utils::reset_color(); 386 | writer.write_fmt(format_args!("{reset_color}"))?; 387 | }; 388 | if !prefix.is_empty() { 389 | writer.write_str(&prefix[2..])?; 390 | if is_last { 391 | writer.write_char('╰')?; 392 | } else { 393 | writer.write_char('├')?; 394 | } 395 | writer.write_char('╴')?; 396 | } 397 | 398 | if let Some(field_name) = field_name { 399 | writer.write_fmt(format_args!("{:}", field_name))?; 400 | } 401 | 402 | if flags.contains(DbgFlags::TYPE_NAME) { 403 | if flags.contains(DbgFlags::COLOR) { 404 | writer.write_fmt(format_args!("{}", utils::type_color()))?; 405 | } 406 | writer.write_fmt(format_args!(": {:}", core::any::type_name::()))?; 407 | if flags.contains(DbgFlags::COLOR) { 408 | writer.write_fmt(format_args!("{}", utils::reset_color()))?; 409 | } 410 | } 411 | 412 | let padding = padded_size - std::mem::size_of_val(self); 413 | if padding != 0 { 414 | writer.write_fmt(format_args!(" [{}B]", padding))?; 415 | } 416 | 417 | writer.write_char('\n')?; 418 | 419 | if is_last { 420 | prefix.push_str(" "); 421 | } else { 422 | prefix.push_str("│ "); 423 | } 424 | 425 | self._mem_dbg_rec_on(writer, total_size, max_depth, prefix, is_last, flags)?; 426 | 427 | prefix.pop(); 428 | prefix.pop(); 429 | 430 | Ok(()) 431 | } 432 | } 433 | -------------------------------------------------------------------------------- /mem_dbg/src/utils.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 3 | * SPDX-FileCopyrightText: 2023 Inria 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Given a float, returns it in a human readable format using SI suffixes. 9 | pub fn humanize_float(mut x: f64) -> (f64, &'static str) { 10 | const UOM: &[&str] = &[ 11 | "qB", "rB", "yB", "zB", "aB", "fB", "pB", "nB", "μB", "mB", " B", "kB", "MB", "GB", "TB", 12 | "PB", "EB", "ZB", "YB", "RB", "QB", 13 | ]; 14 | let mut uom_idx = 10; 15 | debug_assert_eq!(UOM[uom_idx], " B"); 16 | 17 | if x == 0.0 { 18 | return (0.0, UOM[uom_idx]); 19 | } 20 | 21 | if x.abs() > 1.0 { 22 | while x.abs() > 1000.0 && uom_idx < UOM.len() - 1 { 23 | uom_idx += 1; 24 | x /= 1000.0; 25 | } 26 | } else { 27 | while x.abs() < 0.001 && uom_idx > 0 { 28 | uom_idx -= 1; 29 | x *= 1000.0; 30 | } 31 | } 32 | 33 | (x, UOM[uom_idx]) 34 | } 35 | 36 | pub fn color(x: usize) -> &'static str { 37 | const KB: usize = 1024; 38 | const MB: usize = KB * KB; 39 | const GB: usize = MB * KB; 40 | #[allow(clippy::match_overlapping_arm)] 41 | match x { 42 | // white 43 | ..KB => reset_color(), 44 | // green 45 | ..MB => "\x1B[32m", 46 | // yellow 47 | ..GB => "\x1B[33m", 48 | // red 49 | _ => "\x1B[31m", 50 | } 51 | } 52 | 53 | pub fn type_color() -> &'static str { 54 | // custom grey 55 | "\x1B[38;2;128;128;128m" 56 | } 57 | 58 | pub fn reset_color() -> &'static str { 59 | "\x1B[0m" 60 | } 61 | 62 | /// Returns the number of digits of a number. 63 | /// 64 | /// ``` 65 | /// use mem_dbg::n_of_digits; 66 | /// 67 | /// assert_eq!(n_of_digits(0), 1); 68 | /// assert_eq!(n_of_digits(1), 1); 69 | /// assert_eq!(n_of_digits(10), 2); 70 | /// assert_eq!(n_of_digits(100), 3); 71 | /// assert_eq!(n_of_digits(1000), 4); 72 | /// assert_eq!(n_of_digits(10000), 5); 73 | /// assert_eq!(n_of_digits(100000), 6); 74 | /// ``` 75 | pub fn n_of_digits(x: usize) -> usize { 76 | if x == 0 { 77 | return 1; 78 | } 79 | let mut digits = 0; 80 | let mut x = x; 81 | while x > 0 { 82 | digits += 1; 83 | x /= 10; 84 | } 85 | digits 86 | } 87 | -------------------------------------------------------------------------------- /mem_dbg/tests/test_mem_size.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Tommaso Fontana 3 | * SPDX-FileCopyrightText: 2023 Inria 4 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 5 | * 6 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 7 | */ 8 | 9 | #![cfg_attr(feature = "offset_of_enum", feature(offset_of_enum))] 10 | 11 | use core::marker::PhantomData; 12 | use core::mem::size_of; 13 | use mem_dbg::*; 14 | use std::sync::atomic::AtomicU64; 15 | 16 | #[derive(MemSize, MemDbg)] 17 | union SingletonUnion { 18 | a: A, 19 | } 20 | 21 | #[allow(dead_code)] 22 | #[derive(MemSize, MemDbg)] 23 | enum TestEnum { 24 | Unit, 25 | Unit2(), 26 | Unit3 {}, 27 | Union(SingletonUnion), 28 | Unnamed(usize, u8), 29 | Named { 30 | first: usize, 31 | second: PhantomData, 32 | }, 33 | } 34 | 35 | #[test] 36 | fn test_vec_capacity() { 37 | let mut v = vec![Vec::with_capacity(10)]; 38 | v[0].push(1); 39 | v[0].push(2); 40 | // We consider the capacity of the inner vector 41 | assert_eq!( 42 | v.mem_size(SizeFlags::CAPACITY) - v.mem_size(SizeFlags::default()), 43 | 8 * std::mem::size_of::() 44 | ); 45 | } 46 | 47 | #[test] 48 | fn test_vec_copy_or_not() { 49 | #[derive(MemDbg, MemSize, Clone)] 50 | struct NewType(usize); 51 | 52 | assert_eq!( 53 | vec![NewType(1_usize); 10].mem_size(SizeFlags::default()), 54 | vec![1_usize; 10].mem_size(SizeFlags::default()) 55 | ); 56 | } 57 | 58 | #[test] 59 | fn test_boxed_slice_copy_or_not() { 60 | #[derive(MemDbg, MemSize, Clone)] 61 | struct NewType(usize); 62 | 63 | assert_eq!( 64 | vec![NewType(1_usize); 10] 65 | .into_boxed_slice() 66 | .mem_size(SizeFlags::FOLLOW_REFS), 67 | vec![1_usize; 10] 68 | .into_boxed_slice() 69 | .mem_size(SizeFlags::FOLLOW_REFS) 70 | ); 71 | } 72 | 73 | #[test] 74 | fn test_slice_copy_or_not() { 75 | #[derive(MemDbg, MemSize, Clone)] 76 | struct NewType(usize); 77 | 78 | assert_eq!( 79 | vec![NewType(1_usize); 10] 80 | .into_boxed_slice() 81 | .as_ref() 82 | .mem_size(SizeFlags::FOLLOW_REFS), 83 | vec![1_usize; 10] 84 | .into_boxed_slice() 85 | .as_ref() 86 | .mem_size(SizeFlags::FOLLOW_REFS) 87 | ); 88 | } 89 | 90 | #[test] 91 | fn test_array_copy_or_not() { 92 | #[derive(MemDbg, MemSize, Clone, Copy)] 93 | struct NewType(usize); 94 | 95 | assert_eq!( 96 | [NewType(1_usize); 10] 97 | .as_ref() 98 | .mem_size(SizeFlags::FOLLOW_REFS), 99 | [1_usize; 10].as_ref().mem_size(SizeFlags::FOLLOW_REFS) 100 | ); 101 | } 102 | 103 | #[test] 104 | fn test_empty_struct() { 105 | #[derive(MemSize, Clone, Copy)] 106 | #[copy_type] 107 | struct Data {} 108 | let v = Data {}; 109 | assert_eq!(v.mem_size(SizeFlags::default()), 0); 110 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), 0); 111 | } 112 | 113 | #[test] 114 | fn test_struct() { 115 | #[derive(MemSize)] 116 | struct Data { 117 | a: u64, 118 | b: Vec, 119 | } 120 | let mut v = Data { 121 | a: 10, 122 | b: Vec::with_capacity(10), 123 | }; 124 | v.b.push(1); 125 | v.b.push(2); 126 | assert_eq!( 127 | v.mem_size(SizeFlags::default()), 128 | 8 + v.b.mem_size(SizeFlags::default()) 129 | ); 130 | assert_eq!( 131 | v.mem_size(SizeFlags::CAPACITY), 132 | 8 + v.b.mem_size(SizeFlags::CAPACITY) 133 | ); 134 | } 135 | 136 | #[test] 137 | fn test_empty_tuple_struct() { 138 | #[derive(MemSize)] 139 | struct Data(); 140 | let v = Data(); 141 | assert_eq!(v.mem_size(SizeFlags::default()), 0); 142 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), 0); 143 | } 144 | 145 | #[test] 146 | fn test_tuple_struct() { 147 | #[derive(MemSize)] 148 | struct Data(u64, Vec); 149 | let mut v = Data(10, Vec::with_capacity(10)); 150 | v.1.push(1); 151 | v.1.push(2); 152 | assert_eq!( 153 | v.mem_size(SizeFlags::default()), 154 | 8 + v.1.mem_size(SizeFlags::default()) 155 | ); 156 | assert_eq!( 157 | v.mem_size(SizeFlags::CAPACITY), 158 | 8 + v.1.mem_size(SizeFlags::CAPACITY), 159 | ); 160 | } 161 | 162 | #[test] 163 | fn test_padding() { 164 | assert_eq!((0_u8, 0_u64).mem_size(SizeFlags::default()), 16); 165 | #[derive(MemSize)] 166 | struct TuplePadded((u8, u64)); 167 | let v = TuplePadded((0, 0)); 168 | assert_eq!(v.mem_size(SizeFlags::default()), 16); 169 | 170 | #[derive(MemSize)] 171 | struct StructPadded(u8, u64); 172 | let v = StructPadded(0, 0); 173 | assert_eq!(v.mem_size(SizeFlags::default()), 16); 174 | 175 | #[derive(MemSize)] 176 | struct StructStructPadded(StructPadded); 177 | let v = StructStructPadded(StructPadded(0, 0)); 178 | assert_eq!(v.mem_size(SizeFlags::default()), 16); 179 | } 180 | 181 | #[test] 182 | fn test_option() { 183 | let v = Some(1_usize); 184 | assert_eq!( 185 | v.mem_size(SizeFlags::default()), 186 | 2 * core::mem::size_of::(), 187 | ); 188 | assert_eq!( 189 | v.mem_size(SizeFlags::CAPACITY), 190 | v.mem_size(SizeFlags::default()) 191 | ); 192 | let v = Some(Some(1_usize)); 193 | assert_eq!( 194 | v.mem_size(SizeFlags::default()), 195 | 2 * core::mem::size_of::(), 196 | ); 197 | assert_eq!( 198 | v.mem_size(SizeFlags::CAPACITY), 199 | v.mem_size(SizeFlags::default()) 200 | ); 201 | let v = Some(Some(Some(1_usize))); 202 | assert_eq!( 203 | v.mem_size(SizeFlags::default()), 204 | 2 * core::mem::size_of::(), 205 | ); 206 | assert_eq!( 207 | v.mem_size(SizeFlags::CAPACITY), 208 | v.mem_size(SizeFlags::default()) 209 | ); 210 | } 211 | 212 | #[test] 213 | fn test_enum() { 214 | #[derive(MemSize)] 215 | #[repr(u8)] 216 | enum Data { 217 | A, 218 | B(u64), 219 | C(u64, Vec), 220 | } 221 | 222 | let enum_size = core::mem::size_of::(); 223 | 224 | let v = Data::A; 225 | assert_eq!(v.mem_size(SizeFlags::default()), enum_size); 226 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), enum_size); 227 | let v = Data::B(1000); 228 | assert_eq!(v.mem_size(SizeFlags::default()), enum_size); 229 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), enum_size); 230 | let d = vec![1, 2, 3, 4, 5]; 231 | let len = d.len(); 232 | let capacity = d.capacity(); 233 | let v = Data::C(1000, d); 234 | assert_eq!( 235 | v.mem_size(SizeFlags::default()), 236 | enum_size + core::mem::size_of::() * len 237 | ); 238 | assert_eq!( 239 | v.mem_size(SizeFlags::CAPACITY), 240 | enum_size + core::mem::size_of::() * capacity 241 | ); 242 | } 243 | 244 | #[test] 245 | /// 246 | fn test_exotic() { 247 | // A reference cannot be null, so the compiler should use null as Option's 248 | // None variant 249 | let v: Option<&u8> = None; 250 | assert_eq!(core::mem::size_of::(), core::mem::size_of::<&u8>()); 251 | assert_eq!( 252 | core::mem::size_of::(), 253 | core::mem::size_of::>() 254 | ); 255 | assert_eq!( 256 | v.mem_size(SizeFlags::default()), 257 | core::mem::size_of::() 258 | ); 259 | assert_eq!( 260 | v.mem_size(SizeFlags::CAPACITY), 261 | core::mem::size_of::() 262 | ); 263 | 264 | #[derive(MemSize)] 265 | enum Data1 { 266 | A, 267 | B, 268 | } 269 | #[derive(MemSize)] 270 | enum Data2 { 271 | A, 272 | B(Data1), 273 | } 274 | 275 | // nested enums can be flattened IFF they don't have a repr attribute 276 | assert_eq!(core::mem::size_of::(), core::mem::size_of::(),); 277 | 278 | let enum_size = core::mem::size_of::(); 279 | let v = Data1::A; 280 | assert_eq!(v.mem_size(SizeFlags::default()), enum_size); 281 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), enum_size); 282 | let v = Data1::B; 283 | assert_eq!(v.mem_size(SizeFlags::default()), enum_size); 284 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), enum_size); 285 | 286 | let enum_size = core::mem::size_of::(); 287 | let v = Data2::A; 288 | assert_eq!(v.mem_size(SizeFlags::default()), enum_size); 289 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), enum_size); 290 | let v = Data2::B(Data1::A); 291 | assert_eq!(v.mem_size(SizeFlags::default()), enum_size); 292 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), enum_size); 293 | let v = Data2::B(Data1::B); 294 | assert_eq!(v.mem_size(SizeFlags::default()), enum_size); 295 | assert_eq!(v.mem_size(SizeFlags::CAPACITY), enum_size); 296 | } 297 | 298 | #[test] 299 | fn test_tuple() { 300 | assert_eq!((8, 4).mem_size(SizeFlags::default()), 8); 301 | assert_eq!((8, 4).mem_size(SizeFlags::CAPACITY), 8); 302 | } 303 | 304 | #[test] 305 | fn test_atomic() { 306 | assert_eq!(AtomicU64::new(0).mem_size(SizeFlags::default()), 8); 307 | } 308 | 309 | #[test] 310 | fn test_unit() { 311 | assert_eq!(().mem_size(SizeFlags::default()), 0); 312 | } 313 | 314 | #[test] 315 | fn test_phantom() { 316 | struct Dummy(); 317 | #[derive(MemSize, MemDbg)] 318 | struct Example(PhantomData); 319 | 320 | Example::(PhantomData) 321 | .mem_dbg(DbgFlags::default()) 322 | .unwrap(); 323 | } 324 | 325 | #[test] 326 | fn test_vec_strings() { 327 | let data = vec![String::new(), String::new()]; 328 | data.mem_dbg(DbgFlags::default()).unwrap(); 329 | } 330 | 331 | #[test] 332 | fn test_array_u8() { 333 | let data = [0_u8; 10]; 334 | assert_eq!(data.mem_size(SizeFlags::default()), 10); 335 | data.mem_dbg(DbgFlags::default()).unwrap(); 336 | } 337 | 338 | #[test] 339 | fn test_array_empty_struct() { 340 | #[derive(MemSize, MemDbg, Clone, Copy)] 341 | struct Dummy; 342 | let data = [Dummy; 10]; 343 | assert_eq!(data.mem_size(SizeFlags::default()), 0); 344 | data.mem_dbg(DbgFlags::default()).unwrap(); 345 | } 346 | 347 | #[test] 348 | fn test_slice_u8() { 349 | let data = [0_u8; 10].as_slice(); 350 | assert_eq!((*data).mem_size(SizeFlags::default()), 10); 351 | // Autodereferentiation 352 | assert_eq!(data.mem_size(SizeFlags::default()), 10); 353 | assert_eq!( 354 | <&[u8] as MemSize>::mem_size(&data, SizeFlags::default()), 355 | 16 356 | ); 357 | assert_eq!( 358 | <&[u8] as MemSize>::mem_size(&data, SizeFlags::default() | SizeFlags::FOLLOW_REFS), 359 | 26 360 | ); 361 | } 362 | 363 | #[test] 364 | fn test_slice_empty_struct() { 365 | #[derive(MemSize, MemDbg, Clone, Copy)] 366 | struct Dummy; 367 | let data = [Dummy; 10].as_slice(); 368 | assert_eq!((*data).mem_size(SizeFlags::default()), 0); 369 | // Autodereferentiation 370 | assert_eq!(data.mem_size(SizeFlags::default()), 0); 371 | assert_eq!( 372 | <&[Dummy] as MemSize>::mem_size(&data, SizeFlags::default()), 373 | 16 374 | ); 375 | assert_eq!( 376 | <&[Dummy] as MemSize>::mem_size(&data, SizeFlags::default() | SizeFlags::FOLLOW_REFS), 377 | 16 378 | ); 379 | } 380 | 381 | #[test] 382 | fn test_indirect_call() { 383 | #[derive(MemSize, MemDbg)] 384 | struct Dummy(Vec); 385 | 386 | fn test(data: Vec) 387 | where 388 | // this is needed because the type system is not smart enough to infer it 389 | Vec: MemSize + MemDbgImpl, 390 | { 391 | let data = Dummy(data); 392 | data.mem_dbg(DbgFlags::default()).unwrap(); 393 | } 394 | 395 | test(vec![1, 2, 3, 4, 5]); 396 | } 397 | 398 | #[test] 399 | fn test_vec_slice_i64() { 400 | let mut data: Vec = vec![1, 2, 3, 4, 5]; 401 | 402 | // A mutable slice should have the same size as a non mutable one 403 | let non_mutable_slice = data.as_slice(); 404 | let size_of_non_mutable_slice = core::mem::size_of_val(non_mutable_slice); 405 | let non_mutable_slice_shallow_size = 406 | <&[i64] as MemSize>::mem_size(&non_mutable_slice, SizeFlags::default()); 407 | let non_mutable_slice_deep_size = <&[i64] as MemSize>::mem_size( 408 | &non_mutable_slice, 409 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 410 | ); 411 | let mutable_slice = data.as_mut_slice(); 412 | let size_of_mutable_slice = core::mem::size_of_val(mutable_slice); 413 | 414 | let mutable_slice_shallow_size = 415 | <&mut [i64] as MemSize>::mem_size(&mutable_slice, SizeFlags::default()); 416 | let mutable_slice_deep_size = <&mut [i64] as MemSize>::mem_size( 417 | &mutable_slice, 418 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 419 | ); 420 | 421 | assert_eq!( 422 | mutable_slice_shallow_size, non_mutable_slice_shallow_size, 423 | "Expected mutable slice shallow size to be identical to non mutable slice shallow size" 424 | ); 425 | 426 | assert_eq!( 427 | mutable_slice_deep_size, non_mutable_slice_deep_size, 428 | "Expected mutable slice deep size to be identical to non mutable slice deep size" 429 | ); 430 | 431 | assert_eq!(non_mutable_slice_shallow_size, size_of::<&[i64]>()); 432 | 433 | assert_eq!(mutable_slice_shallow_size, size_of::<&mut [i64]>()); 434 | 435 | assert_eq!( 436 | non_mutable_slice_deep_size, 437 | size_of::<&[i64]>() + size_of_non_mutable_slice 438 | ); 439 | 440 | assert_eq!( 441 | mutable_slice_deep_size, 442 | size_of::<&mut [i64]>() + size_of_mutable_slice 443 | ); 444 | } 445 | 446 | #[test] 447 | fn test_vec_slice_i32() { 448 | let mut data: Vec = vec![1, 2, 3, 4, 5]; 449 | 450 | // A mutable slice should have the same size as a non mutable one 451 | let non_mutable_slice = data.as_slice(); 452 | let size_of_non_mutable_slice = core::mem::size_of_val(non_mutable_slice); 453 | let non_mutable_slice_shallow_size = 454 | <&[i32] as MemSize>::mem_size(&non_mutable_slice, SizeFlags::default()); 455 | let non_mutable_slice_deep_size = <&[i32] as MemSize>::mem_size( 456 | &non_mutable_slice, 457 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 458 | ); 459 | let mutable_slice = data.as_mut_slice(); 460 | let size_of_mutable_slice = core::mem::size_of_val(mutable_slice); 461 | let mutable_slice_shallow_size = 462 | <&mut [i32] as MemSize>::mem_size(&mutable_slice, SizeFlags::default()); 463 | let mutable_slice_deep_size = <&mut [i32] as MemSize>::mem_size( 464 | &mutable_slice, 465 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 466 | ); 467 | 468 | assert_eq!( 469 | mutable_slice_shallow_size, non_mutable_slice_shallow_size, 470 | "Expected mutable slice shallow size to be identical to non mutable slice shallow size" 471 | ); 472 | 473 | assert_eq!( 474 | mutable_slice_deep_size, non_mutable_slice_deep_size, 475 | "Expected mutable slice deep size to be identical to non mutable slice deep size" 476 | ); 477 | 478 | assert_eq!(non_mutable_slice_shallow_size, size_of::<&[i64]>()); 479 | 480 | assert_eq!(mutable_slice_shallow_size, size_of::<&mut [i64]>()); 481 | 482 | assert_eq!( 483 | non_mutable_slice_deep_size, 484 | size_of::<&[i64]>() + size_of_non_mutable_slice 485 | ); 486 | 487 | assert_eq!( 488 | mutable_slice_deep_size, 489 | size_of::<&mut [i64]>() + size_of_mutable_slice 490 | ); 491 | } 492 | 493 | #[test] 494 | fn test_array_slice_i64() { 495 | let mut data: [i64; 5] = [1, 2, 3, 4, 5]; 496 | 497 | // A mutable slice should have the same size as a non mutable one 498 | let non_mutable_slice = data.as_slice(); 499 | let size_of_non_mutable_slice = core::mem::size_of_val(non_mutable_slice); 500 | let non_mutable_slice_shallow_size = 501 | <&[i64] as MemSize>::mem_size(&non_mutable_slice, SizeFlags::default()); 502 | let non_mutable_slice_deep_size = <&[i64] as MemSize>::mem_size( 503 | &non_mutable_slice, 504 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 505 | ); 506 | let mutable_slice = data.as_mut_slice(); 507 | let size_of_mutable_slice = core::mem::size_of_val(mutable_slice); 508 | let mutable_slice_shallow_size = 509 | <&mut [i64] as MemSize>::mem_size(&mutable_slice, SizeFlags::default()); 510 | let mutable_slice_deep_size = <&mut [i64] as MemSize>::mem_size( 511 | &mutable_slice, 512 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 513 | ); 514 | 515 | assert_eq!( 516 | mutable_slice_shallow_size, non_mutable_slice_shallow_size, 517 | "Expected mutable slice shallow size to be identical to non mutable slice shallow size" 518 | ); 519 | 520 | assert_eq!( 521 | mutable_slice_deep_size, non_mutable_slice_deep_size, 522 | "Expected mutable slice deep size to be identical to non mutable slice deep size" 523 | ); 524 | 525 | assert_eq!( 526 | non_mutable_slice_deep_size, 527 | core::mem::size_of::<&[i64]>() + size_of_non_mutable_slice 528 | ); 529 | 530 | assert_eq!( 531 | mutable_slice_deep_size, 532 | core::mem::size_of::<&mut [i64]>() + size_of_mutable_slice 533 | ); 534 | } 535 | 536 | #[test] 537 | #[should_panic] 538 | fn test_array_slice_i64_without_specifications() { 539 | let mut data: [i64; 5] = [1, 2, 3, 4, 5]; 540 | 541 | // A mutable slice should have the same size as a non mutable one 542 | let non_mutable_slice_shallow_size = data.as_slice().mem_size(SizeFlags::default()); 543 | let non_mutable_slice_deep_size = data 544 | .as_slice() 545 | .mem_size(SizeFlags::default() | SizeFlags::FOLLOW_REFS); 546 | let mutable_slice_shallow_size = data 547 | .as_mut_slice() 548 | .mem_size(SizeFlags::default() | SizeFlags::FOLLOW_REFS); 549 | let mutable_slice_deep_size = data 550 | .as_mut_slice() 551 | .mem_size(SizeFlags::default() | SizeFlags::FOLLOW_REFS); 552 | 553 | assert_eq!( 554 | mutable_slice_shallow_size, non_mutable_slice_shallow_size, 555 | "Expected mutable slice shallow size to be identical to non mutable slice shallow size" 556 | ); 557 | 558 | assert_eq!( 559 | mutable_slice_deep_size, non_mutable_slice_deep_size, 560 | "Expected mutable slice deep size to be identical to non mutable slice deep size" 561 | ); 562 | } 563 | 564 | #[test] 565 | fn test_array_slice_i32() { 566 | let mut data: [i32; 5] = [1, 2, 3, 4, 5]; 567 | 568 | // A mutable slice should have the same size as a non mutable one 569 | let non_mutable_slice = data.as_slice(); 570 | let size_of_non_mutable_slice = core::mem::size_of_val(non_mutable_slice); 571 | let non_mutable_slice_shallow_size = 572 | <&[i32] as MemSize>::mem_size(&non_mutable_slice, SizeFlags::default()); 573 | let non_mutable_slice_deep_size = <&[i32] as MemSize>::mem_size( 574 | &non_mutable_slice, 575 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 576 | ); 577 | let mutable_slice = data.as_mut_slice(); 578 | let size_of_mutable_slice = core::mem::size_of_val(mutable_slice); 579 | let mutable_slice_shallow_size = 580 | <&mut [i32] as MemSize>::mem_size(&mutable_slice, SizeFlags::default()); 581 | let mutable_slice_deep_size = <&mut [i32] as MemSize>::mem_size( 582 | &mutable_slice, 583 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 584 | ); 585 | 586 | assert_eq!( 587 | mutable_slice_shallow_size, non_mutable_slice_shallow_size, 588 | "Expected mutable slice shallow size to be identical to non mutable slice shallow size" 589 | ); 590 | 591 | assert_eq!( 592 | mutable_slice_deep_size, non_mutable_slice_deep_size, 593 | "Expected mutable slice deep size to be identical to non mutable slice deep size" 594 | ); 595 | 596 | assert_eq!( 597 | non_mutable_slice_deep_size, 598 | core::mem::size_of::<&[i64]>() + size_of_non_mutable_slice 599 | ); 600 | 601 | assert_eq!( 602 | mutable_slice_deep_size, 603 | core::mem::size_of::<&mut [i64]>() + size_of_mutable_slice 604 | ); 605 | } 606 | 607 | #[derive(MemSize)] 608 | struct MutableSliceWrapper<'a> { 609 | data: &'a mut [i64], 610 | } 611 | 612 | #[derive(MemSize)] 613 | struct NonMutableSliceWrapper<'a> { 614 | data: &'a [i64], 615 | } 616 | 617 | #[test] 618 | fn test_compare_structs() { 619 | let mut data: [i64; 5] = [1, 2, 3, 4, 5]; 620 | let mutable_slice = MutableSliceWrapper { 621 | data: data.as_mut_slice(), 622 | }; 623 | let mutable_slice_shallow_size = 624 | ::mem_size(&mutable_slice, SizeFlags::default()); 625 | let mutable_slice_deep_size = ::mem_size( 626 | &mutable_slice, 627 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 628 | ); 629 | let non_mutable_slice = NonMutableSliceWrapper { 630 | data: data.as_slice(), 631 | }; 632 | let non_mutable_slice_shallow_size = 633 | ::mem_size(&non_mutable_slice, SizeFlags::default()); 634 | let non_mutable_slice_deep_size = ::mem_size( 635 | &non_mutable_slice, 636 | SizeFlags::default() | SizeFlags::FOLLOW_REFS, 637 | ); 638 | 639 | assert_eq!( 640 | mutable_slice_shallow_size, non_mutable_slice_shallow_size, 641 | "Expected mutable slice shallow size to be identical to non mutable slice shallow size" 642 | ); 643 | 644 | assert_eq!( 645 | mutable_slice_deep_size, non_mutable_slice_deep_size, 646 | "Expected mutable slice deep size to be identical to non mutable slice deep size" 647 | ); 648 | } 649 | 650 | /// Macro to generate test functions given a type T and the expected size. 651 | macro_rules! test_size { 652 | ($(($object:ty, $shallow_size:expr, $deep_size:expr)),*) => { 653 | $( 654 | paste::paste! { 655 | #[test] 656 | fn []() { 657 | // First, we check that the shallow size of the object is the expected one, 658 | // meaning that we are not following any reference 659 | let mut data: $object = Default::default(); 660 | let shallow_object_size = <$object as MemSize>::mem_size(&data, SizeFlags::default()); 661 | assert_eq!( 662 | shallow_object_size, 663 | $shallow_size, 664 | ); 665 | 666 | // We check that any pointer is always the size of a usize, 667 | // which is the size of a pointer on the current architecture 668 | let reference = &data; 669 | let reference_size = <&'_ $object as MemSize>::mem_size(&reference, SizeFlags::default()); 670 | 671 | assert_eq!( 672 | reference_size, 673 | core::mem::size_of::() 674 | ); 675 | 676 | // Next, we check that the deep size of the object is the expected one, 677 | // meaning that we are following all references 678 | let deep_object_size = <$object as MemSize>::mem_size(&data, SizeFlags::default() | SizeFlags::FOLLOW_REFS); 679 | assert_eq!( 680 | deep_object_size, 681 | $deep_size 682 | ); 683 | 684 | // We now check that the deep size of the reference to this object is equal to the size of a pointer 685 | // plus the deep size of the object 686 | let deep_reference_size = <&'_ $object as MemSize>::mem_size(&reference, SizeFlags::default() | SizeFlags::FOLLOW_REFS); 687 | assert_eq!( 688 | deep_reference_size, 689 | core::mem::size_of::() + deep_object_size 690 | ); 691 | 692 | let mutable_reference = &mut data; 693 | let mutable_reference_size = <&'_ mut $object as MemSize>::mem_size(&mutable_reference, SizeFlags::default()); 694 | // We check that the mutable reference has the same size as the reference 695 | assert_eq!( 696 | mutable_reference_size, 697 | core::mem::size_of::(), 698 | ); 699 | 700 | // We check that the deep size of the mutable reference is the same as the deep size of the reference 701 | // i.e. the size of a pointer plus the deep size of the object 702 | let deep_mutable_reference_size = <&'_ mut $object as MemSize>::mem_size(&mutable_reference, SizeFlags::default() | SizeFlags::FOLLOW_REFS); 703 | assert_eq!( 704 | deep_mutable_reference_size, 705 | deep_reference_size, 706 | ); 707 | } 708 | } 709 | )* 710 | }; 711 | } 712 | 713 | #[derive(MemSize, Default)] 714 | enum TestEnum2 { 715 | #[default] 716 | A, 717 | _B(u64), 718 | _C(u64, Vec), 719 | } 720 | 721 | #[repr(u8)] 722 | #[derive(MemSize, Default)] 723 | enum TestEnumReprU8 { 724 | #[default] 725 | A, 726 | _B(u64), 727 | _C(u64, Vec), 728 | } 729 | 730 | #[derive(MemSize, MemDbg)] 731 | union TestUnion { 732 | a: u64, 733 | } 734 | 735 | impl Default for TestUnion { 736 | fn default() -> Self { 737 | TestUnion { a: 0 } 738 | } 739 | } 740 | 741 | test_size!( 742 | (u8, 1, 1), 743 | (u16, 2, 2), 744 | (u32, 4, 4), 745 | (u64, 8, 8), 746 | (u128, 16, 16), 747 | (i8, 1, 1), 748 | (i16, 2, 2), 749 | (i32, 4, 4), 750 | (i64, 8, 8), 751 | (i128, 16, 16), 752 | (f32, 4, 4), 753 | (f64, 8, 8), 754 | (bool, 1, 1), 755 | (char, 4, 4), 756 | (TestEnum2, 32, 32), 757 | (TestEnumReprU8, 40, 40), 758 | (TestUnion, 8, 8) 759 | ); 760 | 761 | #[derive(MemSize, MemDbg)] 762 | union TestUnionDeep<'a> { 763 | b: &'a TestUnion, 764 | } 765 | 766 | #[derive(MemSize, MemDbg)] 767 | union TestUnionDeepMut<'a> { 768 | b: &'a mut TestUnion, 769 | } 770 | 771 | #[test] 772 | fn test_single_field_union_follow_ref() { 773 | let mut test_union = TestUnion::default(); 774 | let test_union_deep = TestUnionDeep { b: &test_union }; 775 | 776 | // We check that the shallow size of the test union deep is the 777 | // size of a reference (i.e. an usize). 778 | assert_eq!( 779 | ::mem_size(&test_union_deep, SizeFlags::default()), 780 | core::mem::size_of::(), 781 | ); 782 | 783 | // We check that the deep size of the test union deep is the 784 | // size of a reference plus the size of the test union. 785 | assert_eq!( 786 | ::mem_size(&test_union_deep, SizeFlags::FOLLOW_REFS), 787 | core::mem::size_of::() 788 | + ::mem_size(&test_union, SizeFlags::default()), 789 | ); 790 | 791 | let test_union_deep_mut = TestUnionDeepMut { b: &mut test_union }; 792 | 793 | // We check that the shallow size of the test union mut is the 794 | // size of a reference (i.e. an usize) 795 | assert_eq!( 796 | ::mem_size(&test_union_deep_mut, SizeFlags::default()), 797 | core::mem::size_of::(), 798 | ); 799 | 800 | // We check that the deep size of the test union deep mut is the 801 | // size of a reference plus the size of the test union. 802 | assert_eq!( 803 | ::mem_size(&test_union_deep_mut, SizeFlags::FOLLOW_REFS), 804 | core::mem::size_of::() 805 | + ::mem_size(&test_union, SizeFlags::default()), 806 | ); 807 | } 808 | -------------------------------------------------------------------------------- /mem_dbg/tests/test_mem_size_no_import.rs: -------------------------------------------------------------------------------- 1 | //! Test suite to verify whether derive works properly when MemSize is not imported outside of the derive. 2 | 3 | #[derive(mem_dbg::MemSize, mem_dbg::MemDbg)] 4 | struct MyTestStruct(i32); 5 | 6 | #[test] 7 | fn test_mem_size_no_import() { 8 | let my_test_struct = MyTestStruct(42); 9 | let mem_size = ::mem_size( 10 | &my_test_struct, 11 | mem_dbg::SizeFlags::default(), 12 | ); 13 | assert_eq!(mem_size, 4); 14 | } 15 | --------------------------------------------------------------------------------