1use super::error::{BinaryError, BinaryResult};
29use super::packet::{
30 external_capacity, ArityRange, DecodeLimits, LinksPacket, Reference, FIRST_LINK_ADDRESS,
31 IDENTIFIED, LIST, NULL, NUMBER, ONE, STRING,
32};
33use crate::LiNo;
34use std::cell::Cell;
35use std::collections::HashMap;
36
37pub type LinoDocument = Vec<LiNo<String>>;
39
40#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
45pub struct BinaryLinoOptions {
46 pub external_references: bool,
49 pub arity: ArityRange,
53 pub packed_widths: bool,
56}
57
58impl BinaryLinoOptions {
59 pub fn with_external_references(mut self, enabled: bool) -> Self {
61 self.external_references = enabled;
62 self
63 }
64
65 pub fn with_arity(mut self, arity: ArityRange) -> Self {
67 self.arity = arity;
68 self
69 }
70
71 pub fn with_packed_widths(mut self, enabled: bool) -> Self {
73 self.packed_widths = enabled;
74 self
75 }
76
77 pub fn of_packet(packet: &LinksPacket) -> Self {
80 let (shortest, longest) = packet
81 .links()
82 .map(|(_, link)| link.len() as u64)
83 .fold((2, 2), |(shortest, longest), length| {
84 (shortest.min(length), longest.max(length))
85 });
86 let mut widths = packet.sections.iter().map(|section| section.width);
87 let first_width = widths.next();
88 Self {
89 external_references: packet.external_references,
90 arity: ArityRange::between(shortest, longest),
91 packed_widths: widths.any(|width| Some(width) != first_width),
92 }
93 }
94}
95
96pub fn encode_document(
98 document: &[LiNo<String>],
99 options: BinaryLinoOptions,
100) -> BinaryResult<LinksPacket> {
101 encode_document_with_limits(document, options, &DecodeLimits::default())
102}
103
104pub fn encode_document_with_limits(
106 document: &[LiNo<String>],
107 options: BinaryLinoOptions,
108 limits: &DecodeLimits,
109) -> BinaryResult<LinksPacket> {
110 options.arity.validate().map_err(BinaryError::Unencodable)?;
111 if !options.arity.contains(2) {
112 return Err(BinaryError::Unencodable(format!(
113 "arity {} does not include doublets (2)",
114 options.arity
115 )));
116 }
117 let mut pending: Vec<_> = document.iter().map(|link| (link, 0)).collect();
119 let mut budget = limits.max_nodes;
120 let mut strings_left = limits.max_string_bytes;
121 while let Some((link, depth)) = pending.pop() {
122 if depth >= limits.max_depth {
123 return Err(BinaryError::Unencodable(format!(
124 "nesting deeper than {}",
125 limits.max_depth
126 )));
127 }
128 budget = budget
129 .checked_sub(1)
130 .ok_or_else(|| BinaryError::Unencodable("too many LiNo nodes".into()))?;
131 let text = match link {
132 LiNo::Ref(text) => Some(text),
133 LiNo::Link { id, values } => {
134 pending.extend(values.iter().map(|value| (value, depth + 1)));
135 if id.is_some() {
136 budget = budget
137 .checked_sub(1)
138 .ok_or_else(|| BinaryError::Unencodable("too many LiNo nodes".into()))?;
139 }
140 id.as_ref()
141 }
142 };
143 if let Some(text) = text {
144 strings_left = strings_left
145 .checked_sub(text.len())
146 .ok_or_else(|| BinaryError::Unencodable("too many string bytes".into()))?;
147 }
148 }
149 let mut encoder = Encoder::new(options);
150 if !document.is_empty() {
151 let items = document
152 .iter()
153 .map(|link| encoder.encode(link))
154 .collect::<Vec<_>>();
155 encoder.list(items);
156 }
157 let packet = encoder.finish()?;
158 if packet.sections.len() as u64 > limits.max_links || packet.link_count() > limits.max_links {
159 return Err(BinaryError::Unencodable("too many packet links".into()));
160 }
161 let mut references_left = limits.max_references;
162 for (_, link) in packet.links() {
163 references_left = references_left
164 .checked_sub(link.len() as u64)
165 .ok_or_else(|| BinaryError::Unencodable("too many packet references".into()))?;
166 }
167 Ok(packet)
168}
169
170pub fn decode_document(packet: &LinksPacket, limits: &DecodeLimits) -> BinaryResult<LinoDocument> {
172 let decoder = Decoder::new(packet, limits)?;
173 let Some(root) = decoder.links.len().checked_sub(1) else {
174 return Ok(Vec::new());
175 };
176 let root = Reference::Internal(FIRST_LINK_ADDRESS + root as u64);
177 let items = match decoder.view(root) {
178 View::Link(&[Reference::Internal(LIST), chain]) => decoder.chain(chain)?,
179 View::Link(items) if !starts_with_marker(items) => items.to_vec(),
180 _ => return Err(BinaryError::malformed("the root link is not a list")),
181 };
182 let mut budget = limits.max_nodes;
183 items
184 .into_iter()
185 .map(|item| decoder.decode(item, 0, &mut budget))
186 .collect()
187}
188
189pub(crate) fn canonical_number(text: &str) -> Option<u64> {
191 let canonical = !text.is_empty()
192 && text.bytes().all(|byte| byte.is_ascii_digit())
193 && (text == "0" || !text.starts_with('0'));
194 canonical.then(|| text.parse().ok()).flatten()
195}
196
197#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
198enum Node {
199 Internal(u64),
200 External(u64),
201 Doublet(usize),
203 Tuple(usize),
205}
206
207struct Encoder {
208 options: BinaryLinoOptions,
209 doublets: Vec<Vec<Node>>,
210 tuples: Vec<Vec<Node>>,
211 created: HashMap<Vec<Node>, Node>,
212 powers: Vec<Node>,
213}
214
215impl Encoder {
216 fn new(options: BinaryLinoOptions) -> Self {
217 Self {
218 options,
219 doublets: Vec::new(),
220 tuples: Vec::new(),
221 created: HashMap::new(),
222 powers: vec![Node::Internal(ONE)],
223 }
224 }
225
226 fn link(&mut self, items: Vec<Node>) -> Node {
227 if let Some(&node) = self.created.get(&items) {
228 return node;
229 }
230 let refers_to_tuple = items.iter().any(|item| matches!(item, Node::Tuple(_)));
231 let node = if items.len() == 2 && !refers_to_tuple {
232 self.doublets.push(items.clone());
233 Node::Doublet(self.doublets.len() - 1)
234 } else {
235 self.tuples.push(items.clone());
236 Node::Tuple(self.tuples.len() - 1)
237 };
238 self.created.insert(items, node);
239 node
240 }
241
242 fn pair(&mut self, first: Node, second: Node) -> Node {
243 self.link(vec![first, second])
244 }
245
246 fn chain(&mut self, items: &[Node]) -> Node {
247 items
248 .iter()
249 .rev()
250 .fold(Node::Internal(NULL), |tail, &head| self.pair(head, tail))
251 }
252
253 fn fits_one_link(&self, items: usize) -> bool {
256 items != 2 && self.options.arity.contains(items as u64)
257 }
258
259 fn typed(&mut self, marker: u64, elements: Vec<Node>) -> Node {
260 if self.fits_one_link(elements.len() + 1) {
261 let mut items = Vec::with_capacity(elements.len() + 1);
262 items.push(Node::Internal(marker));
263 items.extend(elements);
264 self.link(items)
265 } else {
266 let chain = self.chain(&elements);
267 self.pair(Node::Internal(marker), chain)
268 }
269 }
270
271 fn list(&mut self, elements: Vec<Node>) -> Node {
272 match elements.as_slice() {
273 [] => Node::Internal(NULL),
274 &[first, second] => self.pair(first, second),
275 _ if self.fits_one_link(elements.len()) => self.link(elements),
276 _ => self.typed(LIST, elements),
277 }
278 }
279
280 fn power(&mut self, exponent: usize) -> Node {
281 while self.powers.len() <= exponent {
282 let previous = *self.powers.last().expect("powers start with One");
283 let next = self.pair(previous, previous);
284 self.powers.push(next);
285 }
286 self.powers[exponent]
287 }
288
289 fn unary(&mut self, value: u64) -> Node {
290 let bits = (0..64).rev().filter(|bit| value & (1u64 << bit) != 0);
291 let powers = bits.map(|bit| self.power(bit)).collect::<Vec<_>>();
292 let Some((&last, rest)) = powers.split_last() else {
293 return Node::Internal(NULL);
294 };
295 rest.iter()
296 .rev()
297 .fold(last, |sum, &power| self.pair(power, sum))
298 }
299
300 fn scalar(&mut self, value: u64) -> Node {
301 if self.options.external_references && value <= external_capacity(8) {
302 Node::External(value)
303 } else {
304 self.unary(value)
305 }
306 }
307
308 fn reference(&mut self, text: &str) -> Node {
309 if let Some(value) = canonical_number(text) {
310 if self.options.external_references && value <= external_capacity(8) {
311 return Node::External(value);
312 }
313 let unary = self.unary(value);
314 return self.pair(Node::Internal(NUMBER), unary);
315 }
316 let code_points = text
317 .chars()
318 .map(|character| self.scalar(u64::from(u32::from(character))))
319 .collect();
320 self.typed(STRING, code_points)
321 }
322
323 fn encode(&mut self, link: &LiNo<String>) -> Node {
324 match link {
325 LiNo::Ref(text) => self.reference(text),
326 LiNo::Link { id: None, values } => {
327 let elements = values.iter().map(|value| self.encode(value)).collect();
328 self.list(elements)
329 }
330 LiNo::Link {
331 id: Some(id),
332 values,
333 } => {
334 let mut elements = Vec::with_capacity(values.len() + 1);
335 elements.push(self.reference(id));
336 elements.extend(values.iter().map(|value| self.encode(value)));
337 self.typed(IDENTIFIED, elements)
338 }
339 }
340 }
341
342 fn finish(self) -> BinaryResult<LinksPacket> {
343 let doublet_count = self.doublets.len() as u64;
344 let resolve = |node: &Node| match *node {
345 Node::Internal(address) => Reference::Internal(address),
346 Node::External(value) => Reference::External(value),
347 Node::Doublet(index) => Reference::Internal(FIRST_LINK_ADDRESS + index as u64),
348 Node::Tuple(index) => {
349 Reference::Internal(FIRST_LINK_ADDRESS + doublet_count + index as u64)
350 }
351 };
352 let links: Vec<(u64, Vec<Reference>)> = self
353 .doublets
354 .iter()
355 .chain(&self.tuples)
356 .enumerate()
357 .map(|(index, items)| {
358 (
359 FIRST_LINK_ADDRESS + index as u64,
360 items.iter().map(resolve).collect(),
361 )
362 })
363 .collect();
364 LinksPacket::pack(
365 self.options.external_references,
366 &links,
367 self.options.packed_widths,
368 )
369 }
370}
371
372enum View<'a> {
373 Null,
374 Marker(u64),
375 External(u64),
376 Link(&'a [Reference]),
377}
378
379fn is_marker(reference: Reference) -> bool {
380 matches!(reference, Reference::Internal(address) if (ONE..FIRST_LINK_ADDRESS).contains(&address))
381}
382
383fn starts_with_marker(items: &[Reference]) -> bool {
384 items.first().copied().is_some_and(is_marker)
385}
386
387struct Decoder<'a> {
388 limits: &'a DecodeLimits,
389 string_bytes_left: Cell<usize>,
390 links: Vec<&'a [Reference]>,
392 unary: Vec<Option<u64>>,
394}
395
396impl<'a> Decoder<'a> {
397 fn new(packet: &'a LinksPacket, limits: &'a DecodeLimits) -> BinaryResult<Self> {
398 if packet.sections.len() as u64 > limits.max_links || packet.link_count() > limits.max_links
399 {
400 return Err(BinaryError::LimitExceeded(format!(
401 "packet declares more than {} links",
402 limits.max_links
403 )));
404 }
405 let mut links: Vec<&'a [Reference]> = Vec::new();
406 packet
407 .validate()
408 .map_err(|error| BinaryError::malformed(error.to_string()))?;
409 let mut unary: Vec<Option<u64>> = Vec::new();
410 let mut references_left = limits.max_references;
411 for (address, link) in packet.links() {
412 references_left = references_left
413 .checked_sub(link.len() as u64)
414 .ok_or_else(|| {
415 BinaryError::LimitExceeded(format!(
416 "references exceed the limit of {}",
417 limits.max_references
418 ))
419 })?;
420 let expected = FIRST_LINK_ADDRESS + links.len() as u64;
421 if address != expected {
422 return Err(BinaryError::malformed(format!(
423 "a LiNo packet stores its links contiguously from address \
424 {FIRST_LINK_ADDRESS}, found link {address} where {expected} belongs"
425 )));
426 }
427 if let Some(&target) = link.iter().find(
428 |&&reference| matches!(reference, Reference::Internal(target) if target >= address),
429 ) {
430 return Err(BinaryError::malformed(format!(
431 "link {address} refers to {target:?}, which is not an earlier link"
432 )));
433 }
434 let value_of = |reference: Reference| match reference {
437 Reference::Internal(NULL) => Some(0),
438 Reference::Internal(ONE) => Some(1),
439 Reference::Internal(address) if address >= FIRST_LINK_ADDRESS => {
440 unary[(address - FIRST_LINK_ADDRESS) as usize]
441 }
442 _ => None,
443 };
444 let value = match *link {
445 [source, target] => value_of(source)
446 .zip(value_of(target))
447 .and_then(|(source, target)| source.checked_add(target)),
448 _ => None,
449 };
450 unary.push(value);
451 links.push(link);
452 }
453 Ok(Self {
454 limits,
455 string_bytes_left: Cell::new(limits.max_string_bytes),
456 links,
457 unary,
458 })
459 }
460
461 fn view(&self, reference: Reference) -> View<'a> {
462 match reference {
463 Reference::External(value) => View::External(value),
464 Reference::Internal(NULL) => View::Null,
465 Reference::Internal(address) if address < FIRST_LINK_ADDRESS => View::Marker(address),
466 Reference::Internal(address) => {
467 View::Link(self.links[(address - FIRST_LINK_ADDRESS) as usize])
468 }
469 }
470 }
471
472 fn number(&self, reference: Reference) -> BinaryResult<u64> {
473 match reference {
474 Reference::External(value) => Ok(value),
475 Reference::Internal(NULL) => Ok(0),
476 Reference::Internal(ONE) => Ok(1),
477 Reference::Internal(address) if address >= FIRST_LINK_ADDRESS => self.unary
478 [(address - FIRST_LINK_ADDRESS) as usize]
479 .ok_or_else(|| BinaryError::malformed("expected a unary number")),
480 _ => Err(BinaryError::malformed("expected a unary number")),
481 }
482 }
483
484 fn chain(&self, mut tail: Reference) -> BinaryResult<Vec<Reference>> {
486 let mut elements = Vec::new();
487 loop {
488 match self.view(tail) {
489 View::Null => return Ok(elements),
490 View::Link(&[head, next]) => {
491 if elements.len() >= self.limits.max_nodes {
492 return Err(BinaryError::LimitExceeded("chain too long".into()));
493 }
494 elements.push(head);
495 tail = next;
496 }
497 _ => return Err(BinaryError::malformed("broken element chain")),
498 }
499 }
500 }
501
502 fn typed(
503 &self,
504 marker: u64,
505 elements: &[Reference],
506 depth: usize,
507 budget: &mut usize,
508 ) -> BinaryResult<LiNo<String>> {
509 match marker {
510 NUMBER => match elements {
511 [value] => self.reference_text(self.number(*value)?.to_string()),
512 _ => Err(BinaryError::malformed("a number needs exactly one value")),
513 },
514 STRING => {
515 let mut text =
516 String::with_capacity(elements.len().min(self.string_bytes_left.get()));
517 for &element in elements {
518 let code_point = self.number(element)?;
519 let character = u32::try_from(code_point)
520 .ok()
521 .and_then(char::from_u32)
522 .ok_or_else(|| {
523 BinaryError::malformed(format!("invalid code point {code_point}"))
524 })?;
525 if character.len_utf8()
526 > self.string_bytes_left.get().saturating_sub(text.len())
527 {
528 return Err(BinaryError::LimitExceeded(
529 "too many expanded string bytes".into(),
530 ));
531 }
532 text.push(character);
533 }
534 self.reference_text(text)
535 }
536 LIST => self.list(elements, depth, budget),
537 IDENTIFIED => {
538 let (&id, values) = elements
539 .split_first()
540 .ok_or_else(|| BinaryError::malformed("an identified link needs an id"))?;
541 let LiNo::Ref(id) = self.decode(id, depth, budget)? else {
542 return Err(BinaryError::malformed("a link id must be a reference"));
543 };
544 Ok(LiNo::Link {
545 id: Some(id),
546 values: self.values(values, depth, budget)?,
547 })
548 }
549 _ => Err(BinaryError::malformed(format!(
550 "marker {marker} cannot start a typed value"
551 ))),
552 }
553 }
554
555 fn list(
556 &self,
557 elements: &[Reference],
558 depth: usize,
559 budget: &mut usize,
560 ) -> BinaryResult<LiNo<String>> {
561 Ok(LiNo::Link {
562 id: None,
563 values: self.values(elements, depth, budget)?,
564 })
565 }
566
567 fn values(
568 &self,
569 elements: &[Reference],
570 depth: usize,
571 budget: &mut usize,
572 ) -> BinaryResult<Vec<LiNo<String>>> {
573 elements
574 .iter()
575 .map(|&element| self.decode(element, depth + 1, budget))
576 .collect()
577 }
578
579 fn reference_text(&self, text: String) -> BinaryResult<LiNo<String>> {
580 let remaining = self
581 .string_bytes_left
582 .get()
583 .checked_sub(text.len())
584 .ok_or_else(|| BinaryError::LimitExceeded("too many expanded string bytes".into()))?;
585 self.string_bytes_left.set(remaining);
586 Ok(LiNo::Ref(text))
587 }
588
589 fn decode(
590 &self,
591 reference: Reference,
592 depth: usize,
593 budget: &mut usize,
594 ) -> BinaryResult<LiNo<String>> {
595 if depth >= self.limits.max_depth {
596 return Err(BinaryError::LimitExceeded(format!(
597 "nesting deeper than {}",
598 self.limits.max_depth
599 )));
600 }
601 *budget = budget
602 .checked_sub(1)
603 .ok_or_else(|| BinaryError::LimitExceeded("too many LiNo nodes".into()))?;
604 match self.view(reference) {
605 View::Null => Ok(LiNo::Link {
606 id: None,
607 values: Vec::new(),
608 }),
609 View::External(value) => self.reference_text(value.to_string()),
610 View::Marker(marker) => Err(BinaryError::malformed(format!(
611 "marker {marker} used as a value"
612 ))),
613 View::Link(&[Reference::Internal(NUMBER), value]) => {
614 self.typed(NUMBER, &[value], depth, budget)
615 }
616 View::Link(&[Reference::Internal(marker), chain])
617 if is_marker(Reference::Internal(marker)) =>
618 {
619 let elements = self.chain(chain)?;
620 self.typed(marker, &elements, depth, budget)
621 }
622 View::Link(items) => match items.split_first() {
623 Some((&Reference::Internal(marker), elements)) if starts_with_marker(items) => {
624 self.typed(marker, elements, depth, budget)
625 }
626 _ => self.list(items, depth, budget),
627 },
628 }
629 }
630}