use std::sync::atomic::{AtomicU64, Ordering};
use pagesift_api::types::PlanStep;
use super::*;
const SNIFF_HEAD: usize = 4096;
pub(super) const BASELINE_UNKNOWN: u64 = u64::MAX;
pub(super) fn render_probe(
opts: &ScrapeOptions,
base: Url,
baseline: Arc<AtomicU64>,
) -> Arc<dyn Fn(String) -> pagesift_cdp::ProbeFuture + Send + Sync> {
let only_main_content = opts.only_main_content;
Arc::new(move |html: String| -> pagesift_cdp::ProbeFuture {
let copts = pagesift_content::Options {
base_url: Some(base.clone()),
only_main_content,
status: None,
detect_barrier: false,
..Default::default()
};
let baseline = baseline.clone();
Box::pin(async move {
let known = baseline.load(Ordering::Relaxed);
if known == BASELINE_UNKNOWN {
return false;
}
cpu_task(move || {
let doc = CONTENT_WORKSPACE.with(|ws| {
pagesift_content::process_with_workspace(&html, &copts, &mut ws.borrow_mut())
});
text_weight(&doc.markdown) as u64 <= known
})
.await
.unwrap_or(false)
})
})
}
impl Scraper {
pub(super) fn plan_speculate(&self, url: &str, body: &[u8]) -> (bool, PlanStep) {
let mut step = PlanStep {
action: "speculate".into(),
..PlanStep::default()
};
let head = &body[..body.len().min(SNIFF_HEAD)];
let script = pagesift_flow::sniff::has_script_tag(&String::from_utf8_lossy(head));
let p = self
.planner
.mean(&super::planner::render_family(url, script));
let saved = self.render_stats.extract_estimate(body.len());
let (Some(p), Some(saved)) = (p, saved) else {
return (false, step);
};
let engine = self
.browsers
.first()
.and_then(|(c, _)| c.engine)
.unwrap_or(pagesift_cdp::EngineKind::Chrome);
let c = self.render_stats.min_attempt(engine).as_secs_f64();
let (go, value, charge) = speculate_rule(
p,
saved.as_secs_f64(),
c,
self.render_stats.tab_utilization(),
);
step.chosen = go;
step.learned = true;
step.p = Some(p);
step.value = Some(value);
step.charge = Some(charge);
(go, step)
}
}
fn speculate_rule(p: f64, s: f64, c: f64, rho: f64) -> (bool, f64, f64) {
let price = if rho >= 1.0 {
f64::INFINITY
} else {
1.0 / (1.0 - rho.max(0.0))
};
let value = p * s;
let charge = price * (1.0 - p) * (s + c);
(value > charge, value, charge)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn triggers_follow_the_formula_under_idle_and_saturated_load() {
let (idle, v, ch) = speculate_rule(0.98, 0.05, 0.3, 0.0);
assert!(idle && (v - 0.049).abs() < 1e-12 && (ch - 0.007).abs() < 1e-12);
let (busy, ..) = speculate_rule(0.98, 0.05, 0.3, 0.9);
assert!(!busy);
assert!(!speculate_rule(0.98, 0.05, 0.3, 1.0).0);
assert!(!speculate_rule(0.5, 0.05, 0.3, 0.0).0);
let p = 0.35 / 0.4;
assert!(!speculate_rule(p - 1e-6, 0.05, 0.3, 0.0).0);
assert!(speculate_rule(p + 1e-6, 0.05, 0.3, 0.0).0);
}
}