use std::sync::Arc;
use vyre_foundation::ir::model::expr::Ident;
use vyre_foundation::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
pub const OP_ID: &str = "vyre-primitives::text::line_index";
#[must_use]
pub fn line_index(source: &str, lines: &str, n: u32) -> Program {
let body = vec![Node::Region {
generator: Ident::from(OP_ID),
source_region: None,
body: Arc::new(vec![Node::if_then(
Expr::eq(Expr::InvocationId { axis: 0 }, Expr::u32(0)),
vec![
Node::let_bind("line", Expr::u32(0)),
Node::let_bind("prev_was_cr", Expr::u32(0)),
Node::loop_for(
"i",
Expr::u32(0),
Expr::u32(n),
vec![
Node::let_bind(
"byte",
Expr::bitand(Expr::load(source, Expr::var("i")), Expr::u32(0xFF)),
),
Node::store(lines, Expr::var("i"), Expr::var("line")),
Node::if_then_else(
Expr::eq(Expr::var("byte"), Expr::u32(0x0A)),
vec![
Node::assign("line", Expr::add(Expr::var("line"), Expr::u32(1))),
Node::assign("prev_was_cr", Expr::u32(0)),
],
vec![Node::if_then_else(
Expr::eq(Expr::var("byte"), Expr::u32(0x0D)),
vec![
Node::assign("prev_was_cr", Expr::u32(1)),
],
vec![
Node::if_then(
Expr::eq(Expr::var("prev_was_cr"), Expr::u32(1)),
vec![Node::assign(
"line",
Expr::add(Expr::var("line"), Expr::u32(1)),
)],
),
Node::assign("prev_was_cr", Expr::u32(0)),
],
)],
),
],
),
],
)]),
}];
Program::wrapped(
vec![
BufferDecl::storage(source, 0, BufferAccess::ReadOnly, DataType::U32).with_count(n),
BufferDecl::output(lines, 1, DataType::U32).with_count(n),
],
[1, 1, 1],
body,
)
}
#[must_use]
pub fn cpu_ref(source: &[u8]) -> Vec<u32> {
let mut out = Vec::with_capacity(source.len());
let mut line: u32 = 0;
let mut prev_was_cr = false;
for &byte in source {
if prev_was_cr && byte != b'\n' {
line += 1;
}
out.push(line);
if byte == b'\n' {
line += 1;
prev_was_cr = false;
} else {
prev_was_cr = byte == b'\r';
}
}
out
}
#[cfg(feature = "inventory-registry")]
inventory::submit! {
crate::harness::OpEntry::new(
OP_ID,
|| line_index("source", "lines", 5),
Some(|| {
vec![vec![
vec![0x61, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00],
vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
]]
}),
Some(|| {
vec![vec![
vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00],
]]
}),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cpu_ref_no_newlines() {
assert_eq!(cpu_ref(b"Hello"), vec![0; 5]);
}
#[test]
fn cpu_ref_unix_lf() {
assert_eq!(cpu_ref(b"ab\ncd"), vec![0, 0, 0, 1, 1]);
}
#[test]
fn cpu_ref_windows_crlf() {
assert_eq!(cpu_ref(b"ab\r\ncd"), vec![0, 0, 0, 0, 1, 1]);
}
#[test]
fn cpu_ref_mac_classic_cr() {
assert_eq!(cpu_ref(b"ab\rcd"), vec![0, 0, 0, 1, 1]);
}
#[test]
fn cpu_ref_multiple_newlines() {
assert_eq!(cpu_ref(b"a\n\nb"), vec![0, 0, 1, 2]);
}
#[test]
fn cpu_ref_trailing_lone_cr_does_not_increment_after_eof() {
assert_eq!(cpu_ref(b"ab\r"), vec![0, 0, 0]);
}
}