summaryrefslogtreecommitdiffstats
path: root/third_party/rust/plain/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/rust/plain/src
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/plain/src')
-rw-r--r--third_party/rust/plain/src/error.rs6
-rw-r--r--third_party/rust/plain/src/lib.rs158
-rw-r--r--third_party/rust/plain/src/methods.rs198
-rw-r--r--third_party/rust/plain/src/plain.rs96
-rw-r--r--third_party/rust/plain/src/tests.rs123
5 files changed, 581 insertions, 0 deletions
diff --git a/third_party/rust/plain/src/error.rs b/third_party/rust/plain/src/error.rs
new file mode 100644
index 0000000000..0012eba9bc
--- /dev/null
+++ b/third_party/rust/plain/src/error.rs
@@ -0,0 +1,6 @@
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Error {
+ TooShort,
+ BadAlignment,
+}
diff --git a/third_party/rust/plain/src/lib.rs b/third_party/rust/plain/src/lib.rs
new file mode 100644
index 0000000000..f4eb4badf1
--- /dev/null
+++ b/third_party/rust/plain/src/lib.rs
@@ -0,0 +1,158 @@
+//! A small Rust library that allows users to interpret arrays of bytes
+//! as certain kinds of structures safely.
+//!
+//! This crate provides an unsafe trait [`Plain`](trait.Plain.html), which the user
+//! of the crate uses to mark types for which operations of this library are safe.
+//! See [`Plain`](trait.Plain.html) for the contractual obligation.
+//!
+//! Other than that, everything else in this crate is perfectly safe to use as long
+//! as the `Plain` trait is not implemented on inadmissible types (similar to how
+//! `Send` and `Sync` in the standard library work).
+//!
+//! # Purpose
+//!
+//! In low level systems development, it is sometimes necessary to
+//! interpret locations in memory as data structures. Functions of
+//! this crate serve to avoid pitfalls associated with that, without
+//! having to resort to big, full-featured (de)serialization libraries.
+//!
+//! On the other hand, this crate contains no provisions when it comes
+//! to handling differences in encoding and byte ordering between
+//! platforms. As such, it is entirely unsuitable for processing
+//! external data such as file contents or network packets.
+//!
+//! # Examples
+//!
+//! To start using the crate, simply do `extern crate plain;`.
+//!
+//! If you want your plain types to have methods from this crate, also include `use plain.Plain;`.
+//!
+//! Then it's just a matter of marking the right types and using them.
+//!
+//! ```
+//!
+//! extern crate plain;
+//! use plain::Plain;
+//! use std::mem;
+//!
+//!
+//! #[repr(C)]
+//! #[derive(Default)]
+//! struct ELF64Header {
+//! pub e_ident: [u8; 16],
+//! pub e_type: u16,
+//! pub e_machine: u16,
+//! pub e_version: u32,
+//! pub e_entry: u64,
+//! pub e_phoff: u64,
+//! pub e_shoff: u64,
+//! pub e_flags: u32,
+//! pub e_ehsize: u16,
+//! pub e_phentsize: u16,
+//! pub e_phnum: u16,
+//! pub e_shentsize: u16,
+//! pub e_shnum: u16,
+//! pub e_shstrndx: u16,
+//! }
+//!
+//! // SAFE: ELF64Header satisfies all the requirements of `Plain`.
+//! unsafe impl Plain for ELF64Header {}
+//!
+//! impl ELF64Header {
+//! fn from_bytes(buf: &[u8]) -> &ELF64Header {
+//! plain::from_bytes(buf).expect("The buffer is either too short or not aligned!")
+//! }
+//!
+//! fn from_mut_bytes(buf: &mut [u8]) -> &mut ELF64Header {
+//! plain::from_mut_bytes(buf).expect("The buffer is either too short or not aligned!")
+//! }
+//!
+//! fn copy_from_bytes(buf: &[u8]) -> ELF64Header {
+//! let mut h = ELF64Header::default();
+//! h.copy_from_bytes(buf).expect("The buffer is too short!");
+//! h
+//! }
+//! }
+//!
+//! # fn process_elf(elf: &ELF64Header) {}
+//!
+//! // Conditional copying for ultimate hackery.
+//! fn opportunistic_elf_processing(buf: &[u8]) {
+//! if plain::is_aligned::<ELF64Header>(buf) {
+//! // No copy necessary.
+//! let elf_ref = ELF64Header::from_bytes(buf);
+//! process_elf(elf_ref);
+//! } else {
+//! // Not aligned properly, copy to stack first.
+//! let elf = ELF64Header::copy_from_bytes(buf);
+//! process_elf(&elf);
+//! }
+//! }
+//!
+//! #[repr(C)]
+//! #[derive(Default, Copy, Clone)]
+//! struct ArrayEntry {
+//! pub name: [u8; 32],
+//! pub tag: u32,
+//! pub score: u32,
+//! }
+//!
+//! // SAFE: ArrayEntry satisfies all the requirements of `Plain`.
+//! unsafe impl Plain for ArrayEntry {}
+//!
+//! fn array_from_bytes(buf: &[u8]) -> &[ArrayEntry] {
+//! // NOTE: length is not a concern here,
+//! // since slice_from_bytes() can return empty slice.
+//!
+//! match plain::slice_from_bytes(buf) {
+//! Err(_) => panic!("The buffer is not aligned!"),
+//! Ok(arr) => arr,
+//! }
+//! }
+//!
+//! fn array_from_unaligned_bytes(buf: &[u8]) -> Vec<ArrayEntry> {
+//! let length = buf.len() / mem::size_of::<ArrayEntry>();
+//! let mut result = vec![ArrayEntry::default(); length];
+//! (&mut result).copy_from_bytes(buf).expect("Cannot fail here.");
+//! result
+//! }
+//!
+//! # fn main() {}
+//!
+//! ```
+//!
+//! # Comparison to [`pod`](https://crates.io/crates/pod)
+//!
+//! [`pod`](https://crates.io/crates/pod) is another crate created to help working with plain data.
+//! The major difference between `pod` and `plain` is scope.
+//!
+//! `plain` currently provides only a few functions (+method wrappers) and its implementation
+//! involves very few lines of unsafe code. It can be used in `no_std` code. Also, it doesn't
+//! deal with [endianness](https://en.wikipedia.org/wiki/Endianness) in any way,
+//! so it is only suitable for certain kinds of low-level work.
+//!
+//! `pod`, on the other hand, provides a wide arsenal
+//! of various methods, most of which may be unnecessary for a given use case.
+//! It has dependencies on `std` as well as other crates, but among other things
+//! it provides tools to handle endianness properly.
+//!
+//! In short, `plain` is much, much _plainer_...
+#![no_std]
+
+mod error;
+pub use error::Error;
+
+mod plain;
+pub use plain::Plain;
+
+mod methods;
+pub use methods::{as_bytes, as_mut_bytes, copy_from_bytes, from_bytes, from_mut_bytes, is_aligned,
+ slice_from_bytes, slice_from_bytes_len, slice_from_mut_bytes,
+ slice_from_mut_bytes_len};
+
+#[cfg(test)]
+#[macro_use]
+extern crate std;
+
+#[cfg(test)]
+mod tests;
diff --git a/third_party/rust/plain/src/methods.rs b/third_party/rust/plain/src/methods.rs
new file mode 100644
index 0000000000..58be4a2500
--- /dev/null
+++ b/third_party/rust/plain/src/methods.rs
@@ -0,0 +1,198 @@
+
+use core::{mem, slice};
+
+use {Error, Plain};
+
+/// Check if a byte slice is aligned suitably for type T.
+#[inline]
+pub fn is_aligned<T>(bytes: &[u8]) -> bool {
+ ((bytes.as_ptr() as usize) % mem::align_of::<T>()) == 0
+}
+
+#[inline(always)]
+fn check_alignment<T>(bytes: &[u8]) -> Result<(), Error> {
+ if is_aligned::<T>(bytes) {
+ Ok(())
+ } else {
+ Err(Error::BadAlignment)
+ }
+}
+
+#[inline(always)]
+fn check_length<T>(bytes: &[u8], len: usize) -> Result<(), Error> {
+ if mem::size_of::<T>() > 0 && (bytes.len() / mem::size_of::<T>()) < len {
+ Err(Error::TooShort)
+ } else {
+ Ok(())
+ }
+}
+
+/// Interpret data as bytes. Not safe for data with padding.
+#[inline(always)]
+pub unsafe fn as_bytes<S>(s: &S) -> &[u8]
+where
+ S: ?Sized,
+{
+ let bptr = s as *const S as *const u8;
+ let bsize = mem::size_of_val(s);
+ slice::from_raw_parts(bptr, bsize)
+}
+
+/// Interpret data as mutable bytes.
+/// Reading is not safe for data with padding. Writing is ok.
+#[inline(always)]
+pub unsafe fn as_mut_bytes<S>(s: &mut S) -> &mut [u8]
+where
+ S: Plain + ?Sized,
+{
+ let bptr = s as *mut S as *mut u8;
+ let bsize = mem::size_of_val(s);
+ slice::from_raw_parts_mut(bptr, bsize)
+}
+
+/// Safely converts a byte slice to a reference.
+///
+/// However, if the byte slice is not long enough
+/// to contain target type, or if it doesn't
+/// satisfy the type's alignment requirements,
+/// the function returns an error.
+///
+/// The function will not fail when the
+/// byte slice is longer than necessary, since it is
+/// a common practice to interpret the beginning of
+/// a slice as a fixed-size header.
+///
+/// In many cases it is preferrable to allocate
+/// a value/slice of the target type and use
+/// [`copy_from_bytes()`](fn.copy_from_bytes.html) to copy
+/// data instead. That way, any issues with alignment
+/// are implicitly avoided.
+///
+#[inline]
+pub fn from_bytes<T>(bytes: &[u8]) -> Result<&T, Error>
+where
+ T: Plain,
+{
+ try!(check_alignment::<T>(bytes));
+ try!(check_length::<T>(bytes, 1));
+ Ok(unsafe { &*(bytes.as_ptr() as *const T) })
+}
+
+/// Similar to [`from_bytes()`](fn.from_bytes.html),
+/// except that the output is a slice of T, instead
+/// of a reference to a single T. All concerns about
+/// alignment also apply here, but size is handled
+/// differently.
+///
+/// The result slice's length is set to be
+/// `bytes.len() / size_of::<T>()`, and there
+/// are no requirements for input size. I.e.
+/// the result may be empty slice, and the input
+/// slice doesn't necessarily have to end on `T`'s
+/// boundary. The latter has pragmatic reasons: If the
+/// length of the array is not known in advance,
+/// e.g. if it's terminated by a special element,
+/// it's perfectly legal to turn the whole rest
+/// of data into `&[T]` and set the proper length
+/// after inspecting the array.
+///
+/// In many cases it is preferrable to allocate
+/// a value/slice of the target type and use
+/// [`copy_from_bytes()`](fn.copy_from_bytes.html) to copy
+/// data instead. That way, any issues with alignment
+/// are implicitly avoided.
+///
+#[inline]
+pub fn slice_from_bytes<T>(bytes: &[u8]) -> Result<&[T], Error>
+where
+ T: Plain,
+{
+ let len = bytes.len() / mem::size_of::<T>();
+ slice_from_bytes_len(bytes, len)
+}
+
+
+/// Same as [`slice_from_bytes()`](fn.slice_from_bytes.html),
+/// except that it takes explicit length of the result slice.
+///
+/// If the input slice cannot satisfy the length, returns error.
+/// The input slice is allowed to be longer than necessary.
+///
+#[inline]
+pub fn slice_from_bytes_len<T>(bytes: &[u8], len: usize) -> Result<&[T], Error>
+where
+ T: Plain,
+{
+ try!(check_alignment::<T>(bytes));
+ try!(check_length::<T>(bytes, len));
+ Ok(unsafe {
+ slice::from_raw_parts(bytes.as_ptr() as *const T, len)
+ })
+}
+
+/// See [`from_bytes()`](fn.from_bytes.html).
+///
+/// Does the same, except with mutable references.
+///
+#[inline]
+pub fn from_mut_bytes<T>(bytes: &mut [u8]) -> Result<&mut T, Error>
+where
+ T: Plain,
+{
+ try!(check_alignment::<T>(bytes));
+ try!(check_length::<T>(bytes, 1));
+ Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) })
+}
+
+/// See [`slice_from_bytes()`](fn.slice_from_bytes.html).
+///
+/// Does the same, except with mutable references.
+///
+#[inline]
+pub fn slice_from_mut_bytes<T>(bytes: &mut [u8]) -> Result<&mut [T], Error>
+where
+ T: Plain,
+{
+ let len = bytes.len() / mem::size_of::<T>();
+ slice_from_mut_bytes_len(bytes, len)
+}
+
+/// See [`slice_from_bytes_len()`](fn.slice_from_bytes_len.html).
+///
+/// Does the same, except with mutable references.
+///
+#[inline]
+pub fn slice_from_mut_bytes_len<T>(bytes: &mut [u8], len: usize) -> Result<&mut [T], Error>
+where
+ T: Plain,
+{
+ try!(check_alignment::<T>(bytes));
+ try!(check_length::<T>(bytes, len));
+ Ok(unsafe {
+ slice::from_raw_parts_mut(bytes.as_ptr() as *mut T, len)
+ })
+}
+
+/// Copies data from a byte slice into existing memory.
+/// Suitable when [`from_bytes()`](fn.from_bytes.html) would normally
+/// be used, but the data is not aligned properly in memory.
+///
+/// For an example how to use it, see crate-level documentation.
+///
+#[inline]
+pub fn copy_from_bytes<T>(into: &mut T, bytes: &[u8]) -> Result<(), Error>
+where
+ T: Plain + ?Sized,
+{
+ let sz = mem::size_of_val(into);
+
+ if bytes.len() < sz {
+ return Err(Error::TooShort);
+ }
+
+ unsafe {
+ as_mut_bytes(into).copy_from_slice(&bytes[..sz]);
+ }
+
+ Ok(())
+}
diff --git a/third_party/rust/plain/src/plain.rs b/third_party/rust/plain/src/plain.rs
new file mode 100644
index 0000000000..35522006de
--- /dev/null
+++ b/third_party/rust/plain/src/plain.rs
@@ -0,0 +1,96 @@
+use Error;
+
+/// A trait for plain data types that can be safely read from a byte slice.
+///
+/// A type can be [`Plain`](trait.Plain.html) if it is `#repr(C)` and only contains
+/// data with no possible invalid values. Types that _can't_ be `Plain`
+/// include, but are not limited to, `bool`, `char`, `enum`s, tuples,
+/// pointers and references.
+///
+/// At this moment, `Drop` types are also not legal, because
+/// compiler adds a special "drop flag" into the type. This is slated
+/// to change in the future.
+///
+/// On the other hand, arrays of a `Plain` type, and
+/// structures where all members are plain (and not `Drop`), are okay.
+///
+/// Structures that are not `#repr(C)`, while not necessarily illegal
+/// in principle, are largely useless because they don't have a stable
+/// layout. For example, the compiler is allowed to reorder fields
+/// arbitrarily.
+///
+/// All methods of this trait are implemented automatically as wrappers
+/// for crate-level funtions.
+///
+pub unsafe trait Plain {
+ #[inline(always)]
+ fn from_bytes(bytes: &[u8]) -> Result<&Self, Error>
+ where
+ Self: Sized,
+ {
+ ::from_bytes(bytes)
+ }
+
+ #[inline(always)]
+ fn slice_from_bytes(bytes: &[u8]) -> Result<&[Self], Error>
+ where
+ Self: Sized,
+ {
+ ::slice_from_bytes(bytes)
+ }
+
+ #[inline(always)]
+ fn slice_from_bytes_len(bytes: &[u8], len: usize) -> Result<&[Self], Error>
+ where
+ Self: Sized,
+ {
+ ::slice_from_bytes_len(bytes, len)
+ }
+
+ #[inline(always)]
+ fn from_mut_bytes(bytes: &mut [u8]) -> Result<&mut Self, Error>
+ where
+ Self: Sized,
+ {
+ ::from_mut_bytes(bytes)
+ }
+
+ #[inline(always)]
+ fn slice_from_mut_bytes(bytes: &mut [u8]) -> Result<&mut [Self], Error>
+ where
+ Self: Sized,
+ {
+ ::slice_from_mut_bytes(bytes)
+ }
+
+ #[inline(always)]
+ fn slice_from_mut_bytes_len(bytes: &mut [u8], len: usize) -> Result<&mut [Self], Error>
+ where
+ Self: Sized,
+ {
+ ::slice_from_mut_bytes_len(bytes, len)
+ }
+
+ #[inline(always)]
+ fn copy_from_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> {
+ ::copy_from_bytes(self, bytes)
+ }
+}
+
+unsafe impl Plain for u8 {}
+unsafe impl Plain for u16 {}
+unsafe impl Plain for u32 {}
+unsafe impl Plain for u64 {}
+unsafe impl Plain for usize {}
+
+unsafe impl Plain for i8 {}
+unsafe impl Plain for i16 {}
+unsafe impl Plain for i32 {}
+unsafe impl Plain for i64 {}
+unsafe impl Plain for isize {}
+
+unsafe impl<S> Plain for [S]
+where
+ S: Plain,
+{
+}
diff --git a/third_party/rust/plain/src/tests.rs b/third_party/rust/plain/src/tests.rs
new file mode 100644
index 0000000000..7ab1c3753e
--- /dev/null
+++ b/third_party/rust/plain/src/tests.rs
@@ -0,0 +1,123 @@
+
+
+#![allow(dead_code)]
+
+use ::*;
+use core::mem;
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Eq, Clone, PartialEq)]
+struct Dummy1 {
+ field1: u64,
+ field2: u32,
+ field3: u16,
+ field4: u8,
+ field5: u8,
+}
+
+unsafe impl Plain for Dummy1 {}
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Eq, Clone, PartialEq)]
+struct Dummy2 {
+ field1: u8,
+ field2: u8,
+ field3: u16,
+ field4: u32,
+ field5: u64,
+}
+
+unsafe impl Plain for Dummy2 {}
+
+fn as_bytes<T: ?Sized>(r: &T) -> &[u8] {
+ unsafe { methods::as_bytes(r) }
+}
+
+fn as_mut_bytes<T: Plain + ?Sized>(r: &mut T) -> &mut [u8] {
+ unsafe { methods::as_mut_bytes(r) }
+}
+
+#[test]
+fn one_too_short() {
+ let b = vec![0u8; mem::size_of::<Dummy1>() - 1];
+
+ let r = Dummy1::from_bytes(&b);
+ assert!(r == Err(Error::TooShort));
+}
+
+#[test]
+fn unaligned() {
+ let b = vec![0u8; mem::size_of::<Dummy1>() + 1];
+ let b = &b[1..];
+
+ let r = Dummy1::from_bytes(&b);
+ assert!(r == Err(Error::BadAlignment));
+}
+
+#[test]
+fn copy_test() {
+ let t1 = Dummy1 {
+ field1: 0xaaaaaaaaaaaaaaaau64,
+ field2: 0xbbbbbbbbu32,
+ field3: 0xccccu16,
+ field4: 0xddu8,
+ field5: 0xeeu8,
+ };
+
+ let mut t2 = Dummy2::default();
+
+ assert!(t2.copy_from_bytes(as_bytes(&t1)) == Ok(()));
+
+ assert!(t2.field1 == 0xaau8);
+ assert!(t2.field2 == 0xaau8);
+ assert!(t2.field3 == 0xaaaau16);
+ assert!(t2.field4 == 0xaaaaaaaau32);
+ assert!(t2.field5 == 0xbbbbbbbbccccddeeu64 || t2.field5 == 0xeeddccccbbbbbbbbu64);
+
+ let sz = mem::size_of::<Dummy2>();
+ assert!(t2.copy_from_bytes(&as_bytes(&t1)[..sz - 1]) == Err(Error::TooShort));
+}
+
+#[test]
+fn basic_function() {
+ let t1 = Dummy1 {
+ field1: 0xaaaaaaaaaaaaaaaau64,
+ field2: 0xbbbbbbbbu32,
+ field3: 0xccccu16,
+ field4: 0xddu8,
+ field5: 0xeeu8,
+ };
+
+ let r1: &Dummy2 = from_bytes(as_bytes(&t1)).unwrap();
+
+ assert!(r1.field1 == 0xaau8);
+ assert!(r1.field2 == 0xaau8);
+ assert!(r1.field3 == 0xaaaau16);
+ assert!(r1.field4 == 0xaaaaaaaau32);
+ assert!(r1.field5 == 0xbbbbbbbbccccddeeu64 || r1.field5 == 0xeeddccccbbbbbbbbu64);
+
+ let r2 = as_bytes(r1);
+ assert!(r2.len() == mem::size_of::<Dummy1>());
+ assert!(r2[5] == 0xaa);
+
+ let size = r2.len();
+ let r3 = as_bytes(r2);
+ assert!(r3.len() == size);
+
+ let r4 = Dummy1::from_bytes(r3).unwrap();
+
+ let r5 = from_bytes::<Dummy2>(as_bytes(r4)).unwrap();
+
+ {
+ let r6 = slice_from_bytes::<Dummy1>(as_bytes(r5)).unwrap();
+
+ assert!(r6.len() == 1);
+ assert!(t1 == r6[0]);
+ }
+
+ let r7 = slice_from_bytes::<u64>(as_bytes(r5)).unwrap();
+ assert!(r7.len() == 2);
+
+ assert!(r7[0] == 0xaaaaaaaaaaaaaaaau64);
+ assert!(r7[1] == 0xbbbbbbbbccccddeeu64 || r7[1] == 0xeeddccccbbbbbbbbu64);
+}