Skip to content

Commit 9e04550

Browse files
0.2.9 (stable): Links to work on both GitHub and rustdoc/crates.io (part 3)
1 parent 5f7ed12 commit 9e04550

File tree

5 files changed

+140
-89
lines changed

5 files changed

+140
-89
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ behind `main`. See also [CONTRIBUTING.md](CONTRIBUTING.md).
1313
`ndd::infer::NonDeDuplicatedStr` and `ndd::infer::NonDeDuplicatedCStr`
1414
-->
1515

16+
## 0.2.9 (stable)
17+
18+
`README.md` links to work on both GitHub and `rustdoc` or `crates.io` (more).
19+
1620
## 0.2.8 (stable)
1721

1822
`README.md` links to work on both GitHub and `rustdoc` or `crates.io`.

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ndd"
3-
version = "0.2.8"
3+
version = "0.2.9"
44
edition = "2024"
55

66
license = "BSD-2-Clause OR Apache-2.0 OR MIT"

README.md

Lines changed: 111 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1-
# ndd (Non-De-Duplicated)
2-
31
![GitHub Actions
42
results](https://github.com/peter-lyons-kehl/ndd/actions/workflows/main.yml/badge.svg)
53

6-
## Summary
4+
# Summary
5+
6+
`ndd` (Non-De-Duplicated) is a zero-cost transparent wrapper. For `static` variables that do **not**
7+
share memory with any other `static` or `const` (or local) variables (or literals). Use for `static`
8+
data (single variables/arrays/slices) referenced with references/slices/pointers that are **compared
9+
by address**.
10+
11+
# Use
12+
13+
Use [`ndd::NonDeDuplicated`] to wrap your static data (other than string literals (`&str`) or C
14+
string literal bytes). Use it for (immutable) `static` variables only.
15+
16+
Use [`ndd::NonDeDuplicatedStr`] and [`ndd::NonDeDuplicatedCStr`] to wrap static string slices
17+
(`&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+
`nightly` Rust you can use `ndd::infer:NonDeDuplicatedStr` and `ndd::infer:NonDeDuplicatedCStr` from
20+
**odd-numbered** (`-nightly`) version of `ndd` instead.
721

8-
Zero-cost transparent wrapper. For `static` variables guaranteed not to share memory with any other
9-
`static` or `const` (or local literals). Especially for `static` data (single
10-
variables/arrays/slices) referenced with references/slices/pointers that are **compared by
11-
address**.
22+
See unit tests in [`src/lib.rs`], and [`cross_crate_demo_fix/callee/src/lib.rs`].
1223

13-
## Problem
24+
# Problem
1425

1526
Rust (or, rather, LLVM) by default de-duplicates or reuses **addresses** of `static` variables in
1627
`release` builds. And somewhat in `dev` (debug) builds, too. For most purposes that is good: The
@@ -31,16 +42,16 @@ the API could trigger your designated signalling unintentionally.
3142

3243
That does work out of the box when the client passes a reference/slice defined as `static`: each
3344
`static` gets its own memory space (even with the default `release` optimizations). See a test
34-
[`src/lib.rs` -> `addresses_unique_between_statics()`].
45+
[`src/lib.rs` -> `tests_without_ndd` -> `addresses_unique_between_statics()`].
3546

3647
However, there is a problem (caused by de-duplication in `release`, and for some types even in `dev
3748
or `miri). It affects ("ordinary") `const` values/expressions that equal in value to any `static`
3849
(whether it's a `static` variable, or a static literal), which may be your designated `static`.
3950
Rust/LLVM re-uses address of one such matching `static` for references to any equal value(s) defined
40-
as `const`. See a test [`src/lib.rs` ->
41-
`addresses_not_unique_between_const_and_static()`](https://github.com/peter-lyons-kehl/ndd/blob/26d743d9b7bbaf41155e00174f8827efca5d5f32/src/lib.rs#L95).
42-
Such `const`, `static` or literal could be in 3rd party code, even private. (See
43-
[`cross_crate_demo_bug/`](https://github.com/peter-lyons-kehl/ndd/tree/main/cross_crate_demo_bug))!
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/`].)
4455

