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
187
188
189
//! Crate for manipulating bit vectors and doing arithmetic on arbitrarily sized bit vectors.
//!
//! This crate emphasizes optimizing storage by providing alternative storage options.
//! The module [`fixed`] contains implementations using unsigned integers as storage and thus
//! has a fixed capacity. The module [`dynamic`] contains an implementation using dynamically
//! allocated array of integers as storage and therefore has a dynamic capacity. The module
//! [`auto`] contains an implementation capable of switching at runtime between a fixed or
//! dynamic capacity implementation to try to minimize dynamic memory allocations.
//! All of those implementations implement the [`BitVector`] trait and can be freely mixed together
//! and converted into each other.
//!
//! The different bit vector types represent a vector of bits where the bit at index 0 is the least
//! significant bit and the bit at index `.len() - 1` is the most significant bit. There is no
//! notion of endianness for the bit vector themselves, endianness is only involved when reading or
//! writing a bit vector to or from memory or storage.
//!
//! Arithmetic operation can be applied to bit vectors of different length. The result will always
//! have the length of the longest operand and the smallest operand will be zero extended for the
//! operation. This should match the most intuitive behavior in most cases except when manipulating
//! bit vector supposed to have a sign bit.

#![allow(
    clippy::suspicious_arithmetic_impl,
    clippy::suspicious_op_assign_impl,
    clippy::upper_case_acronyms,
    clippy::comparison_chain,
    clippy::needless_range_loop
)]

use std::fmt::{Binary, Debug, Display, LowerHex, UpperHex};
use std::io::{Read, Write};
use std::ops::Range;

pub mod auto;
pub mod bit;
pub mod dynamic;
pub mod fixed;
pub mod iter;
mod utils;

use bit::Bit;
use iter::BitIterator;

/// An enumeration representing the endianness of an I/O or memory operation.
pub enum Endianness {
    /// Little Endian ordering: bytes are stored from the least significant byte to the most significant byte.
    LE,
    /// big Endian ordering: bytes are stored from the most significant byte to the least significant byte.
    BE,
}

/// An enumeration representing errors which can arise from convertion operation (`from_hex`, `from_binary`, `from_bytes`).
pub enum ConvertError {
    /// The underlying BitVector type did not have enough capacity to perform the operation.
    NotEnoughCapacity,
    /// The source data format is invalid at the specified offset.
    InvalidFormat(usize),
}

impl Debug for ConvertError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ConvertError::NotEnoughCapacity => write!(
                f,
                "Bit vector did not have enough capacity to perform the conversion"
            ),
            ConvertError::InvalidFormat(pos) => write!(
                f,
                "Bit vector conversion method encountered an error at symbol {}",
                pos
            ),
        }
    }
}

impl Display for ConvertError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ConvertError::NotEnoughCapacity => write!(f, "ConvertError::NotEnoughCapacity"),
            ConvertError::InvalidFormat(pos) => write!(f, "ConvertError::InvalidFormat({})", pos),
        }
    }
}

impl std::error::Error for ConvertError {}

/// A trait representing common bit vector operations.
pub trait BitVector:
    Sized + Clone + Debug + PartialEq + Eq + PartialOrd + Ord + Display + Binary + LowerHex + UpperHex
{
    /// Construct an empty pre-allocated with enough capacity to store `length` bits.
    /// Will panic if there is not enough capacity and it is a fixed variant.
    fn with_capacity(length: usize) -> Self;

    /// Construct a bit vector made of `length` 0 bits.
    /// Will panic if there is not enough capacity and it is a fixed variant.
    fn zeros(length: usize) -> Self;

    /// Construct a bit vector made of `length` 1 bits.
    /// Will panic if there is not enough capacity and it is a fixed variant.
    fn ones(length: usize) -> Self;

    /// Construct a bit vector from a binary string made of `'0'` and `'1'`.
    /// Return `None` if the string is invalid or exceed the maximum capacity.
    fn from_binary<S: AsRef<str>>(string: S) -> Result<Self, ConvertError>;

    /// Construct a bit vector from a hex string made of lower case or upper case hexadecimal
    /// characters (mixed case is accepted).
    /// Return `None` if the string is invalid or exceed the maximum capacity.
    fn from_hex<S: AsRef<str>>(string: S) -> Result<Self, ConvertError>;

    /// Construct a bit vector from `bytes` according to the specified `endianness`.
    /// Will panic if the length of `bytes` is larger than the maximum capacity.
    fn from_bytes<B: AsRef<[u8]>>(bytes: B, endianness: Endianness) -> Result<Self, ConvertError>;

    /// Construct a new vector of bytes from the bit vector according to the specified `endianness`.
    /// If the length is not a multiple of 8 bits, he highest weight bits will be padded with `'0'`.
    fn to_vec(&self, endianness: Endianness) -> Vec<u8>;

    /// Construct a bit vector by reading `length` bits from a type implementing `Read` and
    /// arrange them according to the specified `endianness`. If `length` is not a multiple of 8,
    /// the bits remaining in the most signigicant byte will be dropped.
    /// Will panic if there is not enough capacity and it is a fixed variant.
    fn read<R: Read>(
        reader: &mut R,
        length: usize,
        endianness: Endianness,
    ) -> std::io::Result<Self>;

    /// Write the bit vector to a type implementing `Write` and according to the specified
    /// `endianness`. If the length is not a multiple of 8 bits, the most significant byte will be
    /// padded with `'0'`.
    fn write<W: Write>(&self, writer: &mut W, endianness: Endianness) -> std::io::Result<()>;

    /// Return the bit at `index`.
    /// Will panic if `index` is out of bound.
    fn get(&self, index: usize) -> Bit;

    /// Set the bit at `index`.
    /// Will panic if `index` is out of bound.
    fn set(&mut self, index: usize, bit: Bit);

    /// Slice the bit vector using the specified range and copy those bits in a new bit vector.
    /// Will panic if `range` start or end are out of bound.
    fn copy_slice(&self, range: Range<usize>) -> Self;

    /// Push a bit at the end of the bit vector as the most significant bit.
    /// Will panic if there is not enough capacity and it is a fixed variant.
    fn push(&mut self, bit: Bit);

    /// Pop a bit from the end of the bit vector.
    /// Return None if the bit vector is empty.
    fn pop(&mut self) -> Option<Bit>;

    /// Resize the bit vector in place so that its length is equal to `new_length`.
    /// This will either truncate or extend the bit vector. If it is extended, new bits are filled
    /// with `bit`.
    /// Will panic if there is not enough capacity and it is a fixed variant.
    fn resize(&mut self, new_length: usize, bit: Bit);

    /// Shift the bits by one to the left.
    /// The rightmost bit is set to `bit` and the leftmost bit is returned.
    fn shl_in(&mut self, bit: Bit) -> Bit;

    /// Shift the bits by one to the right.
    /// The leftmost bit is set to `bit` and the rightmost bit is returned.
    fn shr_in(&mut self, bit: Bit) -> Bit;

    // Rotate the bits to the left by `rot` positions.
    // Will panic if the rotation amount is larger than this bit vector length.
    fn rotl(&mut self, rot: usize);

    // Rotate the bits to the right by `rot` positions.
    // Will panic if the rotation amount is larger than this bit vector length.
    fn rotr(&mut self, rot: usize);

    /// Return the length of the bit vector in bits.
    fn len(&self) -> usize;

    /// Return wether the bit vector is empty or not.
    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    fn iter(&self) -> BitIterator<'_, Self>;
}

#[cfg(test)]
mod tests;