Initial attempt at no_std

This commit is contained in:
Ivan Smirnov 2022-01-03 17:14:01 +03:00
parent e39bec8725
commit 5469530c64
8 changed files with 83 additions and 27 deletions

View file

@ -16,7 +16,9 @@ exclude = [
]
[features]
default = []
default = ["std"]
alloc = [] # provides access to `Vec` without enabling `std` mode
std = [] # std mode (enabled by default) - provides access to `std::io`, `Error` and `Vec`
reference = [] # follows reference encoder implementation precisely, but may be slightly slower
[dependencies]

View file

@ -1,3 +1,6 @@
#[cfg(any(feature = "std", feature = "alloc"))]
use alloc::{vec, vec::Vec};
#[cfg(feature = "std")]
use std::io::Read;
// TODO: can be removed once https://github.com/rust-lang/rust/issues/74985 is stable
@ -111,6 +114,7 @@ pub fn qoi_decode_to_buf(buf: impl AsMut<[u8]>, data: impl AsRef<[u8]>) -> Resul
Ok(*decoder.header())
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[inline]
pub fn qoi_decode_to_vec(data: impl AsRef<[u8]>) -> Result<(Header, Vec<u8>)> {
let mut decoder = QoiDecoder::new(&data)?;
@ -177,6 +181,7 @@ impl<'a> QoiDecoder<'a> {
Ok(size)
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[inline]
pub fn decode_to_vec(&mut self) -> Result<Vec<u8>> {
let mut out = vec![0; self.header.n_pixels() * self.channels.as_u8() as usize];
@ -184,6 +189,7 @@ impl<'a> QoiDecoder<'a> {
}
}
#[cfg(any(feature = "std"))]
#[inline]
fn qoi_decode_impl_stream<R: Read, const N: usize, const RGBA: bool>(
data: &mut R, out: &mut [u8],
@ -253,6 +259,7 @@ where
Ok(())
}
#[cfg(feature = "std")]
#[inline]
fn qoi_decode_impl_stream_all<R: Read>(
data: &mut R, out: &mut [u8], channels: u8, src_channels: u8,
@ -269,12 +276,14 @@ fn qoi_decode_impl_stream_all<R: Read>(
}
}
#[cfg(feature = "std")]
pub struct QoiStreamDecoder<R> {
reader: R,
header: Header,
channels: Channels,
}
#[cfg(feature = "std")]
impl<R: Read> QoiStreamDecoder<R> {
#[inline]
pub fn new(mut reader: R) -> Result<Self> {

View file

@ -1,4 +1,7 @@
use std::convert::TryFrom;
#[cfg(any(feature = "std", feature = "alloc"))]
use alloc::{vec, vec::Vec};
use core::convert::TryFrom;
#[cfg(feature = "std")]
use std::io::Write;
use crate::consts::{QOI_HEADER_SIZE, QOI_OP_INDEX, QOI_OP_RUN, QOI_PADDING, QOI_PADDING_SIZE};
@ -6,7 +9,9 @@ use crate::error::{Error, Result};
use crate::header::Header;
use crate::pixel::{Pixel, SupportedChannels};
use crate::types::{Channels, ColorSpace};
use crate::utils::{unlikely, BytesMut, GenericWriter, Writer};
#[cfg(feature = "std")]
use crate::utils::GenericWriter;
use crate::utils::{cold, unlikely, BytesMut, Writer};
#[allow(clippy::cast_possible_truncation, unused_assignments)]
fn qoi_encode_impl<W: Writer, const N: usize>(mut buf: W, data: &[u8]) -> Result<usize>
@ -89,6 +94,7 @@ pub fn qoi_encode_to_buf(
QoiEncoder::new(&data, width, height)?.encode_to_buf(buf)
}
#[cfg(any(feature = "alloc", feature = "std"))]
#[inline]
pub fn qoi_encode_to_vec(data: impl AsRef<[u8]>, width: u32, height: u32) -> Result<Vec<u8>> {
QoiEncoder::new(&data, width, height)?.encode_to_vec()
@ -149,6 +155,7 @@ impl<'a> QoiEncoder<'a> {
Ok(QOI_HEADER_SIZE + n_written)
}
#[cfg(any(feature = "alloc", feature = "std"))]
#[inline]
pub fn encode_to_vec(&self) -> Result<Vec<u8>> {
let mut out = vec![0_u8; self.encoded_size_limit()];
@ -157,6 +164,7 @@ impl<'a> QoiEncoder<'a> {
Ok(out)
}
#[cfg(feature = "std")]
#[inline]
pub fn encode_to_stream<W: Write>(&self, writer: &mut W) -> Result<usize> {
writer.write_all(&self.header.encode())?;

View file

@ -1,27 +1,47 @@
use std::convert::Infallible;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io;
use std::result::Result as StdResult;
use core::convert::Infallible;
use core::fmt::{self, Display};
use crate::consts::{QOI_MAGIC, QOI_PIXELS_MAX};
#[derive(Debug)]
pub enum Error {
InvalidChannels { channels: u8 },
EmptyImage { width: u32, height: u32 },
ImageTooLarge { width: u32, height: u32 },
InvalidImageLength { size: usize, width: u32, height: u32 },
InputBufferTooSmall { size: usize, required: usize },
OutputBufferTooSmall { size: usize, required: usize },
InvalidMagic { magic: u32 },
InvalidChannels {
channels: u8,
},
EmptyImage {
width: u32,
height: u32,
},
ImageTooLarge {
width: u32,
height: u32,
},
InvalidImageLength {
size: usize,
width: u32,
height: u32,
},
InputBufferTooSmall {
size: usize,
required: usize,
},
OutputBufferTooSmall {
size: usize,
required: usize,
},
InvalidMagic {
magic: u32,
},
UnexpectedBufferEnd,
InvalidColorSpace { colorspace: u8 },
InvalidColorSpace {
colorspace: u8,
},
InvalidPadding,
IoError(io::Error),
#[cfg(feature = "std")]
IoError(std::io::Error),
}
pub type Result<T> = StdResult<T, Error>;
pub type Result<T> = core::result::Result<T, Error>;
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -57,6 +77,7 @@ impl Display for Error {
Self::InvalidPadding => {
write!(f, "invalid padding (stream end marker)")
}
#[cfg(feature = "std")]
Self::IoError(ref err) => {
write!(f, "i/o error: {}", err)
}
@ -64,7 +85,8 @@ impl Display for Error {
}
}
impl StdError for Error {}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl From<Infallible> for Error {
fn from(_: Infallible) -> Self {
@ -72,8 +94,9 @@ impl From<Infallible> for Error {
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Self::IoError(err)
}
}

View file

@ -1,4 +1,4 @@
use std::convert::TryInto;
use core::convert::TryInto;
use bytemuck::cast_slice;

View file

@ -8,6 +8,11 @@
clippy::module_name_repetitions,
clippy::cargo_common_metadata
)]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#[cfg(all(feature = "alloc", not(any(feature = "std", test))))]
extern crate alloc;
#[cfg(any(feature = "std", test))]
extern crate std as alloc;
mod decode;
mod encode;
@ -20,10 +25,15 @@ mod utils;
#[doc(hidden)]
pub mod consts;
pub use crate::decode::{
qoi_decode_header, qoi_decode_to_buf, qoi_decode_to_vec, QoiDecoder, QoiStreamDecoder,
};
pub use crate::encode::{encoded_size_limit, qoi_encode_to_buf, qoi_encode_to_vec, QoiEncoder};
#[cfg(any(feature = "alloc", feature = "std"))]
pub use crate::decode::qoi_decode_to_vec;
#[cfg(feature = "std")]
pub use crate::decode::QoiStreamDecoder;
pub use crate::decode::{qoi_decode_header, qoi_decode_to_buf, QoiDecoder};
#[cfg(any(feature = "alloc", feature = "std"))]
pub use crate::encode::qoi_encode_to_vec;
pub use crate::encode::{encoded_size_limit, qoi_encode_to_buf, QoiEncoder};
pub use crate::error::{Error, Result};
pub use crate::header::Header;
pub use crate::types::{Channels, ColorSpace};

View file

@ -1,4 +1,4 @@
use std::convert::TryFrom;
use core::convert::TryFrom;
use crate::error::{Error, Result};
use crate::utils::unlikely;

View file

@ -1,3 +1,4 @@
#[cfg(feature = "std")]
use std::io::Write;
use crate::error::Result;
@ -29,17 +30,20 @@ pub trait Writer: Sized {
fn capacity(&self) -> usize;
}
#[cfg(feature = "std")]
pub struct GenericWriter<W> {
writer: W,
n_written: usize,
}
#[cfg(feature = "std")]
impl<W: Write> GenericWriter<W> {
pub fn new(writer: W) -> Self {
Self { writer, n_written: 0 }
}
}
#[cfg(feature = "std")]
impl<W: Write> Writer for GenericWriter<W> {
fn write_one(mut self, v: u8) -> Result<Self> {
self.n_written += 1;