#[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_float2::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_float2::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;
let mut overflowed = false;
while pos < len && bytes[pos].is_ascii_digit() {
if !overflowed {
match value
.checked_mul(10)
.and_then(|v| v.checked_add((bytes[pos] - b'0') as u32))
{
Some(v) => value = v,
None => overflowed = true,
}
}
pos += 1;
}
if overflowed {
value = u32::MAX;
}
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;
}
_ => {}
}
}
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"
| "IFCTRIANGULATEDIRREGULARNETWORK"
| "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)]
#[path = "fast_parse_tests.rs"]
mod tests;