Skip to content

Commit

Permalink
Implements Package::unpack()
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Nov 12, 2023
1 parent f278c14 commit cd81c81
Show file tree
Hide file tree
Showing 9 changed files with 827 additions and 46 deletions.
23 changes: 19 additions & 4 deletions stage0/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,24 @@ extern "C" {
) -> *mut LlvmCall;
pub fn llvm_builder_ret_void(ib: *mut LlvmBuilder) -> *mut LlvmReturn;
pub fn llvm_builder_ret(ib: *mut LlvmBuilder, v: *mut LlvmValue) -> *mut LlvmReturn;
pub fn ZSTD_createCStream() -> *mut ZstdContex;
pub fn ZSTD_freeCStream(zcs: *mut ZstdContex) -> usize;
pub fn ZSTD_createCStream() -> *mut ZSTD_CCtx;
pub fn ZSTD_freeCStream(zcs: *mut ZSTD_CCtx) -> usize;
pub fn ZSTD_compressStream2(
cctx: *mut ZstdContex,
cctx: *mut ZSTD_CCtx,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
endOp: ZSTD_EndDirective,
) -> usize;
pub fn ZSTD_CStreamInSize() -> usize;
pub fn ZSTD_CStreamOutSize() -> usize;
pub fn ZSTD_createDStream() -> *mut ZSTD_DCtx;
pub fn ZSTD_freeDStream(zds: *mut ZSTD_DCtx) -> usize;
pub fn ZSTD_decompressStream(
zds: *mut ZSTD_DCtx,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
) -> usize;
pub fn ZSTD_DStreamInSize() -> usize;
pub fn ZSTD_isError(code: usize) -> u32;
pub fn ZSTD_getErrorName(code: usize) -> *const c_char;
}
Expand All @@ -93,7 +101,14 @@ pub struct LlvmBlock(());
pub struct LlvmBuilder(());
pub struct LlvmCall(());
pub struct LlvmReturn(());
pub struct ZstdContex(());

#[repr(C)]
#[allow(non_camel_case_types)]
pub struct ZSTD_CCtx(());

#[repr(C)]
#[allow(non_camel_case_types)]
pub struct ZSTD_DCtx(());

#[repr(C)]
#[allow(non_camel_case_types)]
Expand Down
37 changes: 35 additions & 2 deletions stage0/src/pkg/dep.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use super::{Package, PackageName, PackageOpenError, PackageUnpackError, PackageVersion};
use super::{
Package, PackageName, PackageNameError, PackageOpenError, PackageUnpackError, PackageVersion,
};
use serde::Serialize;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};
Expand Down Expand Up @@ -84,7 +87,7 @@ impl DependencyResolver {
}

/// A package dependency.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct Dependency {
name: PackageName,
version: PackageVersion,
Expand All @@ -95,6 +98,20 @@ impl Dependency {
Self { name, version }
}

pub fn deserialize<R: Read>(mut r: R) -> Result<Self, DependencyError> {
// Read name.
let mut data = [0; 32];
r.read_exact(&mut data)?;
let name = PackageName::from_bin(&data).map_err(|e| DependencyError::InvalidName(e))?;

// Read version.
let mut data = [0; 8];
r.read_exact(&mut data)?;
let version = PackageVersion::from_bin(u64::from_be_bytes(data));

Ok(Self { name, version })
}

