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] [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 reference = [] # follows reference encoder implementation precisely, but may be slightly slower
[dependencies] [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; use std::io::Read;
// TODO: can be removed once https://github.com/rust-lang/rust/issues/74985 is stable // 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()) Ok(*decoder.header())
} }
#[cfg(any(feature = "std", feature = "alloc"))]
#[inline] #[inline]
pub fn qoi_decode_to_vec(data: impl AsRef<[u8]>) -> Result<(Header, Vec<u8>)> { pub fn qoi_decode_to_vec(data: impl AsRef<[u8]>) -> Result<(Header, Vec<u8>)> {
let mut decoder = QoiDecoder::new(&data)?; let mut decoder = QoiDecoder::new(&data)?;
@ -177,6 +181,7 @@ impl<'a> QoiDecoder<'a> {
Ok(size) Ok(size)
} }
#[cfg(any(feature = "std", feature = "alloc"))]
#[inline] #[inline]
pub fn decode_to_vec(&mut self) -> Result<Vec<u8>> { 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]; 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] #[inline]
fn qoi_decode_impl_stream<R: Read, const N: usize, const RGBA: bool>( fn qoi_decode_impl_stream<R: Read, const N: usize, const RGBA: bool>(
data: &mut R, out: &mut [u8], data: &mut R, out: &mut [u8],
@ -253,6 +259,7 @@ where
Ok(()) Ok(())
} }
#[cfg(feature = "std")]
#[inline] #[inline]
fn qoi_decode_impl_stream_all<R: Read>( fn qoi_decode_impl_stream_all<R: Read>(
data: &mut R, out: &mut [u8], channels: u8, src_channels: u8, 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> { pub struct QoiStreamDecoder<R> {
reader: R, reader: R,
header: Header, header: Header,
channels: Channels, channels: Channels,
} }
#[cfg(feature = "std")]
impl<R: Read> QoiStreamDecoder<R> { impl<R: Read> QoiStreamDecoder<R> {
#[inline] #[inline]
pub fn new(mut reader: R) -> Result<Self> { 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 std::io::Write;
use crate::consts::{QOI_HEADER_SIZE, QOI_OP_INDEX, QOI_OP_RUN, QOI_PADDING, QOI_PADDING_SIZE}; 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::header::Header;
use crate::pixel::{Pixel, SupportedChannels}; use crate::pixel::{Pixel, SupportedChannels};
use crate::types::{Channels, ColorSpace}; 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)] #[allow(clippy::cast_possible_truncation, unused_assignments)]
fn qoi_encode_impl<W: Writer, const N: usize>(mut buf: W, data: &[u8]) -> Result<usize> 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) QoiEncoder::new(&data, width, height)?.encode_to_buf(buf)
} }
#[cfg(any(feature = "alloc", feature = "std"))]
#[inline] #[inline]
pub fn qoi_encode_to_vec(data: impl AsRef<[u8]>, width: u32, height: u32) -> Result<Vec<u8>> { 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() QoiEncoder::new(&data, width, height)?.encode_to_vec()
@ -149,6 +155,7 @@ impl<'a> QoiEncoder<'a> {
Ok(QOI_HEADER_SIZE + n_written) Ok(QOI_HEADER_SIZE + n_written)
} }
#[cfg(any(feature = "alloc", feature = "std"))]
#[inline] #[inline]
pub fn encode_to_vec(&self) -> Result<Vec<u8>> { pub fn encode_to_vec(&self) -> Result<Vec<u8>> {
let mut out = vec![0_u8; self.encoded_size_limit()]; let mut out = vec![0_u8; self.encoded_size_limit()];
@ -157,6 +164,7 @@ impl<'a> QoiEncoder<'a> {
Ok(out) Ok(out)
} }
#[cfg(feature = "std")]
#[inline] #[inline]
pub fn encode_to_stream<W: Write>(&self, writer: &mut W) -> Result<usize> { pub fn encode_to_stream<W: Write>(&self, writer: &mut W) -> Result<usize> {
writer.write_all(&self.header.encode())?; writer.write_all(&self.header.encode())?;

View file

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

View file

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

View file

@ -8,6 +8,11 @@
clippy::module_name_repetitions, clippy::module_name_repetitions,
clippy::cargo_common_metadata 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 decode;
mod encode; mod encode;
@ -20,10 +25,15 @@ mod utils;
#[doc(hidden)] #[doc(hidden)]
pub mod consts; pub mod consts;
pub use crate::decode::{ #[cfg(any(feature = "alloc", feature = "std"))]
qoi_decode_header, qoi_decode_to_buf, qoi_decode_to_vec, QoiDecoder, QoiStreamDecoder, pub use crate::decode::qoi_decode_to_vec;
}; #[cfg(feature = "std")]
pub use crate::encode::{encoded_size_limit, qoi_encode_to_buf, qoi_encode_to_vec, QoiEncoder}; 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::error::{Error, Result};
pub use crate::header::Header; pub use crate::header::Header;
pub use crate::types::{Channels, ColorSpace}; 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::error::{Error, Result};
use crate::utils::unlikely; use crate::utils::unlikely;

View file

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