segsource/lib.rs
1#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3//! Segsource is a crate designed to assist in reading data. Although is specifically designed with
4//! binary (`u8`) data, it is not limited to this.
5//!
6//! ## Overview
7//!
8//! Segsource is designed to easily and efficiently allow the reading of data without using much
9//! memory. It does this by having two basic concepts: [`Source`]s, which own the data and
10//! [`Segment`]s which allow access the data.
11//!
12//! Each [`Segment`] is a thread-safe struct that retains minimal basic information such as the
13//! position of an internal cursor (expressed via the [`Segment::current_offset`] method), a
14//! reference to this data, etcetera.
15//!
16//! ## Feature Flags
17//!
18//! The following features are available for segsource:
19//!
20//! 1. `async` which adds support for various `async` operations using `tokio`.
21//! 2. `derive` which includes several macros for creating structs from [`Segment`]s.
22//! 3. `mmap` which adds support for memory mapped files.
23//! 4. `std` which adds support for file and I/O operations.
24//! 5. `with_bytes` which adds support for using the `bytes` crate.
25//!
26//! Of these, only `derive` and `std` are enabled by default.
27//!
28//! ## Why segsource?
29//!
30//! There are various other crates out there in a similar vein to segsource, and in your use case,
31//! some of them might be a better idea. I'll go through a few of the other options and let you
32//! decide for yourself:
33//!
34//! - `bytes`: `segsource` actually offers native support for `bytes` crate via the appropriately
35//! named `bytes` feature. While bytes is great, it does have its limitations, the two biggest
36//! ones being the most read operations require it to be mutable and that there's no way to go
37//! "back". Segsource solves both of these cases.
38//!
39//! - `binread`: Not a replacement for `segsource` as a whole, but for the derivations provided via
40//! the `derive` feature. As of this writing, `binread` is more feature rich than `segsource`'s
41//! derives (and since [`Segment`]s extend `std::io::Seek` and `std::io::Read`, they will work
42//! with `binread`]. Unfortunately, this again requires the passed in
43//!
44//! - `bitvec`: You may have noticed that you can essentially do simple memory emulation with
45//! `segsource (e.g. you can have an initial offset, you work in offsets, etcetera). Simple, being
46//! the keyword here. `bitvec` is not simple nor can it be given its scope.
47//!
48//! - `std`: You could use various items from the standard library, such as a `Vec` or an
49//! `io::Cursor`, but all of these have limitations (e.g. a `Vec` can't have an initial offset and
50//! a can only move relative to its current position).
51//!
52//! ## Derive
53//!
54//! Documentation is on my TODO list...
55//!
56//! ## Offsets
57//!
58//! Instead of indexes, segsource use offsets. Depending on your use case, these will probably end
59//! up being the same. However, you can specify an initial offset that will essentially change the
60//! index from zero to whatever the initial_offset is.
61//!
62//! For example:
63//!
64//! ```
65//! # use segsource::{VecSource, Source as _, U8Source as _, Endidness};
66//! # type SourceOfYourChoice = VecSource<u8>;
67//! let test_data = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
68//! let source = SourceOfYourChoice::from_u8_slice_with_offset(&test_data, 100, Endidness::Big).
69//! unwrap();
70//! let segment = source.all().unwrap();
71//! assert_eq!(segment.u8_at(100).unwrap(), 0);
72//! ```
73//!
74//! ### Validation
75//!
76//! One thing you may have noticed is that we had to unwrap the value each time. This is because
77//! methods first check to make an offset is valid. For example:
78//!
79//! ```
80//! # use segsource::{VecSource, Source as _, U8Source as _, Endidness, Error};
81//! # type SourceOfYourChoice = VecSource<u8>;
82//! # let test_data = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
83//! # let source = SourceOfYourChoice::from_u8_slice_with_offset(&test_data, 100, Endidness::Big).
84//! # unwrap();
85//! # let segment = source.all().unwrap();
86//! assert!(matches!(segment.u8_at(99), Err(Error::OffsetTooSmall { offset :99 })));
87//! ```
88
89#[cfg(not(feature = "std"))]
90extern crate alloc;
91
92use core::fmt;
93
94pub(crate) mod sources;
95pub use sources::*;
96
97pub(crate) mod error;
98pub use error::*;
99
100pub(crate) mod segment;
101pub use segment::*;
102
103#[cfg(feature = "derive")]
104#[doc(hidden)]
105pub mod derive_extras;
106#[doc(hidden)]
107pub mod marker;
108
109#[cfg(feature = "derive")]
110#[doc(inline)]
111pub use segsource_derive::{FromSegment, TryFromSegment};
112
113#[cfg(feature = "async")]
114#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
115pub mod sync;
116
117#[cfg(test)]
118mod testing;
119
120/// The "endidness" (i.e. big endian or little endian) of binary data. Defaults to the native
121/// endidness.
122#[derive(Debug, Clone, Copy, PartialEq)]
123pub enum Endidness {
124 Big,
125 Little,
126}
127
128impl Endidness {
129 #[cfg(target_endian = "big")]
130 /// Returns the native endidness.
131 #[inline]
132 pub fn native() -> Self {
133 Self::Big
134 }
135 #[cfg(target_endian = "little")]
136 /// Returns the native endidness.
137 #[inline]
138 pub fn native() -> Self {
139 Self::Little
140 }
141}
142
143impl Default for Endidness {
144 #[inline]
145 fn default() -> Self {
146 Self::native()
147 }
148}
149
150impl fmt::Display for Endidness {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self {
153 Self::Big => write!(f, "Big"),
154 Self::Little => write!(f, "Little"),
155 }
156 }
157}