oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1#![allow(dead_code)]
2/*
3 ORIGINAL CODE AUTHORED BY Seph Gentle
4 available at: https://github.com/josephg/editing-traces
5*/
6
7use flate2::bufread::GzDecoder;
8use serde::Deserialize;
9use std::fs::File;
10use std::io::{BufReader, Read};
11
12/// This file contains some simple helpers for loading test data. Its used by benchmarking and
13/// testing code.
14
15/// (position, delete length, insert content).
16#[derive(Debug, Clone, Deserialize, Eq, PartialEq)]
17pub struct TestPatch(pub usize, pub usize, pub String);
18
19#[derive(Debug, Clone, Deserialize, Eq, PartialEq)]
20pub struct TestTxn {
21 // time: String, // ISO String. Unused.
22 pub patches: Vec<TestPatch>,
23}
24
25#[derive(Debug, Clone, Deserialize, Eq, PartialEq)]
26pub struct TestData {
27 #[serde(default)]
28 pub using_byte_positions: bool,
29
30 #[serde(rename = "startContent")]
31 pub start_content: String,
32 #[serde(rename = "endContent")]
33 pub end_content: String,
34
35 pub txns: Vec<TestTxn>,
36}
37
38impl TestData {
39 pub fn len(&self) -> usize {
40 self.txns.iter().map(|txn| txn.patches.len()).sum::<usize>()
41 }
42
43 pub fn is_empty(&self) -> bool {
44 !self.txns.iter().any(|txn| !txn.patches.is_empty())
45 }
46
47 /// This method returns a clone of the testing data using byte offsets instead of codepoint
48 /// indexes.
49 pub fn chars_to_bytes(&self) -> Self {
50 assert_eq!(false, self.using_byte_positions);
51
52 let mut r = ropey::Rope::new();
53
54 Self {
55 using_byte_positions: true,
56 start_content: self.start_content.clone(),
57 end_content: self.end_content.clone(),
58 txns: self
59 .txns
60 .iter()
61 .map(|txn| TestTxn {
62 patches: txn
63 .patches
64 .iter()
65 .map(|TestPatch(pos_chars, del_chars, ins)| {
66 let pos_bytes = r.char_to_byte(*pos_chars);
67 let del_bytes = if *del_chars > 0 {
68 let del_end_bytes = r.char_to_byte(pos_chars + *del_chars);
69 r.remove(*pos_chars..*pos_chars + *del_chars);
70 del_end_bytes - pos_bytes
71 } else {
72 0
73 };
74 if !ins.is_empty() {
75 r.insert(*pos_chars, ins);
76 }
77
78 TestPatch(pos_bytes, del_bytes, ins.clone())
79 })
80 .collect(),
81 })
82 .collect(),
83 }
84 }
85
86 pub fn patches(&self) -> impl Iterator<Item = &TestPatch> {
87 self.txns.iter().flat_map(|txn| txn.patches.iter())
88 }
89}
90
91/// Load the testing data at the specified file. If the filename ends in .gz, it will be
92/// transparently uncompressed.
93///
94/// This method panics if the file does not exist, or is corrupt. It'd be better to have a try_
95/// variant of this method, but given this is mostly for benchmarking and testing, I haven't felt
96/// the need to write that code.
97pub fn load_testing_data(filename: &str) -> TestData {
98 // let start = SystemTime::now();
99 // let mut file = File::open("benchmark_data/automerge-paper.json.gz").unwrap();
100 let file = File::open(filename).unwrap();
101
102 let mut reader = BufReader::new(file);
103 // We could pass the GzDecoder straight to serde, but it makes it way slower to parse for
104 // some reason.
105 let mut raw_json = vec![];
106
107 if filename.ends_with(".gz") {
108 let mut reader = GzDecoder::new(reader);
109 reader.read_to_end(&mut raw_json).unwrap();
110 } else {
111 reader.read_to_end(&mut raw_json).unwrap();
112 }
113
114 let data: TestData = serde_json::from_reader(raw_json.as_slice()).unwrap();
115 data
116}
117