#[inline(always)]
fn is_number_start(b: u8) -> bool {
b.is_ascii_digit() || b == b'-' || b == b'.'
}
#[inline]
fn estimate_float_count(bytes: &[u8]) -> usize {
bytes.len() / 8
}
#[inline]
fn estimate_int_count(bytes: &[u8]) -> usize {
bytes.len() / 4
}
#[inline]
pub fn parse_coordinates_direct(bytes: &[u8]) -> Vec<f32> {
let mut result = Vec::with_capacity(estimate_float_count(bytes));
let mut pos = 0;
let len = bytes.len();
while pos < len {
while pos < len && !is_number_start(bytes[pos]) {
pos += 1;
}
if pos >= len {
break;
}
match fast_float::parse_partial::<f32, _>(&bytes[pos..]) {
Ok((value, consumed)) if consumed > 0 => {
result.push(value);
pos += consumed;
}
_ => {
pos += 1;
}
}
}
result
}
#[inline]
pub fn parse_coordinates_direct_f64(bytes: &[u8]) -> Vec<f64> {
let mut result = Vec::with_capacity(estimate_float_count(bytes));
let mut pos = 0;
let len = bytes.len();
while pos < len {
while pos < len && !is_number_start(bytes[pos]) {
pos += 1;
}
if pos >= len {
break;
}
match fast_float::parse_partial::<f64, _>(&bytes[pos..]) {
Ok((value, consumed)) if consumed > 0 => {
result.push(value);
pos += consumed;
}
_ => {
pos += 1;
}
}
}
result
}
#[inline]
pub fn parse_indices_direct(bytes: &[u8]) -> Vec<u32> {
let mut result = Vec::with_capacity(estimate_int_count(bytes));
let mut pos = 0;
let len = bytes.len();
while pos < len {
while pos < len && !bytes[pos].is_ascii_digit() {
pos += 1;
}
if pos >= len {
break;
}
let mut value: u32 = 0;
while pos < len && bytes[pos].is_ascii_digit() {
value = value
.wrapping_mul(10)
.wrapping_add((bytes[pos] - b'0') as u32);
pos += 1;
}
result.push(value.saturating_sub(1));
}
result
}
#[inline]
pub fn extract_coordinate_list_from_entity(bytes: &[u8]) -> Option<Vec<f32>> {
let start = memchr::memmem::find(bytes, b"((")?;
let end = memchr::memmem::rfind(bytes, b"))")?;
if end <= start {
return None;
}
Some(parse_coordinates_direct(&bytes[start..end + 2]))
}
#[inline]
pub fn extract_face_indices_from_entity(bytes: &[u8]) -> Option<Vec<u32>> {
let mut paren_depth = 0;
let mut comma_count = 0;
let mut attr_start = None;
let mut attr_end = None;
for (i, &b) in bytes.iter().enumerate() {
match b {
b'(' => {
if paren_depth == 1 && comma_count == 3 && attr_start.is_none() {
attr_start = Some(i);
}
paren_depth += 1;
}
b')' => {
paren_depth -= 1;
if paren_depth == 1
&& comma_count == 3
&& attr_start.is_some()
&& attr_end.is_none()
{
attr_end = Some(i + 1);
}
}
b',' if paren_depth == 1 => {
if comma_count == 3 && attr_end.is_none() && attr_start.is_some() {
attr_end = Some(i);
}
comma_count += 1;
if comma_count == 3 {
}
}
_ => {}
}
}
let start = attr_start?;
let end = attr_end?;
if end <= start {
return None;
}
Some(parse_indices_direct(&bytes[start..end]))
}
#[inline]
pub fn should_use_fast_path(type_name: &str) -> bool {
matches!(
type_name.to_uppercase().as_str(),
"IFCCARTESIANPOINTLIST3D"
| "IFCTRIANGULATEDFACESET"
| "IFCPOLYGONALFACESET"
| "IFCINDEXEDPOLYGONALFACE"
)
}
#[inline]
pub fn extract_entity_type_name(bytes: &[u8]) -> Option<&str> {
let eq_pos = bytes.iter().position(|&b| b == b'=')?;
let paren_pos = bytes[eq_pos..].iter().position(|&b| b == b'(')?;
let type_start = eq_pos + 1;
let type_end = eq_pos + paren_pos;
if type_end <= type_start {
return None;
}
std::str::from_utf8(&bytes[type_start..type_end]).ok()
}
#[inline]
pub fn extract_first_entity_ref(bytes: &[u8]) -> Option<u32> {
let paren_pos = bytes.iter().position(|&b| b == b'(')?;
let content = &bytes[paren_pos + 1..];
let hash_pos = content.iter().position(|&b| b == b'#')?;
let id_start = hash_pos + 1;
let mut id: u32 = 0;
let mut i = id_start;
while i < content.len() && content[i].is_ascii_digit() {
id = id.wrapping_mul(10).wrapping_add((content[i] - b'0') as u32);
i += 1;
}
if i > id_start {
Some(id)
} else {
None
}
}
#[derive(Debug, Clone)]
pub struct FastMeshData {
pub positions: Vec<f32>,
pub indices: Vec<u32>,
}
#[inline]
pub fn process_triangulated_faceset_direct<F>(
faceset_bytes: &[u8],
get_entity_bytes: F,
) -> Option<FastMeshData>
where
F: Fn(u32) -> Option<Vec<u8>>,
{
let coord_entity_id = extract_first_entity_ref(faceset_bytes)?;
let coord_bytes = get_entity_bytes(coord_entity_id)?;
let positions = parse_coordinates_direct(&coord_bytes);
let indices = extract_face_indices_from_entity(faceset_bytes)?;
Some(FastMeshData { positions, indices })
}
#[inline]
pub fn extract_entity_refs_from_list(bytes: &[u8]) -> Vec<u32> {
let mut ids = Vec::with_capacity(16);
let mut i = 0;
let len = bytes.len();
while i < len {
while i < len && bytes[i] != b'#' {
i += 1;
}
if i >= len {
break;
}
i += 1;
let mut id: u32 = 0;
while i < len && bytes[i].is_ascii_digit() {
id = id.wrapping_mul(10).wrapping_add((bytes[i] - b'0') as u32);
i += 1;
}
if id > 0 {
ids.push(id);
}
}
ids
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_coordinates_direct() {
let bytes = b"((0.,0.,150.),(0.,40.,140.),(100.,0.,0.))";
let coords = parse_coordinates_direct(bytes);
assert_eq!(coords.len(), 9);
assert!((coords[0] - 0.0).abs() < 0.001);
assert!((coords[1] - 0.0).abs() < 0.001);
assert!((coords[2] - 150.0).abs() < 0.001);
assert!((coords[3] - 0.0).abs() < 0.001);
assert!((coords[4] - 40.0).abs() < 0.001);
assert!((coords[5] - 140.0).abs() < 0.001);
}
#[test]
fn test_parse_indices_direct() {
let bytes = b"((1,2,3),(2,1,4),(5,6,7))";
let indices = parse_indices_direct(bytes);
assert_eq!(indices.len(), 9);
assert_eq!(indices[0], 0); assert_eq!(indices[1], 1); assert_eq!(indices[2], 2); assert_eq!(indices[3], 1); assert_eq!(indices[4], 0); assert_eq!(indices[5], 3); }
#[test]
fn test_parse_scientific_notation() {
let bytes = b"((1.5E-10,2.0e+5,-3.14))";
let coords = parse_coordinates_direct(bytes);
assert_eq!(coords.len(), 3);
assert!((coords[0] - 1.5e-10).abs() < 1e-15);
assert!((coords[1] - 2.0e5).abs() < 1.0);
assert!((coords[2] - (-std::f32::consts::PI)).abs() < 0.01);
}
#[test]
fn test_parse_negative_numbers() {
let bytes = b"((-1.0,-2.5,3.0))";
let coords = parse_coordinates_direct(bytes);
assert_eq!(coords.len(), 3);
assert!((coords[0] - (-1.0)).abs() < 0.001);
assert!((coords[1] - (-2.5)).abs() < 0.001);
assert!((coords[2] - 3.0).abs() < 0.001);
}
#[test]
fn test_extract_coordinate_list() {
let entity = b"#78=IFCCARTESIANPOINTLIST3D(((0.,0.,150.),(100.,0.,0.)));";
let coords = extract_coordinate_list_from_entity(entity).unwrap();
assert_eq!(coords.len(), 6);
assert!((coords[0] - 0.0).abs() < 0.001);
assert!((coords[2] - 150.0).abs() < 0.001);
assert!((coords[3] - 100.0).abs() < 0.001);
}
#[test]
fn test_should_use_fast_path() {
assert!(should_use_fast_path("IFCCARTESIANPOINTLIST3D"));
assert!(should_use_fast_path("IFCTRIANGULATEDFACESET"));
assert!(should_use_fast_path("IfcTriangulatedFaceSet"));
assert!(!should_use_fast_path("IFCWALL"));
assert!(!should_use_fast_path("IFCEXTRUDEDAREASOLID"));
}
}