| 1 | #![allow(dead_code)] |
| 2 | /* |
| 3 | ORIGINAL CODE AUTHORED BY Seph Gentle |
| 4 | available at: https://github.com/josephg/editing-traces |
| 5 | */ |
| 6 | |
| 7 | use flate2::bufread::GzDecoder; |
| 8 | use serde::Deserialize; |
| 9 | use std::fs::File; |
| 10 | use 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)] |
| 17 | pub struct TestPatch(pub usize, pub usize, pub String); |
| 18 | |
| 19 | #[derive(Debug, Clone, Deserialize, Eq, PartialEq)] |
| 20 | pub struct TestTxn { |
| 21 | // time: String, // ISO String. Unused. |
| 22 | pub patches: Vec<TestPatch>, |
| 23 | } |
| 24 | |
| 25 | #[derive(Debug, Clone, Deserialize, Eq, PartialEq)] |
| 26 | pub 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 | |
| 38 | impl 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. |
| 97 | pub 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 | |