smox 0.4.0

Box that stores small objects inline on stack and bigger objects on the heap using Box, Rc or Arc, with CoW semantic
Documentation
A Box like type that keeps small objects on the stack and can allocate bigger objects as
`Box`, `Rc` or `Arc`. `DerefMut` is copy-on-write when the Smox is backed by `Rc` or `Arc`

# Rationale

In generic contexts one often does not know how big the objects will be. If they are small
enough
they can be kept on the stack for better performance. If they are bigger they need to be
allocated on the heap. Smox allows to do this in a transparent way.

# Example

```rust
# #![cfg_attr(feature = "nightly", allow(unused))]
# #[cfg(not(feature = "nightly"))]
# fn main() {
use smox::*;

// For the copy-on-write behavior we need T: Clone
#[derive(Clone)]
struct Generic<T: Clone> {
    value: SmoxRc<T, 16>,
}

impl<T: Clone> Generic<T> {
    fn new(value: T) -> Self {
        Self {
            value: SmoxRc::new(value),
        }
    }
}

// we have the guarantee that Generic<T> will always be at most 16 bytes large.
assert!(std::mem::size_of::<Generic<i32>>() <= 16);
assert!(std::mem::size_of::<Generic<[i32; 100]>>() <= 16);

let mut small = Generic::new(42i32);
*small.value += 1;
assert_eq!(*small.value, 43);

let big = Generic::new([0i32;100]);
// Cheap cloning the underlying Rc
let mut big_clone = big.clone();
// Modifying big_clone will trigger a copy-on-write
big_clone.value[0] = 1;

assert_eq!(big.value[0], 0);
assert_eq!(big_clone.value[0], 1);
# }
# #[cfg(feature = "nightly")]
# fn main() {}
```

# Details

* In the current implementation, the given `SIZE` is also the size of a `Smox` or
  the size of a box types whatever is bigger.
  Current stable rust lacks the `generic_const_exprs` support to constrain the size when
  heap storage is used to a single pointer. This will change in the future, perhaps with a
  `nightly` feature flag.
* The biggest spaces saving are when you can benefit from the copy-on-write behavior of `Rc` or
  `Arc`. Use either of these when there is a chance that the objects will be cloned.
* When your `Smox` should stay as small as possible then try the `MINIMAL_SIZE` as size limit.

# Nightly Support

With the `nightly` feature enabled, Smox uses `generic_const_exprs` to optimize away unused
in-place storage. When `T` is larger than `SIZE`, the in-place storage becomes 0 bytes instead
of wasting `SIZE` bytes. Because of alignment requirements it will still be sized at least
a pointer size. When stored on heap it is a single pointer, when stored in_place it is
`size_of::<T>().next_multiple_of(std::mem::size_of::<usize>())`.

**Note**: Using Smox with generic type parameters in structs requires adding a where bound
`[(); optimal_size::<T, SIZE>()]:` to propagate the constraint. This is an inherent limitation
of `generic_const_exprs`.

```rust
# #![cfg_attr(feature = "nightly", feature(generic_const_exprs))]
# #![cfg_attr(not(feature = "nightly"), allow(unused))]
# #[cfg(feature = "nightly")]
# fn main() {
use smox::*;

// On nightly, the in_place storage is optimized to 0 bytes when T > SIZE
// This test verifies the optimization:

// i32 (4 bytes) fits in SIZE=32, stored in_place
// Size = pointer_size (due to union alignment)
let small: SmoxRc<i32, 32> = SmoxRc::new(42);
assert_eq!(std::mem::size_of_val(&small), std::mem::size_of::<usize>());

// [i32; 100] (400 bytes) > SIZE=32, stored on heap → ptr size (in_place is 0!)
// Without nightly optimization, this would be 32 bytes!
let big: SmoxRc<[i32; 100], 32> = SmoxRc::new([0; 100]);
assert_eq!(std::mem::size_of_val(&big), std::mem::size_of::<usize>());

// Verify values work correctly
let mut s: SmoxRc<i32, 32> = SmoxRc::new(42);
*s += 1;
assert_eq!(*s, 43);

let b: SmoxRc<[i32; 100], 32> = SmoxRc::new([0; 100]);
// Cheap cloning the underlying Rc
let mut b_clone = b.clone();
// Modifying b_clone will trigger a copy-on-write
b_clone[0] = 1;

assert_eq!(b[0], 0);
assert_eq!(b_clone[0], 1);
# }
# #[cfg(not(feature = "nightly"))]
# fn main() {}
```