• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

adierking / unplug / 15792190112

21 Jun 2025 04:38AM UTC coverage: 77.881%. First build
15792190112

push

github

adierking
hsd: Skeletons for DOBJ, JOBJ, POBJ, and display lists

0 of 82 new or added lines in 6 files covered. (0.0%)

20344 of 26122 relevant lines covered (77.88%)

1093681.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/unplug/src/hsd/pointer.rs
1
use super::{Error, Result};
2
use crate::common::ReadFrom;
3
use bumpalo::Bump;
4
use std::cell::{Ref, RefCell, RefMut};
5
use std::io::Read;
6
use std::num::NonZeroU32;
7

8
// This is some convoluted type system stuff to support the node graph (yay, Rust!). Basically, a
9
// Pointer can either be null or hold a reference to a node. When we read a pointer, we need to put
10
// the offset in a queue where we also know what type of node to read there. Each pointer also has
11
// to be given a reference to the default-initialized node, because we don't want to have to deal
12
// with needing a reference to the entire archive every time you dereference a pointer. As the
13
// reader goes through the queue, it will eventually initialize each node.
14
//
15
// The tricky thing here is that to support reading each node based on its type, we have to use a
16
// dyn-compatible trait. So we split the node traits between NodeBase (which is dyn-compatible) and
17
// the public Node trait (which is a marker that ensures a type conforms to the correct traits). We
18
// also have to take a dyn pointer to the reader, and we want to be able to read generic node types,
19
// so we also split that between ReadPointerBase (dyn-compatible) and ReadPointer. This makes it
20
// super easy to write the ReadFrom impls for each node.
21
//
22
// All of the memory is stored in a Bump arena that gets dropped when we're done with the archive,
23
// otherwise managing lifetimes here would become insanely difficult. Currently, we also just put
24
// each node into a RefCell to make borrow checking easier.
25

26
pub trait ReadPointerBase<'a>: Read {
27
    /// Get the arena belonging to the node graph.
28
    fn arena(&self) -> &'a Bump;
29

30
    /// Read a 32-bit offset and validate that it has a relocation pointing to it.
31
    fn read_offset(&mut self) -> Result<Option<NonZeroU32>>;
32

33
    /// Enqueue a node to be read at an offset.
34
    fn add_node(&mut self, offset: u32, node: &'a RefCell<dyn NodeBase<'a>>);
35
}
36

37
pub trait NodeBase<'a>: 'a {
38
    /// Read the node's data from a reader.
39
    fn read<'r>(&mut self, reader: &'r mut dyn ReadPointerBase<'a>) -> Result<()>;
40
}
41

42
// Auto-implement NodeBase for anything which supports ReadFrom<ReadPointer>.
43
impl<'a, T: 'a> NodeBase<'a> for T
44
where
45
    for<'x> T: ReadFrom<dyn ReadPointerBase<'a> + 'x, Error = Error> + 'a,
46
{
47
    fn read<'r>(&mut self, reader: &'r mut dyn ReadPointerBase<'a>) -> Result<()> {
×
48
        *self = T::read_from(reader)?;
×
49
        Ok(())
×
50
    }
×
51
}
52

53
/// Trait for a value that can be default-initialized using an arena.
54
pub trait DefaultIn<'a> {
55
    fn default_in(arena: &'a Bump) -> Self;
56
}
57

58
impl<'a, T: Default> DefaultIn<'a> for T {
59
    fn default_in(_arena: &'a Bump) -> Self {
×
60
        Self::default()
×
61
    }
×
62
}
63

64
/// Marker trait which ensures that a type conforms to all of the necessary traits for a node.
65
/// Technically this is not necessary and we could use just NodeBase everywhere, but this makes it
66
/// more explicit which types are actually nodes.
67
pub trait Node<'a>: NodeBase<'a> + DefaultIn<'a> {}
68

69
// () is a node with nothing in it. Useful for pointers to unimplemented structs.
70
impl Node<'_> for () {}
71
impl<'a, R: ReadPointer<'a> + ?Sized> ReadFrom<R> for () {
72
    type Error = Error;
73
    fn read_from(_reader: &mut R) -> Result<Self> {
×
74
        Ok(())
×
75
    }
×
76
}
77

78
/// Holds a nullable reference to a node in the graph.
79
#[derive(Copy, Clone, PartialEq, Eq)]
×
80
#[repr(transparent)]
81
pub struct Pointer<'a, T: Node<'a>>(Option<&'a RefCell<T>>);
82

83
impl<'a, T: Node<'a> + std::fmt::Debug> std::fmt::Debug for Pointer<'a, T> {
84
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
85
        match self.0 {
×
86
            Some(n) => f.debug_tuple("Pointer").field(&*n.borrow()).finish(),
×
87
            None => f.debug_tuple("Pointer").field(&self.0).finish(),
×
88
        }
89
    }
×
90
}
91

92
impl<'a, T: Node<'a>> Pointer<'a, T> {
93
    pub fn new() -> Self {
×
94
        Self(None)
×
95
    }
×
96

97
    pub fn alloc(arena: &'a Bump, node: T) -> Self {
×
98
        Self(Some(arena.alloc(RefCell::new(node))))
×
99
    }
×
100

101
    pub fn borrow(&self) -> Option<Ref<'a, T>> {
×
102
        self.0.map(|obj| obj.borrow())
×
103
    }
×
104

105
    pub fn borrow_mut(&self) -> Option<RefMut<'a, T>> {
×
106
        self.0.map(|obj| obj.borrow_mut())
×
107
    }
×
108

109
    pub fn is_null(&self) -> bool {
×
110
        self.0.is_none()
×
111
    }
×
112
}
113

114
impl<'a, T: Node<'a>> Default for Pointer<'a, T> {
115
    fn default() -> Self {
×
116
        Self::new()
×
117
    }
×
118
}
119

120
/// Extension trait for ReadPointerBase which provides the generic read_pointer().
121
pub trait ReadPointer<'a>: ReadPointerBase<'a> {
122
    /// Read a pointer from the stream with default-constructed node data.
123
    fn read_pointer<T: Node<'a>>(&mut self) -> Result<Pointer<'a, T>> {
×
NEW
124
        self.read_pointer_into(T::default_in(self.arena()))
×
NEW
125
    }
×
126

127
    /// Read a pointer from the stream. The node data will be read into the given node object.
128
    fn read_pointer_into<T: Node<'a>>(&mut self, node: T) -> Result<Pointer<'a, T>> {
129
        match self.read_offset()? {
×
NEW
130
            Some(offset) => self.read_node(offset.get(), node),
×
131
            None => Ok(Pointer(None)),
×
132
        }
133
    }
×
134

135
    /// Read a node from the stream at the given offset and return a pointer for it.
NEW
136
    fn read_node<T: Node<'a>>(&mut self, offset: u32, node: T) -> Result<Pointer<'a, T>> {
×
NEW
137
        let pointer = Pointer::alloc(self.arena(), node);
×
138
        self.add_node(offset, pointer.0.unwrap());
×
139
        Ok(pointer)
×
140
    }
×
141
}
142

143
impl<'a, R: ReadPointerBase<'a> + ?Sized> ReadPointer<'a> for R {}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc