1use crate::error::{Error, Result};
10use crate::parser::parse_entity;
11use crate::schema_gen::{AttributeValue, DecodedEntity};
12use rustc_hash::FxHashMap;
13use std::sync::Arc;
14
15pub type EntityIndex = FxHashMap<u32, (usize, usize)>;
17
18#[inline]
21pub fn build_entity_index(content: &str) -> EntityIndex {
22 let bytes = content.as_bytes();
23 let len = bytes.len();
24
25 let estimated_entities = len / 50;
27 let mut index = FxHashMap::with_capacity_and_hasher(estimated_entities, Default::default());
28
29 let mut pos = 0;
30
31 while pos < len {
32 let remaining = &bytes[pos..];
34 let hash_offset = match memchr::memchr(b'#', remaining) {
35 Some(offset) => offset,
36 None => break,
37 };
38
39 let start = pos + hash_offset;
40 pos = start + 1;
41
42 let id_start = pos;
44 while pos < len && bytes[pos].is_ascii_digit() {
45 pos += 1;
46 }
47 let id_end = pos;
48
49 while pos < len && bytes[pos].is_ascii_whitespace() {
51 pos += 1;
52 }
53
54 if id_end > id_start && pos < len && bytes[pos] == b'=' {
55 let id = parse_u32_inline(bytes, id_start, id_end);
57
58 let entity_content = &bytes[pos..];
60 if let Some(semicolon_offset) = memchr::memchr(b';', entity_content) {
61 pos += semicolon_offset + 1; index.insert(id, (start, pos));
63 } else {
64 break; }
66 }
67 }
68
69 index
70}
71
72#[inline]
74fn parse_u32_inline(bytes: &[u8], start: usize, end: usize) -> u32 {
75 let mut result: u32 = 0;
76 for &byte in &bytes[start..end] {
77 let digit = byte.wrapping_sub(b'0');
78 result = result.wrapping_mul(10).wrapping_add(digit as u32);
79 }
80 result
81}
82
83pub struct EntityDecoder<'a> {
85 content: &'a str,
86 cache: FxHashMap<u32, Arc<DecodedEntity>>,
89 entity_index: Option<Arc<EntityIndex>>,
93 point_cache: FxHashMap<u32, (f64, f64, f64)>,
96}
97
98impl<'a> EntityDecoder<'a> {
99 pub fn new(content: &'a str) -> Self {
101 Self {
102 content,
103 cache: FxHashMap::default(),
104 entity_index: None,
105 point_cache: FxHashMap::default(),
106 }
107 }
108
109 pub fn with_index(content: &'a str, index: EntityIndex) -> Self {
111 Self {
112 content,
113 cache: FxHashMap::default(),
114 entity_index: Some(Arc::new(index)),
115 point_cache: FxHashMap::default(),
116 }
117 }
118
119 pub fn with_arc_index(content: &'a str, index: Arc<EntityIndex>) -> Self {
121 Self {
122 content,
123 cache: FxHashMap::default(),
124 entity_index: Some(index),
125 point_cache: FxHashMap::default(),
126 }
127 }
128
129 fn build_index(&mut self) {
132 if self.entity_index.is_some() {
133 return; }
135 self.entity_index = Some(Arc::new(build_entity_index(self.content)));
136 }
137
138 #[inline]
141 pub fn decode_at(&mut self, start: usize, end: usize) -> Result<DecodedEntity> {
142 let line = &self.content[start..end];
143 let (id, ifc_type, tokens) = parse_entity(line).map_err(|e| {
144 Error::parse(
146 0,
147 format!(
148 "Failed to parse entity: {:?}, input: {:?}",
149 e,
150 &line[..line.len().min(100)]
151 ),
152 )
153 })?;
154
155 if let Some(entity_arc) = self.cache.get(&id) {
157 return Ok(entity_arc.as_ref().clone());
158 }
159
160 let attributes = tokens
162 .iter()
163 .map(|token| AttributeValue::from_token(token))
164 .collect();
165
166 let entity = DecodedEntity::new(id, ifc_type, attributes);
167 self.cache.insert(id, Arc::new(entity.clone()));
168 Ok(entity)
169 }
170
171 #[inline]
174 pub fn decode_at_with_id(
175 &mut self,
176 id: u32,
177 start: usize,
178 end: usize,
179 ) -> Result<DecodedEntity> {
180 if let Some(entity_arc) = self.cache.get(&id) {
182 return Ok(entity_arc.as_ref().clone());
183 }
184
185 self.decode_at(start, end)
187 }
188
189 #[inline]
191 pub fn decode_by_id(&mut self, entity_id: u32) -> Result<DecodedEntity> {
192 if let Some(entity_arc) = self.cache.get(&entity_id) {
194 return Ok(entity_arc.as_ref().clone());
195 }
196
197 self.build_index();
199
200 let (start, end) = self
202 .entity_index
203 .as_ref()
204 .and_then(|idx| idx.get(&entity_id).copied())
205 .ok_or_else(|| Error::parse(0, format!("Entity #{} not found", entity_id)))?;
206
207 self.decode_at(start, end)
208 }
209
210 #[inline]
213 pub fn resolve_ref(&mut self, attr: &AttributeValue) -> Result<Option<DecodedEntity>> {
214 match attr.as_entity_ref() {
215 Some(id) => Ok(Some(self.decode_by_id(id)?)),
216 None => Ok(None),
217 }
218 }
219
220 pub fn resolve_ref_list(&mut self, attr: &AttributeValue) -> Result<Vec<DecodedEntity>> {
222 let list = attr
223 .as_list()
224 .ok_or_else(|| Error::parse(0, "Expected list".to_string()))?;
225
226 let mut entities = Vec::with_capacity(list.len());
227 for item in list {
228 if let Some(id) = item.as_entity_ref() {
229 entities.push(self.decode_by_id(id)?);
230 }
231 }
232 Ok(entities)
233 }
234
235 pub fn get_cached(&self, entity_id: u32) -> Option<DecodedEntity> {
237 self.cache.get(&entity_id).map(|arc| arc.as_ref().clone())
238 }
239
240 pub fn reserve_cache(&mut self, additional: usize) {
246 self.cache.reserve(additional);
247 }
248
249 pub fn clear_cache(&mut self) {
251 self.cache.clear();
252 self.point_cache.clear();
253 }
254
255 pub fn clear_point_cache(&mut self) {
258 self.point_cache.clear();
259 }
260
261 pub fn cache_size(&self) -> usize {
263 self.cache.len()
264 }
265
266 #[inline]
269 pub fn get_raw_bytes(&mut self, entity_id: u32) -> Option<&'a [u8]> {
270 self.build_index();
271 let (start, end) = self.entity_index.as_ref()?.get(&entity_id).copied()?;
272 Some(&self.content.as_bytes()[start..end])
273 }
274
275 #[inline]
277 pub fn get_raw_content(&mut self, entity_id: u32) -> Option<&'a str> {
278 self.build_index();
279 let (start, end) = self.entity_index.as_ref()?.get(&entity_id).copied()?;
280 Some(&self.content[start..end])
281 }
282
283 #[inline]
287 pub fn get_first_entity_ref_fast(&mut self, entity_id: u32) -> Option<u32> {
288 let bytes = self.get_raw_bytes(entity_id)?;
289 let len = bytes.len();
290 let mut i = 0;
291
292 while i < len && bytes[i] != b'(' {
294 i += 1;
295 }
296 if i >= len {
297 return None;
298 }
299 i += 1; while i < len {
303 while i < len && (bytes[i] == b' ' || bytes[i] == b'\n' || bytes[i] == b'\r') {
305 i += 1;
306 }
307
308 if i >= len {
309 return None;
310 }
311
312 if bytes[i] == b'#' {
313 i += 1;
314 let start = i;
315 while i < len && bytes[i].is_ascii_digit() {
316 i += 1;
317 }
318 if i > start {
319 let mut id = 0u32;
320 for &b in &bytes[start..i] {
321 id = id.wrapping_mul(10).wrapping_add((b - b'0') as u32);
322 }
323 return Some(id);
324 }
325 }
326 i += 1;
327 }
328
329 None
330 }
331
332 #[inline]
336 pub fn get_entity_ref_list_fast(&mut self, entity_id: u32) -> Option<Vec<u32>> {
337 let bytes = self.get_raw_bytes(entity_id)?;
338
339 let mut i = 0;
341 let len = bytes.len();
342
343 while i < len && bytes[i] != b'(' {
345 i += 1;
346 }
347 if i >= len {
348 return None;
349 }
350 i += 1; while i < len && bytes[i] != b'(' {
354 i += 1;
355 }
356 if i >= len {
357 return None;
358 }
359 i += 1; let mut ids = Vec::with_capacity(32);
363
364 while i < len {
365 while i < len
367 && (bytes[i] == b' ' || bytes[i] == b',' || bytes[i] == b'\n' || bytes[i] == b'\r')
368 {
369 i += 1;
370 }
371
372 if i >= len || bytes[i] == b')' {
373 break;
374 }
375
376 if bytes[i] == b'#' {
378 i += 1;
379 let start = i;
380 while i < len && bytes[i].is_ascii_digit() {
381 i += 1;
382 }
383 if i > start {
384 let mut id = 0u32;
386 for &b in &bytes[start..i] {
387 id = id.wrapping_mul(10).wrapping_add((b - b'0') as u32);
388 }
389 ids.push(id);
390 }
391 } else {
392 i += 1; }
394 }
395
396 if ids.is_empty() {
397 None
398 } else {
399 Some(ids)
400 }
401 }
402
403 #[inline]
407 pub fn get_polyloop_point_ids_fast(&mut self, entity_id: u32) -> Option<Vec<u32>> {
408 let bytes = self.get_raw_bytes(entity_id)?;
409
410 let mut i = 0;
412 let len = bytes.len();
413
414 while i < len && bytes[i] != b'(' {
416 i += 1;
417 }
418 if i >= len {
419 return None;
420 }
421 i += 1; while i < len && bytes[i] != b'(' {
425 i += 1;
426 }
427 if i >= len {
428 return None;
429 }
430 i += 1; let mut point_ids = Vec::with_capacity(8); while i < len {
436 while i < len
438 && (bytes[i] == b' ' || bytes[i] == b',' || bytes[i] == b'\n' || bytes[i] == b'\r')
439 {
440 i += 1;
441 }
442
443 if i >= len || bytes[i] == b')' {
444 break;
445 }
446
447 if bytes[i] == b'#' {
449 i += 1;
450 let start = i;
451 while i < len && bytes[i].is_ascii_digit() {
452 i += 1;
453 }
454 if i > start {
455 let mut id = 0u32;
457 for &b in &bytes[start..i] {
458 id = id.wrapping_mul(10).wrapping_add((b - b'0') as u32);
459 }
460 point_ids.push(id);
461 }
462 } else {
463 i += 1; }
465 }
466
467 if point_ids.is_empty() {
468 None
469 } else {
470 Some(point_ids)
471 }
472 }
473
474 #[inline]
478 pub fn get_cartesian_point_fast(&mut self, entity_id: u32) -> Option<(f64, f64, f64)> {
479 let bytes = self.get_raw_bytes(entity_id)?;
480
481 let mut i = 0;
483 let len = bytes.len();
484
485 while i < len && bytes[i] != b'(' {
487 i += 1;
488 }
489 if i >= len {
490 return None;
491 }
492 i += 1; while i < len && bytes[i] != b'(' {
496 i += 1;
497 }
498 if i >= len {
499 return None;
500 }
501 i += 1; let x = parse_next_float(&bytes[i..], &mut i)?;
505
506 let y = parse_next_float(&bytes[i..], &mut i)?;
508
509 let z = parse_next_float(&bytes[i..], &mut i).unwrap_or(0.0);
511
512 Some((x, y, z))
513 }
514
515 #[inline]
519 pub fn get_face_bound_fast(&mut self, entity_id: u32) -> Option<(u32, bool, bool)> {
520 let bytes = self.get_raw_bytes(entity_id)?;
521 let len = bytes.len();
522
523 let mut eq_pos = 0;
525 while eq_pos < len && bytes[eq_pos] != b'=' {
526 eq_pos += 1;
527 }
528 if eq_pos >= len {
529 return None;
530 }
531
532 let mut is_outer = false;
536 let mut i = eq_pos + 1;
537 while i + 4 < len && bytes[i] != b'(' {
539 if bytes[i] == b'O'
540 && bytes[i + 1] == b'U'
541 && bytes[i + 2] == b'T'
542 && bytes[i + 3] == b'E'
543 && bytes[i + 4] == b'R'
544 {
545 is_outer = true;
546 break;
547 }
548 i += 1;
549 }
550 while i < len && bytes[i] != b'(' {
552 i += 1;
553 }
554 if i >= len {
555 return None;
556 }
557
558 i += 1; while i < len && (bytes[i] == b' ' || bytes[i] == b'\n' || bytes[i] == b'\r') {
562 i += 1;
563 }
564
565 if i >= len || bytes[i] != b'#' {
567 return None;
568 }
569 i += 1;
570
571 let start = i;
573 while i < len && bytes[i].is_ascii_digit() {
574 i += 1;
575 }
576 if i <= start {
577 return None;
578 }
579 let mut loop_id = 0u32;
580 for &b in &bytes[start..i] {
581 loop_id = loop_id.wrapping_mul(10).wrapping_add((b - b'0') as u32);
582 }
583
584 while i < len && bytes[i] != b',' {
587 i += 1;
588 }
589 i += 1; while i < len && (bytes[i] == b' ' || bytes[i] == b'\n' || bytes[i] == b'\r') {
593 i += 1;
594 }
595
596 let orientation = if i + 2 < len && bytes[i] == b'.' && bytes[i + 2] == b'.' {
598 bytes[i + 1] != b'F'
599 } else {
600 true };
602
603 Some((loop_id, orientation, is_outer))
604 }
605
606 #[inline]
611 pub fn get_polyloop_coords_fast(&mut self, entity_id: u32) -> Option<Vec<(f64, f64, f64)>> {
612 self.build_index();
614 let index = self.entity_index.as_ref()?;
615 let bytes_full = self.content.as_bytes();
616
617 let (start, end) = index.get(&entity_id).copied()?;
619 let bytes = &bytes_full[start..end];
620
621 let mut i = 0;
623 let len = bytes.len();
624
625 while i < len && bytes[i] != b'(' {
627 i += 1;
628 }
629 if i >= len {
630 return None;
631 }
632 i += 1; while i < len && bytes[i] != b'(' {
636 i += 1;
637 }
638 if i >= len {
639 return None;
640 }
641 i += 1; let mut coords = Vec::with_capacity(8); while i < len {
647 while i < len
649 && (bytes[i] == b' ' || bytes[i] == b',' || bytes[i] == b'\n' || bytes[i] == b'\r')
650 {
651 i += 1;
652 }
653
654 if i >= len || bytes[i] == b')' {
655 break;
656 }
657
658 if bytes[i] == b'#' {
660 i += 1;
661 let id_start = i;
662 while i < len && bytes[i].is_ascii_digit() {
663 i += 1;
664 }
665 if i > id_start {
666 let mut point_id = 0u32;
668 for &b in &bytes[id_start..i] {
669 point_id = point_id.wrapping_mul(10).wrapping_add((b - b'0') as u32);
670 }
671
672 if let Some((pt_start, pt_end)) = index.get(&point_id).copied() {
675 if let Some(coord) =
676 parse_cartesian_point_inline(&bytes_full[pt_start..pt_end])
677 {
678 coords.push(coord);
679 }
680 }
681 }
682 } else {
683 i += 1; }
685 }
686
687 if coords.len() >= 3 {
688 Some(coords)
689 } else {
690 None
691 }
692 }
693
694 #[inline]
698 pub fn get_polyloop_coords_cached(&mut self, entity_id: u32) -> Option<Vec<(f64, f64, f64)>> {
699 self.build_index();
701 let index = self.entity_index.as_ref()?;
702 let bytes_full = self.content.as_bytes();
703
704 let (start, end) = index.get(&entity_id).copied()?;
706 let bytes = &bytes_full[start..end];
707
708 let mut i = 0;
710 let len = bytes.len();
711
712 while i < len && bytes[i] != b'(' {
714 i += 1;
715 }
716 if i >= len {
717 return None;
718 }
719 i += 1; while i < len && bytes[i] != b'(' {
723 i += 1;
724 }
725 if i >= len {
726 return None;
727 }
728 i += 1; let mut coords = Vec::with_capacity(8);
733 let mut expected_count = 0u32;
734
735 while i < len {
736 while i < len
738 && (bytes[i] == b' ' || bytes[i] == b',' || bytes[i] == b'\n' || bytes[i] == b'\r')
739 {
740 i += 1;
741 }
742
743 if i >= len || bytes[i] == b')' {
744 break;
745 }
746
747 if bytes[i] == b'#' {
749 i += 1;
750 let id_start = i;
751 while i < len && bytes[i].is_ascii_digit() {
752 i += 1;
753 }
754 if i > id_start {
755 expected_count += 1; let mut point_id = 0u32;
759 for &b in &bytes[id_start..i] {
760 point_id = point_id.wrapping_mul(10).wrapping_add((b - b'0') as u32);
761 }
762
763 if let Some(&coord) = self.point_cache.get(&point_id) {
765 coords.push(coord);
766 } else {
767 if let Some((pt_start, pt_end)) = index.get(&point_id).copied() {
769 if let Some(coord) =
770 parse_cartesian_point_inline(&bytes_full[pt_start..pt_end])
771 {
772 self.point_cache.insert(point_id, coord);
773 coords.push(coord);
774 }
775 }
776 }
777 }
778 } else {
779 i += 1; }
781 }
782
783 if coords.len() >= 3 && coords.len() == expected_count as usize {
786 Some(coords)
787 } else {
788 None
789 }
790 }
791}
792
793#[inline]
796fn parse_cartesian_point_inline(bytes: &[u8]) -> Option<(f64, f64, f64)> {
797 let len = bytes.len();
798 let mut i = 0;
799
800 while i < len && bytes[i] != b'(' {
802 i += 1;
803 }
804 if i >= len {
805 return None;
806 }
807 i += 1; while i < len && bytes[i] != b'(' {
811 i += 1;
812 }
813 if i >= len {
814 return None;
815 }
816 i += 1; let x = parse_float_inline(&bytes[i..], &mut i)?;
820
821 let y = parse_float_inline(&bytes[i..], &mut i)?;
823
824 let z = parse_float_inline(&bytes[i..], &mut i).unwrap_or(0.0);
826
827 Some((x, y, z))
828}
829
830#[inline]
832fn parse_float_inline(bytes: &[u8], offset: &mut usize) -> Option<f64> {
833 let len = bytes.len();
834 let mut i = 0;
835
836 while i < len
838 && (bytes[i] == b' ' || bytes[i] == b',' || bytes[i] == b'\n' || bytes[i] == b'\r')
839 {
840 i += 1;
841 }
842
843 if i >= len || bytes[i] == b')' {
844 return None;
845 }
846
847 match fast_float::parse_partial::<f64, _>(&bytes[i..]) {
849 Ok((value, consumed)) if consumed > 0 => {
850 *offset += i + consumed;
851 Some(value)
852 }
853 _ => None,
854 }
855}
856
857#[inline]
859fn parse_next_float(bytes: &[u8], offset: &mut usize) -> Option<f64> {
860 let len = bytes.len();
861 let mut i = 0;
862
863 while i < len
865 && (bytes[i] == b' ' || bytes[i] == b',' || bytes[i] == b'\n' || bytes[i] == b'\r')
866 {
867 i += 1;
868 }
869
870 if i >= len || bytes[i] == b')' {
871 return None;
872 }
873
874 match fast_float::parse_partial::<f64, _>(&bytes[i..]) {
876 Ok((value, consumed)) if consumed > 0 => {
877 *offset += i + consumed;
878 Some(value)
879 }
880 _ => None,
881 }
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887 use crate::IfcType;
888
889 #[test]
890 fn test_decode_entity() {
891 let content = r#"
892#1=IFCPROJECT('2vqT3bvqj9RBFjLlXpN8n9',$,$,$,$,$,$,$,$);
893#2=IFCWALL('3a4T3bvqj9RBFjLlXpN8n0',$,$,$,'Wall-001',$,#3,#4);
894#3=IFCLOCALPLACEMENT($,#4);
895#4=IFCAXIS2PLACEMENT3D(#5,$,$);
896#5=IFCCARTESIANPOINT((0.,0.,0.));
897"#;
898
899 let mut decoder = EntityDecoder::new(content);
900
901 let start = content.find("#2=").unwrap();
903 let end = content[start..].find(';').unwrap() + start + 1;
904
905 let entity = decoder.decode_at(start, end).unwrap();
906 assert_eq!(entity.id, 2);
907 assert_eq!(entity.ifc_type, IfcType::IfcWall);
908 assert_eq!(entity.attributes.len(), 8);
909 assert_eq!(entity.get_string(4), Some("Wall-001"));
910 assert_eq!(entity.get_ref(6), Some(3));
911 assert_eq!(entity.get_ref(7), Some(4));
912 }
913
914 #[test]
915 fn test_decode_by_id() {
916 let content = r#"
917#1=IFCPROJECT('guid',$,$,$,$,$,$,$,$);
918#5=IFCWALL('guid2',$,$,$,'Wall-001',$,$,$);
919#10=IFCDOOR('guid3',$,$,$,'Door-001',$,$,$);
920"#;
921
922 let mut decoder = EntityDecoder::new(content);
923
924 let entity = decoder.decode_by_id(5).unwrap();
925 assert_eq!(entity.id, 5);
926 assert_eq!(entity.ifc_type, IfcType::IfcWall);
927 assert_eq!(entity.get_string(4), Some("Wall-001"));
928
929 assert_eq!(decoder.cache_size(), 1);
931 let cached = decoder.get_cached(5).unwrap();
932 assert_eq!(cached.id, 5);
933 }
934
935 #[test]
936 fn test_resolve_ref() {
937 let content = r#"
938#1=IFCPROJECT('guid',$,$,$,$,$,$,$,$);
939#2=IFCWALL('guid2',$,$,$,$,$,#1,$);
940"#;
941
942 let mut decoder = EntityDecoder::new(content);
943
944 let wall = decoder.decode_by_id(2).unwrap();
945 let placement_attr = wall.get(6).unwrap();
946
947 let referenced = decoder.resolve_ref(placement_attr).unwrap().unwrap();
948 assert_eq!(referenced.id, 1);
949 assert_eq!(referenced.ifc_type, IfcType::IfcProject);
950 }
951
952 #[test]
953 fn test_resolve_ref_list() {
954 let content = r#"
955#1=IFCPROJECT('guid',$,$,$,$,$,$,$,$);
956#2=IFCWALL('guid1',$,$,$,$,$,$,$);
957#3=IFCDOOR('guid2',$,$,$,$,$,$,$);
958#4=IFCRELCONTAINEDINSPATIALSTRUCTURE('guid3',$,$,$,(#2,#3),$,#1);
959"#;
960
961 let mut decoder = EntityDecoder::new(content);
962
963 let rel = decoder.decode_by_id(4).unwrap();
964 let elements_attr = rel.get(4).unwrap();
965
966 let elements = decoder.resolve_ref_list(elements_attr).unwrap();
967 assert_eq!(elements.len(), 2);
968 assert_eq!(elements[0].id, 2);
969 assert_eq!(elements[0].ifc_type, IfcType::IfcWall);
970 assert_eq!(elements[1].id, 3);
971 assert_eq!(elements[1].ifc_type, IfcType::IfcDoor);
972 }
973
974 #[test]
975 fn test_cache() {
976 let content = r#"
977#1=IFCPROJECT('guid',$,$,$,$,$,$,$,$);
978#2=IFCWALL('guid2',$,$,$,$,$,$,$);
979"#;
980
981 let mut decoder = EntityDecoder::new(content);
982
983 assert_eq!(decoder.cache_size(), 0);
984
985 decoder.decode_by_id(1).unwrap();
986 assert_eq!(decoder.cache_size(), 1);
987
988 decoder.decode_by_id(2).unwrap();
989 assert_eq!(decoder.cache_size(), 2);
990
991 decoder.decode_by_id(1).unwrap();
993 assert_eq!(decoder.cache_size(), 2);
994
995 decoder.clear_cache();
996 assert_eq!(decoder.cache_size(), 0);
997 }
998}