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