Skip to content

Commit a17c093

Browse files
authored
feat: no std (#78)
1 parent ce103e6 commit a17c093

File tree

11 files changed

+188
-31
lines changed

11 files changed

+188
-31
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ jobs:
4747
uses: actions-rust-lang/[email protected]
4848
with:
4949
toolchain: ${{ matrix.toolchain.version }}
50+
target: thumbv6m-none-eabi
5051

5152
- name: Install just, nextest
5253
uses: taiki-e/[email protected]
@@ -55,3 +56,7 @@ jobs:
5556

5657
- name: Test
5758
run: just test
59+
60+
- name: Build (no-std)
61+
if: matrix.toolchain.name == 'stable'
62+
run: just build-no-std

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ authors = [
88
"Rob Ede <[email protected]>",
99
]
1010
keywords = ["byte", "byte-size", "utility", "human-readable", "format"]
11-
categories = ["development-tools", "filesystem"]
11+
categories = ["development-tools", "filesystem", "no-std"]
1212
repository = "https://github.com/bytesize-rs/bytesize"
1313
license = "Apache-2.0"
1414
edition = "2021"
1515
rust-version = "1.70"
1616

1717
[features]
18-
default = []
18+
default = ["std"]
19+
std = []
1920
arbitrary = ["dep:arbitrary"]
2021
serde = ["dep:serde"]
2122

ensure-no-std/Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ensure-no-std/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "ensure-no-std"
3+
version = "0.1.0"
4+
publish = false
5+
edition = "2018"
6+
7+
[profile.dev]
8+
panic = "abort"
9+
10+
[profile.release]
11+
panic = "abort"
12+
13+
[dependencies]
14+
bytesize = { path = "..", default-features = false }

ensure-no-std/src/compat_test.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use alloc::string::ToString as _;
2+
3+
use bytesize::ByteSize;
4+
5+
pub fn create_byte_size() {
6+
ByteSize::kib(44).to_string();
7+
}

ensure-no-std/src/main.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![no_std]
2+
#![no_main]
3+
#![allow(dead_code, clippy::from_over_into)]
4+
5+
extern crate alloc;
6+
7+
use alloc::alloc::{GlobalAlloc, Layout};
8+
9+
struct NoopAllocator;
10+
11+
#[global_allocator]
12+
static ALLOCATOR: NoopAllocator = NoopAllocator;
13+
14+
unsafe impl Sync for NoopAllocator {}
15+
16+
unsafe impl GlobalAlloc for NoopAllocator {
17+
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
18+
unimplemented!()
19+
}
20+
21+
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
22+
unimplemented!()
23+
}
24+
}
25+
26+
#[panic_handler]
27+
fn panic(_info: &core::panic::PanicInfo) -> ! {
28+
loop {}
29+
}
30+
31+
#[no_mangle]
32+
pub extern "C" fn _start() -> ! {
33+
loop {}
34+
}
35+
36+
mod compat_test;

justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ test-coverage-codecov toolchain="":
6666
test-coverage-lcov toolchain="":
6767
cargo {{ toolchain }} llvm-cov --workspace --all-features --lcov --output-path lcov.info
6868

69+
# Build crate for a no-std target.
70+
build-no-std:
71+
cargo build --target=thumbv6m-none-eabi --manifest-path=./ensure-no-std/Cargo.toml
72+
6973
# Document crates in workspace.
7074
[group("docs")]
7175
doc *args:

