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
#![no_std]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
// TODO: add f32 and f64 support (see `std::num::flt2dec`, maybe?)

//! Compile-time size optimization for numeric primitives.
//! Macros return smallest numeric type capable of fitting a static bounds.
//! For unsigned integers, macro input is a maximum.
//! For signed integers, macro input may be a maximum or a minimum.
//!
//! * Can save memory at zero runtime cost.
//! * Embedded-friendly: `!#[no_std]`.
//! * Safe: `#![forbid(unsafe_code)]`.
//!
//! ### Example: Collection Index
//!
//! When the size of a collection is known at compile-time, the variable used to index it can be size-optimized.
//!
//! * **Target:** Value for collection/container index operator
//! * **Yield:**  `x * 1` where:
//!     * `x < size_of<usize>()`
//!
//! ```
//! use smallnum::{small_unsigned, SmallUnsigned};
//! use core::mem::size_of_val;
//!
//! const MAX_SIZE: usize = 500;
//! let mut my_array: [u8; MAX_SIZE] = [0x00; MAX_SIZE];
//!
//! let idx: usize = 5;
//! let small_idx: small_unsigned!(MAX_SIZE) = 5;
//!
//! // Equivalent values
//! my_array[idx] = 0xff;
//! assert_eq!(my_array[idx], my_array[small_idx.usize()]);
//!
//! // Memory savings (6 bytes on a 64-bit system)
//! #[cfg(target_pointer_width = "64")]
//! assert_eq!(size_of_val(&idx) - size_of_val(&small_idx), 6);
//! ```
//!
//! ### Example: Tree Node Metadata
//!
//! When the maximum capacity of a tree is known at compile time, metadata stored in every node can be size-optimized.
//!
//! * **Target:** Internal metatdata
//! * **Yield:**  `x * n` where:
//!     * `x <= size_of<usize>()`
//!     * `n == node_cnt`
//!
//! ```
//! use smallnum::small_unsigned;
//! use core::mem::size_of;
//!
//! const MAX_CAPACITY: usize = 50_000;
//!
//! // Regular node in a binary tree
//! pub struct BinTree<T> {
//!     value: T,
//!     left_child: Option<Box<BinTree<T>>>,
//!     right_child: Option<Box<BinTree<T>>>,
//!     subtree_size: usize,
//! }
//!
//! // Node with size-optimized metadata
//! pub struct SmallBinTree<T> {
//!     value: T,
//!     left_child: Option<Box<SmallBinTree<T>>>,
//!     right_child: Option<Box<SmallBinTree<T>>>,
//!     subtree_size: small_unsigned!(MAX_CAPACITY),
//! }
//!
//! // Per-node memory savings (8 bytes on a 64-bit system)
//! #[cfg(target_pointer_width = "64")]
//! assert_eq!(size_of::<BinTree<i16>>() - size_of::<SmallBinTree<i16>>(), 8);
//! ```
//!
//! ### Example: Index-based Graphs
//!
//! When implementing an [{index,arena}-based graph](http://smallcultfollowing.com/babysteps/blog/2015/04/06/modeling-graphs-in-rust-using-vector-indices/) whose maximum capacity is known at compile-time, indexes stored in every structure (edge or node) can be size-optimized.
//!
//! * **Target:** Internal "pointer" representation
//! * **Yield:**  `(x + y) * n` where:
//!     * `x <= size_of<usize>()`
//!     * `y <= size_of<Option<usize>>()`
//!     * `n == edge_cnt`
//!
//! ```
//! use smallnum::small_unsigned;
//! use core::mem::size_of;
//!
//! const MAX_CAPACITY: usize = 50_000;
//!
//! // Based on "Modeling graphs in Rust using vector indices" by Niko Matsakis (April 2015)
//! // http://smallcultfollowing.com/babysteps/blog/2015/04/06/modeling-graphs-in-rust-using-vector-indices/
//!
//! // Unoptimized indexes
//! pub type NodeIdx = usize;
//! pub type EdgeIdx = usize;
//!
//! pub struct EdgeData {
//!     target: NodeIdx,
//!     next_outgoing_edge: Option<EdgeIdx>
//! }
//!
//! // Optimized indexes
//! pub type SmallNodeIdx = small_unsigned!(MAX_CAPACITY);
//! pub type SmallEdgeIdx = small_unsigned!(MAX_CAPACITY);
//!
//! pub struct SmallEdgeData {
//!     target: SmallNodeIdx,
//!     next_outgoing_edge: Option<SmallEdgeIdx>
//! }
//!
//! // Per-edge memory savings (18 bytes on a 64-bit system)
//! #[cfg(target_pointer_width = "64")]
//! assert_eq!(size_of::<EdgeData>() - size_of::<SmallEdgeData>(), 18);
//! ```
//!
//! ### Macro <-> Type Selection Set
//!
//! * [`small_unsigned!`](crate::small_unsigned) <-> (`u8`, `u16`, `u32`, `u64`, `u128`)
//! * [`small_signed!`](crate::small_signed) <-> (`i8`, `i16`, `i32`, `i64`, `i128`)

mod unsigned;
pub use crate::unsigned::{ShrinkUnsigned, SmallUnsigned};

mod signed;
pub use crate::signed::{ShrinkSigned, SmallSigned};