Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
uses: actions-rust-lang/[email protected]
with:
toolchain: ${{ matrix.toolchain.version }}
target: thumbv6m-none-eabi

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

- name: Test
run: just test

- name: Build (no-std)
if: matrix.toolchain.name == 'stable'
run: just build-no-std
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ authors = [
"Rob Ede <[email protected]>",
]
keywords = ["byte", "byte-size", "utility", "human-readable", "format"]
categories = ["development-tools", "filesystem"]
categories = ["development-tools", "filesystem", "no-std"]
repository = "https://github.com/bytesize-rs/bytesize"
license = "Apache-2.0"
edition = "2021"
rust-version = "1.70"

[features]
default = []
default = ["std"]
std = []
arbitrary = ["dep:arbitrary"]
serde = ["dep:serde"]

Expand Down
14 changes: 14 additions & 0 deletions ensure-no-std/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions ensure-no-std/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "ensure-no-std"
version = "0.1.0"
publish = false
edition = "2018"

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

[dependencies]
bytesize = { path = "..", default-features = false }
7 changes: 7 additions & 0 deletions ensure-no-std/src/compat_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use alloc::string::ToString as _;

use bytesize::ByteSize;

pub fn create_byte_size() {
ByteSize::kib(44).to_string();
}
36 changes: 36 additions & 0 deletions ensure-no-std/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#![no_std]
#![no_main]
#![allow(dead_code, clippy::from_over_into)]

extern crate alloc;

use alloc::alloc::{GlobalAlloc, Layout};

struct NoopAllocator;

#[global_allocator]
static ALLOCATOR: NoopAllocator = NoopAllocator;

unsafe impl Sync for NoopAllocator {}

unsafe impl GlobalAlloc for NoopAllocator {
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
unimplemented!()
}

unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
unimplemented!()
}
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}

#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}

mod compat_test;
4 changes: 4 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ test-coverage-codecov toolchain="":
test-coverage-lcov toolchain="":
cargo {{ toolchain }} llvm-cov --workspace --all-features --lcov --output-path lcov.info

# Build crate for a no-std target.
build-no-std:
cargo build --target=thumbv6m-none-eabi --manifest-path=./ensure-no-std/Cargo.toml