src/display.rs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ impl fmt::Display for Display {
122122
let bytes = self.byte_size.as_u64();
123123

124124
let unit = self.format.unit();
125+
#[allow(unused_variables)] // used in std contexts
125126
let unit_base = self.format.unit_base();
126127

127128
let unit_prefixes = self.format.unit_prefixes();
@@ -132,10 +133,12 @@ impl fmt::Display for Display {
132133
write!(f, "{bytes}{unit_separator}B")?;
133134
} else {
134135
let size = bytes as f64;
135-
let exp = match (size.ln() / unit_base) as usize {
136-
0 => 1,
137-
e => e,
138-
};
136+
137+
#[cfg(feature = "std")]
138+
let exp = ideal_unit_std(size, unit_base);
139+
140+
#[cfg(not(feature = "std"))]
141+
let exp = ideal_unit_no_std(size, unit);
139142

140143
let unit_prefix = unit_prefixes[exp - 1] as char;
141144

@@ -150,10 +153,67 @@ impl fmt::Display for Display {
150153
}
151154
}
152155

156+
#[allow(dead_code)] // used in no-std contexts
157+
fn ideal_unit_no_std(size: f64, unit: u64) -> usize {
158+
assert!(size >= unit as f64, "only called when bytes >= unit");
159+
160+
let mut ideal_prefix = 0;
161+
let mut ideal_size = size;
162+
163+
loop {
164+
ideal_prefix += 1;
165+
ideal_size /= unit as f64;
166+
167+
if ideal_size < unit as f64 {
168+
break;
169+
}
170+
}
171+
172+
ideal_prefix
173+
}
174+
175+
#[cfg(feature = "std")]
176+
#[allow(dead_code)] // used in std contexts
177+
fn ideal_unit_std(size: f64, unit_base: f64) -> usize {
178+
assert!(size.ln() >= unit_base, "only called when bytes >= unit");
179+
180+
match (size.ln() / unit_base) as usize {
181+
0 => unreachable!(),
182+
e => e,
183+
}
184+
}
185+
153186
#[cfg(test)]
154187
mod tests {
188+
use alloc::string::ToString as _;
189+
155190
use super::*;
156191

192+
#[cfg(feature = "std")]
193+
quickcheck::quickcheck! {
194+
#[test]
195+
fn ideal_unit_selection_std_no_std_iec(bytes: ByteSize) -> bool {
196+
if bytes.0 < 1025 {
197+
return true;
198+
}
199+
200+
let size = bytes.0 as f64;
201+
202+
ideal_unit_std(size, crate::LN_KIB) == ideal_unit_no_std(size, crate::KIB)
203+
}
204+
205+
#[test]
206+
fn ideal_unit_selection_std_no_std_si(bytes: ByteSize) -> bool {
207+
if bytes.0 < 1025 {
208+
return true;
209+
}
210+
211+
let size = bytes.0 as f64;
212+
213+
ideal_unit_std(size, crate::LN_KB) == ideal_unit_no_std(size, crate::KB)
214+
}
215+
}
216+
157217
#[test]
158218
fn to_string_iec() {
159219
let display = Display {

src/lib.rs

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141
//! assert_eq!(ByteSize::gb(996), minus);
4242
//! ```
4343
44-
use std::{
45-
fmt,
46-
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
47-
};
44+
#![cfg_attr(not(feature = "std"), no_std)]
45+
46+
extern crate alloc;
47+
48+
use alloc::string::ToString as _;
49+
use core::{fmt, ops};
4850

4951
#[cfg(feature = "arbitrary")]
5052
mod arbitrary;
@@ -251,15 +253,15 @@ impl fmt::Debug for ByteSize {
251253

252254
macro_rules! commutative_op {
253255
($t:ty) => {
254-
impl Add<ByteSize> for $t {
256+
impl ops::Add<ByteSize> for $t {
255257
type Output = ByteSize;
256258
#[inline(always)]
257259
fn add(self, rhs: ByteSize) -> ByteSize {
258260
ByteSize(rhs.0 + (self as u64))
259261
}
260262
}
261263

262-
impl Mul<ByteSize> for $t {
264+
impl ops::Mul<ByteSize> for $t {
263265
type Output = ByteSize;
264266
#[inline(always)]
265267
fn mul(self, rhs: ByteSize) -> ByteSize {
@@ -274,7 +276,7 @@ commutative_op!(u32);
274276
commutative_op!(u16);
275277
commutative_op!(u8);
276278

277-
impl Add<ByteSize> for ByteSize {
279+
impl ops::Add<ByteSize> for ByteSize {
278280
type Output = ByteSize;
279281

280282
#[inline(always)]
@@ -283,14 +285,14 @@ impl Add<ByteSize> for ByteSize {
283285
}
284286
}
285287

286-
impl AddAssign<ByteSize> for ByteSize {
288+
impl ops::AddAssign<ByteSize> for ByteSize {
287289
#[inline(always)]
288290
fn add_assign(&mut self, rhs: ByteSize) {
289291
self.0 += rhs.0
290292
}
291293
}
292294

293-
impl<T> Add<T> for ByteSize
295+
impl<T> ops::Add<T> for ByteSize
294296
where
295297
T: Into<u64>,
296298
{
@@ -301,7 +303,7 @@ where
301303
}
302304
}
303305

304-
impl<T> AddAssign<T> for ByteSize
306+
impl<T> ops::AddAssign<T> for ByteSize
305307
where
306308
T: Into<u64>,
307309
{
@@ -311,7 +313,7 @@ where
311313
}
312314
}
313315

314-
impl Sub<ByteSize> for ByteSize {
316+
impl ops::Sub<ByteSize> for ByteSize {
315317
type Output = ByteSize;
316318

317319
#[inline(always)]
@@ -320,14 +322,14 @@ impl Sub<ByteSize> for ByteSize {
320322
}
321323
}
322324

323-
impl SubAssign<ByteSize> for ByteSize {
325+
impl ops::SubAssign<ByteSize> for ByteSize {
324326
#[inline(always)]
325327
fn sub_assign(&mut self, rhs: ByteSize) {
326328
self.0 -= rhs.0
327329
}
328330
}
329331

330-
impl<T> Sub<T> for ByteSize
332+
impl<T> ops::Sub<T> for ByteSize
331333
where
332334
T: Into<u64>,
333335
{
@@ -338,7 +340,7 @@ where
338340
}
339341
}
340342

341-
impl<T> SubAssign<T> for ByteSize
343+
impl<T> ops::SubAssign<T> for ByteSize
342344
where
343345
T: Into<u64>,
344346
{
@@ -348,7 +350,7 @@ where
348350
}
349351
}
350352

351-
impl<T> Mul<T> for ByteSize
353+
impl<T> ops::Mul<T> for ByteSize
352354
where
353355
T: Into<u64>,
354356
{
@@ -359,7 +361,7 @@ where
359361
}
360362
}
361363

362-
impl<T> MulAssign<T> for ByteSize
364+
impl<T> ops::MulAssign<T> for ByteSize
363365
where
364366
T: Into<u64>,
365367
{
@@ -371,6 +373,8 @@ where
371373

372374
#[cfg(test)]
373375
mod property_tests {
376+
use alloc::string::{String, ToString as _};
377+
374378
use super::*;
375379

376380
impl quickcheck::Arbitrary for ByteSize {
@@ -393,15 +397,21 @@ mod property_tests {
393397
size.to_string().len() < 11
394398
}
395399

396-
// // currently fails on input like "14.0 EiB"
397-
// fn string_round_trip(size: ByteSize) -> bool {
398-
// size.to_string().parse::<ByteSize>().unwrap() == size
399-
// }
400+
fn string_round_trip(size: ByteSize) -> bool {
401+
// currently fails on many inputs above the pebibyte level
402+
if size > ByteSize::pib(1) {
403+
return true;
404+
}
405+
406+
size.to_string().parse::<ByteSize>().unwrap() == size
407+
}
400408
}
401409
}
402410

403411
#[cfg(test)]
404412
mod tests {
413+
use alloc::format;
414+
405415
use super::*;
406416

407417
#[test]

0 commit comments

Comments
 (0)