Skip to main content

pulsify_protocol/
lib.rs

1//! Pulsify protocol v1. The transport/patch journal is independent of a CRDT engine.
2use std::collections::{BTreeMap, BTreeSet};
3use std::fmt;
4
5pub const MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
6pub const MAX_FRAME_BYTES: usize = 8 * 1024 * 1024;
7pub const MAX_PATCH_BYTES: usize = 1024 * 1024;
8pub const MAX_VECTOR_ENTRIES: usize = 1024;
9pub const MAX_MESSAGE_ROWS: usize = 4096;
10pub const MAX_SCHEMA_NODES: usize = 300_000;
11const MAX_ROW_BATCH_NODES: usize = MAX_SCHEMA_NODES - (3 * MAX_VECTOR_ENTRIES + 16);
12pub type VersionVector = Vec<(u64, u64)>;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct Error {
16    pub code: &'static str,
17    pub message: String,
18}
19impl Error {
20    fn new(code: &'static str, message: &str) -> Self {
21        Self {
22            code,
23            message: message.into(),
24        }
25    }
26}
27impl fmt::Display for Error {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        write!(f, "{}: {}", self.code, self.message)
30    }
31}
32impl std::error::Error for Error {}
33type Result<T> = std::result::Result<T, Error>;
34fn invalid(message: &str) -> Error {
35    Error::new("INVALID_MESSAGE", message)
36}
37fn limit(message: &str) -> Error {
38    Error::new("LIMIT_EXCEEDED", message)
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct PatchRow {
43    pub doc_id: String,
44    pub sid: u64,
45    pub clock: u64,
46    pub span: u64,
47    pub dependencies: VersionVector,
48    pub operations: Vec<u8>,
49}
50impl PatchRow {
51    pub fn end(&self) -> u64 {
52        self.clock.saturating_add(self.span)
53    }
54    fn validate(&self, doc_id: &str) -> Result<()> {
55        validate_vector(&self.dependencies)?;
56        if self.doc_id != doc_id
57            || self.sid < 65536
58            || self.sid > MAX_SAFE_INTEGER
59            || self.clock == 0
60            || self.span == 0
61            || self.end() > MAX_SAFE_INTEGER
62            || self.operations.is_empty()
63            || own_clock(&self.dependencies, self.sid) > self.clock
64        {
65            return Err(invalid("Invalid patch segment"));
66        }
67        if self.operations.len() > MAX_PATCH_BYTES {
68            return Err(limit("Patch exceeds 1 MiB"));
69        }
70        Ok(())
71    }
72}
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum Body {
75    Connect {
76        vv: VersionVector,
77    },
78    Sync {
79        found: bool,
80        complete: bool,
81        vv: VersionVector,
82        rows: Vec<PatchRow>,
83    },
84    SyncBack {
85        vv: VersionVector,
86        rows: Vec<PatchRow>,
87    },
88    Update {
89        vv: VersionVector,
90        rows: Vec<PatchRow>,
91    },
92    Ack {
93        vv: VersionVector,
94    },
95    Error {
96        code: String,
97        message: String,
98    },
99}
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct Message {
102    pub doc_id: String,
103    pub request_id: u64,
104    pub body: Body,
105}
106impl Message {
107    fn validate(&self) -> Result<()> {
108        if self.doc_id.is_empty()
109            || self.doc_id.contains('\0')
110            || self.doc_id.encode_utf16().count() > 1024
111            || self.request_id > MAX_SAFE_INTEGER
112        {
113            return Err(invalid("Invalid message header"));
114        }
115        match &self.body {
116            Body::Connect { vv } | Body::Ack { vv } => validate_vector(vv)?,
117            Body::Sync { vv, rows, .. }
118            | Body::SyncBack { vv, rows }
119            | Body::Update { vv, rows } => {
120                validate_vector(vv)?;
121                if rows.len() > MAX_MESSAGE_ROWS {
122                    return Err(limit("Too many rows"));
123                }
124                for row in rows {
125                    row.validate(&self.doc_id)?;
126                }
127            }
128            Body::Error { .. } => {}
129        }
130        let nodes = match &self.body {
131            Body::Connect { vv } | Body::Ack { vv } => 8 + 3 * vv.len(),
132            Body::Sync { vv, rows, .. } => {
133                11 + 3 * vv.len() + rows.iter().map(row_nodes).sum::<usize>()
134            }
135            Body::SyncBack { vv, rows } | Body::Update { vv, rows } => {
136                9 + 3 * vv.len() + rows.iter().map(row_nodes).sum::<usize>()
137            }
138            Body::Error { .. } => 9,
139        };
140        if nodes > MAX_SCHEMA_NODES {
141            return Err(limit("Structure limit exceeded"));
142        }
143        Ok(())
144    }
145}
146fn own_clock(vv: &VersionVector, sid: u64) -> u64 {
147    vv.iter()
148        .find(|(id, _)| *id == sid)
149        .map(|(_, clock)| *clock)
150        .unwrap_or(0)
151}
152pub fn validate_vector(vv: &VersionVector) -> Result<()> {
153    if vv.len() > MAX_VECTOR_ENTRIES {
154        return Err(invalid("Too many actors"));
155    }
156    let mut previous = None;
157    for &(sid, clock) in vv {
158        if sid > MAX_SAFE_INTEGER
159            || clock == 0
160            || clock > MAX_SAFE_INTEGER
161            || previous.is_some_and(|id| sid <= id)
162        {
163            return Err(invalid(
164                "Vector must have unique sorted actors and positive clocks",
165            ));
166        }
167        previous = Some(sid);
168    }
169    Ok(())
170}
171fn covers(have: &BTreeMap<u64, u64>, required: &VersionVector) -> bool {
172    required
173        .iter()
174        .all(|(sid, clock)| have.get(sid).copied().unwrap_or(0) >= *clock)
175}
176
177struct Writer(Vec<u8>);
178impl Writer {
179    fn uint(&mut self, mut value: u64) {
180        while value >= 128 {
181            self.0.push((value as u8 & 127) | 128);
182            value >>= 7;
183        }
184        self.0.push(value as u8);
185    }
186    fn bytes(&mut self, value: &[u8]) {
187        self.uint(value.len() as u64);
188        self.0.extend_from_slice(value);
189    }
190    fn text(&mut self, value: &str) {
191        self.bytes(value.as_bytes());
192    }
193    fn vector(&mut self, vv: &VersionVector) {
194        self.uint(vv.len() as u64);
195        for (sid, clock) in vv {
196            self.uint(*sid);
197            self.uint(*clock);
198        }
199    }
200    fn rows(&mut self, rows: &[PatchRow]) {
201        self.uint(rows.len() as u64);
202        for row in rows {
203            self.text(&row.doc_id);
204            self.uint(row.sid);
205            self.uint(row.clock);
206            self.uint(row.span);
207            self.vector(&row.dependencies);
208            self.bytes(&row.operations);
209        }
210    }
211}
212struct Reader<'a> {
213    bytes: &'a [u8],
214    pos: usize,
215}
216impl<'a> Reader<'a> {
217    fn byte(&mut self) -> Result<u8> {
218        let b = self
219            .bytes
220            .get(self.pos)
221            .copied()
222            .ok_or_else(|| invalid("Truncated frame"))?;
223        self.pos += 1;
224        Ok(b)
225    }
226    fn uint(&mut self) -> Result<u64> {
227        let mut result = 0_u64;
228        for i in 0..8 {
229            let b = self.byte()?;
230            result |= u64::from(b & 127) << (7 * i);
231            if result > MAX_SAFE_INTEGER {
232                return Err(invalid("Integer overflow"));
233            }
234            if b < 128 {
235                if i > 0 && b == 0 {
236                    return Err(invalid("Non-canonical varint"));
237                }
238                return Ok(result);
239            }
240        }
241        Err(invalid("Integer overflow"))
242    }
243    fn count(&mut self, max: usize) -> Result<usize> {
244        let n = self.uint()?;
245        if n > max as u64 {
246            return Err(limit("Collection limit exceeded"));
247        }
248        Ok(n as usize)
249    }
250    fn bytes(&mut self, max: usize) -> Result<Vec<u8>> {
251        let n = self.count(max)?;
252        let end = self
253            .pos
254            .checked_add(n)
255            .ok_or_else(|| invalid("Length overflow"))?;
256        let result = self
257            .bytes
258            .get(self.pos..end)
259            .ok_or_else(|| invalid("Truncated bytes"))?
260            .to_vec();
261        self.pos = end;
262        Ok(result)
263    }
264    fn text(&mut self) -> Result<String> {
265        String::from_utf8(self.bytes(MAX_FRAME_BYTES)?).map_err(|_| invalid("Invalid UTF-8"))
266    }
267    fn boolean(&mut self) -> Result<bool> {
268        match self.byte()? {
269            0 => Ok(false),
270            1 => Ok(true),
271            _ => Err(invalid("Invalid boolean")),
272        }
273    }
274    fn vector(&mut self) -> Result<VersionVector> {
275        let n = self.count(MAX_VECTOR_ENTRIES)?;
276        let mut result = Vec::with_capacity(n);
277        for _ in 0..n {
278            result.push((self.uint()?, self.uint()?));
279        }
280        validate_vector(&result)?;
281        Ok(result)
282    }
283    fn rows(&mut self) -> Result<Vec<PatchRow>> {
284        let n = self.count(MAX_MESSAGE_ROWS)?;
285        let mut result = Vec::with_capacity(n);
286        for _ in 0..n {
287            result.push(PatchRow {
288                doc_id: self.text()?,
289                sid: self.uint()?,
290                clock: self.uint()?,
291                span: self.uint()?,
292                dependencies: self.vector()?,
293                operations: self.bytes(MAX_PATCH_BYTES)?,
294            });
295        }
296        Ok(result)
297    }
298}
299// Envelope v2 carries one lossless scalar run. Relays expand it without an
300// application schema; their outgoing v1 rows preserve every original identity.
301fn uint_size(mut n: u64) -> usize {
302    let mut size = 1;
303    while n >= 128 {
304        size += 1;
305        n >>= 7;
306    }
307    size
308}
309impl Reader<'_> {
310    fn run_rows(&mut self, doc_id: &str, vv_len: usize) -> Result<Vec<PatchRow>> {
311        let sid = self.uint()?;
312        let clock = self.uint()?;
313        let count = self.count(MAX_MESSAGE_ROWS)?;
314        let lamport = self.uint()?;
315        if sid < 65536
316            || clock == 0
317            || lamport == 0
318            || count < 2
319            || clock + count as u64 > MAX_SAFE_INTEGER
320            || lamport + count as u64 > MAX_SAFE_INTEGER
321        {
322            return Err(invalid("Invalid run range"));
323        }
324        let schema = self.text()?;
325        let target = self.text()?;
326        let tag = self.uint()?;
327        if tag > 1 {
328            return Err(invalid("Invalid run field"));
329        }
330        let field_text = if tag == 0 { Some(self.text()?) } else { None };
331        let field_number = if tag == 1 { self.uint()? } else { 0 };
332        let layout = self.uint()?;
333        let external = self.vector()?;
334        if external.iter().any(|(actor, _)| *actor == sid)
335            || external.len() + 1 > MAX_VECTOR_ENTRIES
336        {
337            return Err(invalid("Invalid run dependencies"));
338        }
339        if 16 + vv_len * 3 + count * (7 + 3 * (external.len() + 1)) > MAX_SCHEMA_NODES {
340            return Err(limit("Expanded run node limit"));
341        }
342        let mut prefix = Writer(Vec::new());
343        prefix.text(&schema);
344        prefix.uint(1);
345        prefix.uint(1);
346        prefix.text(&target);
347        prefix.uint(tag);
348        if let Some(field) = field_text {
349            prefix.text(&field);
350        } else {
351            prefix.uint(field_number);
352        }
353        prefix.uint(layout);
354        let mut values = Vec::with_capacity(count);
355        let mut expanded = self.bytes.len();
356        for i in 0..count {
357            let value = self.bytes(MAX_PATCH_BYTES)?;
358            let op_bytes = 1
359                + uint_size(lamport + i as u64)
360                + prefix.0.len()
361                + uint_size(value.len() as u64)
362                + value.len()
363                + 2;
364            if op_bytes > MAX_PATCH_BYTES {
365                return Err(limit("Expanded run payload limit"));
366            }
367            expanded += op_bytes
368                + (external.len() + usize::from(clock + i as u64 > 1)) * 16
369                + doc_id.encode_utf16().count() * 2
370                + 40;
371            if expanded > 64 * 1024 * 1024 {
372                return Err(limit("Expanded run byte limit"));
373            }
374            values.push(value);
375        }
376        let mut rows = Vec::with_capacity(count);
377        for (i, value) in values.into_iter().enumerate() {
378            let mut operations = Writer(vec![3]);
379            operations.uint(lamport + i as u64);
380            operations.0.extend_from_slice(&prefix.0);
381            operations.bytes(&value);
382            operations.uint(0);
383            operations.uint(0);
384            let mut dependencies = external.clone();
385            if clock + i as u64 > 1 {
386                let at = dependencies.partition_point(|(actor, _)| *actor < sid);
387                dependencies.insert(at, (sid, clock + i as u64));
388            }
389            rows.push(PatchRow {
390                doc_id: doc_id.to_owned(),
391                sid,
392                clock: clock + i as u64,
393                span: 1,
394                dependencies,
395                operations: operations.0,
396            });
397        }
398        Ok(rows)
399    }
400}
401
402pub fn encode(message: &Message) -> Result<Vec<u8>> {
403    message.validate()?;
404    let mut writer = Writer(vec![1, 3]);
405    writer.uint(match message.body {
406        Body::Connect { .. } => 1,
407        Body::Sync { .. } => 2,
408        Body::SyncBack { .. } => 3,
409        Body::Update { .. } => 4,
410        Body::Ack { .. } => 5,
411        Body::Error { .. } => 6,
412    });
413    writer.text(&message.doc_id);
414    writer.uint(message.request_id);
415    match &message.body {
416        Body::Connect { vv } | Body::Ack { vv } => writer.vector(vv),
417        Body::Sync {
418            found,
419            complete,
420            vv,
421            rows,
422        } => {
423            writer.0.push(*found as u8);
424            writer.0.push(*complete as u8);
425            writer.vector(vv);
426            writer.rows(rows);
427        }
428        Body::SyncBack { vv, rows } | Body::Update { vv, rows } => {
429            writer.vector(vv);
430            writer.rows(rows);
431        }
432        Body::Error { code, message } => {
433            writer.text(code);
434            writer.text(message);
435        }
436    }
437    if writer.0.len() > MAX_FRAME_BYTES {
438        return Err(limit("Frame exceeds 8 MiB"));
439    }
440    Ok(writer.0)
441}
442pub fn decode(bytes: &[u8]) -> Result<Message> {
443    if bytes.len() > MAX_FRAME_BYTES {
444        return Err(limit("Frame exceeds 8 MiB"));
445    }
446    let mut reader = Reader { bytes, pos: 0 };
447    let protocol = reader.uint()?;
448    let operations = reader.uint()?;
449    if !((protocol == 1 || protocol == 2) && operations == 3) {
450        return Err(Error::new(
451            "UNSUPPORTED_VERSION",
452            "Unsupported protocol or operation version",
453        ));
454    }
455    let kind = reader.uint()?;
456    if !(1..=6).contains(&kind) {
457        return Err(invalid("Unknown message kind"));
458    }
459    let doc_id = reader.text()?;
460    let request_id = reader.uint()?;
461    if protocol == 2 && !(2..=4).contains(&kind) {
462        return Err(invalid("Invalid run message kind"));
463    }
464    let body = match kind {
465        1 => Body::Connect {
466            vv: reader.vector()?,
467        },
468        2 => {
469            let found = reader.boolean()?;
470            let complete = reader.boolean()?;
471            let vv = reader.vector()?;
472            let rows = if protocol == 2 {
473                reader.run_rows(&doc_id, vv.len())?
474            } else {
475                reader.rows()?
476            };
477            Body::Sync {
478                found,
479                complete,
480                vv,
481                rows,
482            }
483        }
484        3 | 4 => {
485            let vv = reader.vector()?;
486            let rows = if protocol == 2 {
487                reader.run_rows(&doc_id, vv.len())?
488            } else {
489                reader.rows()?
490            };
491            if kind == 3 {
492                Body::SyncBack { vv, rows }
493            } else {
494                Body::Update { vv, rows }
495            }
496        }
497        5 => Body::Ack {
498            vv: reader.vector()?,
499        },
500        6 => Body::Error {
501            code: reader.text()?,
502            message: reader.text()?,
503        },
504        _ => unreachable!(),
505    };
506    if reader.pos != bytes.len() {
507        return Err(invalid("Trailing bytes"));
508    }
509    let message = Message {
510        doc_id,
511        request_id,
512        body,
513    };
514    message.validate()?;
515    Ok(message)
516}
517
518/// Opaque immutable segments; the frontier advances only when dependencies exist.
519#[derive(Clone, Debug)]
520pub struct PatchLog {
521    pub doc_id: String,
522    rows: BTreeMap<(u64, u64), PatchRow>,
523    pending: BTreeSet<(u64, u64)>,
524    frontier: BTreeMap<u64, u64>,
525    bytes: usize,
526}
527impl PatchLog {
528    pub fn new(doc_id: &str) -> Self {
529        Self {
530            doc_id: doc_id.into(),
531            rows: BTreeMap::new(),
532            pending: BTreeSet::new(),
533            frontier: BTreeMap::new(),
534            bytes: 0,
535        }
536    }
537    pub fn vector(&self) -> VersionVector {
538        self.frontier
539            .iter()
540            .map(|(sid, clock)| (*sid, *clock))
541            .collect()
542    }
543    pub fn pending_count(&self) -> usize {
544        self.pending.len()
545    }
546    pub fn missing(&self, vv: &VersionVector) -> Vec<PatchRow> {
547        self.rows
548            .values()
549            .filter(|row| row.end() > own_clock(vv, row.sid))
550            .cloned()
551            .collect()
552    }
553    pub fn append(&mut self, rows: &[PatchRow]) -> Result<()> {
554        let mut next = self.clone();
555        for row in rows {
556            row.validate(&self.doc_id)?;
557            let key = (row.sid, row.clock);
558            if let Some(existing) = next.rows.get(&key) {
559                if existing != row {
560                    return Err(Error::new("CONFLICT", "Conflicting segment"));
561                }
562                continue;
563            }
564            for other in next.rows.values().filter(|other| other.sid == row.sid) {
565                if row.clock < other.end() && other.clock < row.end() {
566                    return Err(Error::new("CONFLICT", "Overlapping segments"));
567                }
568                let (previous, after) = if row.clock > other.clock {
569                    (other, row)
570                } else {
571                    (row, other)
572                };
573                if own_clock(&after.dependencies, row.sid) < previous.end() {
574                    return Err(Error::new("CONFLICT", "Actor history fork"));
575                }
576            }
577            next.bytes += row.operations.len()
578                + row.dependencies.len() * 16
579                + row.doc_id.encode_utf16().count() * 2
580                + 40;
581            if next.rows.len() >= 100_000 || next.bytes > 64 * 1024 * 1024 {
582                return Err(limit("Document history limit exceeded"));
583            }
584            next.rows.insert(key, row.clone());
585            next.pending.insert(key);
586        }
587        loop {
588            let ready: Vec<_> = next
589                .pending
590                .iter()
591                .filter(|key| {
592                    let row = &next.rows[key];
593                    covers(&next.frontier, &row.dependencies)
594                        && row.clock == next.frontier.get(&row.sid).copied().unwrap_or(1)
595                })
596                .copied()
597                .collect();
598            if ready.is_empty() {
599                break;
600            }
601            for key in ready {
602                let row = &next.rows[&key];
603                next.frontier
604                    .entry(row.sid)
605                    .and_modify(|clock| *clock = (*clock).max(row.end()))
606                    .or_insert(row.end());
607                next.pending.remove(&key);
608            }
609        }
610        if next.frontier.len() > MAX_VECTOR_ENTRIES {
611            return Err(limit("Too many actors"));
612        }
613        *self = next;
614        Ok(())
615    }
616}
617
618fn row_nodes(row: &PatchRow) -> usize {
619    7 + 3 * row.dependencies.len()
620}
621
622fn chunk_rows(rows: Vec<PatchRow>) -> Vec<Vec<PatchRow>> {
623    let mut chunks = Vec::new();
624    let mut chunk = Vec::new();
625    let mut bytes = 0;
626    let mut nodes = 0;
627    for row in rows {
628        let size = row.operations.len()
629            + row.dependencies.len() * 16
630            + row.doc_id.encode_utf16().count() * 4
631            + 64;
632        let cost = row_nodes(&row);
633        if !chunk.is_empty()
634            && (bytes + size > 2 * MAX_PATCH_BYTES
635                || chunk.len() >= MAX_MESSAGE_ROWS
636                || nodes + cost > MAX_ROW_BATCH_NODES)
637        {
638            chunks.push(std::mem::take(&mut chunk));
639            bytes = 0;
640            nodes = 0;
641        }
642        bytes += size;
643        nodes += cost;
644        chunk.push(row);
645    }
646    if !chunk.is_empty() || chunks.is_empty() {
647        chunks.push(chunk);
648    }
649    chunks
650}
651
652/// Single-peer reference relay for embedding or the line-based interoperability CLI.
653#[derive(Default)]
654pub struct Relay {
655    documents: BTreeMap<String, PatchLog>,
656}
657impl Relay {
658    pub fn receive(&mut self, message: Message) -> Result<Vec<Message>> {
659        message.validate()?;
660        let Message {
661            doc_id,
662            request_id,
663            body,
664        } = message;
665        let responses = match body {
666            Body::Connect { vv } => {
667                let log = self.documents.get(&doc_id);
668                let rows = log.map(|log| log.missing(&vv)).unwrap_or_default();
669                let vector = log.map(PatchLog::vector).unwrap_or_default();
670                let chunks = chunk_rows(rows);
671                let last = chunks.len() - 1;
672                chunks
673                    .into_iter()
674                    .enumerate()
675                    .map(|(i, rows)| Message {
676                        doc_id: doc_id.clone(),
677                        request_id,
678                        body: Body::Sync {
679                            found: log.is_some(),
680                            complete: i == last,
681                            vv: vector.clone(),
682                            rows,
683                        },
684                    })
685                    .collect()
686            }
687            Body::SyncBack { rows, .. } | Body::Update { rows, .. } => {
688                let log = self
689                    .documents
690                    .entry(doc_id.clone())
691                    .or_insert_with(|| PatchLog::new(&doc_id));
692                match log.append(&rows) {
693                    Ok(()) => vec![Message {
694                        doc_id,
695                        request_id,
696                        body: Body::Ack { vv: log.vector() },
697                    }],
698                    Err(error) => vec![Message {
699                        doc_id,
700                        request_id,
701                        body: Body::Error {
702                            code: error.code.into(),
703                            message: error.message,
704                        },
705                    }],
706                }
707            }
708            _ => return Err(invalid("Expected connect or patch upload")),
709        };
710        Ok(responses)
711    }
712}
713
714#[cfg(test)]
715mod tests {
716    use super::*;
717    #[test]
718    fn dense_vectors_are_chunked_within_the_shared_structure_budget() {
719        let vv: VersionVector = (0..1024).map(|i| (65536 + i, 1)).collect();
720        let rows: Vec<_> = (0..127)
721            .map(|i| PatchRow {
722                doc_id: "dense".into(),
723                sid: 200_000 + i,
724                clock: 1,
725                span: 1,
726                dependencies: vv.clone(),
727                operations: vec![1],
728            })
729            .collect();
730        let message = Message {
731            doc_id: "dense".into(),
732            request_id: 1,
733            body: Body::Update {
734                vv: vv.clone(),
735                rows: rows.clone(),
736            },
737        };
738        assert!(encode(&message).is_err());
739        let mut unchecked = Writer(vec![1, 3, 4]);
740        unchecked.text("dense");
741        unchecked.uint(1);
742        unchecked.vector(&vv);
743        unchecked.rows(&rows);
744        assert!(decode(&unchecked.0).is_err());
745        let chunks = chunk_rows(rows);
746        assert_eq!(chunks.len(), 2);
747        for rows in chunks {
748            let message = Message {
749                doc_id: "dense".into(),
750                request_id: 1,
751                body: Body::Sync {
752                    found: true,
753                    complete: true,
754                    vv: vv.clone(),
755                    rows,
756                },
757            };
758            assert_eq!(decode(&encode(&message).unwrap()).unwrap(), message);
759        }
760    }
761    #[test]
762    fn shared_binary_fixtures() {
763        let fixtures: serde_json::Value =
764            serde_json::from_str(include_str!("../../../fixtures/protocol.json")).unwrap();
765        for fixture in fixtures["messages"].as_array().unwrap() {
766            let bytes: Vec<u8> = fixture["bytes"]
767                .as_array()
768                .unwrap()
769                .iter()
770                .map(|b| b.as_u64().unwrap() as u8)
771                .collect();
772            let message = decode(&bytes).unwrap();
773            assert_eq!(encode(&message).unwrap(), bytes, "{}", fixture["name"]);
774            for i in 0..bytes.len() {
775                assert!(decode(&bytes[..i]).is_err());
776            }
777            let mut trailing = bytes.clone();
778            trailing.push(0);
779            assert!(decode(&trailing).is_err());
780        }
781    }
782    #[test]
783    fn malformed_values() {
784        assert!(decode(&[1, 3, 99]).is_err());
785        assert!(decode(&[2, 1]).is_err());
786        // State-only envelopes are never accepted as durable exact history.
787        assert!(decode(&[3, 3, 4]).is_err());
788        assert!(decode(&[1, 3, 1, 128, 0]).is_err());
789        assert!(validate_vector(&vec![(65536, 1), (65536, 2)]).is_err());
790        assert!(validate_vector(&vec![(65536, MAX_SAFE_INTEGER + 1)]).is_err());
791        let mut seed = 42_u32;
792        for n in 0..1000 {
793            let mut bytes = vec![1, 3];
794            for _ in 0..n % 64 {
795                seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
796                bytes.push(seed as u8);
797            }
798            let _ = decode(&bytes);
799        }
800    }
801    #[test]
802    fn gaps_duplicates_and_conflicts() {
803        let first = PatchRow {
804            doc_id: "test".into(),
805            sid: 65536,
806            clock: 1,
807            span: 2,
808            dependencies: vec![],
809            operations: vec![1],
810        };
811        let second = PatchRow {
812            clock: 3,
813            dependencies: vec![(65536, 3)],
814            ..first.clone()
815        };
816        let mut log = PatchLog::new("test");
817        log.append(&[second.clone()]).unwrap();
818        assert_eq!(log.vector(), vec![]);
819        log.append(&[first.clone()]).unwrap();
820        assert_eq!(log.vector(), vec![(65536, 5)]);
821        assert_eq!(log.pending_count(), 0);
822        log.append(&[first.clone(), second]).unwrap();
823        assert!(log
824            .append(&[PatchRow {
825                operations: vec![2],
826                ..first.clone()
827            }])
828            .is_err());
829        assert!(log.append(&[PatchRow { clock: 2, ..first }]).is_err());
830        assert!(log.missing(&log.vector()).is_empty());
831    }
832    #[test]
833    fn actor_frontier_does_not_skip_an_undeclared_gap() {
834        let mut log = PatchLog::new("gap");
835        log.append(&[PatchRow {
836            doc_id: "gap".into(),
837            sid: 65536,
838            clock: 10,
839            span: 1,
840            dependencies: vec![],
841            operations: vec![1],
842        }])
843        .unwrap();
844        assert!(log.vector().is_empty());
845        assert_eq!(log.pending_count(), 1);
846        assert!(encode(&Message {
847            doc_id: "bad\0id".into(),
848            request_id: 1,
849            body: Body::Connect { vv: vec![] }
850        })
851        .is_err());
852    }
853}