# Document crates in workspace.
[group("docs")]
doc *args:
Expand Down
68 changes: 64 additions & 4 deletions src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ impl fmt::Display for Display {
let bytes = self.byte_size.as_u64();

let unit = self.format.unit();
#[allow(unused_variables)] // used in std contexts
let unit_base = self.format.unit_base();
Comment on lines +125 to 126
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also use conditional compiling like #[cfg(feature = 'std')] here?


let unit_prefixes = self.format.unit_prefixes();
Expand All @@ -132,10 +133,12 @@ impl fmt::Display for Display {
write!(f, "{bytes}{unit_separator}B")?;
} else {
let size = bytes as f64;
let exp = match (size.ln() / unit_base) as usize {
0 => 1,
e => e,
};

#[cfg(feature = "std")]
let exp = ideal_unit_std(size, unit_base);

#[cfg(not(feature = "std"))]
let exp = ideal_unit_no_std(size, unit);

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

Expand All @@ -150,10 +153,67 @@ impl fmt::Display for Display {
}
}

#[allow(dead_code)] // used in no-std contexts
fn ideal_unit_no_std(size: f64, unit: u64) -> usize {
assert!(size >= unit as f64, "only called when bytes >= unit");

let mut ideal_prefix = 0;
let mut ideal_size = size;

loop {
ideal_prefix += 1;
ideal_size /= unit as f64;

if ideal_size < unit as f64 {
break;
}
}

ideal_prefix
}

#[cfg(feature = "std")]
#[allow(dead_code)] // used in std contexts
fn ideal_unit_std(size: f64, unit_base: f64) -> usize {
assert!(size.ln() >= unit_base, "only called when bytes >= unit");

match (size.ln() / unit_base) as usize {
0 => unreachable!(),
e => e,
}
}

#[cfg(test)]
mod tests {
use alloc::string::ToString as _;

use super::*;

#[cfg(feature = "std")]
quickcheck::quickcheck! {
#[test]
fn ideal_unit_selection_std_no_std_iec(bytes: ByteSize) -> bool {
if bytes.0 < 1025 {
return true;
}

let size = bytes.0 as f64;

ideal_unit_std(size, crate::LN_KIB) == ideal_unit_no_std(size, crate::KIB)
}

#[test]
fn ideal_unit_selection_std_no_std_si(bytes: ByteSize) -> bool {
if bytes.0 < 1025 {
return true;
}

let size = bytes.0 as f64;

ideal_unit_std(size, crate::LN_KB) == ideal_unit_no_std(size, crate::KB)
}
}

#[test]
fn to_string_iec() {
let display = Display {
Expand Down
50 changes: 30 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@
//! assert_eq!(ByteSize::gb(996), minus);
//! ```

use std::{
fmt,
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
};
#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;

use alloc::string::ToString as _;
use core::{fmt, ops};

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

macro_rules! commutative_op {
($t:ty) => {
impl Add<ByteSize> for $t {
impl ops::Add<ByteSize> for $t {
type Output = ByteSize;
#[inline(always)]
fn add(self, rhs: ByteSize) -> ByteSize {
ByteSize(rhs.0 + (self as u64))
}
}

impl Mul<ByteSize> for $t {
impl ops::Mul<ByteSize> for $t {
type Output = ByteSize;
#[inline(always)]
fn mul(self, rhs: ByteSize) -> ByteSize {
Expand All @@ -274,7 +276,7 @@ commutative_op!(u32);
commutative_op!(u16);
commutative_op!(u8);

impl Add<ByteSize> for ByteSize {
impl ops::Add<ByteSize> for ByteSize {
type Output = ByteSize;

#[inline(always)]
Expand All @@ -283,14 +285,14 @@ impl Add<ByteSize> for ByteSize {
}
}

impl AddAssign<ByteSize> for ByteSize {
impl ops::AddAssign<ByteSize> for ByteSize {
#[inline(always)]
fn add_assign(&mut self, rhs: ByteSize) {
self.0 += rhs.0
}
}

impl<T> Add<T> for ByteSize
impl<T> ops::Add<T> for ByteSize
where
T: Into<u64>,
{
Expand All @@ -301,7 +303,7 @@ where
}
}

impl<T> AddAssign<T> for ByteSize
impl<T> ops::AddAssign<T> for ByteSize
where
T: Into<u64>,
{
Expand All @@ -311,7 +313,7 @@ where
}
}

impl Sub<ByteSize> for ByteSize {
impl ops::Sub<ByteSize> for ByteSize {
type Output = ByteSize;

#[inline(always)]
Expand All @@ -320,14 +322,14 @@ impl Sub<ByteSize> for ByteSize {
}
}

impl SubAssign<ByteSize> for ByteSize {
impl ops::SubAssign<ByteSize> for ByteSize {
#[inline(always)]
fn sub_assign(&mut self, rhs: ByteSize) {
self.0 -= rhs.0
}
}

impl<T> Sub<T> for ByteSize
impl<T> ops::Sub<T> for ByteSize
where
T: Into<u64>,
{
Expand All @@ -338,7 +340,7 @@ where
}
}

impl<T> SubAssign<T> for ByteSize
impl<T> ops::SubAssign<T> for ByteSize
where
T: Into<u64>,
{
Expand All @@ -348,7 +350,7 @@ where
}
}

impl<T> Mul<T> for ByteSize
impl<T> ops::Mul<T> for ByteSize
where
T: Into<u64>,
{
Expand All @@ -359,7 +361,7 @@ where
}
}

impl<T> MulAssign<T> for ByteSize
impl<T> ops::MulAssign<T> for ByteSize
where
T: Into<u64>,
{
Expand All @@ -371,6 +373,8 @@ where

#[cfg(test)]
mod property_tests {
use alloc::string::{String, ToString as _};

use super::*;

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

// // currently fails on input like "14.0 EiB"
// fn string_round_trip(size: ByteSize) -> bool {
// size.to_string().parse::<ByteSize>().unwrap() == size
// }
fn string_round_trip(size: ByteSize) -> bool {
// currently fails on many inputs above the pebibyte level
if size > ByteSize::pib(1) {
return true;
}

size.to_string().parse::<ByteSize>().unwrap() == size
}
}
}

#[cfg(test)]
mod tests {
use alloc::format;

use super::*;

#[test]
Expand Down
Loading
Loading