pub fn serialize<W: Write>(&self, mut w: W) -> Result<(), std::io::Error> {
w.write_all(&self.name.to_bin())?;
w.write_all(&self.version.to_bin().to_be_bytes())
Expand Down Expand Up @@ -122,3 +139,19 @@ pub enum DependencyResolveError {
#[error("cannot unpack the package")]
UnpackPackageFailed(#[source] PackageUnpackError),
}

/// Represents an error when [`Dependency`] is failed to construct.
#[derive(Debug, Error)]
pub enum DependencyError {
#[error("cannot read data")]
ReadDataFailed(#[source] std::io::Error),

#[error("invalid package name")]
InvalidName(#[source] PackageNameError),
}

impl From<std::io::Error> for DependencyError {
fn from(value: std::io::Error) -> Self {
Self::ReadDataFailed(value)
}
}
117 changes: 114 additions & 3 deletions stage0/src/pkg/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use super::TypeDeclaration;
use super::{TypeDeclaration, TypeDeserializeError};
use std::collections::HashSet;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use thiserror::Error;

/// A Nitro library.
///
Expand Down Expand Up @@ -73,10 +74,120 @@ impl Library {
}
}
}

pub(super) fn unpack<R, B, T>(mut data: R, bin: B, types: T) -> Result<(), LibraryUnpackError>
where
R: Read,
B: AsRef<Path>,
T: AsRef<Path>,
{
// Check magic.
let mut magic = [0u8; 4];

data.read_exact(&mut magic)?;

if magic.ne(b"\x7FNLM") {
return Err(LibraryUnpackError::NotNitroLibrary);
}

// Iterate over the entries.
let mut bin = File::create(bin).map_err(LibraryUnpackError::WriteBinaryFailed)?;
let mut types = File::create(types).map_err(LibraryUnpackError::WriteTypeFailed)?;
let mut sys = None;

loop {
// Read entry type.
let mut ty = 0;

data.read_exact(std::slice::from_mut(&mut ty))?;

// Process the entry.
match ty {
Self::ENTRY_END => break,
Self::ENTRY_TYPES => {
// Read types count.
let mut buf = [0u8; 4];
data.read_exact(&mut buf)?;
let ntype: usize = u32::from_be_bytes(buf).try_into().unwrap();

// Read types.
for i in 0..ntype {
let ty = TypeDeclaration::deserialize(&mut data)
.map_err(|e| LibraryUnpackError::ReadTypeFailed(i, e))?;
ty.serialize(&mut types)
.map_err(LibraryUnpackError::WriteTypeFailed)?;
}
}
Self::ENTRY_SYSTEM => {
// Read name length.
let mut buf = [0u8; 2];
data.read_exact(&mut buf)?;
let len: usize = u16::from_be_bytes(buf).into();

// Read name.
let mut buf = vec![0u8; len];
data.read_exact(&mut buf)?;

match String::from_utf8(buf) {
Ok(v) => sys = Some(v),
Err(_) => return Err(LibraryUnpackError::InvalidSystemName),
}
}
v => return Err(LibraryUnpackError::UnknownEntry(v)),
}
}

// Write binary.
match sys {
Some(name) => {
bin.write_all(b"\x7FNLS")
.map_err(LibraryUnpackError::WriteBinaryFailed)?;
bin.write_all(name.as_bytes())
.map_err(LibraryUnpackError::WriteBinaryFailed)?;
}
None => {
std::io::copy(&mut data, &mut bin)
.map_err(LibraryUnpackError::WriteBinaryFailed)?;
}
}

Ok(())
}
}

/// A library's binary.
pub enum LibraryBinary {
Bundle(PathBuf),
System(String),
}

/// Represents an error when [`Library`] is failed to unpack from a serialized data.
#[derive(Debug, Error)]
pub enum LibraryUnpackError {
#[error("cannot read data")]
ReadDataFailed(#[source] std::io::Error),

#[error("the data is not a Nitro library")]
NotNitroLibrary,

#[error("cannot write binary")]
WriteBinaryFailed(#[source] std::io::Error),

#[error("cannot write type")]
WriteTypeFailed(#[source] std::io::Error),

#[error("cannot read type #{0}")]
ReadTypeFailed(usize, #[source] TypeDeserializeError),

#[error("invalid name for system library")]
InvalidSystemName,

#[error("unknown entry {0}")]
UnknownEntry(u8),
}

impl From<std::io::Error> for LibraryUnpackError {
fn from(value: std::io::Error) -> Self {
Self::ReadDataFailed(value)
}
}
28 changes: 25 additions & 3 deletions stage0/src/pkg/meta.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use serde::de::{Error, Unexpected, Visitor};
use serde::{Deserialize, Deserializer};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Display, Formatter};
use std::num::ParseIntError;
use std::str::FromStr;
use thiserror::Error;

/// Metadata for a Nitro package.
#[derive(Deserialize, Serialize)]
pub struct PackageMeta {
name: PackageName,
version: PackageVersion,
Expand All @@ -29,10 +30,18 @@ impl PackageMeta {
///
/// A package name must start with a lower case ASCII and followed by zero of more 0-9 and a-z (only
/// lower case). The maximum length is 32 characters.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
pub struct PackageName(String);

impl PackageName {
pub fn from_bin(bin: &[u8; 32]) -> Result<Self, PackageNameError> {
let len = bin.iter().position(|&b| b == 0).unwrap_or(bin.len());

std::str::from_utf8(&bin[..len])
.map_err(|_| PackageNameError::NotDigitOrLowerCase)?
.parse()
}

pub fn as_str(&self) -> &str {
&self.0
}
Expand Down Expand Up @@ -110,7 +119,11 @@ pub struct PackageVersion {
}

impl PackageVersion {
pub fn new(major: u16, minor: u16, patch: u16) -> Self {
pub fn from_bin(bin: u64) -> Self {
let major: u16 = (bin >> 32) as u16;
let minor: u16 = (bin >> 16) as u16;
let patch: u16 = bin as u16;

Self {
major,
minor,
Expand Down Expand Up @@ -140,6 +153,15 @@ impl<'a> Deserialize<'a> for PackageVersion {
}
}

impl Serialize for PackageVersion {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}

impl FromStr for PackageVersion {
type Err = PackageVersionError;

Expand Down
Loading

0 comments on commit cd81c81

Please sign in to comment.