ssi_status/impl/bitstring_status_list/
mod.rs

1//! W3C Bitstring Status List v1.0 (Candidate Recommendation Draft 10 June 2024)
2//!
3//! A privacy-preserving, space-efficient, and high-performance mechanism for
4//! publishing status information such as suspension or revocation of Verifiable
5//! Credentials through use of bitstrings.
6//!
7//! See: <https://www.w3.org/TR/vc-bitstring-status-list/>
8use core::fmt;
9use iref::UriBuf;
10use serde::{Deserialize, Serialize};
11use std::{hash::Hash, str::FromStr, time::Duration};
12
13use crate::{Overflow, StatusMap, StatusSizeError};
14
15mod syntax;
16pub use syntax::*;
17
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct StatusMessage {
20    #[serde(with = "prefixed_hexadecimal")]
21    pub status: u8,
22    pub message: String,
23}
24
25impl StatusMessage {
26    pub fn new(status: u8, message: String) -> Self {
27        Self { status, message }
28    }
29}
30
31#[derive(Debug, thiserror::Error)]
32#[error("invalid status size `{0}`")]
33pub struct InvalidStatusSize(u8);
34
35impl From<InvalidStatusSize> for StatusSizeError {
36    fn from(_value: InvalidStatusSize) -> Self {
37        Self::Invalid
38    }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
42pub struct StatusSize(u8);
43
44impl TryFrom<u8> for StatusSize {
45    type Error = InvalidStatusSize;
46
47    fn try_from(value: u8) -> Result<Self, Self::Error> {
48        if (1..=8).contains(&value) {
49            Ok(Self(value))
50        } else {
51            Err(InvalidStatusSize(value))
52        }
53    }
54}
55
56impl From<StatusSize> for u8 {
57    fn from(value: StatusSize) -> Self {
58        value.0
59    }
60}
61
62impl Default for StatusSize {
63    fn default() -> Self {
64        Self::DEFAULT
65    }
66}
67
68impl StatusSize {
69    pub const DEFAULT: Self = Self(1);
70
71    pub fn is_default(&self) -> bool {
72        *self == Self::DEFAULT
73    }
74
75    fn offset_of(&self, index: usize) -> Offset {
76        let bit_offset = self.0 as usize * index;
77        Offset {
78            byte: bit_offset / 8,
79            bit: bit_offset % 8,
80        }
81    }
82
83    fn last_of(&self, index: usize) -> Offset {
84        let bit_offset = self.0 as usize * index + self.0 as usize - 1;
85        Offset {
86            byte: bit_offset / 8,
87            bit: bit_offset % 8,
88        }
89    }
90
91    fn mask(&self) -> u8 {
92        if self.0 == 8 {
93            0xff
94        } else {
95            (1 << self.0) - 1
96        }
97    }
98}
99
100impl<'de> Deserialize<'de> for StatusSize {
101    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102    where
103        D: serde::Deserializer<'de>,
104    {
105        u8::deserialize(deserializer)?
106            .try_into()
107            .map_err(serde::de::Error::custom)
108    }
109}
110
111#[derive(Debug)]
112struct Offset {
113    byte: usize,
114    bit: usize,
115}
116
117impl Offset {
118    fn left_shift(&self, status_size: StatusSize) -> (i32, Option<u32>) {
119        let high = (8 - status_size.0 as isize - self.bit as isize) as i32;
120        let low = if high < 0 {
121            Some((8 + high) as u32)
122        } else {
123            None
124        };
125
126        (high, low)
127    }
128}
129
130/// Maximum duration, in milliseconds, an implementer is allowed to cache a
131/// status list.
132///
133/// Default value is 300000.
134#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
135#[serde(transparent)]
136pub struct TimeToLive(pub u64);
137
138impl Default for TimeToLive {
139    fn default() -> Self {
140        Self::DEFAULT
141    }
142}
143
144impl TimeToLive {
145    pub const DEFAULT: Self = Self(300000);
146
147    pub fn is_default(&self) -> bool {
148        *self == Self::DEFAULT
149    }
150}
151
152impl From<TimeToLive> for Duration {
153    fn from(value: TimeToLive) -> Self {
154        Duration::from_millis(value.0)
155    }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
159#[serde(rename_all = "camelCase")]
160pub enum StatusPurpose {
161    /// Cancel the validity of a verifiable credential.
162    ///
163    /// This status is not reversible.
164    Revocation,
165
166    /// Temporarily prevent the acceptance of a verifiable credential.
167    ///
168    /// This status is reversible.
169    Suspension,
170
171    /// Convey an arbitrary message related to the status of the verifiable
172    /// credential.
173    ///
174    /// The actual message is stored in the status list entry, in
175    /// [`BitstringStatusListEntry::status_messages`].
176    Message,
177}
178
179impl StatusPurpose {
180    /// Creates a new status purpose from its name.
181    pub fn from_name(name: &str) -> Option<Self> {
182        match name {
183            "revocation" => Some(Self::Revocation),
184            "suspension" => Some(Self::Suspension),
185            "message" => Some(Self::Message),
186            _ => None,
187        }
188    }
189
190    /// Returns the name of this status purpose.
191    pub fn name(&self) -> &'static str {
192        match self {
193            Self::Revocation => "revocation",
194            Self::Suspension => "suspension",
195            Self::Message => "message",
196        }
197    }
198
199    /// Returns the string representation of this status purpose.
200    ///
201    /// Same as [`Self::name`].
202    pub fn as_str(&self) -> &'static str {
203        self.name()
204    }
205
206    /// Turns this status purpose into its name.
207    ///
208    /// Same as [`Self::name`].
209    pub fn into_name(self) -> &'static str {
210        self.name()
211    }
212
213    /// Turns this status purpose into its string representation.
214    ///
215    /// Same as [`Self::name`].
216    pub fn into_str(self) -> &'static str {
217        self.name()
218    }
219}
220
221impl<'a> From<&'a StatusPurpose> for crate::StatusPurpose<&'a str> {
222    fn from(value: &'a StatusPurpose) -> Self {
223        match value {
224            StatusPurpose::Revocation => Self::Revocation,
225            StatusPurpose::Suspension => Self::Suspension,
226            StatusPurpose::Message => Self::Other("message"),
227        }
228    }
229}
230
231impl<'a> PartialEq<crate::StatusPurpose<&'a str>> for StatusPurpose {
232    fn eq(&self, other: &crate::StatusPurpose<&'a str>) -> bool {
233        matches!(
234            (self, other),
235            (Self::Revocation, crate::StatusPurpose::Revocation)
236                | (Self::Suspension, crate::StatusPurpose::Suspension)
237                | (Self::Message, crate::StatusPurpose::Other("message"))
238        )
239    }
240}
241
242impl fmt::Display for StatusPurpose {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        self.name().fmt(f)
245    }
246}
247
248/// Error raised when converting a string into a [`StatusPurpose`] fails.
249#[derive(Debug, Clone, thiserror::Error)]
250#[error("invalid status purpose: {0}")]
251pub struct InvalidStatusPurpose(pub String);
252
253impl FromStr for StatusPurpose {
254    type Err = InvalidStatusPurpose;
255
256    fn from_str(s: &str) -> Result<Self, Self::Err> {
257        Self::from_name(s).ok_or_else(|| InvalidStatusPurpose(s.to_owned()))
258    }
259}
260
261/// Bit-string with status size.
262///
263/// This type is similar to [`BitString`] but also stores the bit size of
264/// each item (status size) and the number of items in the list.
265/// This provides a safer access to the underlying bit-string, ensuring that
266/// status and list boundaries are respected.
267#[derive(Debug, Clone)]
268pub struct SizedBitString {
269    inner: BitString,
270    status_size: StatusSize,
271    len: usize,
272}
273
274impl SizedBitString {
275    /// Creates a new empty sized status list.
276    pub fn new(status_size: StatusSize) -> Self {
277        Self {
278            inner: BitString::new(),
279            status_size,
280            len: 0,
281        }
282    }
283
284    /// Creates a new bit-string of the given length, using `f` to initialize
285    /// every status.
286    ///
287    /// The `f` function is called with the index of the initialized status.
288    pub fn new_with(
289        status_size: StatusSize,
290        len: usize,
291        mut f: impl FnMut(usize) -> u8,
292    ) -> Result<Self, Overflow> {
293        let mut result = Self::with_capacity(status_size, len);
294
295        for i in 0..len {
296            result.push(f(i))?;
297        }
298
299        Ok(result)
300    }
301
302    /// Creates a new bit-string of the given length, setting every status
303    /// to the same value.
304    pub fn new_with_value(
305        status_size: StatusSize,
306        len: usize,
307        value: u8,
308    ) -> Result<Self, Overflow> {
309        Self::new_with(status_size, len, |_| value)
310    }
311
312    /// Creates a new bit-string of the given length, setting every status
313    /// to 0.
314    pub fn new_zeroed(status_size: StatusSize, len: usize) -> Self {
315        Self::new_with_value(status_size, len, 0).unwrap() // 0 cannot overflow.
316    }
317
318    /// Creates a new bit-string with the given status size and capacity
319    /// (in number of statuses).
320    pub fn with_capacity(status_size: StatusSize, capacity: usize) -> Self {
321        Self {
322            inner: BitString::with_capacity(status_size, capacity),
323            status_size,
324            len: 0,
325        }
326    }
327
328    /// Creates a bit-string from a byte array and status size.
329    pub fn from_bytes(status_size: StatusSize, bytes: Vec<u8>) -> Self {
330        let len = bytes.len() * 8usize / status_size.0 as usize;
331        Self {
332            inner: BitString::from_bytes(bytes),
333            status_size,
334            len,
335        }
336    }
337
338    pub fn status_size(&self) -> StatusSize {
339        self.status_size
340    }
341
342    /// Checks if the list is empty.
343    pub fn is_empty(&self) -> bool {
344        self.len == 0
345    }
346
347    /// Returns the length of the list (number of statuses).
348    pub fn len(&self) -> usize {
349        self.len
350    }
351
352    /// Returns the value stored in the list at the given index.
353    pub fn get(&self, index: usize) -> Option<u8> {
354        if index >= self.len {
355            return None;
356        }
357
358        self.inner.get(self.status_size, index)
359    }
360
361    /// Push a new value into the bit-string.
362    ///
363    /// Returns the index of the newly inserted value in the list,
364    /// or an error if the value is too large w.r.t. `status_size`.
365    pub fn push(&mut self, value: u8) -> Result<usize, Overflow> {
366        let masked_value = value & self.status_size.mask();
367        if masked_value != value {
368            return Err(Overflow::Value(value));
369        }
370
371        let index = self.len;
372        let offset = self.status_size.offset_of(index);
373
374        let (high_shift, low_shift) = offset.left_shift(self.status_size);
375
376        if offset.byte == self.inner.0.len() {
377            self.inner
378                .0
379                .push(masked_value.overflowing_signed_shl(high_shift).0);
380        } else {
381            self.inner.0[offset.byte] |= masked_value.overflowing_signed_shl(high_shift).0
382        }
383
384        if let Some(low_shift) = low_shift {
385            self.inner.0.push(masked_value.overflowing_shl(low_shift).0);
386        }
387
388        self.len += 1;
389        Ok(index)
390    }
391
392    /// Sets the value at the given index.
393    ///
394    /// Returns the previous value, or an `Overflow` error if either the index
395    /// is out of bounds or the value is too large.
396    pub fn set(&mut self, index: usize, value: u8) -> Result<u8, Overflow> {
397        if index >= self.len {
398            return Err(Overflow::Index(index));
399        }
400
401        self.inner.set(self.status_size, index, value)
402    }
403
404    /// Returns an iterator over all the statuses stored in this bit-string.
405    pub fn iter(&self) -> BitStringIter {
406        self.inner.iter(self.status_size)
407    }
408
409    /// Encodes the bit-string.
410    pub fn encode(&self) -> EncodedList {
411        self.inner.encode()
412    }
413
414    pub fn into_unsized(self) -> BitString {
415        self.inner
416    }
417
418    /// Consumes the bit-string and returns its raw bytes.
419    pub fn into_bytes(self) -> Vec<u8> {
420        self.inner.into_bytes()
421    }
422}
423
424impl From<SizedBitString> for Vec<u8> {
425    fn from(value: SizedBitString) -> Self {
426        value.into_bytes()
427    }
428}
429
430/// Bit-string as defined by the W3C Bitstring Status List specification.
431///
432/// Bits are indexed from most significant to least significant.
433/// ```text
434/// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ... | n-8 | n-7 | n-6 | n-5 | n-4 | n-3 | n-2 | n-1 |
435/// | byte 0                        | ... | byte k-1                                      |
436/// ```
437///
438/// See: <https://www.w3.org/TR/vc-bitstring-status-list/#bitstring-encoding>
439///
440/// This type does not store the actual status size (the size of each item)
441/// nor the total number of items in the list. Use the [`SizedBitString`] type
442/// to access the list safely with regard to the items boundaries.
443#[derive(Debug, Default, Clone)]
444pub struct BitString(Vec<u8>);
445
446impl BitString {
447    /// Creates a new empty bit-string.
448    pub fn new() -> Self {
449        Self::default()
450    }
451
452    /// Creates a new bit-string of the given length, using `f` to initialize
453    /// every status.
454    ///
455    /// The `f` function is called with the index of the initialized status.
456    pub fn new_with(
457        status_size: StatusSize,
458        len: usize,
459        f: impl FnMut(usize) -> u8,
460    ) -> Result<Self, Overflow> {
461        SizedBitString::new_with(status_size, len, f).map(SizedBitString::into_unsized)
462    }
463
464    /// Creates a new bit-string of the given length, setting every status
465    /// to the same value.
466    pub fn new_with_value(
467        status_size: StatusSize,
468        len: usize,
469        value: u8,
470    ) -> Result<Self, Overflow> {
471        Self::new_with(status_size, len, |_| value)
472    }
473
474    /// Creates a new bit-string of the given length, setting every status
475    /// to 0.
476    pub fn new_zeroed(status_size: StatusSize, len: usize) -> Self {
477        Self::new_with_value(status_size, len, 0).unwrap() // 0 cannot overflow.
478    }
479
480    /// Creates a new bit-string with the given status size and capacity
481    /// (in number of statuses).
482    pub fn with_capacity(status_size: StatusSize, capacity: usize) -> Self {
483        Self(Vec::with_capacity(
484            (capacity * status_size.0 as usize).div_ceil(8),
485        ))
486    }
487
488    /// Creates a bit-string from a byte array and status size.
489    pub fn from_bytes(bytes: Vec<u8>) -> Self {
490        Self(bytes)
491    }
492
493    /// Returns the value stored in the list at the given index.
494    pub fn get(&self, status_size: StatusSize, index: usize) -> Option<u8> {
495        if status_size.last_of(index).byte >= self.0.len() {
496            return None;
497        }
498
499        let offset = status_size.offset_of(index);
500        let (high_shift, low_shift) = offset.left_shift(status_size);
501
502        Some(self.get_at(status_size, offset.byte, high_shift, low_shift))
503    }
504
505    fn get_at(
506        &self,
507        status_size: StatusSize,
508        byte_offset: usize,
509        high_shift: i32,
510        low_shift: Option<u32>,
511    ) -> u8 {
512        let high = self
513            .0
514            .get(byte_offset)
515            .unwrap()
516            .overflowing_signed_shr(high_shift)
517            .0;
518
519        let low = match low_shift {
520            Some(low_shift) => {
521                self.0
522                    .get(byte_offset + 1)
523                    .unwrap()
524                    .overflowing_shr(low_shift)
525                    .0
526            }
527            None => 0,
528        };
529
530        (high | low) & status_size.mask()
531    }
532
533    /// Sets the value at the given index.
534    ///
535    /// Returns the previous value, or an `Overflow` error if either the index
536    /// is out of bounds or the value is too large.
537    pub fn set(
538        &mut self,
539        status_size: StatusSize,
540        index: usize,
541        value: u8,
542    ) -> Result<u8, Overflow> {
543        if status_size.last_of(index).byte >= self.0.len() {
544            return Err(Overflow::Index(index));
545        }
546
547        let mask = status_size.mask();
548        let masked_value = value & mask;
549        if masked_value != value {
550            return Err(Overflow::Value(value));
551        }
552
553        let offset = status_size.offset_of(index);
554        let (high_shift, low_shift) = offset.left_shift(status_size);
555
556        let old_value = self.get_at(status_size, offset.byte, high_shift, low_shift);
557
558        self.0[offset.byte] &= !mask.overflowing_signed_shl(high_shift).0; // clear high
559        self.0[offset.byte] |= masked_value.overflowing_signed_shl(high_shift).0; // set high
560        if let Some(low_shift) = low_shift {
561            self.0[offset.byte + 1] &= !mask.overflowing_shl(low_shift).0; // clear low
562            self.0[offset.byte + 1] |= masked_value.overflowing_shl(low_shift).0;
563            // set low
564        }
565
566        Ok(old_value)
567    }
568
569    /// Returns an iterator over all the statuses stored in this bit-string.
570    pub fn iter(&self, status_size: StatusSize) -> BitStringIter {
571        BitStringIter {
572            bit_string: self,
573            status_size,
574            index: 0,
575        }
576    }
577
578    /// Encodes the bit-string.
579    pub fn encode(&self) -> EncodedList {
580        EncodedList::encode(&self.0)
581    }
582
583    /// Consumes the bit-string and returns its raw bytes.
584    pub fn into_bytes(self) -> Vec<u8> {
585        self.0
586    }
587}
588
589impl From<BitString> for Vec<u8> {
590    fn from(value: BitString) -> Self {
591        value.into_bytes()
592    }
593}
594
595trait OverflowingSignedShift: Sized {
596    fn overflowing_signed_shl(self, shift: i32) -> (Self, bool);
597
598    fn overflowing_signed_shr(self, shift: i32) -> (Self, bool);
599}
600
601impl OverflowingSignedShift for u8 {
602    fn overflowing_signed_shl(self, shift: i32) -> (u8, bool) {
603        if shift < 0 {
604            self.overflowing_shr(shift.unsigned_abs())
605        } else {
606            self.overflowing_shl(shift.unsigned_abs())
607        }
608    }
609
610    fn overflowing_signed_shr(self, shift: i32) -> (u8, bool) {
611        if shift < 0 {
612            self.overflowing_shl(shift.unsigned_abs())
613        } else {
614            self.overflowing_shr(shift.unsigned_abs())
615        }
616    }
617}
618
619/// Status list.
620///
621/// This type does not store the actual status size (the size of each item)
622/// nor the total number of items in the list. Use the [`SizedStatusList`] type
623/// to access the list safely with regard to the items boundaries.
624#[derive(Debug, Clone)]
625pub struct StatusList {
626    bit_string: BitString,
627    ttl: TimeToLive,
628}
629
630impl StatusList {
631    pub fn new(ttl: TimeToLive) -> Self {
632        Self {
633            bit_string: BitString::new(),
634            ttl,
635        }
636    }
637
638    pub fn from_bytes(bytes: impl Into<Vec<u8>>, ttl: TimeToLive) -> Self {
639        Self {
640            bit_string: BitString::from_bytes(bytes.into()),
641            ttl,
642        }
643    }
644
645    pub fn get(&self, status_size: StatusSize, index: usize) -> Option<u8> {
646        self.bit_string.get(status_size, index)
647    }
648
649    pub fn set(
650        &mut self,
651        status_size: StatusSize,
652        index: usize,
653        value: u8,
654    ) -> Result<u8, Overflow> {
655        self.bit_string.set(status_size, index, value)
656    }
657
658    pub fn iter(&self, status_size: StatusSize) -> BitStringIter {
659        self.bit_string.iter(status_size)
660    }
661
662    pub fn to_credential_subject(
663        &self,
664        id: Option<UriBuf>,
665        status_purpose: StatusPurpose,
666    ) -> BitstringStatusList {
667        BitstringStatusList::new(id, status_purpose, self.bit_string.encode(), self.ttl)
668    }
669}
670
671/// Status list with status size.
672///
673/// This type is similar to [`StatusList`] but also stores the bit size of
674/// each item (status size) and the number of items in the list.
675/// This provides a safer access to the underlying bit-string, ensuring that
676/// status and list boundaries are respected.
677#[derive(Debug, Clone)]
678pub struct SizedStatusList {
679    bit_string: SizedBitString,
680    ttl: TimeToLive,
681}
682
683impl SizedStatusList {
684    pub fn new(status_size: StatusSize, ttl: TimeToLive) -> Self {
685        Self {
686            bit_string: SizedBitString::new(status_size),
687            ttl,
688        }
689    }
690
691    pub fn from_bytes(status_size: StatusSize, bytes: Vec<u8>, ttl: TimeToLive) -> Self {
692        Self {
693            bit_string: SizedBitString::from_bytes(status_size, bytes),
694            ttl,
695        }
696    }
697
698    pub fn get(&self, index: usize) -> Option<u8> {
699        self.bit_string.get(index)
700    }
701
702    pub fn set(&mut self, index: usize, value: u8) -> Result<u8, Overflow> {
703        self.bit_string.set(index, value)
704    }
705
706    pub fn push(&mut self, value: u8) -> Result<usize, Overflow> {
707        self.bit_string.push(value)
708    }
709
710    pub fn iter(&self) -> BitStringIter {
711        self.bit_string.iter()
712    }
713
714    pub fn to_credential_subject(
715        &self,
716        id: Option<UriBuf>,
717        status_purpose: StatusPurpose,
718    ) -> BitstringStatusList {
719        BitstringStatusList::new(id, status_purpose, self.bit_string.encode(), self.ttl)
720    }
721
722    pub fn into_unsized(self) -> StatusList {
723        StatusList {
724            bit_string: self.bit_string.into_unsized(),
725            ttl: self.ttl,
726        }
727    }
728}
729
730pub struct BitStringIter<'a> {
731    bit_string: &'a BitString,
732    status_size: StatusSize,
733    index: usize,
734}
735
736impl Iterator for BitStringIter<'_> {
737    type Item = u8;
738
739    fn next(&mut self) -> Option<Self::Item> {
740        self.bit_string
741            .get(self.status_size, self.index)
742            .inspect(|_| {
743                self.index += 1;
744            })
745    }
746}
747
748impl StatusMap for StatusList {
749    type Key = usize;
750    type StatusSize = StatusSize;
751    type Status = u8;
752
753    fn time_to_live(&self) -> Option<Duration> {
754        Some(self.ttl.into())
755    }
756
757    fn get_by_key(
758        &self,
759        status_size: Option<StatusSize>,
760        key: Self::Key,
761    ) -> Result<Option<u8>, StatusSizeError> {
762        Ok(self
763            .bit_string
764            .get(status_size.ok_or(StatusSizeError::Missing)?, key))
765    }
766}
767
768mod prefixed_hexadecimal {
769    use serde::{Deserialize, Deserializer, Serialize, Serializer};
770
771    pub fn serialize<S>(value: &u8, serializer: S) -> Result<S::Ok, S::Error>
772    where
773        S: Serializer,
774    {
775        format!("{value:#x}").serialize(serializer)
776    }
777
778    pub fn deserialize<'de, D>(deserializer: D) -> Result<u8, D::Error>
779    where
780        D: Deserializer<'de>,
781    {
782        let string = String::deserialize(deserializer)?;
783        let number = string
784            .strip_prefix("0x")
785            .ok_or_else(|| serde::de::Error::custom("missing `0x` prefix"))?;
786        u8::from_str_radix(number, 16).map_err(serde::de::Error::custom)
787    }
788}
789
790#[cfg(test)]
791mod tests {
792    use rand::{rngs::StdRng, RngCore, SeedableRng};
793
794    use crate::Overflow;
795
796    use super::{SizedBitString, StatusSize};
797
798    fn random_bit_string(
799        rng: &mut StdRng,
800        status_size: StatusSize,
801        len: usize,
802    ) -> (Vec<u8>, SizedBitString) {
803        let mut values = Vec::with_capacity(len);
804
805        for _ in 0..len {
806            values.push((rng.next_u32() & 0xff) as u8 & status_size.mask())
807        }
808
809        let mut bit_string = SizedBitString::new(status_size);
810        for &s in &values {
811            bit_string.push(s).unwrap();
812        }
813
814        (values, bit_string)
815    }
816
817    fn randomized_roundtrip(seed: u64, status_size: StatusSize, len: usize) {
818        let mut rng = StdRng::seed_from_u64(seed);
819        let (values, bit_string) = random_bit_string(&mut rng, status_size, len);
820
821        let encoded = bit_string.encode();
822        let decoded = SizedBitString::from_bytes(status_size, encoded.decode(None).unwrap());
823
824        assert!(decoded.len() >= len);
825
826        for (i, item) in values.into_iter().enumerate().take(len) {
827            assert_eq!(decoded.get(i), Some(item))
828        }
829    }
830
831    fn randomized_write(seed: u64, status_size: StatusSize, len: usize) {
832        let mut rng = StdRng::seed_from_u64(seed);
833        let (mut values, mut bit_string) = random_bit_string(&mut rng, status_size, len);
834
835        for _ in 0..len {
836            let i = (rng.next_u32() as usize) % len;
837            let value = (rng.next_u32() & 0xff) as u8 & status_size.mask();
838            bit_string.set(i, value).unwrap();
839            values[i] = value;
840        }
841
842        for (i, item) in values.into_iter().enumerate().take(len) {
843            assert_eq!(bit_string.get(i), Some(item))
844        }
845    }
846
847    #[test]
848    fn randomized_roundtrip_1bit() {
849        for i in 0..10 {
850            randomized_roundtrip(i, 1u8.try_into().unwrap(), 10);
851        }
852
853        for i in 0..10 {
854            randomized_roundtrip(i, 1u8.try_into().unwrap(), 100);
855        }
856
857        for i in 0..10 {
858            randomized_roundtrip(i, 1u8.try_into().unwrap(), 1000);
859        }
860    }
861
862    #[test]
863    fn randomized_write_1bits() {
864        for i in 0..10 {
865            randomized_write(i, 1u8.try_into().unwrap(), 10);
866        }
867
868        for i in 0..10 {
869            randomized_write(i, 1u8.try_into().unwrap(), 100);
870        }
871
872        for i in 0..10 {
873            randomized_write(i, 1u8.try_into().unwrap(), 1000);
874        }
875    }
876
877    #[test]
878    fn randomized_roundtrip_3bits() {
879        for i in 0..10 {
880            randomized_roundtrip(i, 3u8.try_into().unwrap(), 10);
881        }
882
883        for i in 0..10 {
884            randomized_roundtrip(i, 3u8.try_into().unwrap(), 100);
885        }
886
887        for i in 0..10 {
888            randomized_roundtrip(i, 3u8.try_into().unwrap(), 1000);
889        }
890    }
891
892    #[test]
893    fn randomized_write_3bits() {
894        for i in 0..10 {
895            randomized_write(i, 3u8.try_into().unwrap(), 10);
896        }
897
898        for i in 0..10 {
899            randomized_write(i, 3u8.try_into().unwrap(), 100);
900        }
901
902        for i in 0..10 {
903            randomized_write(i, 3u8.try_into().unwrap(), 1000);
904        }
905    }
906
907    #[test]
908    fn randomized_roundtrip_7bits() {
909        for i in 0..10 {
910            randomized_roundtrip(i, 7u8.try_into().unwrap(), 10);
911        }
912
913        for i in 0..10 {
914            randomized_roundtrip(i, 7u8.try_into().unwrap(), 100);
915        }
916
917        for i in 0..10 {
918            randomized_roundtrip(i, 7u8.try_into().unwrap(), 1000);
919        }
920    }
921
922    #[test]
923    fn randomized_write_7bits() {
924        for i in 0..10 {
925            randomized_write(i, 7u8.try_into().unwrap(), 10);
926        }
927
928        for i in 0..10 {
929            randomized_write(i, 7u8.try_into().unwrap(), 100);
930        }
931
932        for i in 0..10 {
933            randomized_write(i, 7u8.try_into().unwrap(), 1000);
934        }
935    }
936
937    #[test]
938    fn overflows() {
939        let mut rng = StdRng::seed_from_u64(0);
940        let (_, mut bitstring) = random_bit_string(&mut rng, 1u8.try_into().unwrap(), 15);
941
942        // Out of bounds.
943        assert!(bitstring.get(15).is_none());
944
945        // Out of bounds (even if there are enough bytes in the list).
946        assert_eq!(bitstring.set(15, 0), Err(Overflow::Index(15)));
947
948        // Too many bits.
949        assert_eq!(bitstring.set(14, 2), Err(Overflow::Value(2)));
950    }
951
952    #[test]
953    fn deserialize_status_size_1() {
954        assert!(serde_json::from_str::<StatusSize>("1").is_ok())
955    }
956
957    #[test]
958    fn deserialize_status_size_2() {
959        assert!(serde_json::from_str::<StatusSize>("2").is_ok())
960    }
961
962    #[test]
963    fn deserialize_status_size_3() {
964        assert!(serde_json::from_str::<StatusSize>("3").is_ok())
965    }
966
967    #[test]
968    fn deserialize_status_size_negative() {
969        assert!(serde_json::from_str::<StatusSize>("-1").is_err())
970    }
971
972    #[test]
973    fn deserialize_status_size_overflow() {
974        assert!(serde_json::from_str::<StatusSize>("9").is_err())
975    }
976}