diff --git a/Cargo.toml b/Cargo.toml index eb363fa..770f55d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,14 +16,17 @@ byteorder = "1.4.3" crc = "3.0.0" log = { version = "0.4.17", optional = true } env_logger = { version = "0.9.0", optional = true } +core2 = "0.4" [dev-dependencies] rust-lzma = "0.5" [features] +default = ["std"] enable_logging = ["env_logger", "log"] stream = [] raw_decoder = [] +std = [] [package.metadata.docs.rs] features = ["stream", "raw_decoder"] diff --git a/src/decode/lzbuffer.rs b/src/decode/lzbuffer.rs index e5aacb7..945b266 100644 --- a/src/decode/lzbuffer.rs +++ b/src/decode/lzbuffer.rs @@ -1,4 +1,7 @@ use crate::error; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; pub trait LzBuffer diff --git a/src/decode/lzma.rs b/src/decode/lzma.rs index 7d1d5b3..f35df11 100644 --- a/src/decode/lzma.rs +++ b/src/decode/lzma.rs @@ -3,7 +3,12 @@ use crate::decode::rangecoder::{BitTree, LenDecoder, RangeDecoder}; use crate::decompress::{Options, UnpackedSize}; use crate::error; use crate::util::vec2d::Vec2D; +#[cfg(not(feature = "std"))] +use alloc::string::String; use byteorder::{LittleEndian, ReadBytesExt}; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; /// Maximum input data that can be processed in one iteration. @@ -163,7 +168,7 @@ impl LzmaParams { pub(crate) struct DecoderState { // Buffer input data here if we need more for decompression. Up to // MAX_REQUIRED_INPUT bytes can be consumed during one iteration. - partial_input_buf: std::io::Cursor<[u8; MAX_REQUIRED_INPUT]>, + partial_input_buf: io::Cursor<[u8; MAX_REQUIRED_INPUT]>, pub(crate) lzma_props: LzmaProperties, unpacked_size: Option, literal_probs: Vec2D, @@ -186,7 +191,7 @@ impl DecoderState { pub fn new(lzma_props: LzmaProperties, unpacked_size: Option) -> Self { lzma_props.validate(); DecoderState { - partial_input_buf: std::io::Cursor::new([0; MAX_REQUIRED_INPUT]), + partial_input_buf: io::Cursor::new([0; MAX_REQUIRED_INPUT]), lzma_props, unpacked_size, literal_probs: Vec2D::init(0x400, (1 << (lzma_props.lc + lzma_props.lp), 0x300)), @@ -404,7 +409,7 @@ impl DecoderState { range: u32, code: u32, ) -> error::Result<()> { - let mut temp = std::io::Cursor::new(buf); + let mut temp = io::Cursor::new(buf); let mut rangecoder = RangeDecoder::from_parts(&mut temp, range, code); let _ = self.process_next_inner(output, &mut rangecoder, false)?; Ok(()) diff --git a/src/decode/lzma2.rs b/src/decode/lzma2.rs index 4fc9a85..4e97187 100644 --- a/src/decode/lzma2.rs +++ b/src/decode/lzma2.rs @@ -3,8 +3,10 @@ use crate::decode::lzma::{DecoderState, LzmaProperties}; use crate::decode::{lzbuffer, rangecoder}; use crate::error; use byteorder::{BigEndian, ReadBytesExt}; -use std::io; -use std::io::Read; +#[cfg(not(feature = "std"))] +use core2::io::{self, Read}; +#[cfg(feature = "std")] +use std::io::{self, Read}; #[derive(Debug)] /// Raw decoder for LZMA2. diff --git a/src/decode/rangecoder.rs b/src/decode/rangecoder.rs index 52271f9..34f1da1 100644 --- a/src/decode/rangecoder.rs +++ b/src/decode/rangecoder.rs @@ -1,6 +1,9 @@ use crate::decode::util; use crate::error; use byteorder::{BigEndian, ReadBytesExt}; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; pub struct RangeDecoder<'a, R> diff --git a/src/decode/stream.rs b/src/decode/stream.rs index 15a2c30..ca4e9ba 100644 --- a/src/decode/stream.rs +++ b/src/decode/stream.rs @@ -3,8 +3,18 @@ use crate::decode::lzma::{DecoderState, LzmaParams}; use crate::decode::rangecoder::RangeDecoder; use crate::decompress::Options; use crate::error::Error; -use std::fmt::Debug; +#[cfg(not(feature = "std"))] +use core::fmt::{self, Debug}; +#[cfg(not(feature = "std"))] +use core::u64::MAX as U64_MAX; +#[cfg(not(feature = "std"))] +use core2::io::{self, BufRead, Cursor, Read, Write}; +#[cfg(feature = "std")] +use std::fmt::{self, Debug}; +#[cfg(feature = "std")] use std::io::{self, BufRead, Cursor, Read, Write}; +#[cfg(feature = "std")] +use std::u64::MAX as U64_MAX; /// Minimum header length to be read. /// - props: u8 (1 byte) @@ -51,7 +61,7 @@ impl Debug for RunState where W: Write, { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("RunState") .field("range", &self.range) .field("code", &self.code) @@ -212,7 +222,7 @@ impl Debug for Stream where W: Write + Debug, { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("Stream") .field("tmp", &self.tmp.position()) .field("state", &self.state) @@ -278,7 +288,7 @@ where // reset the cursor because we may have partial reads input.set_position(0); let bytes_read = input.read(&mut self.tmp.get_mut()[..])?; - let bytes_read = if bytes_read < std::u64::MAX as usize { + let bytes_read = if bytes_read < U64_MAX as usize { bytes_read as u64 } else { return Err(io::Error::new( @@ -351,6 +361,8 @@ impl From for io::Error { #[cfg(test)] mod test { use super::*; + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; /// Test an empty stream #[test] diff --git a/src/decode/util.rs b/src/decode/util.rs index 623a870..91f957c 100644 --- a/src/decode/util.rs +++ b/src/decode/util.rs @@ -1,3 +1,6 @@ +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; pub fn read_tag(input: &mut R, tag: &[u8]) -> io::Result { diff --git a/src/decode/xz.rs b/src/decode/xz.rs index ef0fa1c..2e225b7 100644 --- a/src/decode/xz.rs +++ b/src/decode/xz.rs @@ -5,9 +5,13 @@ use crate::decode::util; use crate::error; use crate::xz::crc::{CRC32, CRC64}; use crate::xz::{footer, header, CheckMethod, StreamFlags}; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; -use std::io; -use std::io::Read; +#[cfg(not(feature = "std"))] +use core2::io::{self, Read}; +#[cfg(feature = "std")] +use std::io::{self, Read}; #[derive(Debug)] struct Record { diff --git a/src/encode/dumbencoder.rs b/src/encode/dumbencoder.rs index f1574c5..606b433 100644 --- a/src/encode/dumbencoder.rs +++ b/src/encode/dumbencoder.rs @@ -1,6 +1,9 @@ use crate::compress::{Options, UnpackedSize}; use crate::encode::rangecoder; use byteorder::{LittleEndian, WriteBytesExt}; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; pub struct Encoder<'a, W> diff --git a/src/encode/lzma2.rs b/src/encode/lzma2.rs index ead0726..f37546d 100644 --- a/src/encode/lzma2.rs +++ b/src/encode/lzma2.rs @@ -1,4 +1,9 @@ +#[cfg(not(feature = "std"))] +use alloc::vec; use byteorder::{BigEndian, WriteBytesExt}; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; pub fn encode_stream(input: &mut R, output: &mut W) -> io::Result<()> diff --git a/src/encode/rangecoder.rs b/src/encode/rangecoder.rs index da5385d..cf576d2 100644 --- a/src/encode/rangecoder.rs +++ b/src/encode/rangecoder.rs @@ -1,4 +1,9 @@ +#[cfg(all(test, not(feature = "std")))] +use alloc::{vec, vec::Vec}; use byteorder::WriteBytesExt; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; pub struct RangeEncoder<'a, W> diff --git a/src/encode/util.rs b/src/encode/util.rs index 625bec1..ff3462c 100644 --- a/src/encode/util.rs +++ b/src/encode/util.rs @@ -1,3 +1,6 @@ +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; // A Write computing a digest on the bytes written. diff --git a/src/encode/xz.rs b/src/encode/xz.rs index 42bf9ef..2a4cee6 100644 --- a/src/encode/xz.rs +++ b/src/encode/xz.rs @@ -3,8 +3,10 @@ use crate::encode::{lzma2, util}; use crate::xz::crc::CRC32; use crate::xz::{footer, header, CheckMethod, StreamFlags}; use byteorder::{LittleEndian, WriteBytesExt}; -use std::io; -use std::io::Write; +#[cfg(not(feature = "std"))] +use core2::io::{self, Write}; +#[cfg(feature = "std")] +use std::io::{self, Write}; pub fn encode_stream(input: &mut R, output: &mut W) -> io::Result<()> where diff --git a/src/error.rs b/src/error.rs index 2ee0fbe..4af7f1a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,17 @@ //! Error handling. -use std::fmt::Display; -use std::{io, result}; +#[cfg(not(feature = "std"))] +use alloc::string::String; +#[cfg(not(feature = "std"))] +use core::fmt::{self, Display}; +#[cfg(not(feature = "std"))] +use core::result; +#[cfg(not(feature = "std"))] +use core2::{error, io}; +#[cfg(feature = "std")] +use std::fmt::{self, Display}; +#[cfg(feature = "std")] +use std::{error, io, result}; /// Library errors. #[derive(Debug)] @@ -26,7 +36,7 @@ impl From for Error { } impl Display for Error { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::IoError(e) => write!(fmt, "io error: {}", e), Error::HeaderTooShort(e) => write!(fmt, "header too short: {}", e), @@ -36,8 +46,8 @@ impl Display for Error { } } -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::IoError(e) | Error::HeaderTooShort(e) => Some(e), Error::LzmaError(_) | Error::XzError(_) => None, @@ -48,15 +58,15 @@ impl std::error::Error for Error { #[cfg(test)] mod test { use super::Error; + #[cfg(not(feature = "std"))] + use core2::io; + #[cfg(feature = "std")] + use std::io; #[test] fn test_display() { assert_eq!( - Error::IoError(std::io::Error::new( - std::io::ErrorKind::Other, - "this is an error" - )) - .to_string(), + Error::IoError(io::Error::new(io::ErrorKind::Other, "this is an error")).to_string(), "io error: this is an error" ); assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index f8bac25..406f1b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,13 @@ //! Pure-Rust codecs for LZMA, LZMA2, and XZ. #![cfg_attr(docsrs, feature(doc_cfg, doc_cfg_hide))] +#![cfg_attr(no_std, not(feature(std)))] #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![forbid(unsafe_code)] +#[cfg(not(feature = "std"))] +extern crate alloc; + #[macro_use] mod macros; @@ -15,6 +19,9 @@ pub mod error; mod util; mod xz; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; /// Compression helpers. diff --git a/src/util/vec2d.rs b/src/util/vec2d.rs index 36c775f..820e402 100644 --- a/src/util/vec2d.rs +++ b/src/util/vec2d.rs @@ -1,3 +1,8 @@ +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; +#[cfg(not(feature = "std"))] +use core::ops::{Index, IndexMut}; +#[cfg(feature = "std")] use std::ops::{Index, IndexMut}; /// A 2 dimensional matrix in row-major order backed by a contiguous slice. diff --git a/src/xz/header.rs b/src/xz/header.rs index b1c2802..9adc73b 100644 --- a/src/xz/header.rs +++ b/src/xz/header.rs @@ -5,6 +5,10 @@ use crate::error; use crate::xz::crc::CRC32; use crate::xz::StreamFlags; use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] +use std::io; /// File format magic header signature, see sect. 2.1.1.1. pub(crate) const XZ_MAGIC: &[u8] = &[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]; @@ -19,7 +23,7 @@ impl StreamHeader { /// Parse a Stream Header from a buffered reader. pub(crate) fn parse
(input: &mut BR) -> error::Result where - BR: std::io::BufRead, + BR: io::BufRead, { if !util::read_tag(input, XZ_MAGIC)? { return Err(error::Error::XzError(format!( diff --git a/src/xz/mod.rs b/src/xz/mod.rs index aae2dd1..9348c24 100644 --- a/src/xz/mod.rs +++ b/src/xz/mod.rs @@ -5,6 +5,9 @@ //! [spec]: https://tukaani.org/xz/xz-file-format.txt use crate::error; +#[cfg(not(feature = "std"))] +use core2::io; +#[cfg(feature = "std")] use std::io; pub(crate) mod crc; @@ -85,12 +88,19 @@ impl From for u8 { mod test { use super::*; use byteorder::{BigEndian, ReadBytesExt}; - use std::io::{Seek, SeekFrom}; + #[cfg(not(feature = "std"))] + use core::u8::MAX as U8_MAX; + #[cfg(not(feature = "std"))] + use core2::io::{self, Seek, SeekFrom}; + #[cfg(feature = "std")] + use std::io::{self, Seek, SeekFrom}; + #[cfg(feature = "std")] + use std::u8::MAX as U8_MAX; #[test] fn test_checkmethod_roundtrip() { let mut count_valid = 0; - for input in 0..std::u8::MAX { + for input in 0..U8_MAX { if let Ok(check) = CheckMethod::try_from(input) { let output: u8 = check.into(); assert_eq!(input, output); @@ -106,7 +116,7 @@ mod test { check_method: CheckMethod::Crc32, }; - let mut cursor = std::io::Cursor::new(vec![0u8; 2]); + let mut cursor = io::Cursor::new(vec![0u8; 2]); let len = input.serialize(&mut cursor).unwrap(); assert_eq!(len, 2);