Skip to content

Commit ceae38e

Browse files
NonDeDuplicatedCStr
1 parent 1c7b232 commit ceae38e

File tree

4 files changed

+77
-32
lines changed

4 files changed

+77
-32
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ This reflects GIT commits on `main` branch (the default branch), that is, `stabl
77
[`nightly` GIT branch](https://github.com/peter-lyons-kehl/ndd/tree/nightly) may occasionally be
88
behind `main`. See also [CONTRIBUTING.md](CONTRIBUTING.md).
99

10-
## 0.2.6 (stable) and 0.3.6-nightly
10+
## 0.2.7 (stable) and 0.3.7-nightly
11+
12+
`NonDeDuplicatedCStr` for FFI CStr.
13+
14+
## 0.2.6 (stable)
1115

1216
- Renamed `cross-crate-demo` -> `cross-crate-demo-problem`.
1317
- Moved `cross-crate-demo/bin*/*.sh` scripts one level deeper to `invocation_scripts`.

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.6"
3+
version = "0.2.7"
44
edition = "2024"
55

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

src/lib.rs

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use core::any::Any;
55
use core::cell::Cell;
6+
use core::ffi::CStr;
67
use core::marker::PhantomData;
78

89
/// A zero-cost wrapper guaranteed not to share its memory location with any other valid (in-scope)
@@ -71,41 +72,53 @@ impl<T: Any + Send + Sync> NonDeDuplicated<T> {
7172
}
7273
}
7374

75+
/// Separate from [bytes_to_array], so that we help monomorphization surface area to be smaller.
76+
const fn copy_bytes_to_array(to: &mut [u8], from: &[u8], len: usize) {
77+
if from.len() > len {
78+
let msg = match from.len() - len {
79+
1 => "Target length is 1 byte too small.",
80+
2 => "Target length is 2 bytes too small.",
81+
3 => "Target length is 3 bytes too small.",
82+
4 => "Target length is 4 bytes too small.",
83+
_ => "Target length is more than 4 bytes too small.",
84+
};
85+
panic!("{}", msg)
86+
}
87+
if from.len() < len {
88+
let msg = match len - from.len() {
89+
1 => "Target length is 1 byte too large.",
90+
2 => "Target length is 2 bytes too large.",
91+
3 => "Target length is 3 bytes too large.",
92+
4 => "Target length is 4 bytes too large.",
93+
_ => "Target length is more than 4 bytes too large.",
94+
};
95+
panic!("{}", msg)
96+
}
97+
if to.len() != len {
98+
panic!("Target slice length differs to the specified length.")
99+
}
100+
101+
let mut i = 0;
102+
while i < len {
103+
to[i] = from[i];
104+
i += 1;
105+
}
106+
}
107+
108+
const fn bytes_to_array<const N: usize>(bytes: &[u8]) -> [u8; N] {
109+
let mut arr = [0u8; N];
110+
copy_bytes_to_array(&mut arr, bytes, N);
111+
arr
112+
}
113+
74114
/// For non-de-duplicated string slices stored in `static` variables.
75115
pub type NonDeDuplicatedStr<'from, const N: usize> =
76116
NonDeDuplicatedFlexible<&'from str, [u8; N], str>;
77117
impl<'from, const N: usize> NonDeDuplicatedStr<'from, N> {
78118
/// Construct a new instance.
79119
pub const fn new(s: &str) -> Self {
80-
if s.len() > N {
81-
let msg = match s.len() - N {
82-
1 => "N is 1 byte too small.",
83-
2 => "N is 2 bytes too small.",
84-
3 => "N is 3 bytes too small.",
85-
4 => "N is 4 bytes too small.",
86-
_ => "N is more than 4 bytes too small.",
87-
};
88-
panic!("{}", msg)
89-
}
90-
if s.len() < N {
91-
let msg = match N - s.len() {
92-
1 => "N is 1 byte too large.",
93-
2 => "N is 2 bytes too large.",
94-
3 => "N is 3 bytes too large.",
95-
4 => "N is 4 bytes too large.",
96-
_ => "N is more than 4 bytes too large.",
97-
};
98-
panic!("{}", msg)
99-
}
100-
let bytes: &[u8] = s.as_bytes();
101-
let mut arr = [0u8; N];
102-
let mut i = 0;
103-
while i < bytes.len() {
104-
arr[i] = bytes[i];
105-
i += 1;
106-
}
107120
Self {
108-
cell: Cell::new(arr),
121+
cell: Cell::new(bytes_to_array(s.as_bytes())),
109122
_f: PhantomData,
110123
_t: PhantomData,
111124
}
@@ -114,7 +127,7 @@ impl<'from, const N: usize> NonDeDuplicatedStr<'from, N> {
114127
/// Get a reference.
115128
///
116129
/// Implementation details: Since this type, and this function, is intended to be used for
117-
/// `static` or `const` variables, speed doesn't matter. So, we use [core::str::from_utf8]
130+
/// `static` variables only, speed doesn't matter here. So, we use [core::str::from_utf8]
118131
/// (instead of [core::str::from_utf8_unchecked]).
119132
pub const fn get(&self) -> &str {
120133
let ptr = self.cell.as_ptr();
@@ -126,6 +139,34 @@ impl<'from, const N: usize> NonDeDuplicatedStr<'from, N> {
126139
}
127140
}
128141

142+
/// For non-de-duplicated string slices stored in `static` variables.
143+
pub type NonDeDuplicatedCStr<'from, const N: usize> =
144+
NonDeDuplicatedFlexible<&'from CStr, [u8; N], CStr>;
145+
impl<'from, const N: usize> NonDeDuplicatedCStr<'from, N> {
146+
/// Construct a new instance.
147+
pub const fn new(s: &CStr) -> Self {
148+
Self {
149+
cell: Cell::new(bytes_to_array(s.to_bytes())),
150+
_f: PhantomData,
151+
_t: PhantomData,
152+
}
153+
}
154+
155+
/// Get a reference.
156+
///
157+
/// Implementation details: Since this type, and this function, is intended to be used for
158+
/// `static` variables only, speed doesn't matter here. So, we use [CStr::from_bytes_with_nul]
159+
/// (instead of [CStr::from_bytes_with_nul_unchecked]).
160+
pub const fn get(&self) -> &CStr {
161+
let ptr = self.cell.as_ptr();
162+
let bytes = unsafe { &*ptr };
163+
match CStr::from_bytes_with_nul(bytes) {
164+
Ok(s) => s,
165+
Err(_) => unreachable!(),
166+
}
167+
}
168+
}
169+
129170
/// For now, [Sync] (and [NonDeDuplicatedFlexible] in general) requires that `OWN` is both [Sync]
130171
/// AND [Send], following
131172
/// [std::sync::Mutex](https://doc.rust-lang.org/nightly/std/sync/struct.Mutex.html#impl-Sync-for-Mutex%3CT%3E).

0 commit comments

Comments
 (0)