WIP rust implementation

This commit is contained in:
Jacob Lifshay 2025-12-23 04:41:09 -08:00
parent 03bfdd3d99
commit 5247d69ebd
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
6 changed files with 1146 additions and 1 deletions

3
.gitignore vendored
View file

@ -3,4 +3,5 @@
*.egg-info
__pycache__
*.log
/powerisa-instructions.xml
/powerisa-instructions.xml
/target

75
Cargo.lock generated Normal file
View file

@ -0,0 +1,75 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "parse_powerisa_pdf"
version = "0.1.0"
dependencies = [
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "parse_powerisa_pdf"
version = "0.1.0"
edition = "2024"
license = "LGPL-3.0-or-later"
[dependencies]
serde = { version = "1.0.228", features = ["derive"] }

2
src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
mod pdf;
mod util;

956
src/pdf.rs Normal file
View file

@ -0,0 +1,956 @@
use crate::util::ArcOrRef;
use serde::{de, forward_to_deserialize_any};
use std::{
cell::RefCell,
collections::BTreeMap,
convert::Infallible,
fmt::{self, Write},
iter::FusedIterator,
marker::PhantomData,
num::NonZero,
sync::{Arc, Weak},
};
#[derive(Debug)]
pub(crate) enum PdfParseError {
InvalidFieldKind {
containing_ty: &'static str,
field: &'static str,
expected_kind: &'static str,
kind: &'static str,
},
Custom(String),
}
impl From<Infallible> for PdfParseError {
fn from(value: Infallible) -> Self {
match value {}
}
}
impl fmt::Display for PdfParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
&PdfParseError::InvalidFieldKind {
containing_ty,
field,
expected_kind,
kind,
} => write!(
f,
"invalid field kind: {containing_ty}.{field}: expected {expected_kind}, got {kind}"
),
PdfParseError::Custom(msg) => f.write_str(msg),
}
}
}
impl std::error::Error for PdfParseError {}
impl de::Error for PdfParseError {
fn custom<T>(msg: T) -> Self
where
T: fmt::Display,
{
PdfParseError::Custom(msg.to_string())
}
}
impl<'de> de::IntoDeserializer<'de, PdfParseError> for PdfName {
type Deserializer = PdfObject;
fn into_deserializer(self) -> Self::Deserializer {
self.into()
}
}
impl<'de> de::IntoDeserializer<'de, PdfParseError> for PdfObject {
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
impl PdfObject {
const SERDE_FIELD_NAME: &str = "__PdfObject__look_in_thread_local";
const SERDE_NAME_AND_FIELDS: (&str, &[&str]) = ("PdfObject", &[Self::SERDE_FIELD_NAME]);
fn with_thread_local<R>(f: impl FnOnce(&RefCell<Option<Self>>) -> R) -> R {
thread_local! {
static CURRENT_OBJECT: RefCell<Option<PdfObject>> = const { RefCell::new(None) };
}
CURRENT_OBJECT.with(f)
}
fn set_thread_local_scoped<R>(self, f: impl FnOnce() -> R) -> R {
Self::with_thread_local(|current_object| {
struct PutBackOnDrop<'a> {
current_object: &'a RefCell<Option<PdfObject>>,
old_object: Option<PdfObject>,
}
impl Drop for PutBackOnDrop<'_> {
fn drop(&mut self) {
self.current_object.replace(self.old_object.take());
}
}
let put_back_on_drop = PutBackOnDrop {
current_object,
old_object: current_object.replace(Some(self)),
};
let retval = f();
drop(put_back_on_drop);
retval
})
}
fn take_thread_local() -> Option<PdfObject> {
Self::with_thread_local(RefCell::take)
}
}
trait PdfObjectDeserializeHelperTrait: Sized {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result;
fn from_pdf_object<E: de::Error>(
value: PdfObject,
expected: &dyn de::Expected,
) -> Result<Self, E>;
}
struct PdfObjectDeserializeHelper<T: PdfObjectDeserializeHelperTrait>(T);
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Deserialize<'de>
for PdfObjectDeserializeHelper<T>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct PdfObjectVisitor<T>(PhantomData<T>);
fn expected_pdf_object<E: de::Error, T: PdfObjectDeserializeHelperTrait>() -> E {
de::Error::invalid_type(de::Unexpected::Map, &PdfObjectVisitor::<T>(PhantomData))
}
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Visitor<'de> for PdfObjectVisitor<T> {
type Value = PdfObject;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
T::expecting(formatter)
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
struct Field<T>(PhantomData<T>);
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Deserialize<'de> for Field<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
deserializer.deserialize_identifier(Field(PhantomData))
}
}
impl<'de, T: PdfObjectDeserializeHelperTrait> de::Visitor<'de> for Field<T> {
type Value = Self;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
T::expecting(formatter)
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
if v == PdfObject::SERDE_FIELD_NAME {
Ok(self)
} else {
Err(expected_pdf_object::<E, T>())
}
}
}
let (Field::<T>(PhantomData), ()) = map
.next_entry()?
.ok_or_else(expected_pdf_object::<A::Error, T>)?;
let None = map.next_entry::<Field<T>, ()>()? else {
return Err(expected_pdf_object::<_, T>());
};
PdfObject::take_thread_local().ok_or_else(expected_pdf_object::<_, T>)
}
}
let (name, fields) = PdfObject::SERDE_NAME_AND_FIELDS;
let pdf_object =
deserializer.deserialize_struct(name, fields, PdfObjectVisitor::<T>(PhantomData))?;
T::from_pdf_object::<D::Error>(pdf_object, &PdfObjectVisitor::<T>(PhantomData)).map(Self)
}
}
macro_rules! forward_deserialize_to_pdf_object_helper {
($ty:ty) => {
impl<'de> de::Deserialize<'de> for $ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let PdfObjectDeserializeHelper(v) = de::Deserialize::deserialize(deserializer)?;
Ok(v)
}
}
};
}
forward_deserialize_to_pdf_object_helper!(PdfObject);
impl PdfObjectDeserializeHelperTrait for PdfObject {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PdfObject")
}
fn from_pdf_object<E: de::Error>(
value: PdfObject,
_expected: &dyn de::Expected,
) -> Result<Self, E> {
Ok(value)
}
}
forward_deserialize_to_pdf_object_helper!(PdfObjectIndirect);
impl PdfObjectDeserializeHelperTrait for PdfObjectIndirect {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PdfObjectIndirect")
}
fn from_pdf_object<E: de::Error>(
value: PdfObject,
expected: &dyn de::Expected,
) -> Result<Self, E> {
match value {
PdfObject::Indirect(v) => Ok(v),
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
}
}
}
forward_deserialize_to_pdf_object_helper!(PdfString);
impl PdfObjectDeserializeHelperTrait for PdfString {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PdfString")
}
fn from_pdf_object<E: de::Error>(
value: PdfObject,
expected: &dyn de::Expected,
) -> Result<Self, E> {
match value {
PdfObject::String(v) => Ok(v),
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
}
}
}
forward_deserialize_to_pdf_object_helper!(PdfName);
impl PdfObjectDeserializeHelperTrait for PdfName {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PdfName")
}
fn from_pdf_object<E: de::Error>(
value: PdfObject,
expected: &dyn de::Expected,
) -> Result<Self, E> {
match value {
PdfObject::Name(v) => Ok(v),
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
}
}
}
forward_deserialize_to_pdf_object_helper!(PdfArray);
impl PdfObjectDeserializeHelperTrait for PdfArray {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PdfArray")
}
fn from_pdf_object<E: de::Error>(
value: PdfObject,
expected: &dyn de::Expected,
) -> Result<Self, E> {
match value {
PdfObject::Array(v) => Ok(v),
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
}
}
}
forward_deserialize_to_pdf_object_helper!(PdfDictionary);
impl PdfObjectDeserializeHelperTrait for PdfDictionary {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PdfDictionary")
}
fn from_pdf_object<E: de::Error>(
value: PdfObject,
expected: &dyn de::Expected,
) -> Result<Self, E> {
match value {
PdfObject::Dictionary(v) => Ok(v),
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
}
}
}
forward_deserialize_to_pdf_object_helper!(PdfStream);
impl PdfObjectDeserializeHelperTrait for PdfStream {
fn expecting(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PdfStream")
}
fn from_pdf_object<E: de::Error>(
value: PdfObject,
expected: &dyn de::Expected,
) -> Result<Self, E> {
match value {
PdfObject::Stream(v) => Ok(v),
_ => Err(E::invalid_type(value.as_unexpected(), expected)),
}
}
}
impl<'de> de::Deserializer<'de> for PdfObject {
type Error = PdfParseError;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match PdfObjectDirect::from(self) {
PdfObjectDirect::Boolean(v) => visitor.visit_bool(v),
PdfObjectDirect::Integer(v) => visitor.visit_i32(v),
PdfObjectDirect::Real(v) => visitor.visit_f32(v),
v @ (PdfObjectDirect::String(_) | PdfObjectDirect::Stream(_)) => {
Err(de::Error::invalid_type(v.as_unexpected(), &visitor))
}
PdfObjectDirect::Name(v) => {
if let Ok(v) = str::from_utf8(v.as_bytes()) {
visitor.visit_str(v)
} else {
Err(de::Error::invalid_type(
PdfObject::from(v).as_unexpected(),
&visitor,
))
}
}
PdfObjectDirect::Array(v) => {
visitor.visit_seq(de::value::SeqDeserializer::new(v.iter().cloned()))
}
PdfObjectDirect::Dictionary(v) => {
visitor.visit_map(de::value::MapDeserializer::new(v.into_iter()))
}
PdfObjectDirect::Null(PdfNull {}) => visitor.visit_unit(),
}
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match (name, fields) {
PdfObject::SERDE_NAME_AND_FIELDS => self.set_thread_local_scoped(|| {
visitor.visit_map(de::value::MapDeserializer::new(std::iter::once((
PdfObject::SERDE_FIELD_NAME,
(),
))))
}),
_ => self.deserialize_any(visitor),
}
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
let is_null = match self {
Self::Indirect(ref v) => !v.exists(),
Self::Null(_) => true,
_ => false,
};
if is_null {
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf unit unit_struct seq tuple
tuple_struct map enum identifier ignored_any
}
}
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct PdfString {
bytes: ArcOrRef<'static, [u8]>,
}
impl std::fmt::Debug for PdfString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PdfString")
.field("bytes", &&*self.bytes)
.finish()
}
}
impl PdfString {
pub(crate) fn new(bytes: ArcOrRef<'static, [u8]>) -> Self {
Self { bytes }
}
pub(crate) fn bytes(&self) -> &ArcOrRef<'static, [u8]> {
&self.bytes
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct PdfName {
bytes: ArcOrRef<'static, [u8]>,
}
impl PdfName {
pub(crate) fn try_new(bytes: impl Into<ArcOrRef<'static, [u8]>>) -> Option<Self> {
let bytes = bytes.into();
if bytes.contains(&0) {
None
} else {
Some(Self { bytes })
}
}
#[track_caller]
pub(crate) const fn new_static(bytes: &'static [u8]) -> Self {
let mut i = 0;
while i < bytes.len() {
if bytes[i] == 0 {
panic!("shouldn't contain any nul bytes");
}
i += 1;
}
Self {
bytes: ArcOrRef::Ref(bytes),
}
}
#[track_caller]
pub(crate) fn new(bytes: ArcOrRef<'static, [u8]>) -> Self {
Self::try_new(bytes).expect("shouldn't contain any nul bytes")
}
pub(crate) fn as_bytes(&self) -> &ArcOrRef<'static, [u8]> {
&self.bytes
}
}
macro_rules! make_pdf_names {
(
$vis:vis mod $pdf_names:ident {
$($ident:ident;)*
}
) => {
$vis mod $pdf_names {
$(#[allow(non_upper_case_globals)]
$vis const $ident: $crate::pdf::PdfName = $crate::pdf::PdfName::new_static(stringify!($ident).as_bytes());)*
}
};
}
make_pdf_names! {
pub(crate) mod pdf_names {
DecodeParms;
DL;
F;
FDecodeParms;
FFilter;
Filter;
Length;
}
}
impl fmt::Debug for PdfName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PdfName({self})")
}
}
impl fmt::Display for PdfName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("/")?;
for &b in self.bytes.iter() {
match b {
0x21..=0x7E if b != b'#' => f.write_char(b.into())?,
_ => write!(f, "#{b:02X}")?,
}
}
Ok(())
}
}
macro_rules! make_pdf_object {
(
$(
#[from = $($from:ident)?, as_unexpected = |$as_unexpected_arg:pat_param| $as_unexpected_expr:expr]
$Variant:ident($ty:ty),
)+
) => {
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum PdfObjectNonNull {
$($Variant($ty),)*
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum PdfObjectDirect {
$($Variant($ty),)*
Null(PdfNull),
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum PdfObject {
$($Variant($ty),)*
Null(PdfNull),
Indirect(PdfObjectIndirect),
}
$($(
impl From<$ty> for PdfObjectNonNull {
fn $from(value: $ty) -> Self {
Self::$Variant(value)
}
}
impl From<$ty> for PdfObjectDirect {
fn $from(value: $ty) -> Self {
Self::$Variant(value)
}
}
impl From<$ty> for PdfObject {
fn $from(value: $ty) -> Self {
Self::$Variant(value)
}
}
impl From<Option<$ty>> for PdfObjectDirect {
fn $from(value: Option<$ty>) -> Self {
match value {
Some(value) => Self::$Variant(value),
None => Self::Null(PdfNull),
}
}
}
impl From<Option<$ty>> for PdfObject {
fn $from(value: Option<$ty>) -> Self {
match value {
Some(value) => Self::$Variant(value),
None => Self::Null(PdfNull),
}
}
}
)?)*
impl From<PdfObjectNonNull> for PdfObjectDirect {
fn from(value: PdfObjectNonNull) -> Self {
match value {
$(PdfObjectNonNull::$Variant(v) => Self::$Variant(v),)*
}
}
}
impl From<PdfObjectNonNull> for PdfObject {
fn from(value: PdfObjectNonNull) -> Self {
match value {
$(PdfObjectNonNull::$Variant(v) => Self::$Variant(v),)*
}
}
}
impl From<PdfObjectDirect> for PdfObject {
fn from(value: PdfObjectDirect) -> Self {
match value {
$(PdfObjectDirect::$Variant(v) => Self::$Variant(v),)*
PdfObjectDirect::Null(v) => Self::Null(v),
}
}
}
impl From<PdfObject> for PdfObjectDirect {
fn from(value: PdfObject) -> Self {
match value {
$(PdfObject::$Variant(v) => Self::$Variant(v),)*
PdfObject::Null(v) => Self::Null(v),
PdfObject::Indirect(v) => v.into(),
}
}
}
impl PdfObjectNonNull {
fn as_unexpected(&self) -> de::Unexpected<'static> {
match *self {
$(PdfObjectNonNull::$Variant($as_unexpected_arg) => $as_unexpected_expr,)*
}
}
}
impl PdfObjectDirect {
fn as_unexpected(&self) -> de::Unexpected<'static> {
match *self {
$(PdfObjectDirect::$Variant($as_unexpected_arg) => $as_unexpected_expr,)*
PdfObjectDirect::Null(_) => de::Unexpected::Option,
}
}
}
impl PdfObject {
fn as_unexpected(&self) -> de::Unexpected<'static> {
match *self {
$(PdfObject::$Variant($as_unexpected_arg) => $as_unexpected_expr,)*
PdfObject::Null(_) => de::Unexpected::Option,
PdfObject::Indirect(ref v) => v.get().as_unexpected(),
}
}
}
const _: () = {
fn _assert_impls_deserialize<T: de::DeserializeOwned>() {}
$(let _ = _assert_impls_deserialize::<$ty>;)*
};
};
}
make_pdf_object! {
#[from = from, as_unexpected = |v| de::Unexpected::Bool(v)]
Boolean(bool),
#[from = from, as_unexpected = |v| de::Unexpected::Signed(v.into())]
Integer(i32),
#[from = from, as_unexpected = |v| de::Unexpected::Float(v.into())]
Real(f32),
#[from = from, as_unexpected = |_| de::Unexpected::Other("PdfString")]
String(PdfString),
#[from = from, as_unexpected = |_| de::Unexpected::Other("PdfName")]
Name(PdfName),
#[from = from, as_unexpected = |_| de::Unexpected::Seq]
Array(PdfArray),
#[from = from, as_unexpected = |_| de::Unexpected::Map]
Dictionary(PdfDictionary),
#[from = from, as_unexpected = |_| de::Unexpected::Other("PdfStream")]
Stream(PdfStream),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PdfNull;
impl From<PdfNull> for PdfObjectDirect {
fn from(v: PdfNull) -> Self {
Self::Null(v)
}
}
impl From<PdfNull> for PdfObject {
fn from(v: PdfNull) -> Self {
Self::Null(v)
}
}
impl From<PdfObjectIndirect> for PdfObject {
fn from(v: PdfObjectIndirect) -> Self {
Self::Indirect(v)
}
}
#[derive(Clone, Debug)]
pub(crate) struct PdfObjectIndirect {
xref_table: Weak<PdfObjects>,
object_number: NonZero<u32>,
generation_number: u16,
}
impl PartialEq for PdfObjectIndirect {
fn eq(&self, other: &Self) -> bool {
let Self {
xref_table,
object_number,
generation_number,
} = self;
xref_table.ptr_eq(&other.xref_table)
&& *object_number == other.object_number
&& *generation_number == other.generation_number
}
}
impl PdfObjectIndirect {
pub fn exists(&self) -> bool {
todo!()
}
pub fn get(&self) -> PdfObjectDirect {
todo!()
}
}
impl From<PdfObjectIndirect> for PdfObjectDirect {
fn from(value: PdfObjectIndirect) -> Self {
value.get()
}
}
#[derive(Clone, PartialEq)]
pub(crate) struct PdfDictionary {
fields: Arc<BTreeMap<PdfName, PdfObject>>,
}
impl PdfDictionary {
pub(crate) fn fields(&self) -> &Arc<BTreeMap<PdfName, PdfObject>> {
&self.fields
}
pub(crate) fn into_fields(self) -> Arc<BTreeMap<PdfName, PdfObject>> {
self.fields
}
pub(crate) fn iter(&self) -> std::collections::btree_map::Iter<'_, PdfName, PdfObject> {
self.fields.iter()
}
pub(crate) fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where
PdfName: std::borrow::Borrow<Q> + Ord,
Q: Ord,
{
self.fields.contains_key(key)
}
pub(crate) fn get<Q: ?Sized>(&self, key: &Q) -> Option<&PdfObject>
where
PdfName: std::borrow::Borrow<Q> + Ord,
Q: Ord,
{
self.fields.get(key)
}
}
impl FromIterator<(PdfName, PdfObject)> for PdfDictionary {
fn from_iter<T: IntoIterator<Item = (PdfName, PdfObject)>>(iter: T) -> Self {
Self {
fields: Arc::new(BTreeMap::from_iter(
iter.into_iter()
.filter(|(_name, value)| !matches!(value, PdfObject::Null(_))),
)),
}
}
}
impl IntoIterator for PdfDictionary {
type Item = (PdfName, PdfObject);
type IntoIter = std::collections::btree_map::IntoIter<PdfName, PdfObject>;
fn into_iter(self) -> Self::IntoIter {
Arc::unwrap_or_clone(self.fields).into_iter()
}
}
impl<'a> IntoIterator for &'a PdfDictionary {
type Item = (&'a PdfName, &'a PdfObject);
type IntoIter = std::collections::btree_map::Iter<'a, PdfName, PdfObject>;
fn into_iter(self) -> Self::IntoIter {
self.fields.iter()
}
}
impl fmt::Debug for PdfDictionary {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self).finish()
}
}
#[derive(Clone, Default, PartialEq)]
pub(crate) struct PdfArray {
elements: Arc<[PdfObject]>,
}
impl PdfArray {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn elements(&self) -> &Arc<[PdfObject]> {
&self.elements
}
pub(crate) fn into_elements(self) -> Arc<[PdfObject]> {
self.elements
}
pub(crate) fn iter(&self) -> std::slice::Iter<'_, PdfObject> {
self.elements.iter()
}
}
impl FromIterator<PdfObject> for PdfArray {
fn from_iter<T: IntoIterator<Item = PdfObject>>(iter: T) -> Self {
Self {
elements: Arc::from_iter(iter),
}
}
}
#[derive(Clone)]
pub(crate) struct PdfArrayIntoIter {
indexes: std::ops::Range<usize>,
elements: Arc<[PdfObject]>,
}
impl Iterator for PdfArrayIntoIter {
type Item = PdfObject;
fn next(&mut self) -> Option<Self::Item> {
self.indexes.next().map(|i| self.elements[i].clone())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.indexes.nth(n).map(|i| self.elements[i].clone())
}
fn last(self) -> Option<Self::Item> {
self.indexes.last().map(|i| self.elements[i].clone())
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.indexes
.fold(init, |init, i| f(init, self.elements[i].clone()))
}
}
impl FusedIterator for PdfArrayIntoIter {}
impl DoubleEndedIterator for PdfArrayIntoIter {
fn next_back(&mut self) -> Option<Self::Item> {
self.indexes.next_back().map(|i| self.elements[i].clone())
}
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.indexes.nth_back(n).map(|i| self.elements[i].clone())
}
fn rfold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
self.indexes
.rfold(init, |init, i| f(init, self.elements[i].clone()))
}
}
impl ExactSizeIterator for PdfArrayIntoIter {}
impl IntoIterator for PdfArray {
type Item = PdfObject;
type IntoIter = PdfArrayIntoIter;
fn into_iter(self) -> Self::IntoIter {
PdfArrayIntoIter {
indexes: 0..self.elements.len(),
elements: self.elements,
}
}
}
impl<'a> IntoIterator for &'a PdfArray {
type Item = &'a PdfObject;
type IntoIter = std::slice::Iter<'a, PdfObject>;
fn into_iter(self) -> Self::IntoIter {
self.elements.iter()
}
}
impl fmt::Debug for PdfArray {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.elements.fmt(f)
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct PdfStream {
dictionary: PdfDictionary,
data: Arc<u8>,
}
pub(crate) enum PdfBody {}
pub(crate) struct PdfObjects {}
pub(crate) struct PdfXRefTable {}
pub(crate) struct Pdf {
pub(crate) header: PdfHeader,
pub(crate) body: PdfBody,
}
pub(crate) struct PdfHeader {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_dict() -> Result<(), PdfParseError> {
#[derive(serde::Deserialize, Debug, PartialEq)]
struct TestStruct {
a: i32,
c: i32,
b: i32,
#[serde(flatten)]
others: PdfDictionary,
}
let v: TestStruct =
de::Deserialize::deserialize(PdfObject::from(PdfDictionary::from_iter([
(PdfName::new_static(b"a"), 1.into()),
(PdfName::new_static(b"c"), 7.into()),
(PdfName::new_static(b"b"), 5.into()),
(PdfName::new_static(b"d"), false.into()),
(PdfName::new_static(b"e"), PdfNull.into()),
(
PdfName::new_static(b"f"),
PdfString::new(ArcOrRef::Ref(b"test")).into(),
),
])))?;
let expected = TestStruct {
a: 1,
c: 7,
b: 5,
others: PdfDictionary::from_iter([
(PdfName::new_static(b"d"), false.into()),
(
PdfName::new_static(b"f"),
PdfString::new(ArcOrRef::Ref(b"test")).into(),
),
]),
};
assert_eq!(v, expected);
Ok(())
}
}

102
src/util.rs Normal file
View file

@ -0,0 +1,102 @@
use std::{
borrow::Borrow,
fmt,
hash::{Hash, Hasher},
sync::Arc,
};
pub(crate) enum ArcOrRef<'a, T: ?Sized> {
Arc(Arc<T>),
Ref(&'a T),
}
impl<'a, T: ?Sized> AsRef<T> for ArcOrRef<'a, T> {
fn as_ref(&self) -> &T {
self
}
}
impl<'a, T: ?Sized> Borrow<T> for ArcOrRef<'a, T> {
fn borrow(&self) -> &T {
self
}
}
impl<'a, T: ?Sized> From<Arc<T>> for ArcOrRef<'a, T> {
fn from(value: Arc<T>) -> Self {
Self::Arc(value)
}
}
impl<'a, T: ?Sized> From<&'a T> for ArcOrRef<'a, T> {
fn from(value: &'a T) -> Self {
Self::Ref(value)
}
}
impl<'a, T: ?Sized> Default for ArcOrRef<'a, T>
where
&'a T: Default,
{
fn default() -> Self {
Self::Ref(Default::default())
}
}
impl<T: ?Sized> Clone for ArcOrRef<'_, T> {
fn clone(&self) -> Self {
match self {
Self::Arc(v) => Self::Arc(v.clone()),
Self::Ref(v) => Self::Ref(v),
}
}
}
impl<T: ?Sized + Hash> Hash for ArcOrRef<'_, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
T::hash(self, state)
}
}
impl<'a, 'b, T: ?Sized + PartialEq<U>, U: ?Sized> PartialEq<ArcOrRef<'b, U>> for ArcOrRef<'a, T> {
fn eq(&self, other: &ArcOrRef<'b, U>) -> bool {
T::eq(self, other)
}
}
impl<T: ?Sized + Eq> Eq for ArcOrRef<'_, T> {}
impl<'a, 'b, T: ?Sized + PartialOrd<U>, U: ?Sized> PartialOrd<ArcOrRef<'b, U>> for ArcOrRef<'a, T> {
fn partial_cmp(&self, other: &ArcOrRef<'b, U>) -> Option<std::cmp::Ordering> {
T::partial_cmp(self, other)
}
}
impl<T: ?Sized + Ord> Ord for ArcOrRef<'_, T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
T::cmp(self, other)
}
}
impl<T: ?Sized> std::ops::Deref for ArcOrRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
ArcOrRef::Arc(v) => v,
ArcOrRef::Ref(v) => v,
}
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for ArcOrRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::fmt(self, f)
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for ArcOrRef<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::fmt(self, f)
}
}