1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::convert::TryInto;
use crate::blocks::*;
use crate::memory::*;
use crate::source::*;
const MAGIC_BYTES: &[u8] = b"SemDoc";
const VERSION: u16 = 0;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SemDoc<S: Source> {
pub block: Block<S>,
}
impl<S: Source> SemDoc<S> {
pub fn new(block: Block<S>) -> Self {
Self { block }
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![];
bytes.extend_from_slice(MAGIC_BYTES);
bytes.extend_from_slice(&VERSION.to_be_bytes());
bytes.extend_from_slice(
&self
.block
.to_molecule()
.to_atoms()
.iter()
.map(|atom| atom.to_bytes())
.flatten()
.collect::<Vec<_>>(),
);
bytes
}
pub fn into_pure(self) -> Result<SemDoc<Pure>, S::Error> {
Ok(SemDoc::<Pure> {
block: self.block.into_pure()?,
})
}
}
impl SemDoc<Memory> {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, SemDocError> {
if bytes.len() < 8 {
return Err(SemDocError::UnexpectedEnd);
}
if !bytes.starts_with(MAGIC_BYTES) {
return Err(SemDocError::MagicBytesInvalid);
}
if u16::from_be_bytes(bytes[6..8].try_into().unwrap())
!= VERSION
{
return Err(SemDocError::UnknownVersion);
}
let block = Block::from(&MemoryMolecule::from(&bytes[8..]));
Ok(SemDoc { block })
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum SemDocError {
UnexpectedEnd,
MagicBytesInvalid,
UnknownVersion,
}
#[cfg(test)]
mod test {
use super::*;
use quickcheck::*;
impl quickcheck::Arbitrary for SemDoc<Pure> {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Self {
block: Block::arbitrary(g),
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(self.block.shrink().map(|block| Self { block }))
}
}
quickcheck! {
fn prop(doc: SemDoc<Pure>) -> bool {
let reencoded = match SemDoc::from_bytes(&doc.to_bytes()).map(|doc| doc.without_source_errors()) {
Ok(Ok(doc)) => doc,
Ok(Err(_)) => return false,
Err(_) => return false,
};
reencoded == doc
}
}
}