1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! This crate contains common developer utilities for crates within the `awint`
//! system, such as macros that needed a separate crate because
//! `#[macro_export]` unconditionally causes macros to be publicly accessible.
//! In rare circumstances, someone might want to use the items here for new
//! storage types or highly optimized routines, but most users should never have
//! to interact with this. Be aware that safety requirements can change over
//! time, check `bits.rs` under `awint_core`.
//!
//! There is a hidden reexport of this crate for `awint_core`, `awint_ext`, and
//! `awint`.

#![no_std]
// TODO
#![cfg_attr(feature = "const_support", feature(const_slice_from_raw_parts_mut))]
#![cfg_attr(feature = "const_support", feature(const_mut_refs))]
// not const and tends to be longer
#![allow(clippy::manual_range_contains)]
#![allow(clippy::needless_range_loop)]

// TODO when const traits are stabilized, try introducing the `BitsInternals`
// trait again

mod macros;
mod raw_bits;
mod serde_common;
mod widening;

use core::num::NonZeroUsize;

pub use raw_bits::{CustomDst, RawBits, RawStackBits};
pub use serde_common::*;
pub use widening::{dd_division, widen_add, widen_mul_add, widening_mul_add_u128};

// If more than one flag is active it will cause an error because two `Digits`
// are defined. However, we have this one duplication check in case of trying to
// use `--all-features`.
#[cfg(all(feature = "u8_digits", feature = "u64_digits"))]
compile_error!(
    "Attempted to activate multiple `*_digits` features at the same time. This is likely because \
     `--all-features` was used, which does not work for `awint`."
);

/// The basic element of the internal slice in `Bits`. This should be a type
/// alias of the unsigned integer of the architecture's registers. On most
/// architectures, this is simply `usize`, however there are cases such as AVR
/// where the pointer size is 16 bits but the register size is 8 bits. If this
/// were not register size, it can incur excessive unrolling or underutilization
/// for every loop in the internals.
#[cfg(not(any(
    feature = "u8_digits",
    feature = "u16_digits",
    feature = "u32_digits",
    feature = "u64_digits",
    feature = "u128_digits",
    target_arch = "avr",
)))]
pub type Digit = usize;
#[cfg(any(feature = "u8_digits", target_arch = "avr"))]
pub type Digit = u8;
#[cfg(feature = "u16_digits")]
pub type Digit = u16;
#[cfg(feature = "u32_digits")]
pub type Digit = u32;
#[cfg(feature = "u64_digits")]
pub type Digit = u64;
#[cfg(feature = "u128_digits")]
pub type Digit = u128;

/// Signed version of `Digit`
#[cfg(not(any(
    feature = "u8_digits",
    feature = "u16_digits",
    feature = "u32_digits",
    feature = "u64_digits",
    feature = "u128_digits",
    target_arch = "avr",
)))]
pub type IDigit = isize;
#[cfg(any(feature = "u8_digits", target_arch = "avr"))]
pub type IDigit = i8;
#[cfg(feature = "u16_digits")]
pub type IDigit = i16;
#[cfg(feature = "u32_digits")]
pub type IDigit = i32;
#[cfg(feature = "u64_digits")]
pub type IDigit = i64;
#[cfg(feature = "u128_digits")]
pub type IDigit = i128;

/// Bitwidth of a `Digit`
pub const BITS: usize = Digit::BITS as usize;

/// Maximum value of a `Digit`
pub const MAX: Digit = Digit::MAX;

/// Number of bytes in a `Digit`
pub const DIGIT_BYTES: usize = (Digit::BITS / u8::BITS) as usize;

/// Number of bits in a `usize`
pub const USIZE_BITS: usize = usize::BITS as usize;

/// Subset of `awint::awi`
pub mod awi {
    // everything except for `char`, `str`, `f32`, and `f64`
    pub use core::primitive::{
        bool, i128, i16, i32, i64, i8, isize, u128, u16, u32, u64, u8, usize,
    };

    pub use Option::{self, None, Some};
    pub use Result::{self, Err, Ok};

    pub use crate::bw;
}

/// Utility free function for converting a `usize` to a `NonZeroUsize`. This is
/// mainly intended for usage with literals, and shouldn't be used for fallible
/// conversions.
///
/// # Panics
///
/// If `w == 0`, this function will panic.
#[inline]
#[track_caller]
#[must_use]
pub const fn bw(w: usize) -> NonZeroUsize {
    match NonZeroUsize::new(w) {
        None => {
            panic!("tried to construct an invalid bitwidth of 0 using the `awint::bw` function")
        }
        Some(w) => w,
    }
}

/// Returns the number of extra bits given `w`
#[inline]
pub const fn extra_u(w: usize) -> usize {
    w & (BITS - 1)
}

/// Returns the number of _whole_ digits (not including a digit with unused
/// bits) given `w`
#[inline]
pub const fn digits_u(w: usize) -> usize {
    w.wrapping_shr(BITS.trailing_zeros())
}

/// Returns the number of extra bits given `w`
#[inline]
pub const fn extra(w: NonZeroUsize) -> usize {
    extra_u(w.get())
}

/// Returns the number of _whole_ digits (not including a digit with unused
/// bits) given `w`
#[inline]
pub const fn digits(w: NonZeroUsize) -> usize {
    digits_u(w.get())
}

/// Returns the number of `Digit`s needed to represent `w`, including any
/// digit with unused bits
#[inline]
pub const fn total_digits(w: NonZeroUsize) -> NonZeroUsize {
    // Safety: if `digits(w)` is zero, `extra(w)` must be nonzero
    unsafe { NonZeroUsize::new_unchecked(digits(w).wrapping_add((extra(w) != 0) as usize)) }
}

/// Location for an item in the source code. This is essentially a
/// `std::panic::Location<'static>` with all public fields.
#[derive(Debug, Clone, Copy)]
pub struct Location {
    pub file: &'static str,
    pub line: u32,
    pub col: u32,
}

impl Location {
    /// If a dummy `Location` is needed as some argument
    pub fn dummy() -> Self {
        Self {
            file: "dummy location",
            line: u32::MAX,
            col: u32::MAX,
        }
    }
}