#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
#![warn(rust_2018_idioms)]
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod config;
pub mod de;
pub mod display;
pub mod enc;
pub mod error;
#[doc(hidden)]
pub mod decode {
pub use crate::de::*;
}
#[doc(hidden)]
pub mod encode {
#[cfg(feature = "alloc")]
pub use crate::enc::VecWriter;
pub use crate::enc::{SizeWriter, SliceWriter, Writer};
}
pub(crate) mod utils;
pub(crate) mod varint;
mod features;
#[cfg(feature = "simd")]
pub mod simd;
#[cfg(any(feature = "compression-lz4", feature = "compression-zstd"))]
pub mod compression;
pub mod versioning;
#[cfg(feature = "alloc")]
pub mod streaming;
#[cfg(feature = "async-tokio")]
pub mod async_tokio {
pub use crate::streaming::AsyncStreamingDecoder as AsyncDecoder;
pub use crate::streaming::AsyncStreamingEncoder as AsyncEncoder;
pub use crate::streaming::{
CancellableAsyncDecoder, CancellableAsyncEncoder, CancellationToken,
};
pub use crate::streaming::{StreamingConfig, StreamingProgress};
}
#[cfg(feature = "async-tokio")]
pub mod async_io {
pub use crate::streaming::AsyncStreamingDecoder as AsyncDecoder;
pub use crate::streaming::AsyncStreamingEncoder as AsyncEncoder;
pub use crate::streaming::{
CancellableAsyncDecoder, CancellableAsyncEncoder, CancellationToken,
};
pub use crate::streaming::{StreamingConfig, StreamingProgress};
}
pub mod validation;
#[cfg(feature = "checksum")]
pub mod checksum;
pub use de::BorrowDecode;
#[cfg(feature = "std")]
pub use de::BufferedIoReader;
pub use de::Decode;
pub use display::EncodedBytes;
#[cfg(feature = "alloc")]
pub use display::EncodedBytesOwned;
pub use enc::Encode;
pub use error::{Error, Result};
#[cfg(feature = "derive")]
pub use oxicode_derive::{BorrowDecode, Decode, Encode};
#[cfg(feature = "serde")]
pub use features::serde;
#[cfg(feature = "alloc")]
pub fn encode_to_vec<E: Encode>(value: &E) -> Result<alloc::vec::Vec<u8>> {
encode_to_vec_with_config(value, config::standard())
}
#[cfg(feature = "alloc")]
pub fn encode_to_vec_with_config<E: Encode, C: config::Config>(
value: &E,
config: C,
) -> Result<alloc::vec::Vec<u8>> {
let writer = enc::VecWriter::new();
let mut encoder = enc::EncoderImpl::new(writer, config);
value.encode(&mut encoder)?;
Ok(encoder.into_writer().into_vec())
}
pub fn encoded_size<E: Encode>(value: &E) -> Result<usize> {
encoded_size_with_config(value, config::standard())
}
pub fn encoded_size_with_config<E: Encode, C: config::Config>(
value: &E,
config: C,
) -> Result<usize> {
let writer = enc::SizeWriter::new();
let mut encoder = enc::EncoderImpl::new(writer, config);
value.encode(&mut encoder)?;
Ok(encoder.into_writer().bytes_written())
}
pub fn encode_into_slice<E: Encode, C: config::Config>(
value: E,
dst: &mut [u8],
config: C,
) -> Result<usize> {
let writer = enc::SliceWriter::new(dst);
let mut encoder = enc::EncoderImpl::new(writer, config);
value.encode(&mut encoder)?;
Ok(encoder.into_writer().bytes_written())
}
pub fn encode_to_fixed_array<const N: usize, E: Encode>(value: &E) -> Result<([u8; N], usize)> {
encode_to_fixed_array_with_config(value, config::standard())
}
pub fn encode_to_fixed_array_with_config<const N: usize, E: Encode, C: config::Config>(
value: &E,
config: C,
) -> Result<([u8; N], usize)> {
let mut buf = [0u8; N];
let writer = enc::SliceWriter::new(&mut buf);
let mut encoder = enc::EncoderImpl::new(writer, config);
value.encode(&mut encoder)?;
let bytes_written = encoder.into_writer().bytes_written();
Ok((buf, bytes_written))
}
pub fn decode_value<D: Decode>(src: &[u8]) -> Result<D> {
let (value, _) = decode_from_slice(src)?;
Ok(value)
}
#[cfg(feature = "alloc")]
pub fn encode_bytes<E: Encode>(value: &E) -> Result<alloc::vec::Vec<u8>> {
encode_to_vec(value)
}
#[cfg(feature = "alloc")]
pub fn encode_copy<E: Encode + Copy>(value: E) -> Result<alloc::vec::Vec<u8>> {
encode_to_vec(&value)
}
#[cfg(feature = "alloc")]
pub fn encode_iter_to_vec<E: Encode, I: IntoIterator<Item = E>>(
iter: I,
) -> Result<alloc::vec::Vec<u8>> {
encode_iter_to_vec_with_config(iter, config::standard())
}
#[cfg(feature = "alloc")]
pub fn encode_iter_to_vec_with_config<E: Encode, I: IntoIterator<Item = E>, C: config::Config>(
iter: I,
config: C,
) -> Result<alloc::vec::Vec<u8>> {
let items: alloc::vec::Vec<E> = iter.into_iter().collect();
encode_to_vec_with_config(&items, config)
}
#[cfg(feature = "alloc")]
pub fn encode_seq_to_vec<E: Encode, I: ExactSizeIterator<Item = E>>(
iter: I,
) -> Result<alloc::vec::Vec<u8>> {
encode_seq_to_vec_with_config(iter, config::standard())
}
#[cfg(feature = "alloc")]
pub fn encode_seq_to_vec_with_config<
E: Encode,
I: ExactSizeIterator<Item = E>,
C: config::Config,
>(
iter: I,
config: C,
) -> Result<alloc::vec::Vec<u8>> {
let len = iter.len();
let writer = enc::VecWriter::new();
let mut encoder = enc::EncoderImpl::new(writer, config);
enc::encode_slice_len(&mut encoder, len)?;
for item in iter {
item.encode(&mut encoder)?;
}
Ok(encoder.into_writer().into_vec())
}
pub fn encode_seq_into_slice<E: Encode, I: ExactSizeIterator<Item = E>>(
iter: I,
dst: &mut [u8],
) -> Result<usize> {
encode_seq_into_slice_with_config(iter, dst, config::standard())
}
pub fn encode_seq_into_slice_with_config<
E: Encode,
I: ExactSizeIterator<Item = E>,
C: config::Config,
>(
iter: I,
dst: &mut [u8],
config: C,
) -> Result<usize> {
let len = iter.len();
let writer = enc::SliceWriter::new(dst);
let mut encoder = enc::EncoderImpl::new(writer, config);
enc::encode_slice_len(&mut encoder, len)?;
for item in iter {
item.encode(&mut encoder)?;
}
Ok(encoder.into_writer().bytes_written())
}
#[cfg(feature = "alloc")]
pub struct DecodeIter<T, D: de::Decoder> {
decoder: D,
remaining: u64,
_marker: core::marker::PhantomData<T>,
}
#[cfg(feature = "alloc")]
impl<T: de::Decode, D: de::Decoder<Context = ()>> Iterator for DecodeIter<T, D> {
type Item = Result<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
self.remaining -= 1;
Some(T::decode(&mut self.decoder))
}
}
#[cfg(feature = "alloc")]
pub fn decode_iter_from_slice<T: de::Decode>(
src: &[u8],
) -> Result<DecodeIter<T, de::DecoderImpl<de::SliceReader<'_>, config::Configuration>>> {
decode_iter_from_slice_with_config(src, config::standard())
}
#[cfg(feature = "alloc")]
pub fn decode_iter_from_slice_with_config<T: de::Decode, C: config::Config>(
src: &[u8],
config: C,
) -> Result<DecodeIter<T, de::DecoderImpl<de::SliceReader<'_>, C>>> {
use de::Decode as _;
let reader = de::SliceReader::new(src);
let mut decoder = de::DecoderImpl::new(reader, config);
let remaining = u64::decode(&mut decoder)?;
Ok(DecodeIter {
decoder,
remaining,
_marker: core::marker::PhantomData,
})
}
pub fn encoded_bytes(bytes: &[u8]) -> display::EncodedBytes<'_> {
display::EncodedBytes(bytes)
}
#[cfg(feature = "alloc")]
pub fn encode_to_display<E: Encode>(value: &E) -> Result<display::EncodedBytesOwned> {
let bytes = encode_to_vec(value)?;
Ok(display::EncodedBytesOwned(bytes))
}
#[cfg(feature = "alloc")]
pub fn encode_versioned_value<E: Encode>(
value: &E,
version: versioning::Version,
) -> Result<alloc::vec::Vec<u8>> {
let payload = encode_to_vec(value)?;
versioning::encode_versioned(&payload, version)
}
#[cfg(feature = "alloc")]
pub fn decode_versioned_value<D: Decode>(src: &[u8]) -> Result<(D, versioning::Version, usize)> {
let (payload, version) = versioning::decode_versioned(src)?;
let header_size = src.len() - payload.len();
let (value, consumed) = decode_from_slice(&payload)?;
Ok((value, version, header_size + consumed))
}
#[cfg(feature = "std")]
pub fn encode_to_writer<E: Encode, W: std::io::Write>(value: &E, writer: W) -> Result<usize> {
let io_writer = enc::IoWriter::new(writer);
let mut encoder = enc::EncoderImpl::new(io_writer, config::standard());
value.encode(&mut encoder)?;
Ok(encoder.into_writer().bytes_written())
}
#[cfg(feature = "std")]
pub fn decode_from_reader<D: Decode, R: std::io::Read>(reader: R) -> Result<(D, usize)> {
struct CountingReader<R: std::io::Read> {
inner: R,
count: usize,
}
impl<R: std::io::Read> CountingReader<R> {
fn new(inner: R) -> Self {
Self { inner, count: 0 }
}
}
impl<R: std::io::Read> de::Reader for CountingReader<R> {
fn read(&mut self, bytes: &mut [u8]) -> Result<()> {
self.inner.read_exact(bytes).map_err(|e| {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
Error::UnexpectedEnd {
additional: bytes.len(),
}
} else {
Error::Io {
kind: e.kind(),
message: e.to_string(),
}
}
})?;
self.count += bytes.len();
Ok(())
}
}
let counting = CountingReader::new(reader);
let mut decoder = de::DecoderImpl::new(counting, config::standard());
let value = D::decode(&mut decoder)?;
let bytes_read = decoder.reader().count;
Ok((value, bytes_read))
}
#[cfg(feature = "std")]
pub fn encode_to_writer_with_config<E: Encode, W: std::io::Write, C: config::Config>(
value: &E,
writer: W,
config: C,
) -> Result<usize> {
let io_writer = enc::IoWriter::new(writer);
let mut encoder = enc::EncoderImpl::new(io_writer, config);
value.encode(&mut encoder)?;
Ok(encoder.into_writer().bytes_written())
}
#[cfg(feature = "std")]
pub fn decode_from_reader_with_config<D: Decode, R: std::io::Read, C: config::Config>(
reader: R,
config: C,
) -> Result<(D, usize)> {
struct CountingReader<R: std::io::Read> {
inner: R,
count: usize,
}
impl<R: std::io::Read> CountingReader<R> {
fn new(inner: R) -> Self {
Self { inner, count: 0 }
}
}
impl<R: std::io::Read> de::Reader for CountingReader<R> {
fn read(&mut self, bytes: &mut [u8]) -> Result<()> {
self.inner.read_exact(bytes).map_err(|e| {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
Error::UnexpectedEnd {
additional: bytes.len(),
}
} else {
Error::Io {
kind: e.kind(),
message: e.to_string(),
}
}
})?;
self.count += bytes.len();
Ok(())
}
}
let counting = CountingReader::new(reader);
let mut decoder = de::DecoderImpl::new(counting, config);
let value = D::decode(&mut decoder)?;
let bytes_read = decoder.reader().count;
Ok((value, bytes_read))
}
#[cfg(feature = "std")]
pub fn encode_to_vec_with_size_hint<E: Encode>(
value: &E,
size_hint: usize,
) -> Result<std::vec::Vec<u8>> {
let mut buf = std::vec::Vec::with_capacity(size_hint);
let io_writer = enc::IoWriter::new(&mut buf);
let mut encoder = enc::EncoderImpl::new(io_writer, config::standard());
value.encode(&mut encoder)?;
let len = encoder.into_writer().bytes_written();
buf.truncate(len);
Ok(buf)
}
#[cfg(feature = "std")]
pub fn decode_from_buffered_read<D: Decode, R: std::io::Read>(
src: R,
config: impl config::Config,
) -> Result<D> {
let reader = de::read::BufferedIoReader::new(src);
let mut decoder = de::DecoderImpl::new(reader, config);
D::decode(&mut decoder)
}
pub fn decode_from_slice<D: Decode>(src: &[u8]) -> Result<(D, usize)> {
decode_from_slice_with_config(src, config::standard())
}
pub fn decode_from_slice_with_config<D: Decode, C: config::Config>(
src: &[u8],
config: C,
) -> Result<(D, usize)> {
let reader = de::SliceReader::new(src);
let mut decoder = de::DecoderImpl::new(reader, config);
let result = D::decode(&mut decoder)?;
let bytes_read = src.len() - decoder.reader().slice.len();
Ok((result, bytes_read))
}
pub fn encode_into_writer<E: Encode, W: enc::Writer, C: config::Config>(
value: E,
writer: W,
config: C,
) -> Result<()> {
let mut encoder = enc::EncoderImpl::new(writer, config);
value.encode(&mut encoder)?;
Ok(())
}
#[cfg(feature = "std")]
pub fn encode_into_std_write<E: Encode, W: std::io::Write, C: config::Config>(
value: E,
writer: W,
config: C,
) -> Result<usize> {
let io_writer = enc::IoWriter::new(writer);
let mut encoder = enc::EncoderImpl::new(io_writer, config);
value.encode(&mut encoder)?;
Ok(encoder.into_writer().bytes_written())
}
pub fn decode_from_de_reader<D: Decode, R: de::Reader, C: config::Config>(
reader: R,
config: C,
) -> Result<D> {
let mut decoder = de::DecoderImpl::new(reader, config);
D::decode(&mut decoder)
}
#[cfg(feature = "std")]
pub fn decode_from_std_read<D: Decode, R: std::io::Read, C: config::Config>(
reader: R,
config: C,
) -> Result<D> {
let io_reader = de::IoReader::new(reader);
let mut decoder = de::DecoderImpl::new(io_reader, config);
D::decode(&mut decoder)
}
#[cfg(feature = "std")]
pub fn encode_to_hex<E: Encode>(value: &E) -> Result<String> {
let bytes = encode_to_vec(value)?;
Ok(bytes
.iter()
.fold(String::with_capacity(bytes.len() * 2), |mut s, b| {
use std::fmt::Write as _;
let _ = write!(s, "{:02x}", b);
s
}))
}
#[cfg(feature = "std")]
pub fn decode_from_hex<D: Decode>(hex: &str) -> Result<(D, usize)> {
if hex.len() % 2 != 0 {
return Err(Error::InvalidData {
message: "hex string has odd length",
});
}
let bytes = (0..hex.len())
.step_by(2)
.map(|i| {
hex.get(i..i + 2)
.and_then(|s| u8::from_str_radix(s, 16).ok())
})
.collect::<Option<Vec<u8>>>()
.ok_or(Error::InvalidData {
message: "invalid hex string: contains non-hex characters",
})?;
decode_from_slice(&bytes)
}
#[cfg(feature = "std")]
pub fn encode_to_file<E: Encode>(value: &E, path: impl AsRef<std::path::Path>) -> Result<()> {
encode_to_file_with_config(value, path, config::standard())
}
#[cfg(feature = "std")]
pub fn encode_to_file_with_config<E: Encode, C: config::Config>(
value: &E,
path: impl AsRef<std::path::Path>,
config: C,
) -> Result<()> {
let file = std::fs::File::create(path)?;
let io_writer = enc::IoWriter::new(file);
let mut encoder = enc::EncoderImpl::new(io_writer, config);
value.encode(&mut encoder)?;
Ok(())
}
#[cfg(feature = "std")]
pub fn decode_from_file<D: Decode>(path: impl AsRef<std::path::Path>) -> Result<D> {
decode_from_file_with_config(path, config::standard())
}
#[cfg(feature = "std")]
pub fn decode_from_file_with_config<D: Decode, C: config::Config>(
path: impl AsRef<std::path::Path>,
config: C,
) -> Result<D> {
let file = std::fs::File::open(path)?;
decode_from_std_read(file, config)
}
pub fn decode_from_slice_with_context<Ctx, D, C: config::Config>(
src: &[u8],
config: C,
context: Ctx,
) -> Result<(D, usize)>
where
D: de::Decode<Ctx>,
{
let reader = de::SliceReader::new(src);
let mut decoder = de::DecoderImpl::with_context(reader, config, context);
let result = D::decode(&mut decoder)?;
let bytes_read = src.len() - decoder.reader().slice.len();
Ok((result, bytes_read))
}
pub fn borrow_decode_from_slice<'a, D>(src: &'a [u8]) -> Result<(D, usize)>
where
D: de::BorrowDecode<'a>,
{
borrow_decode_from_slice_with_config(src, config::standard())
}
#[cfg(all(feature = "checksum", feature = "alloc"))]
pub fn encode_to_vec_checked<E: Encode>(value: &E) -> Result<alloc::vec::Vec<u8>> {
checksum::encode_with_checksum(value)
}
#[cfg(all(feature = "checksum", feature = "alloc"))]
pub fn decode_from_slice_checked<D: Decode>(src: &[u8]) -> Result<(D, usize)> {
checksum::decode_with_checksum(src)
}
pub fn borrow_decode_from_slice_with_config<'a, D, C: config::Config>(
src: &'a [u8],
config: C,
) -> Result<(D, usize)>
where
D: de::BorrowDecode<'a>,
{
let reader = de::SliceReader::new(src);
let mut decoder = de::DecoderImpl::new(reader, config);
let result = D::borrow_decode(&mut decoder)?;
let bytes_read = src.len() - decoder.reader().slice.len();
Ok((result, bytes_read))
}
#[cfg(test)]
mod tests {
#[test]
fn test_basic_encoding() {
assert_eq!(2 + 2, 4);
}
}