Skip to content

Commit

Permalink
lib: enable no-std compatibility
Browse files Browse the repository at this point in the history
Add a default `std` feature, and use compatible data structures from
`alloc`, `core`, and `core2` for `no-std` builds.

To build for `no-std` pass `--no-default-features` on the command line,
or `default-features = false` in the Cargo.toml of another
library/binary using `lzma-rs`.
  • Loading branch information
rmsyn committed Jan 28, 2023
1 parent da82bd1 commit adcf6af
Show file tree
Hide file tree
Showing 18 changed files with 116 additions and 27 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
3 changes: 3 additions & 0 deletions src/decode/lzbuffer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::error;
#[cfg(not(feature = "std"))]
use core2::io;
#[cfg(feature = "std")]
use std::io;

pub trait LzBuffer<W>
Expand Down
11 changes: 8 additions & 3 deletions src/decode/lzma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<u64>,
literal_probs: Vec2D<u16>,
Expand All @@ -186,7 +191,7 @@ impl DecoderState {
pub fn new(lzma_props: LzmaProperties, unpacked_size: Option<u64>) -> 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)),
Expand Down Expand Up @@ -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(())
Expand Down
6 changes: 4 additions & 2 deletions src/decode/lzma2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions src/decode/rangecoder.rs
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
20 changes: 16 additions & 4 deletions src/decode/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -51,7 +61,7 @@ impl<W> Debug for RunState<W>
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)
Expand Down Expand Up @@ -212,7 +222,7 @@ impl<W> Debug for Stream<W>
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)
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -351,6 +361,8 @@ impl From<Error> for io::Error {
#[cfg(test)]
mod test {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

/// Test an empty stream
#[test]
Expand Down
3 changes: 3 additions & 0 deletions src/decode/util.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(not(feature = "std"))]
use core2::io;
#[cfg(feature = "std")]
use std::io;

pub fn read_tag<R: io::BufRead>(input: &mut R, tag: &[u8]) -> io::Result<bool> {
Expand Down
8 changes: 6 additions & 2 deletions src/decode/xz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions src/encode/dumbencoder.rs
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
5 changes: 5 additions & 0 deletions src/encode/lzma2.rs
Original file line number Diff line number Diff line change
@@ -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<R, W>(input: &mut R, output: &mut W) -> io::Result<()>
Expand Down
5 changes: 5 additions & 0 deletions src/encode/rangecoder.rs
Original file line number Diff line number Diff line change
@@ -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>
Expand Down
3 changes: 3 additions & 0 deletions src/encode/util.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
6 changes: 4 additions & 2 deletions src/encode/xz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<R, W>(input: &mut R, output: &mut W) -> io::Result<()>
where
Expand Down
30 changes: 20 additions & 10 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -26,7 +36,7 @@ impl From<io::Error> 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),
Expand All @@ -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,
Expand All @@ -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!(
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions src/util/vec2d.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
6 changes: 5 additions & 1 deletion src/xz/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -19,7 +23,7 @@ impl StreamHeader {
/// Parse a Stream Header from a buffered reader.
pub(crate) fn parse<BR>(input: &mut BR) -> error::Result<Self>
where
BR: std::io::BufRead,
BR: io::BufRead,
{
if !util::read_tag(input, XZ_MAGIC)? {
return Err(error::Error::XzError(format!(
Expand Down
16 changes: 13 additions & 3 deletions src/xz/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -85,12 +88,19 @@ impl From<CheckMethod> 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);
Expand All @@ -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);

Expand Down

0 comments on commit adcf6af

Please sign in to comment.