1- ![ GitHub Actions
2- results] ( https://github.com/peter-lyons-kehl/ndd/actions/workflows/main.yml/badge.svg )
1+ [ ![ GitHub Actions
2+ results] ( https://github.com/peter-lyons-kehl/ndd/actions/workflows/main.yml/badge.svg )] ( https://github.com/peter-lyons-kehl/ndd/actions )
33
44# Summary
5+ ## Purpose
56
6- ` ndd ` (Non-De-Duplicated) is a zero-cost transparent wrapper. For ` static ` variables that do ** not**
7+ ` ndd ` (Non-De-Duplicated) is a zero-cost transparent wrapper for ` static ` variables that do ** not**
78share memory with any other ` static ` or ` const ` (or local) variables (or literals). Use for ` static `
89data (single variables/arrays/slices) referenced with references/slices/pointers that are ** compared
910by address** .
1011
11- # Use
12+ ## Use
1213
1314Use [ ` ndd::NonDeDuplicated ` ] to wrap your static data (other than string literals (` &str ` ) or C
1415string literal bytes). Use it for (immutable) ` static ` variables only.
1516
1617Use [ ` ndd::NonDeDuplicatedStr ` ] and [ ` ndd::NonDeDuplicatedCStr ` ] to wrap static string slices
1718(` &str ` ) and C strings (owned bytes that defer to ` &CStr ` ). These two types need a ` const ` generic
18- parameter ` N ` , which is the length (in bytes). There is no way around this (in stable Rust). On
19+ parameter ` N ` , which is the length (in bytes). There is no way around this (on stable Rust). On
1920` nightly ` Rust you can use ` ndd::infer:NonDeDuplicatedStr ` and ` ndd::infer:NonDeDuplicatedCStr ` from
2021** odd-numbered** (` -nightly ` ) version of ` ndd ` instead.
2122
2223See unit tests in [ ` src/lib.rs ` ] , and [ ` cross_crate_demo_fix/callee/src/lib.rs ` ] .
2324
24- # Problem
25+ ## Problem
2526
2627Rust (or, rather, LLVM) by default de-duplicates or reuses ** addresses** of ` static ` variables in
2728` release ` builds. And somewhat in ` dev ` (debug) builds, too. For most purposes that is good: The
@@ -44,22 +45,22 @@ That does work out of the box when the client passes a reference/slice defined a
4445` static ` gets its own memory space (even with the default ` release ` optimizations). See a test
4546[ ` src/lib.rs ` -> ` tests_without_ndd ` -> ` addresses_unique_between_statics() ` ] .
4647
47- However, there is a problem (caused by de-duplication in ` release ` , and for some types even in `dev
48- or ` miri). It affects ("ordinary") ` const` values/expressions that equal in value to any ` static`
49- (whether it's a ` static ` variable, or a static literal), which may be your designated ` static ` .
50- Rust/LLVM re-uses address of one such matching ` static ` for references to any equal value(s) defined
51- as ` const ` . See <!-- padding for re-wrap -->
52- [ ` src/lib.rs ` -> ` tests_without_ndd ` -> ` u8_global_const_and_global_static_release() ` ] . Such
53- ` const ` , ` static ` or literal could be in 3rd party code, even private. (See
54- [ ` cross_crate_demo_bug/ ` ] .)
48+ However, there is a problem (caused by de-duplication in ` release ` builds, and for some types even
49+ in ` dev ` or [ ` MIRI ` ] ). It affects ("ordinary") ` const ` values/expressions that equal in value to any
50+ ` static ` (whether it's a ` static ` variable, or a static literal), which may be your designated
51+ ` static ` . Rust/LLVM re-uses address of one such matching ` static ` for references to any equal
52+ value(s) defined as ` const ` . See <!-- padding for re-wrap --> [ ` src/lib.rs ` -> ` tests_without_ndd `
53+ -> ` u8_global_const_and_global_static_release() ` ] . Such ` const ` , ` static ` or literal could be in 3rd
54+ party code, even private. (See [ ` cross_crate_demo_bug/ ` ] .)
5555
5656Things get worse: ` dev ` builds don't have this consistent:
5757
5858- For some types (` u8 ` , numeric primitive-based enums) ` dev ` builds don't reuse ` static ` addresses
5959 for references/slices to ` const ` values. But
6060- For other types (` str ` ), ` dev ` builds do reuse them...
6161
62- ` MIRI ` reuses ` static ` addresses even less (than ` dev ` does), but it still does reuse them sometimes
62+ [ ` MIRI ` ] reuses ` static ` addresses even less (than ` dev ` does), but it still does reuse them
63+ sometimes
6364- for example, between byte (` &CStr ` ) literals (` b"Hello" ` ) and equal string (` &str ` ) literals
6465 (technically, subslices: ` "Hello" ` ).
6566
@@ -80,7 +81,7 @@ lto = "fat"
8081opt-level = 2
8182```
8283
83- # Solution
84+ ## Solution
8485
8586[ ` ndd::NonDeDuplicated ` ] uses [ ` core::cell::Cell ` ] to hold the data passed in by the user. There is
8687no mutation and no mutation access. The only access it gives to the inner data is through shared
@@ -246,7 +247,7 @@ run on Alpine Linux (without `libc`) and are POSIX-compliant:
246247- ` cargo doc --no-deps --quiet `
247248- ` cargo test `
248249- ` cargo test --release `
249- - with [ MIRI] ( https://github.com/rust-lang/miri ) :
250+ - with [ ` MIRI ` ]
250251 - ` rustup install nightly --profile minimal `
251252 - ` rustup +nightly component add miri `
252253 - ` cargo +nightly miri test `
@@ -310,6 +311,7 @@ The following side fruit is `std`-only, but related: `std::sync::mutex::data_ptr
310311[ `core::ptr::eq` ] : https://doc.rust-lang.org/1.86.0/core/ptr/fn.eq.html
311312[ `core::ptr::addr_eq` ] : https://doc.rust-lang.org/1.86.0/core/ptr/fn.addr_eq.html
312313[ `src/lib.rs` -> `tests_without_ndd` -> `addresses_unique_between_statics()` ] : src/lib.rs#L269
314+ [ `MIRI` ] : https://github.com/rust-lang/miri
313315[ ` src/lib.rs ` -> ` tests_without_ndd ` -> ` u8_global_const_and_global_static_release() ` ] :
314316 src/lib.rs#L278
315317[ `cross_crate_demo_bug/` ] : cross_crate_demo_bug/
0 commit comments