1#[macro_use]
2extern crate log;
3#[macro_use]
4extern crate serde_derive;
5#[macro_use]
6extern crate bitflags;
7#[macro_use]
8extern crate pijul_macros;
9#[macro_use]
10extern crate thiserror;
11#[cfg(test)]
12#[macro_use]
13extern crate quickcheck;
14
15pub mod alive;
16pub mod apply;
17pub mod change;
18pub mod changestore;
19mod diff;
20pub mod fs;
21pub mod missing_context;
22pub mod output;
23pub mod path;
24pub mod pristine;
25pub mod record;
26pub mod small_string;
27mod text_encoding;
28mod unrecord;
29mod vector2;
30pub mod vertex_buffer;
31pub mod working_copy;
32
33pub mod key;
34#[cfg(feature = "zstd")]
35pub mod tag;
36
37#[cfg(test)]
38mod tests;
39
40use std::sync::{LazyLock, Mutex};
41
42pub const DOT_DIR: &str = ".pijul";
43pub const DEFAULT_CHANNEL: &str = "main";
44
45#[derive(Debug, Error)]
46#[error("Parse error: {:?}", s)]
47pub struct ParseError {
48 s: String,
49}
50
51#[derive(Debug, Error, Serialize, Deserialize)]
52pub enum RemoteError {
53 #[error("Repository not found: {}", url)]
54 RepositoryNotFound { url: String },
55 #[error("Channel {} not found for repository {}", channel, url)]
56 ChannelNotFound { channel: String, url: String },
57 #[error("Ambiguous path: {}", path)]
58 AmbiguousPath { path: String },
59 #[error("Path not found: {}", path)]
60 PathNotFound { path: String },
61 #[error("Change not found: {}", change)]
62 ChangeNotFound { change: String },
63}
64
65pub use crate::apply::Workspace as ApplyWorkspace;
66pub use crate::apply::{apply_change_arc, ApplyError, LocalApplyError};
67pub use crate::diff::DEFAULT_SEPARATOR;
68pub use crate::fs::{FsError, WorkingCopyIterator};
69pub use crate::output::{Archive, Conflict};
70pub use crate::pristine::{
71 ArcTxn, Base32, ChangeId, ChannelMutTxnT, ChannelRef, ChannelTxnT, DepsTxnT, EdgeFlags,
72 GraphTxnT, Hash, Inode, Merkle, MutTxnT, OwnedPathId, RemoteRef, TreeTxnT, TxnT, Vertex,
73};
74pub use crate::record::Builder as RecordBuilder;
75pub use crate::record::{Algorithm, InodeUpdate};
76pub use crate::text_encoding::Encoding;
77pub use crate::unrecord::UnrecordError;
78
79pub type Hasher = std::collections::hash_map::RandomState;
80
81pub type HashMap<K, V> = std::collections::HashMap<K, V, Hasher>;
82pub type HashSet<K> = std::collections::HashSet<K, Hasher>;
83
84impl<
85 T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>
86 + ::sanakirja::RootPageMut
87 + ::sanakirja::Commit
88 + ::sanakirja::AllocPage<Error = ::sanakirja::Error>,
89 > MutTxnTExt for pristine::sanakirja::GenericTxn<T>
90{
91}
92impl<T: ::sanakirja::LoadPage<Error = ::sanakirja::Error> + ::sanakirja::RootPage> TxnTExt
93 for pristine::sanakirja::GenericTxn<T>
94{
95}
96
97pub fn commit<T: pristine::MutTxnT>(
98 txn: std::sync::Arc<std::sync::RwLock<T>>,
99) -> Result<(), T::GraphError> {
100 let txn = if let Ok(txn) = std::sync::Arc::try_unwrap(txn) {
101 txn.into_inner().unwrap()
102 } else {
103 unreachable!()
104 };
105 txn.commit()
106}
107
108pub trait MutTxnTExt: pristine::MutTxnT {
109 fn apply_root_change_if_needed<C: changestore::ChangeStore, R: rand::Rng>(
110 &mut self,
111 changes: &C,
112 channel: &ChannelRef<Self>,
113 rng: R,
114 ) -> Result<
115 Option<(pristine::Hash, u64, pristine::Merkle)>,
116 crate::apply::ApplyError<C::Error, Self>,
117 > {
118 crate::apply::apply_root_change(self, channel, changes, rng)
119 }
120
121 fn apply_change_ws<C: changestore::ChangeStore>(
122 &mut self,
123 changes: &C,
124 channel: &mut Self::Channel,
125 hash: &crate::pristine::Hash,
126 workspace: &mut ApplyWorkspace,
127 ) -> Result<(u64, pristine::Merkle), crate::apply::ApplyError<C::Error, Self>> {
128 crate::apply::apply_change_ws(changes, self, channel, hash, workspace)
129 }
130
131 fn apply_change_rec_ws<C: changestore::ChangeStore>(
132 &mut self,
133 changes: &C,
134 channel: &mut Self::Channel,
135 hash: &crate::pristine::Hash,
136 workspace: &mut ApplyWorkspace,
137 ) -> Result<(), crate::apply::ApplyError<C::Error, Self>> {
138 crate::apply::apply_change_rec_ws(changes, self, channel, hash, workspace, false)
139 }
140
141 fn apply_change<C: changestore::ChangeStore>(
142 &mut self,
143 changes: &C,
144 channel: &mut Self::Channel,
145 hash: &pristine::Hash,
146 ) -> Result<(u64, pristine::Merkle), crate::apply::ApplyError<C::Error, Self>> {
147 crate::apply::apply_change(changes, self, channel, hash)
148 }
149
150 fn apply_change_rec<C: changestore::ChangeStore>(
151 &mut self,
152 changes: &C,
153 channel: &mut Self::Channel,
154 hash: &pristine::Hash,
155 ) -> Result<(), crate::apply::ApplyError<C::Error, Self>> {
156 crate::apply::apply_change_rec(changes, self, channel, hash, false)
157 }
158
159 fn apply_deps_rec<C: changestore::ChangeStore>(
160 &mut self,
161 changes: &C,
162 channel: &mut Self::Channel,
163 hash: &pristine::Hash,
164 ) -> Result<(), crate::apply::ApplyError<C::Error, Self>> {
165 crate::apply::apply_change_rec(changes, self, channel, hash, true)
166 }
167
168 fn apply_local_change_ws(
169 &mut self,
170 channel: &pristine::ChannelRef<Self>,
171 change: &change::Change,
172 hash: &pristine::Hash,
173 inode_updates: &HashMap<usize, InodeUpdate>,
174 workspace: &mut ApplyWorkspace,
175 ) -> Result<(u64, pristine::Merkle), crate::apply::LocalApplyError<Self>> {
176 crate::apply::apply_local_change_ws(self, channel, change, hash, inode_updates, workspace)
177 }
178
179 fn apply_local_change(
180 &mut self,
181 channel: &crate::pristine::ChannelRef<Self>,
182 change: &crate::change::Change,
183 hash: &pristine::Hash,
184 inode_updates: &HashMap<usize, InodeUpdate>,
185 ) -> Result<(u64, pristine::Merkle), crate::apply::LocalApplyError<Self>> {
186 crate::apply::apply_local_change(self, channel, change, hash, inode_updates)
187 }
188
189 fn apply_recorded<C: changestore::ChangeStore>(
190 &mut self,
191 channel: &mut pristine::ChannelRef<Self>,
192 recorded: record::Recorded,
193 changestore: &C,
194 ) -> Result<pristine::Hash, crate::apply::ApplyError<C::Error, Self>> {
195 let contents_hash = {
196 let mut hasher = pristine::Hasher::default();
197 hasher.update(&recorded.contents.lock()[..]);
198 hasher.finish()
199 };
200 let mut change = change::LocalChange {
201 offsets: change::Offsets::default(),
202 hashed: change::Hashed {
203 version: change::VERSION,
204 contents_hash,
205 changes: recorded
206 .actions
207 .into_iter()
208 .map(|rec| rec.globalize(self).unwrap())
209 .collect(),
210 metadata: Vec::new(),
211 dependencies: Vec::new(),
212 extra_known: Vec::new(),
213 header: change::ChangeHeader::default(),
214 },
215 unhashed: None,
216 contents: std::sync::Arc::try_unwrap(recorded.contents)
217 .unwrap()
218 .into_inner(),
219 };
220 let hash = changestore
221 .save_change(&mut change, |_, _| Ok(()))
222 .map_err(apply::ApplyError::Changestore)?;
223 apply::apply_local_change(self, channel, &change, &hash, &recorded.updatables)
224 .map_err(ApplyError::LocalChange)?;
225 Ok(hash)
226 }
227
228 fn unrecord<C: changestore::ChangeStore, W: working_copy::WorkingCopy>(
229 &mut self,
230 changes: &C,
231 channel: &pristine::ChannelRef<Self>,
232 hash: &pristine::Hash,
233 salt: u64,
234 working_copy: &W,
235 ) -> Result<bool, unrecord::UnrecordError<C::Error, W::Error, Self>> {
236 unrecord::unrecord(self, channel, changes, hash, salt, working_copy)
237 }
238
239 fn add_file(&mut self, path: &str, salt: u64) -> Result<Inode, fs::FsError<Self>> {
243 fs::add_inode(self, None, path, false, salt)
244 }
245
246 fn add_dir(&mut self, path: &str, salt: u64) -> Result<Inode, fs::FsError<Self>> {
251 fs::add_inode(self, None, path, true, salt)
252 }
253
254 fn add(&mut self, path: &str, is_dir: bool, salt: u64) -> Result<Inode, fs::FsError<Self>> {
258 fs::add_inode(self, None, path, is_dir, salt)
259 }
260
261 fn move_file(&mut self, a: &str, b: &str, salt: u64) -> Result<(), fs::FsError<Self>> {
262 fs::move_file(self, a, b, salt)
263 }
264
265 fn remove_file(&mut self, a: &str) -> Result<(), fs::FsError<Self>> {
266 fs::remove_file(self, a)
267 }
268}
269
270pub trait TxnTExt: pristine::TxnT {
271 fn is_directory(&self, inode: pristine::Inode) -> Result<bool, Self::TreeError> {
272 fs::is_directory(self, inode).map_err(|e| e.0)
273 }
274
275 fn is_tracked(&self, path: &str) -> Result<bool, Self::TreeError> {
276 fs::is_tracked(self, path).map_err(|e| e.0)
277 }
278
279 fn iter_working_copy<'a>(&'a self) -> WorkingCopyIterator<'a, Self> {
280 fs::iter_working_copy(self, pristine::Inode::ROOT)
281 }
282
283 fn iter_graph_children<'txn, 'changes, P>(
284 &'txn self,
285 changes: &'changes P,
286 channel: &'txn Self::Channel,
287 key: pristine::Position<ChangeId>,
288 ) -> Result<fs::GraphChildren<'txn, 'changes, Self, P>, Self::GraphError>
289 where
290 P: changestore::ChangeStore,
291 {
292 fs::iter_graph_children(self, changes, &self.graph(channel), key)
293 }
294
295 fn has_change(
296 &self,
297 channel: &pristine::ChannelRef<Self>,
298 hash: &pristine::Hash,
299 ) -> Result<Option<u64>, Self::GraphError> {
300 if let Some(cid) = pristine::GraphTxnT::get_internal(self, &hash.into()).map_err(|e| e.0)? {
301 self.get_changeset(self.changes(&channel.read()), cid)
302 .map_err(|e| e.0)
303 .map(|x| x.map(|x| u64::from_le(x.0)))
304 } else {
305 Ok(None)
306 }
307 }
308
309 fn is_alive(
310 &self,
311 channel: &Self::Channel,
312 a: &pristine::Vertex<pristine::ChangeId>,
313 ) -> Result<bool, Self::GraphError> {
314 pristine::is_alive(self, self.graph(channel), a).map_err(|e| e.0)
315 }
316
317 fn current_state(&self, channel: &Self::Channel) -> Result<pristine::Merkle, Self::GraphError> {
318 pristine::current_state(self, channel).map_err(|e| e.0)
319 }
320
321 fn log<'channel, 'txn>(
322 &'txn self,
323 channel: &'channel Self::Channel,
324 from: u64,
325 ) -> Result<Log<'txn, Self>, Self::GraphError> {
326 Ok(Log {
327 txn: self,
328 iter: pristine::changeid_log(self, channel, pristine::L64(from.to_le()))
329 .map_err(|e| e.0)?,
330 })
331 }
332
333 fn log_for_path<'channel, 'txn>(
334 &'txn self,
335 channel: &'channel Self::Channel,
336 pos: pristine::Position<pristine::ChangeId>,
337 from: u64,
338 ) -> Result<pristine::PathChangeset<'channel, 'txn, Self>, Self::GraphError> {
339 pristine::log_for_path(self, channel, pos, from).map_err(|e| e.0)
340 }
341
342 fn rev_log_for_path<'channel, 'txn>(
343 &'txn self,
344 channel: &'channel Self::Channel,
345 pos: pristine::Position<pristine::ChangeId>,
346 from: u64,
347 ) -> Result<pristine::RevPathChangeset<'channel, 'txn, Self>, Self::DepsError> {
348 pristine::rev_log_for_path(self, channel, pos, from).map_err(|e| e.0)
349 }
350
351 fn reverse_log<'channel, 'txn>(
352 &'txn self,
353 channel: &'channel Self::Channel,
354 from: Option<u64>,
355 ) -> Result<RevLog<'txn, Self>, Self::GraphError> {
356 Ok(RevLog {
357 txn: self,
358 iter: pristine::changeid_rev_log(self, channel, from.map(|x| pristine::L64(x.to_le())))
359 .map_err(|e| e.0)?,
360 })
361 }
362
363 fn changeid_reverse_log<'txn>(
364 &'txn self,
365 channel: &Self::Channel,
366 from: Option<pristine::L64>,
367 ) -> Result<
368 pristine::RevCursor<
369 Self,
370 &'txn Self,
371 Self::RevchangesetCursor,
372 pristine::L64,
373 pristine::Pair<pristine::ChangeId, pristine::SerializedMerkle>,
374 >,
375 Self::GraphError,
376 > {
377 pristine::changeid_rev_log(self, channel, from).map_err(|e| e.0)
378 }
379
380 fn get_changes(
381 &self,
382 channel: &pristine::ChannelRef<Self>,
383 n: u64,
384 ) -> Result<Option<(pristine::Hash, pristine::Merkle)>, Self::GraphError> {
385 if let Some(p) = self
386 .get_revchangeset(self.rev_changes(&channel.read()), &pristine::L64(n.to_le()))
387 .map_err(|e| e.0)?
388 {
389 Ok(Some((
390 self.get_external(&p.a.into())
391 .map_err(|e| e.0)?
392 .unwrap()
393 .into(),
394 (&p.b).into(),
395 )))
396 } else {
397 Ok(None)
398 }
399 }
400
401 fn get_revchanges(
402 &self,
403 channel: &pristine::ChannelRef<Self>,
404 h: &pristine::Hash,
405 ) -> Result<Option<u64>, Self::GraphError> {
406 if let Some(h) = pristine::GraphTxnT::get_internal(self, &h.into()).map_err(|e| e.0)? {
407 self.get_changeset(self.changes(&channel.read()), h)
408 .map_err(|e| e.0)
409 .map(|x| x.map(|x| u64::from_le(x.0)))
410 } else {
411 Ok(None)
412 }
413 }
414
415 fn touched_files<'a>(
416 &'a self,
417 h: &pristine::Hash,
418 ) -> Result<Option<Touched<'a, Self>>, Self::DepsError> {
419 if let Some(id) = pristine::GraphTxnT::get_internal(self, &h.into()).map_err(|e| e.0)? {
420 Ok(Some(Touched {
421 txn: self,
422 iter: self.iter_rev_touched_files(id, None).map_err(|e| e.0)?,
423 id: *id,
424 }))
425 } else {
426 Ok(None)
427 }
428 }
429
430 fn find_oldest_path<C: changestore::ChangeStore>(
431 &self,
432 changes: &C,
433 channel: &pristine::ChannelRef<Self>,
434 position: &pristine::Position<pristine::Hash>,
435 ) -> Result<Option<(String, bool)>, output::FileError<C::Error, Self>> {
436 let position = pristine::Position {
437 change: *pristine::GraphTxnT::get_internal(self, &position.change.into())?.unwrap(),
438 pos: position.pos,
439 };
440 if let Some((a, b)) = fs::find_path(changes, self, &channel.read(), false, position)? {
441 Ok(Some((a.join("/"), b)))
442 } else {
443 Ok(None)
444 }
445 }
446
447 fn find_youngest_path<C: changestore::ChangeStore>(
448 &self,
449 changes: &C,
450 channel: &pristine::ChannelRef<Self>,
451 position: pristine::Position<pristine::Hash>,
452 ) -> Result<Option<(String, bool)>, output::FileError<C::Error, Self>> {
453 let position = pristine::Position {
454 change: *pristine::GraphTxnT::get_internal(self, &position.change.into())?.unwrap(),
455 pos: position.pos,
456 };
457 if let Some((a, b)) = fs::find_path(changes, self, &channel.read(), true, position)? {
458 Ok(Some((a.join("/"), b)))
459 } else {
460 Ok(None)
461 }
462 }
463
464 fn follow_oldest_path<C: changestore::ChangeStore>(
465 &self,
466 changes: &C,
467 channel: &pristine::ChannelRef<Self>,
468 path: &str,
469 ) -> Result<(pristine::Position<pristine::ChangeId>, bool), fs::FsErrorC<C::Error, Self>> {
470 fs::follow_oldest_path(changes, self, &channel.read(), path)
471 }
472
473 fn iter_adjacent<'txn>(
474 &'txn self,
475 graph: &'txn Self::Channel,
476 key: Vertex<pristine::ChangeId>,
477 min_flag: pristine::EdgeFlags,
478 max_flag: pristine::EdgeFlags,
479 ) -> Result<pristine::AdjacentIterator<'txn, Self>, pristine::TxnErr<Self::GraphError>> {
480 pristine::iter_adjacent(self, self.graph(graph), key, min_flag, max_flag)
481 }
482}
483
484impl<T: ChannelTxnT + TreeTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>> ArcTxn<T> {
485 pub fn archive<C: changestore::ChangeStore, A: Archive, W: working_copy::WorkingCopy>(
486 &self,
487 changes: &C,
488 channel: &pristine::ChannelRef<T>,
489 arch: &mut A,
490 ) -> Result<Vec<output::Conflict>, output::ArchiveError<C::Error, T, A::Error, W::Error>> {
491 output::archive::<_, _, _, _, W>(changes, self, channel, &mut std::iter::empty(), arch)
492 }
493
494 pub fn archive_prefix<
495 'a,
496 C: changestore::ChangeStore,
497 I: Iterator<Item = &'a str>,
498 A: Archive,
499 W: working_copy::WorkingCopy,
500 >(
501 &self,
502 changes: &C,
503 channel: &pristine::ChannelRef<T>,
504 prefix: &mut I,
505 arch: &mut A,
506 ) -> Result<Vec<output::Conflict>, output::ArchiveError<C::Error, T, A::Error, W::Error>> {
507 output::archive::<_, _, _, _, W>(changes, self, channel, prefix, arch)
508 }
509}
510
511impl<T: MutTxnT> ArcTxn<T> {
512 pub fn archive_with_state<
513 P: changestore::ChangeStore,
514 A: Archive,
515 W: working_copy::WorkingCopy,
516 >(
517 &self,
518 changes: &P,
519 channel: &pristine::ChannelRef<T>,
520 state: &pristine::Merkle,
521 extra: &[pristine::Hash],
522 arch: &mut A,
523 salt: u64,
524 working_copy: &W,
525 ) -> Result<Vec<output::Conflict>, output::ArchiveError<P::Error, T, A::Error, W::Error>> {
526 self.archive_prefix_with_state(
527 changes,
528 channel,
529 state,
530 extra,
531 &mut std::iter::empty(),
532 arch,
533 salt,
534 working_copy,
535 )
536 }
537
538 pub fn archive_prefix_with_state<
542 'a,
543 P: changestore::ChangeStore,
544 A: Archive,
545 W: working_copy::WorkingCopy,
546 I: Iterator<Item = &'a str>,
547 >(
548 &self,
549 changes: &P,
550 channel: &pristine::ChannelRef<T>,
551 state: &pristine::Merkle,
552 extra: &[pristine::Hash],
553 prefix: &mut I,
554 arch: &mut A,
555 salt: u64,
556 working_copy: &W,
557 ) -> Result<Vec<output::Conflict>, output::ArchiveError<P::Error, T, A::Error, W::Error>> {
558 let mut unrecord = Vec::new();
559 let mut found = false;
560 let mut txn = self.write();
561 for x in pristine::changeid_rev_log(&*txn, &channel.read(), None)? {
562 let (_, p) = x?;
563 let m: Merkle = (&p.b).into();
564 if &m == state {
565 found = true;
566 break;
567 } else {
568 unrecord.push(p.a.into())
569 }
570 }
571 debug!("unrecord = {:?}", unrecord);
572 if found {
573 for h in unrecord.iter() {
574 let h = txn.get_external(h)?.unwrap().into();
575 unrecord::unrecord(&mut *txn, channel, changes, &h, salt, working_copy)?;
576 }
577 {
578 let mut channel_ = channel.write();
579 for app in extra.iter() {
580 crate::apply::apply_change_rec(changes, &mut *txn, &mut channel_, app, false)?
581 }
582 }
583 std::mem::drop(txn);
584 output::archive::<_, _, _, _, W>(changes, self, channel, prefix, arch)
585 } else {
586 Err(output::ArchiveError::StateNotFound { state: *state })
587 }
588 }
589}
590
591pub struct Log<'txn, T: pristine::ChannelTxnT> {
592 txn: &'txn T,
593 iter: pristine::Cursor<
594 T,
595 &'txn T,
596 T::RevchangesetCursor,
597 pristine::L64,
598 pristine::Pair<pristine::ChangeId, pristine::SerializedMerkle>,
599 >,
600}
601
602impl<'txn, T: pristine::ChannelTxnT> Iterator for Log<'txn, T> {
603 type Item = Result<
604 (
605 u64,
606 (
607 &'txn pristine::SerializedHash,
608 &'txn pristine::SerializedMerkle,
609 ),
610 ),
611 T::GraphError,
612 >;
613 fn next(&mut self) -> Option<Self::Item> {
614 match self.iter.next() {
615 Some(Ok((n, p))) => {
616 let ext = match self.txn.get_external(&p.a) {
617 Err(pristine::TxnErr(e)) => return Some(Err(e)),
618 Ok(Some(ext)) => ext,
619 Ok(None) => panic!("Unknown change {:?}", p),
620 };
621 Some(Ok((u64::from_le(n.0), (ext, &p.b))))
622 }
623 None => None,
624 Some(Err(e)) => Some(Err(e.0)),
625 }
626 }
627}
628
629pub struct RevLog<'txn, T: pristine::ChannelTxnT> {
630 txn: &'txn T,
631 iter: pristine::RevCursor<
632 T,
633 &'txn T,
634 T::RevchangesetCursor,
635 pristine::L64,
636 pristine::Pair<pristine::ChangeId, pristine::SerializedMerkle>,
637 >,
638}
639
640impl<'txn, T: pristine::ChannelTxnT> Iterator for RevLog<'txn, T> {
641 type Item = Result<
642 (
643 u64,
644 (
645 &'txn pristine::SerializedHash,
646 &'txn pristine::SerializedMerkle,
647 ),
648 ),
649 T::GraphError,
650 >;
651 fn next(&mut self) -> Option<Self::Item> {
652 match self.iter.next() {
653 Some(Ok((n, p))) => match self.txn.get_external(&p.a.into()) {
654 Ok(Some(ext)) => Some(Ok((u64::from_le(n.0), (ext, &p.b)))),
655 Err(e) => Some(Err(e.0)),
656 Ok(None) => panic!("Unknown change {:?}", p),
657 },
658 None => None,
659 Some(Err(e)) => Some(Err(e.0)),
660 }
661 }
662}
663
664pub struct Touched<'txn, T: pristine::DepsTxnT> {
665 txn: &'txn T,
666 iter: pristine::Cursor<
667 T,
668 &'txn T,
669 T::Rev_touched_filesCursor,
670 pristine::ChangeId,
671 pristine::Position<pristine::ChangeId>,
672 >,
673 id: pristine::ChangeId,
674}
675
676impl<
677 'txn,
678 T: pristine::DepsTxnT + pristine::GraphTxnT<GraphError = <T as pristine::DepsTxnT>::DepsError>,
679 > Iterator for Touched<'txn, T>
680{
681 type Item = Result<pristine::Position<pristine::Hash>, T::DepsError>;
682 fn next(&mut self) -> Option<Self::Item> {
683 while let Some(x) = self.iter.next() {
684 let (cid, file) = match x {
685 Ok(x) => x,
686 Err(e) => return Some(Err(e.0)),
687 };
688 if *cid > self.id {
689 return None;
690 } else if *cid == self.id {
691 let change = match self.txn.get_external(&file.change) {
692 Ok(ext) => ext.unwrap(),
693 Err(e) => return Some(Err(e.0)),
694 };
695 return Some(Ok(pristine::Position {
696 change: change.into(),
697 pos: file.pos,
698 }));
699 }
700 }
701 None
702 }
703}
704
705#[doc(hidden)]
706#[derive(Debug, Default, Clone)]
707pub struct Timers {
708 pub alive_output: std::time::Duration,
709 pub alive_graph: std::time::Duration,
710 pub alive_retrieve: std::time::Duration,
711 pub alive_contents: std::time::Duration,
712 pub alive_write: std::time::Duration,
713 pub record: std::time::Duration,
714 pub apply: std::time::Duration,
715 pub repair_context: std::time::Duration,
716 pub check_cyclic_paths: std::time::Duration,
717 pub find_alive: std::time::Duration,
718}
719
720pub static TIMERS: LazyLock<Mutex<Timers>> = LazyLock::new(|| {
721 Mutex::new(Timers {
722 alive_output: std::time::Duration::from_secs(0),
723 alive_graph: std::time::Duration::from_secs(0),
724 alive_retrieve: std::time::Duration::from_secs(0),
725 alive_contents: std::time::Duration::from_secs(0),
726 alive_write: std::time::Duration::from_secs(0),
727 record: std::time::Duration::from_secs(0),
728 apply: std::time::Duration::from_secs(0),
729 repair_context: std::time::Duration::from_secs(0),
730 check_cyclic_paths: std::time::Duration::from_secs(0),
731 find_alive: std::time::Duration::from_secs(0),
732 })
733});
734
735#[doc(hidden)]
736pub fn reset_timers() {
737 *TIMERS.lock().unwrap() = Timers::default();
738}
739#[doc(hidden)]
740pub fn get_timers() -> Timers {
741 TIMERS.lock().unwrap().clone()
742}
743
744pub(crate) fn get_valid_encoding(
745 enc: &chardetng::EncodingDetector,
746 tld: Option<&[u8]>,
747 allow_utf8: bool,
748 buffer: &[u8],
749) -> Option<&'static encoding_rs::Encoding> {
750 if let (encoding, true) = enc.guess_assess(tld, allow_utf8) {
751 if let (s, e, false) = encoding.decode(buffer) {
752 let reencoded = encoding.encode(&s).0;
753
754 if reencoded == buffer {
757 return Some(e);
758 } else if encoding == encoding_rs::UTF_8
759 && buffer.starts_with(b"\xef\xbb\xbf")
760 && &buffer[3..] == &*reencoded {
761 return Some(e);
764 }
765 }
766 }
767 None
768}