satint 0.1.2

Saturating integer scalar wrappers for no_std Rust
Documentation

Saturating integer scalar wrappers

satint provides small no_std, no-alloc wrappers around Rust primitive integers that use saturating arithmetic for ordinary integer operations.

The crate exposes signed aliases (Si8, Si16, Si32, Si64, Si128) and unsigned aliases (Su8, Su16, Su32, Su64, Su128), plus matching constructor functions (si8, su32, and so on).

Why

Rust already has primitive methods like u8::saturating_add, but using them consistently can be noisy when a value should saturate by default. satint makes that behavior part of the type:

use satint::{Su8, su8};

assert_eq!((su8(250) + su8(10)).into_inner(), u8::MAX);
assert_eq!((su8(0) - su8(1)).into_inner(), 0);

let mut health = Su8::MAX;
health += su8(1);
assert_eq!(health, Su8::MAX);

Supported Types

Signed:

use satint::{Si8, Si16, Si32, Si64, Si128};
use satint::{si8, si16, si32, si64, si128};

Unsigned:

use satint::{Su8, Su16, Su32, Su64, Su128};
use satint::{su8, su16, su32, su64, su128};

Each alias is a transparent wrapper over core::num::Saturating<T>.

Arithmetic

+, -, and * saturate for same-width values:

use satint::{Si32, Su8, si32, su8};

assert_eq!((Su8::MAX + su8(1)).into_inner(), u8::MAX);
assert_eq!((su8(0) - su8(1)).into_inner(), 0);

assert_eq!((Si32::MAX + si32(1)).into_inner(), i32::MAX);
assert_eq!((Si32::MIN - si32(1)).into_inner(), i32::MIN);
assert_eq!((si32(6) * si32(-7)).into_inner(), -42);

Primitive right-hand sides are also supported:

use satint::{Su16, su16};

let mut value = su16(10);
value += 5;
value *= 3;

assert_eq!(value.into_inner(), 45);
assert_eq!((Su16::MAX + 1).into_inner(), u16::MAX);

Division And Remainder

Division and remainder are intentionally checked methods rather than / and % operator impls. They return None for division by zero, and for signed overflow such as MIN / -1.

use satint::{Si32, si32, su32};

assert_eq!(si32(20).checked_div(si32(3)), Some(si32(6)));
assert_eq!(si32(20).checked_rem(si32(3)), Some(si32(2)));

assert_eq!(su32(20).checked_div(su32(0)), None);
assert_eq!(Si32::MIN.checked_div(si32(-1)), None);

Conversions

Lossless widening conversions use From / Into:

use satint::{Si32, Su16, Su32, si8, su8};

let signed: Si32 = si8(-5).into();
let unsigned: Su32 = su8(200).into();
let unsigned_to_signed: Si32 = Su16::new(40_000).into();

assert_eq!(signed.into_inner(), -5);
assert_eq!(unsigned.into_inner(), 200);
assert_eq!(unsigned_to_signed.into_inner(), 40_000);

Fallible narrowing and cross-sign conversions use TryFrom:

use satint::{Si8, Su8, si16, su16};

assert_eq!(Su8::try_from(su16(40)).map(Su8::into_inner), Ok(40));
assert!(Su8::try_from(su16(300)).is_err());

assert_eq!(Si8::try_from(si16(-50)).map(Si8::into_inner), Ok(-50));
assert!(Si8::try_from(si16(300)).is_err());

Clamping conversions use SaturatingFrom or SaturatingInto:

use satint::{SaturatingFrom, SaturatingInto, Si8, Su8, si16, su16};

assert_eq!(Su8::saturating_from(su16(300)).into_inner(), u8::MAX);
assert_eq!(Si8::saturating_from(si16(-300)).into_inner(), i8::MIN);

let value: Su8 = su16(999).saturating_into();
assert_eq!(value.into_inner(), u8::MAX);

Primitive integers can also be used as the source:

use satint::{SaturatingInto, Si32, Su32};

let signed: Si32 = 42_i32.saturating_into();
let unsigned: Su32 = 42_i32.saturating_into();

assert_eq!(signed.into_inner(), 42);
assert_eq!(unsigned.into_inner(), 42);

Widening Arithmetic

Same-sign wider-left-hand-side + and - are supported when the right-hand side always fits in the left-hand side:

use satint::{si8, si16, su16, su32};

assert_eq!((su32(40) + su16(2)).into_inner(), 42);
assert_eq!((si16(40) - si8(2)).into_inner(), 38);

This is intentionally one-directional: the wider type must be on the left.

Constants And Iterators

Concrete aliases provide MIN, MAX, ZERO, and ONE.

Sum and Product are implemented for scalar values and references:

use satint::{Su32, su32};

let values = [su32(1), su32(2), su32(3), su32(4)];

assert_eq!(values.iter().copied().sum::<Su32>().into_inner(), 10);
assert_eq!(values.iter().product::<Su32>().into_inner(), 24);

Optional serde Support

Enable the serde feature to serialize and deserialize scalar values as their inner integer representation:

[dependencies]
satint = { version = "0.1", features = ["serde"] }

The feature depends on serde without enabling serde's default features, so it remains compatible with no_std users.

no_std

satint is #![no_std] and does not use alloc.

Panics

No operation in this crate panics. Arithmetic operators saturate, division and remainder are exposed only as checked_div / checked_rem returning Option, and conversions are either lossless (From), fallible (TryFrom), or clamping (SaturatingFrom / SaturatingInto). The crate is also #![forbid(unsafe_code)].

Limitations

  • This crate is for integer types only. Floats do not have integer-style saturating bounds.
  • Only +, -, *, their assignment forms, and signed unary - are operator overloads.
  • Division and remainder are available only through checked_div and checked_rem.
  • Cross-width arithmetic is limited to same-sign wider-left-hand-side + and -.
  • Mixed signed/unsigned arithmetic is not implemented directly. Convert first with From, TryFrom, or the saturating conversion traits.
  • Saturation is not error reporting. If you need to detect overflow, use primitive checked arithmetic or fallible conversions where appropriate.

License

Licensed under the MIT or the Apache license.