From 5469530c64edecabe7e71c659071ce4975dd460d Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Mon, 3 Jan 2022 17:14:01 +0300 Subject: [PATCH] Initial attempt at no_std --- Cargo.toml | 4 +++- src/decode.rs | 9 ++++++++ src/encode.rs | 12 +++++++++-- src/error.rs | 59 +++++++++++++++++++++++++++++++++++---------------- src/header.rs | 2 +- src/lib.rs | 18 ++++++++++++---- src/types.rs | 2 +- src/utils.rs | 4 ++++ 8 files changed, 83 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c0f61ba..2500e2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/src/decode.rs b/src/decode.rs index 962cd8b..ee8bfb0 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -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)> { 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> { 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( data: &mut R, out: &mut [u8], @@ -253,6 +259,7 @@ where Ok(()) } +#[cfg(feature = "std")] #[inline] fn qoi_decode_impl_stream_all( data: &mut R, out: &mut [u8], channels: u8, src_channels: u8, @@ -269,12 +276,14 @@ fn qoi_decode_impl_stream_all( } } +#[cfg(feature = "std")] pub struct QoiStreamDecoder { reader: R, header: Header, channels: Channels, } +#[cfg(feature = "std")] impl QoiStreamDecoder { #[inline] pub fn new(mut reader: R) -> Result { diff --git a/src/encode.rs b/src/encode.rs index bd08c77..f1201ec 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -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(mut buf: W, data: &[u8]) -> Result @@ -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> { 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> { 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(&self, writer: &mut W) -> Result { writer.write_all(&self.header.encode())?; diff --git a/src/error.rs b/src/error.rs index 02d12f0..b8ad4f6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 = StdResult; +pub type Result = core::result::Result; 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 for Error { fn from(_: Infallible) -> Self { @@ -72,8 +94,9 @@ impl From for Error { } } -impl From for Error { - fn from(err: io::Error) -> Self { +#[cfg(feature = "std")] +impl From for Error { + fn from(err: std::io::Error) -> Self { Self::IoError(err) } } diff --git a/src/header.rs b/src/header.rs index 2214e6a..091b38c 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,4 +1,4 @@ -use std::convert::TryInto; +use core::convert::TryInto; use bytemuck::cast_slice; diff --git a/src/lib.rs b/src/lib.rs index dd06310..f60e515 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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}; diff --git a/src/types.rs b/src/types.rs index 48419d1..659f5f9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -use std::convert::TryFrom; +use core::convert::TryFrom; use crate::error::{Error, Result}; use crate::utils::unlikely; diff --git a/src/utils.rs b/src/utils.rs index d790d80..d7b6f9e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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 { writer: W, n_written: usize, } +#[cfg(feature = "std")] impl GenericWriter { pub fn new(writer: W) -> Self { Self { writer, n_written: 0 } } } +#[cfg(feature = "std")] impl Writer for GenericWriter { fn write_one(mut self, v: u8) -> Result { self.n_written += 1;