1use std::{
33 collections::{BTreeMap, BTreeSet},
34 fmt::Write as _,
35 rc::Rc,
36};
37
38use geop_core_math::geop_error::{GeopError, GeopResult};
39use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
40use serde_json::value::RawValue;
41
42use crate::{
43 reader::{Reader, Reads},
44 runner::Runner,
45};
46
47pub fn made_kept<T: 'static>(
53 reader: &Reader,
54 runner: &Runner,
55 key: &str,
56 keeper: Keeper<T>,
57 make: impl FnOnce(&Reader) -> GeopResult<T>,
58) -> (GeopResult<Rc<T>>, Reads) {
59 let (part, before, own) = (reader.part(), reader.position(), reader.own());
60 let path = &runner.location.file;
61 let Some(cache) = runner.workspace.as_deref().and_then(|w| w.cache()) else {
62 let (made, reads) = reader.recorded(make);
63 return (made.map(Rc::new), reads);
64 };
65 let kind = std::any::type_name::<T>();
66 let args = digest_debug("made of the program", key);
67 let records = cache.builds(path, |builds| builds.records("", key));
68 for record in records.iter().flatten().rev() {
69 if record.kind != kind || record.derivation.args != args {
70 continue;
71 }
72 let reads = (record.derivation.reads.iter())
73 .map(|(kept, digest)| {
74 let (read, now) =
75 part.resolve(kept, before, own, runner, reader.outside_reader())?;
76 (now == *digest).then_some(read)
77 })
78 .collect::<Option<Vec<_>>>();
79 let Some(reads) = reads else {
80 continue;
81 };
82 if let Some(Ok(value)) = cache.builds(path, |builds| builds.value(record, &keeper)) {
83 cache.builds(path, |builds| builds.used(record));
84 runner.count_restored();
85 return (Ok(Rc::new(value)), Reads(reads));
86 }
87 }
88 let (made, reads) = reader.recorded(make);
89 let made = made.map(Rc::new);
90 let derivation = (reads.0.iter())
91 .map(|read| part.read_digest(read, before, own, runner, reader.outside_reader()))
92 .collect::<Option<Vec<_>>>()
93 .map(|reads| Derivation { args, reads });
94 if let (Ok(value), Some(derivation)) = (&made, derivation) {
95 let digest = derivation.digest("", key, kind);
96 let value = value.clone();
97 cache.builds(path, |builds| {
98 if !builds.has("", key, &derivation, digest) {
99 builds.add(Made {
100 step: String::new(),
101 target: key.to_string(),
102 kind,
103 derivation: Rc::new(derivation),
104 digest,
105 write: Box::new(move |pool| (keeper.write)(&value, pool)),
106 });
107 }
108 });
109 }
110 (made, reads)
111}
112
113pub const FORMAT: u32 = 1;
116
117const KEPT_RECORDS: usize = 16;
121
122pub struct Keeper<T> {
126 pub write: fn(&T, &mut Pool) -> GeopResult<String>,
129 pub read: fn(&str, &Pool) -> GeopResult<T>,
131}
132
133impl<T> Clone for Keeper<T> {
134 fn clone(&self) -> Self {
135 *self
136 }
137}
138
139impl<T> Copy for Keeper<T> {}
140
141impl<T: Serialize + DeserializeOwned> Keeper<T> {
142 pub const SERDE: Self = Self {
145 write: write_serde::<T>,
146 read: read_serde::<T>,
147 };
148}
149
150fn write_serde<T: Serialize>(value: &T, _: &mut Pool) -> GeopResult<String> {
151 to_text(value)
152}
153
154fn read_serde<T: DeserializeOwned>(text: &str, _: &Pool) -> GeopResult<T> {
155 from_text(text)
156}
157
158fn to_text<T: Serialize>(value: &T) -> GeopResult<String> {
160 serde_json::to_string(value).map_err(|e| GeopError::new(format!("writing a kept value: {e}")))
161}
162
163fn from_text<T: DeserializeOwned>(text: &str) -> GeopResult<T> {
165 serde_json::from_str(text).map_err(|e| GeopError::new(format!("reading a kept value: {e}")))
166}
167
168#[derive(Default)]
172pub struct Pool {
173 values: BTreeMap<String, Box<RawValue>>,
174 used: BTreeSet<String>,
177 read: std::cell::RefCell<BTreeMap<String, Rc<dyn std::any::Any>>>,
179}
180
181impl Pool {
182 pub fn put<V: Serialize>(&mut self, value: &V) -> GeopResult<String> {
184 let text = to_text(value)?;
185 let key = format!("{:032x}", digest_text(&text));
186 if !self.values.contains_key(&key) {
187 let raw = RawValue::from_string(text)
188 .map_err(|e| GeopError::new(format!("writing a kept value: {e}")))?;
189 self.values.insert(key.clone(), raw);
190 }
191 self.used.insert(key.clone());
192 Ok(key)
193 }
194
195 pub fn get<V: DeserializeOwned>(&self, key: &str) -> GeopResult<V> {
197 let raw = (self.values.get(key)).ok_or_else(|| {
198 GeopError::new(format!("a kept value names {key}, which is not kept"))
199 })?;
200 from_text(raw.get())
201 }
202
203 pub fn shared<V: 'static>(
208 &self,
209 key: &str,
210 read: impl FnOnce() -> GeopResult<V>,
211 ) -> GeopResult<Rc<V>> {
212 let at = format!("{}:{key}", std::any::type_name::<V>());
213 if let Some(kept) = self.read.borrow().get(&at)
214 && let Ok(kept) = kept.clone().downcast::<V>()
215 {
216 return Ok(kept);
217 }
218 let kept = Rc::new(read()?);
219 (self.read.borrow_mut()).insert(at, kept.clone());
220 Ok(kept)
221 }
222
223 pub fn len(&self) -> usize {
225 self.values.len()
226 }
227
228 pub fn is_empty(&self) -> bool {
230 self.values.is_empty()
231 }
232
233 fn take_used(&mut self) -> BTreeSet<String> {
235 std::mem::take(&mut self.used)
236 }
237}
238
239#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
242pub struct Digest(pub u128);
243
244impl Serialize for Digest {
245 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
246 serializer.collect_str(&format_args!("{:032x}", self.0))
247 }
248}
249
250impl<'de> Deserialize<'de> for Digest {
251 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
252 let text = String::deserialize(deserializer)?;
253 u128::from_str_radix(&text, 16)
254 .map(Digest)
255 .map_err(serde::de::Error::custom)
256 }
257}
258
259pub(crate) struct Hasher(u128);
262
263impl Default for Hasher {
264 fn default() -> Self {
265 Self(0x6c62272e07bb014262b821756295c58d)
266 }
267}
268
269impl Hasher {
270 pub(crate) fn bytes(&mut self, bytes: &[u8]) {
271 const PRIME: u128 = 0x0000000001000000000000000000013B;
272 for &byte in bytes {
273 self.0 ^= u128::from(byte);
274 self.0 = self.0.wrapping_mul(PRIME);
275 }
276 }
277
278 pub(crate) fn text(&mut self, text: &str) -> &mut Self {
280 self.bytes(&(text.len() as u64).to_le_bytes());
281 self.bytes(text.as_bytes());
282 self
283 }
284
285 pub(crate) fn digest(&mut self, digest: Digest) -> &mut Self {
286 self.bytes(&digest.0.to_le_bytes());
287 self
288 }
289
290 pub(crate) fn finish(&self) -> Digest {
291 Digest(self.0)
292 }
293}
294
295impl std::fmt::Write for Hasher {
296 fn write_str(&mut self, s: &str) -> std::fmt::Result {
297 self.bytes(s.as_bytes());
298 Ok(())
299 }
300}
301
302pub(crate) fn digest_text(text: &str) -> u128 {
304 let mut hasher = Hasher::default();
305 hasher.bytes(text.as_bytes());
306 hasher.0
307}
308
309pub(crate) fn digest_debug<T: std::fmt::Debug + ?Sized>(what: &str, value: &T) -> Digest {
314 let mut hasher = Hasher::default();
315 hasher.text(what);
316 let _ = write!(hasher, "{value:?}");
318 hasher.finish()
319}
320
321#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
324pub(crate) enum KeptRead {
325 Target(String),
327 Input(String),
328 State(String),
329 File(String),
330 Listed(String),
332 Inside(String, Box<KeptRead>),
333}
334
335#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
338pub(crate) struct Derivation {
339 pub args: Digest,
340 pub reads: Vec<(KeptRead, Digest)>,
341}
342
343impl Derivation {
344 pub(crate) fn digest(&self, step: &str, target: &str, kind: &str) -> Digest {
346 let mut hasher = Hasher::default();
347 hasher.text(step).text(target).text(kind).digest(self.args);
348 for (read, digest) in &self.reads {
349 let _ = write!(hasher, "{read:?}");
350 hasher.digest(*digest);
351 }
352 hasher.finish()
353 }
354}
355
356#[derive(Serialize, Deserialize)]
360pub(crate) struct Record {
361 pub step: String,
362 pub target: String,
363 pub kind: String,
364 pub derivation: Derivation,
365 pub digest: Digest,
366 value: Box<RawValue>,
367 uses: BTreeSet<String>,
368}
369
370impl Record {
371 fn is(&self, derivation: &Derivation, digest: Digest) -> bool {
373 self.digest == digest && self.derivation == *derivation
374 }
375}
376
377pub(crate) struct Made {
380 pub step: String,
381 pub target: String,
382 pub kind: &'static str,
383 pub derivation: Rc<Derivation>,
384 pub digest: Digest,
385 pub write: WriteMade,
387}
388
389pub(crate) type WriteMade = Box<dyn Fn(&mut Pool) -> GeopResult<String>>;
391
392#[derive(Serialize, Deserialize)]
394struct File {
395 format: u32,
396 build: String,
398 records: Vec<Record>,
399 pool: BTreeMap<String, Box<RawValue>>,
400}
401
402#[derive(Default)]
406pub(crate) struct Builds {
407 records: BTreeMap<(String, String), Vec<Rc<Record>>>,
408 pool: Pool,
409 made: Vec<Made>,
410 order: Option<BTreeSet<String>>,
415 ordered: bool,
418 changed: bool,
419}
420
421impl Builds {
422 pub(crate) fn read(text: Option<&str>, build: &str) -> Self {
425 let file = text.and_then(|text| serde_json::from_str::<File>(text).ok());
426 let Some(file) = file.filter(|file| file.format == FORMAT && file.build == build) else {
427 return Self::default();
428 };
429 let mut records: BTreeMap<_, Vec<Rc<Record>>> = BTreeMap::new();
430 for record in file.records {
431 let key = (record.step.clone(), record.target.clone());
432 records.entry(key).or_default().push(Rc::new(record));
433 }
434 Self {
435 records,
436 pool: Pool {
437 values: file.pool,
438 ..Pool::default()
439 },
440 ..Self::default()
441 }
442 }
443
444 pub(crate) fn records(&self, step: &str, target: &str) -> Vec<Rc<Record>> {
446 let key = (step.to_string(), target.to_string());
447 self.records.get(&key).cloned().unwrap_or_default()
448 }
449
450 pub(crate) fn value<T>(&self, record: &Record, keeper: &Keeper<T>) -> GeopResult<T> {
452 (keeper.read)(record.value.get(), &self.pool).map_err(|e| {
453 e.with_context(format!(
454 "reading the kept target `{}` of step `{}`",
455 record.target, record.step
456 ))
457 })
458 }
459
460 pub(crate) fn used(&mut self, record: &Rc<Record>) {
463 let key = (record.step.clone(), record.target.clone());
464 if let Some(records) = self.records.get_mut(&key)
465 && let Some(at) = records.iter().position(|r| Rc::ptr_eq(r, record))
466 {
467 let record = records.remove(at);
468 records.push(record);
469 }
470 }
471
472 pub(crate) fn has(
474 &self,
475 step: &str,
476 target: &str,
477 derivation: &Derivation,
478 digest: Digest,
479 ) -> bool {
480 let key = (step.to_string(), target.to_string());
481 let kept = (self.records.get(&key).into_iter().flatten()).any(|r| r.is(derivation, digest));
482 kept || (self.made.iter()).any(|m| {
483 m.step == step
484 && m.target == target
485 && m.digest == digest
486 && *m.derivation == *derivation
487 })
488 }
489
490 pub(crate) fn add(&mut self, made: Made) {
493 let same = |m: &Made| m.step == made.step && m.target == made.target;
494 if self.made.iter().filter(|m| same(m)).count() >= KEPT_RECORDS
495 && let Some(at) = self.made.iter().position(same)
496 {
497 self.made.remove(at);
498 }
499 self.made.push(made);
500 self.changed = true;
501 }
502
503 pub(crate) fn order(&mut self, order: &[String]) {
506 let mut order: BTreeSet<String> = order.iter().cloned().collect();
507 if std::mem::replace(&mut self.ordered, true)
508 && let Some(noted) = &self.order
509 {
510 order.extend(noted.iter().cloned());
511 }
512 if self.order.as_ref() != Some(&order) {
513 self.changed = true;
514 self.order = Some(order);
515 }
516 }
517
518 pub(crate) fn write(&mut self, build: &str) -> Option<String> {
524 if !std::mem::take(&mut self.changed) {
525 return None;
526 }
527 self.ordered = false;
528 for made in std::mem::take(&mut self.made) {
529 let key = (made.step.clone(), made.target.clone());
530 let records = self.records.entry(key).or_default();
531 if let Some(at) = records
532 .iter()
533 .position(|r| r.is(&made.derivation, made.digest))
534 {
535 let record = records.remove(at);
536 records.push(record);
537 continue;
538 }
539 self.pool.take_used();
540 let Ok(text) = (made.write)(&mut self.pool) else {
541 continue;
542 };
543 let Ok(value) = RawValue::from_string(text) else {
544 continue;
545 };
546 records.push(Rc::new(Record {
547 step: made.step,
548 target: made.target,
549 kind: made.kind.to_string(),
550 derivation: (*made.derivation).clone(),
551 digest: made.digest,
552 value,
553 uses: self.pool.take_used(),
554 }));
555 }
556 if let Some(order) = &self.order {
557 (self.records).retain(|(step, _), _| step.is_empty() || order.contains(step));
559 }
560 for records in self.records.values_mut() {
561 let excess = records.len().saturating_sub(KEPT_RECORDS);
562 records.drain(..excess);
563 }
564 let used: BTreeSet<&String> = (self.records.values().flatten())
565 .flat_map(|record| &record.uses)
566 .collect();
567 let used: BTreeSet<String> = used.into_iter().cloned().collect();
568 self.pool.values.retain(|key, _| used.contains(key));
569 let file = FileRef {
570 format: FORMAT,
571 build,
572 records: self.records.values().flatten().map(|r| &**r).collect(),
573 pool: &self.pool.values,
574 };
575 serde_json::to_string(&file).ok()
576 }
577}
578
579#[derive(Serialize)]
581struct FileRef<'a> {
582 format: u32,
583 build: &'a str,
584 records: Vec<&'a Record>,
585 pool: &'a BTreeMap<String, Box<RawValue>>,
586}