4556
Things get worse: `dev` builds don't have this consistent:
4657

@@ -69,42 +80,33 @@ lto = "fat"
6980
opt-level = 2
7081
```
7182

72-
## Solution
83+
# Solution
7384

74-
`ndd:NonDeDuplicated` uses
75-
[`core::cell::Cell`](https://doc.rust-lang.org/nightly/core/cell/struct.Cell.html) to hold the data
76-
passed in by the user. There is no mutation and no mutation access. The only access it gives to the
77-
inner data is through shared references.
85+
[`ndd::NonDeDuplicated`] uses [`core::cell::Cell`] to hold the data passed in by the user. There is
86+
no mutation and no mutation access. The only access it gives to the inner data is through shared
87+
references.
7888

79-
Unlike `Cell` (and friends), `NonDeDuplicated` **does** implement
80-
[`core::marker::Sync`](https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html) (if the inner
81-
data's type implements `Send` and `Sync`). It can safely do so, because it never provides mutable
82-
access, and it never mutates the inner data. That is similar to how
83-
[`std::sync::Mutex`](https://doc.rust-lang.org/nightly/std/sync/struct.Mutex.html#impl-Sync-for-Mutex%3CT%3E)
84-
implements `Sync`, too.
89+
Unlike [`core::cell::Cell`] (and friends), `NonDeDuplicated` **does** implement
90+
[`core::marker::Sync`] (if the inner data's type implements [`core::marker::Send`] and
91+
[`core::marker::Sync`]). It can safely do so, because it never provides mutable access, and it never
92+
mutates the inner data. That is similar to how [`std::sync::Mutex`] implements
93+
[`core::marker::Sync`], too.
8594

86-
See [`src/lib.rs` ->
87-
`tests_behavior_with_ndd`](https://github.com/search?q=repo%3Apeter-lyons-kehl%2Fndd+tests_behavior_with_ndd+path%3Asrc%2Flib.rs&type=code).
95+
See [`src/lib.rs` -> `tests_with_ndd`].
8896

89-
## Use
97+
# Compatibility
9098

91-
Use `ndd::NonDeDuplicated` to wrap your static data. Use it for (immutable) `static` variables only.
92-
Do **not** use it for locals or on heap. That is validated by implementation of
93-
[core::ops::Drop](https://doc.rust-lang.org/nightly/core/ops/trait.Drop.html), which `panic`-s in
94-
`dev` builds.
95-
96-
See unit tests in [src/lib.rs](src/lib.rs).
97-
98-
## Compatibility
99-
100-
`ndd` is `no_std`-compatible and it doesn't need heap (`alloc`) either. Release versions
99+
`ndd` is `no_std`-compatible and it doesn't need heap ([`alloc`]) either. Release versions
101100
(**even**-numbered major versions, and **not** `-nightly` pre-releases) compile with `stable` Rust.
102101
(More below.)
103102

104-
### Stable is always forward compatible
103+
Do **not** use it for locals or on heap. That is validated by implementation of [`core::ops::Drop`],
104+
which `panic`s in `dev` builds.
105105

106-
`ndd` is planned to be always below version `1.0`. (If a need arises for big incompatible
107-
functionality, that can go in a new crate.)
106+
## Always forward compatible
107+
108+
`ndd` is planned to be always below version `1.0`. So stable (**even**-numbered) versions will be
109+
forward compatible. (If a need ever arises for big incompatibility, that can go in a new crate.)
108110

109111
That allows you to specify `ndd` as a dependency with version `0.*`, which will match ANY **major**
110112
versions (below `1.0`, of course). That will match the newest (**even**-numbered major) stable
@@ -113,7 +115,7 @@ version (available for your Rust) automatically.
113115
This is special only to `0.*` - it is **not** possible to have a wildcard matching various **major**
114116
versions `1.0` or higher.
115117

116-
### Versioning convention:
118+
## Versioning schema
117119

118120
- **Even**-numbered major versions (`0.2`, `0.4`...)
119121
- are for **stable** functionality only.
@@ -123,8 +125,8 @@ versions `1.0` or higher.
123125
- **Odd**-numbered major versions (`0.3`, `0.5`...)
124126
- always contain `-nightly` (pre-release identifier) in their name.
125127
- are, indeed, for `nightly` (**unstable**) functionality, and need `nightly` Rust toolchain
126-
(indicated with `rust-toolchain.toml` which is present on [`nightly`
127-
branch](https://github.com/peter-lyons-kehl/ndd/tree/nightly) GIT branch only).
128+
(indicated with `rust-toolchain.toml` which is present on [`nightly` GIT
129+
branch](https://github.com/peter-lyons-kehl/ndd/tree/nightly) only).
128130
- include functionality already present in some lower stable versions. Not all of them - only:
129131
- stable versions with a **lower major** numeric version, and
130132
- if the stable **major** version is lower by **`0.1` only** (and not by more), then the stable
@@ -154,33 +156,36 @@ versions `1.0` or higher.
154156
trick](https://github.com/dtolnay/semver-trick). See also [The Cargo Book > Dependency
155157
Resolution](https://rustwiki.org/en/cargo/reference/resolver.html#version-incompatibility-hazards).
156158

157-
However, the only type exported from `ndd` is `ndd::NonDeDuplicated`. It is a zero-cost wrapper
158-
suitable for immutable `static` variables. It is normally not being passed around as a
159-
parameter/return type or a composite type. And its functions can get inlined/optimized away. So,
160-
there shouldn't be any big binary size/speed difference, or usability difference, if there happen
161-
to be multiple major versions of `ndd` in use at the same time. They would be all isolated. So
162-
SemVer trick may be unnecessary.
159+
However, the only types exported from `ndd` is [`ndd::NonDeDuplicated`],
160+
[`ndd::NonDeDuplicatedStr`] and [`ndd::NonDeDuplicatedCStr`]. They are zero-cost wrappers suitable
161+
for immutable `static` variables. They are normally not being passed around as a parameter/return
162+
type or a composite type. And their functions can get inlined/optimized away. So, there shouldn't
163+
be any big binary size/speed difference, or usability difference, if there happen to be multiple
164+
major versions of `ndd` crate in use at the same time. They would be all isolated. So SemVer trick
165+
may be unnecessary.
166+
167+
Crate version is validated by GIT [`pre-commit`] and by [GitHub Actions].
163168

164-
#### Rule of thumb for stable versions
169+
### Rule of thumb for stable versions
165170

166171
On `stable` Rust, always specify `ndd` with version `0.*`. Then, automatically:
167172

168173
- you will get the newest available even-numbered major (stable) version, and
169174
- your libraries will work with any newer **odd-numbered** major (`-nightly`) version of `ndd`, too,
170175
if any dependency (direct or transitive) requires it.
171176

172-
#### Rule of thumb for unstable versions
177+
### Rule of thumb for unstable versions
173178

174179
To find out the highest **even-numbered** (stable) version whose functionality is included in a
175180
given **odd-numbered** (`-nightly`) version, decrement the **odd-numbered** version by `0.1` (and
176181
remove the `-nightly` suffix).
177182

178-
### Nightly versioning
183+
## Nightly versioning
179184

180185
We prefer not to introduce temporary cargo features. Removing a feature later is a breaking change.
181186
And we don't want just to make such a feature no-op and let it sit around.
182187

183-
So, instead, any `nightly`-only functionality is in separate version stream(s) that always
188+
So, instead, any `nightly`-only functionality has separate versions that always
184189

185190
- are **pre-releases** (as per [The Cargo Book > Specifying Dependencies >
186191
Pre-releases](https://doc.rust-lang.org/nightly/cargo/reference/specifying-dependencies.html#pre-releases)
@@ -195,7 +200,7 @@ So, instead, any `nightly`-only functionality is in separate version stream(s) t
195200

196201
As per Rust resolver rules, a stable (**non**-pre-release) version will NOT match/auto-update to a
197202
**pre-release** version on its own. Therefore, if your crate and/or its dependencies specify `ndd`
198-
version as `0.*`, they will **not** accidentally request an **odd**-numbered major (`-nightly`) on
203+
version as `0.*`, they will **not** accidentally request an **odd**-numbered (`-nightly`) major on
199204
their own.
200205

201206
They can get a (`-nightly`) version, but only if another crate requires it. That's up to the
@@ -204,39 +209,37 @@ consumer.
204209
If you want more control over stable versions, you can fix the **even**-numbered major version, and
205210
use an asterisk mask for the minor version, like `0.2.*`. But then you lose automatic major updates.
206211

207-
### Nightly functionality
212+
## Nightly functionality
208213

209-
Functionality of odd-numbered major (`-nightly`) versions is always subject to change.
214+
WARNING: Functionality of odd-numbered major (`-nightly`) versions is always subject to change!
210215

211-
The following extra functionality is available on `0.3.1-nightly`. You need `nightly` Rust toolchain
212-
(of course).
216+
The following extra functionality is available on `0.3.5-nightly`. Of course, you need `nightly`
217+
Rust toolchain.
213218

214-
#### as_array_of_cells
219+
### as_array_of_cells
215220

216-
`ndd::NonDeDuplicated` has function `as_array_of_cells`, similar to Rust's
217-
[`core::cell::Cell::as_array_of_cells`](https://doc.rust-lang.org/nightly/core/cell/struct.Cell.html#method.as_array_of_cells)
218-
(which will, hopefully, become stable in 1.91).
221+
[`ndd::NonDeDuplicated`] has function `as_array_of_cells`, similar to Rust's
222+
[`core::cell::Cell::as_array_of_cells`] (which will, hopefully, become stable in Rust 1.91).
219223

220-
#### as_slice_of_cells
224+
### as_slice_of_cells
221225

222-
Similar to `as_array_of_cells`, `ndd::NonDeDuplicated` has function `as_slice_of_cells`. That
226+
Similar to `as_array_of_cells`, [`ndd::NonDeDuplicated`] has function `as_slice_of_cells`. That
223227
**can** be stable with with Rust `1.88`+. However, to simplify versioning, it's bundled in
224-
`-nightly` together with `as_array_of_cells`. If you need it earlier, get in touch.
228+
`-nightly` together with `as_array_of_cells`. and may become stable at the same time. If you need it
229+
earlier, get in touch.
225230

226-
#### const Deref and From
231+
### const Deref and From
227232

228-
[core::ops::Deref](https://doc.rust-lang.org/nightly/core/ops/trait.Deref.html) and
229-
[core::convert::From](https://doc.rust-lang.org/nightly/core/convert/trait.From.html) are
230-
implemented as `const`. As of mid 2025, `const` traits are having high traction in Rust. Hopefully
231-
this will be stable not in years, but sooner.
233+
[`core::ops::Deref`] and [`core::convert::From`] are implemented as `const`. As of mid 2025, `const`
234+
traits are having high traction in Rust. Hopefully this will be stable not in years, but sooner.
232235

233236
These traits are **not** implemented in stable versions at all. Why? Because `ndd` types are
234237
intended for `static` variables, so non-`const` functions don't help us.
235238

236-
## Quality
239+
# Quality assurance
237240

238-
Checks and tests are run by [GitHub Actions (CI)](.github/workflows/main.yml). All scripts run on
239-
Alpine Linux and are POSIX-compliant:
241+
Checks and tests are run by [GitHub Actions], which uses `rust:1.87-alpine` container. All scripts
242+
run on Alpine Linux (without `libc`) and are POSIX-compliant:
240243

241244
- `cargo clippy`
242245
- `cargo fmt --check`
@@ -276,25 +279,52 @@ Alpine Linux and are POSIX-compliant:
276279
- `cross_crate_demo_fix/bin_fat_lto/not_deduplicated.sh release const_option_u8`
277280
- `cross_crate_demo_fix/bin_fat_lto/not_deduplicated.sh dev const_bytes`
278281
- `cross_crate_demo_fix/bin_fat_lto/not_deduplicated.sh release const_bytes`
279-
- validate the versioning convention:
280-
- [`pre-commit`](./pre-commit)
282+
- validate the versioning schema:
283+
- [`pre-commit`]
281284

282-
## Use cases
285+
# Use cases
283286

284287
Used by
285288
[`hash-injector::signal`](https://github.com/peter-lyons-kehl/hash-injector/blob/main/lib/src/signal.rs).
286289

287-
## Updates
290+
# Updates
288291

289292
Please subscribe for low frequency updates at
290-
[#2](https://github.com/peter-lyons-kehl/ndd/issues/2).
293+
[peter-lyons-kehl/ndd/issues#2](https://github.com/peter-lyons-kehl/ndd/issues/2).
291294

292-
## Side fruit
295+
# Side fruit
293296

294-
The following side fruit is `std`-only, but related: `std::sync::mutex::data_ptr(&self)` is now
297+
The following side fruit is `std`-only, but related: `std::sync::mutex::data_ptr(&self)` is now a
295298
`const` function: pull request
296299
[rust-lang/rust#146904](https://github.com/rust-lang/rust/pull/146904).
297300

301+
<!-- 1. Link URLs to be used on GitHub.
302+
2. Relative links also work auto-magically on https://crates.io/crates/ndd.
303+
3. Keep them in the same order as used above.
304+
-->
305+
[`ndd::NonDeDuplicated`]: https://docs.rs/ndd/latest/ndd/type.NonDeDuplicated.html
306+
[`ndd::NonDeDuplicatedStr`]: https://docs.rs/ndd/latest/ndd/type.NonDeDuplicatedStr.html
307+
[`ndd::NonDeDuplicatedCStr`]: https://docs.rs/ndd/latest/ndd/type.NonDeDuplicatedCStr.html
308+
[`src/lib.rs`]: src/lib.rs
309+
[`cross_crate_demo_fix/callee/src/lib.rs`]: cross_crate_demo_fix/callee/src/lib.rs
298310
[`core::ptr::eq`]: https://doc.rust-lang.org/1.86.0/core/ptr/fn.eq.html
299311
[`core::ptr::addr_eq`]: https://doc.rust-lang.org/1.86.0/core/ptr/fn.addr_eq.html
300-
[`src/lib.rs` -> `addresses_unique_between_statics()`]: src/lib.rs#L246
312+
[`src/lib.rs` -> `tests_without_ndd` -> `addresses_unique_between_statics()`]: src/lib.rs#L269
313+
[`src/lib.rs` -> `tests_without_ndd` -> `u8_global_const_and_global_static_release()`]:
314+
src/lib.rs#L278
315+
[`cross_crate_demo_bug/`]: cross_crate_demo_bug/
316+
[`core::cell::Cell`]: https://doc.rust-lang.org/1.86.0/core/cell/struct.Cell.html
317+
[`core::marker::Sync`]: https://doc.rust-lang.org/1.86.0/core/marker/trait.Sync.html
318+
[`core::marker::Send`]: https://doc.rust-lang.org/1.86.0/core/marker/trait.Send.html
319+
[`std::sync::Mutex`]:
320+
https://doc.rust-lang.org/1.86.0/std/sync/struct.Mutex.html#impl-Sync-for-Mutex<T>
321+
[`src/lib.rs` -> `tests_with_ndd`]: src/lib.rs#L355
322+
[`alloc`]: https://doc.rust-lang.org/1.86.0/alloc/index.html
323+
[`core::ops::Drop`]: https://doc.rust-lang.org/1.86.0/core/ops/trait.Drop.html
324+
[`pre-commit`]: pre-commit
325+
[GitHub Actions]: .github/workflows/main.yml
326+
<!-- nightly-only: -->
327+
[`core::cell::Cell::as_array_of_cells`]:
328+
https://doc.rust-lang.org/nightly/core/cell/struct.Cell.html#method.as_array_of_cells
329+
[`core::ops::Deref`]: https://doc.rust-lang.org/nightly/core/ops/trait.Deref.html
330+
[`core::convert::From`]: https://doc.rust-lang.org/nightly/core/convert/trait.From.html

src/lib.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
1-
// OVerride links, so that rustdoc can point them locally (or to docs.io, if run on docs.io).
2-
//! [`src/lib.rs` -> `addresses_unique_between_statics()`]:
3-
//! https://github.com/peter-lyons-kehl/ndd/blob/main/src/lib.rs#L246
1+
// Override link URLs for local rustdoc and for docs.rs.
2+
//
3+
// Keep the following links in the same order as they appear in ../README.md. See also bottom of
4+
// ../README.md.
5+
//! [`ndd::NonDeDuplicated`]: crate::NonDeDuplicated
6+
//! [`ndd::NonDeDuplicatedStr`]: crate::NonDeDuplicatedStr
7+
//! [`ndd::NonDeDuplicatedCStr`]: crate::NonDeDuplicatedCStr
8+
//!
9+
//! [`core::ptr::eq`]: core::ptr::eq
10+
//! [`core::ptr::addr_eq`]: core::ptr::addr_eq
11+
//! [`core::cell::Cell`]: core::cell::Cell
12+
//! [`core::marker::Sync`]: core::marker::Sync
13+
//! [`core::marker::Send`]: core::marker::Send
14+
//! [`std::sync::Mutex`]: std::sync::Mutex#impl-Sync-for-Mutex<T>
15+
//! [`alloc`]: alloc
16+
//! [`core::ops::Drop`]: core::ops::Drop
17+
//! [`core::cell::Cell::as_array_of_cells`]: core::cell::Cell::as_array_of_cells
18+
//! [`core::ops::Deref`]: core::ops::Deref
19+
//! [`core::convert::From`]: core::convert::From
420
#![doc = include_str!("../README.md")]
521
#![cfg_attr(not(any(doc, test)), no_std)]
6-
#![allow(incomplete_features)]
22+
#[cfg(doc)]
23+
extern crate alloc;
724

825
use core::any::Any;
926
use core::cell::Cell;
@@ -237,7 +254,7 @@ mod tests_shared {
237254

238255
/// These tests don't actually test `ndd`, but they test the behavior **without** `ndd`.
239256
#[cfg(test)]
240-
mod tests_behavior_without_ndd {
257+
mod tests_without_ndd {
241258
use crate::tests_shared::{STR_CONST_FROM_BYTE_ARRAY_HI, STR_CONST_FROM_BYTE_STRING_HELLO};
242259
use core::ptr;
243260

@@ -332,7 +349,7 @@ mod tests_behavior_without_ndd {
332349
}
333350

334351
#[cfg(test)]
335-
mod tests_behavior_with_ndd {
352+
mod tests_with_ndd {
336353
use super::*;
337354
use crate::tests_shared::{STR_CONST_FROM_BYTE_ARRAY_HI, STR_CONST_FROM_BYTE_STRING_HELLO};
338355
use core::ptr;

0 commit comments

Comments
 (0)