Skip to main content

EntityDecoder

Struct EntityDecoder 

Source
pub struct EntityDecoder<'a> { /* private fields */ }
Expand description

Entity decoder for lazy parsing from raw IFC bytes.

String attributes are decoded lossily when tokens become AttributeValues; structural scanning and byte offsets always use the original source bytes.

Implementations§

Source§

impl<'a> EntityDecoder<'a>

Source

pub fn with_arc_columnar_index<T>( content: &'a T, index: Arc<ColumnarEntityIndex>, ) -> Self
where T: AsRef<[u8]> + ?Sized,

Create a decoder backed by a shared columnar index (wasm shared-index path). Mirrors EntityDecoder::with_arc_index but installs the compact representation.

Source

pub fn set_columnar_index(&mut self, index: Arc<ColumnarEntityIndex>)

Install a shared columnar index into an existing decoder. Like EntityDecoder::set_entity_index but for the compact representation; afterwards build_index no-ops.

Source§

impl<'a> EntityDecoder<'a>

Source

pub fn new<T>(content: &'a T) -> Self
where T: AsRef<[u8]> + ?Sized,

Create new decoder

Source

pub fn with_index<T>(content: &'a T, index: EntityIndex) -> Self
where T: AsRef<[u8]> + ?Sized,

Create decoder with pre-built index (faster for repeated lookups)

Source

pub fn with_arc_index<T>(content: &'a T, index: Arc<EntityIndex>) -> Self
where T: AsRef<[u8]> + ?Sized,

Create decoder with shared Arc index (for parallel processing)

Source

pub fn set_entity_index(&mut self, index: Arc<EntityIndex>)

Install a pre-built index into this decoder. Used when the caller already built the index during another pass (e.g. the processor scan loop) so the decoder does not lazily rebuild it via build_index. Set it before any decode_by_id/ref-resolving call; afterwards build_index no-ops.

Source

pub fn decode_at(&mut self, start: usize, end: usize) -> Result<DecodedEntity>

Decode entity at byte offset Returns cached entity if already decoded

Validates the (start, end) span against self.content.len() before slicing. Out-of-range or inverted spans return Error::parse instead of panicking — callers (e.g. decode_and_cache, decode_at_with_id, the streaming pre-pass shard mergers) hand us spans derived from untrusted/streamed entity-index data, and a malformed span must not take down the whole worker.

Source

pub fn decode_at_uncached( &self, start: usize, end: usize, ) -> Result<DecodedEntity>

Decode the entity in [start, end) without touching the cache.

decode_at memoizes every entity it parses, which is the right trade-off for geometry sub-tree walks (the same points/profiles are revisited many times). For a single linear pass over every entity in a large model — tens of millions of rows — that cache grows without bound and dominates memory. This variant parses and returns the entity but never inserts it, so a streaming walk stays O(1) in entity count. The caller owns the result; for identical bytes it yields an identical DecodedEntity to decode_at, only without the cache side effect.

Source

pub fn decode_at_with_id( &mut self, id: u32, start: usize, end: usize, ) -> Result<DecodedEntity>

Decode entity at byte offset with known ID (faster - checks cache before parsing) Use this when the scanner provides the entity ID to avoid re-parsing cached entities

Source

pub fn decode_by_id(&mut self, entity_id: u32) -> Result<DecodedEntity>

Decode entity by ID - O(1) lookup using entity index

Source

pub fn plane_angle_to_radians(&mut self) -> f64

Multiplier that converts file plane-angle units to radians.

Lazy-resolved on first call by scanning for IFCPROJECT and reading its IFCUNITASSIGNMENT. Cached for subsequent calls. Returns 1.0 when no plane-angle unit is declared (IFC spec default = RADIAN).

Use this at curve-sampling time wherever an IfcParameterValue is interpreted as an angle (IfcCircle / IfcEllipse trim parameters). Without it, value.to_radians() is correct only for DEGREE files and silently shrinks arcs on RADIAN files (issue #820).

Source

pub fn length_unit_scale(&mut self) -> f64

Multiplier that converts file length units to metres (1.0 for metre files, 0.001 for millimetre files, …). Lazy-resolved on first call by scanning for IFCPROJECT and reading its IFCUNITASSIGNMENT, then cached. Returns 1.0 when no length unit is declared.

Use this to express an absolute metric tolerance in file units — e.g. a curve-tessellation chord-deviation budget that stays constant in millimetres whether the file is authored in mm or m.

Source

pub fn seed_unit_scales( &mut self, length_unit_scale: f64, plane_angle_to_radians: f64, )

Pre-seed the unit-scale caches so Self::length_unit_scale and Self::plane_angle_to_radians return immediately without the full-file IFCPROJECT scan.

Both lazy resolvers walk the whole DATA section to locate the (singleton) IFCPROJECT. That scan is O(file size) and IFCPROJECT legally sits anywhere — IfcOpenShell emits it near the end, so on a large model the scan touches tens of MB. The cache is per-decoder, and the parallel geometry pipeline builds a fresh decoder per element, so without seeding every arc-bearing element re-pays the scan (≈135 ms each on a 75 MB file). The orchestrator resolves both scales once on a warm shared decoder and seeds each worker decoder here.

Source

pub fn resolve_ref( &mut self, attr: &AttributeValue, ) -> Result<Option<DecodedEntity>>

Resolve entity reference (follow #ID) Returns None for null/derived values

Source

pub fn resolve_ref_list( &mut self, attr: &AttributeValue, ) -> Result<Vec<DecodedEntity>>

Resolve list of entity references

Source

pub fn get_cached(&self, entity_id: u32) -> Option<DecodedEntity>

Get cached entity (without decoding)

Source

pub fn reserve_cache(&mut self, additional: usize)

Reserve cache capacity to avoid HashMap resizing during processing. For a 487 MB file with 208 K building elements, the cache can grow to 300 K+ entries (elements + representation chains + placements). Pre-allocating avoids ~6 resize-and-rehash operations that each copy all entries, reducing both peak memory spikes and timing variance.

Source

pub fn inject_shared_cache( &mut self, shared: &FxHashMap<u32, Arc<DecodedEntity>>, )

Inject a pre-warmed Arc-shared cache into this decoder’s local cache.

Used by the de-normalized parallel path: a serial pre-pass builds a shared Arc<FxHashMap<u32, Arc<DecodedEntity>>> containing all entities reachable from the jobs. Each rayon task then injects that shared cache into its own decoder via this method, so the per-task hot path hits in-WASM-heap Arc handles instead of SAB-imported atomic memory.

Cost: one Arc::clone per cached entry (atomic refcount bump). For a typical 100K-entry cache × 9 rayon tasks = 900K atomics total, ~90 ms wall (incurred ONCE at task setup; the parallel hot path then runs lock-free against the populated cache).

Source

pub fn decode_and_cache( &mut self, id: u32, start: usize, end: usize, ) -> Result<Arc<DecodedEntity>>

Decode + cache without returning. Used by the pre-warm pass to populate a shared cache. Returns the cached Arc so the caller can chase references without re-decoding.

Source

pub fn drain_cache(&mut self) -> FxHashMap<u32, Arc<DecodedEntity>>

Drain the populated cache out of this decoder for sharing across rayon tasks. After calling this, the decoder is empty (cache moved out); callers typically then drop the decoder.

Source

pub fn clear_cache(&mut self)

Clear all caches to free memory

Source

pub fn clear_point_cache(&mut self)

Clear only the point coordinate cache (used after BREP preprocessing). The entity cache is preserved for subsequent geometry processing.

Source

pub fn take_point_cache(&mut self) -> FxHashMap<u32, (f64, f64, f64)>

Move the CartesianPoint coordinate cache OUT of this decoder, leaving it empty. Paired with Self::set_point_cache to hoist the cache across the per-element decoders a single worker builds within one batch: the cache is pure memoization of content + point id -> coords, so reusing it across elements is byte-identical (speed only). Cheap: moves the map header, not its contents.

Source

pub fn set_point_cache(&mut self, cache: FxHashMap<u32, (f64, f64, f64)>)

Install a previously-accumulated point cache (see Self::take_point_cache). Does not reset the hit/miss counters, which stay per-decoder so each job’s Self::point_cache_stats reflect only that job’s activity.

Source

pub fn point_cache_stats(&self) -> (u64, u64)

(hits, misses) served by Self::get_polyloop_coords_cached over this decoder’s lifetime. A hit is a CartesianPoint served from the cache; a miss is one parsed for the first time. Non-zero hits after processing more than one faceted part with a shared point list prove cross-element memoization.

Source

pub fn take_placement_transform_cache(&mut self) -> FxHashMap<u32, [f64; 16]>

Move the placement-transform memo OUT of this decoder, leaving it empty. Paired with Self::set_placement_transform_cache to hoist the cache across the per-element decoders a single worker builds within one batch, exactly like Self::take_point_cache. The memo is a pure function of content + placement id (deterministic parent * local composition), so reusing it across elements is byte-identical (speed only). Cheap: moves the map header, not its contents.

Source

pub fn set_placement_transform_cache( &mut self, cache: FxHashMap<u32, [f64; 16]>, )

Install a previously-accumulated placement-transform memo (see Self::take_placement_transform_cache).

Source

pub fn get_placement_transform_cached(&self, id: u32) -> Option<[f64; 16]>

Read a memoized placement world transform by placement id. Returns a copy ([f64; 16] is Copy) so the caller can drop the borrow before reconstructing its Matrix4. The array is the opaque column-major layout written by Self::cache_placement_transform.

Source

pub fn cache_placement_transform(&mut self, id: u32, transform: [f64; 16])

Memoize a resolved placement world transform under its placement id. Only the geometry router’s real computed transforms (IfcLocalPlacement / linear / grid) are stored here; identity/depth-guard fallbacks are not, so the memo stays a pure function of the placement id (byte-identical reuse).

Source

pub fn cache_size(&self) -> usize

Get cache size

Source

pub fn get_raw_bytes(&mut self, entity_id: u32) -> Option<&'a [u8]>

Get raw bytes for an entity (for direct/fast parsing) Returns the full entity line including type and attributes

Source

pub fn get_first_entity_ref_fast(&mut self, entity_id: u32) -> Option<u32>

Fast extraction of first entity ref from raw bytes Useful for BREP -> shell ID, Face -> FaceBound, etc. Returns the first entity reference ID found in the entity

Source

pub fn get_entity_ref_list_fast(&mut self, entity_id: u32) -> Option<Vec<u32>>

Fast extraction of entity reference IDs from a list attribute in raw bytes Useful for getting face list from ClosedShell, bounds from Face, etc. Returns list of entity IDs

Source

pub fn get_polyloop_point_ids_fast( &mut self, entity_id: u32, ) -> Option<Vec<u32>>

Fast extraction of PolyLoop point IDs directly from raw bytes Bypasses full entity decoding for BREP optimization Returns list of entity IDs for CartesianPoints

Source

pub fn get_cartesian_point_fast( &mut self, entity_id: u32, ) -> Option<(f64, f64, f64)>

Fast extraction of CartesianPoint coordinates directly from raw bytes Bypasses full entity decoding for ~3x speedup on BREP-heavy files Returns (x, y, z) as f64 tuple

Source

pub fn get_face_bound_fast( &mut self, entity_id: u32, ) -> Option<(u32, bool, bool)>

Fast extraction of FaceBound info directly from raw bytes Returns (loop_id, orientation, is_outer_bound) Bypasses full entity decoding for BREP optimization

Source

pub fn get_polyloop_coords_fast( &mut self, entity_id: u32, ) -> Option<Vec<(f64, f64, f64)>>

Fast extraction of PolyLoop COORDINATES directly from raw bytes This is the ultimate fast path - extracts all coordinates in one go Avoids N+1 HashMap lookups by batching point extraction Returns Vec of (x, y, z) coordinate tuples

Source

pub fn get_polyloop_coords_cached( &mut self, entity_id: u32, ) -> Option<Vec<(f64, f64, f64)>>

Fast extraction of PolyLoop COORDINATES with point caching Uses a cache to avoid re-parsing the same cartesian points For files with many faces sharing points, this can be 2-3x faster

Auto Trait Implementations§

§

impl<'a> Freeze for EntityDecoder<'a>

§

impl<'a> RefUnwindSafe for EntityDecoder<'a>

§

impl<'a> Send for EntityDecoder<'a>

§

impl<'a> Sync for EntityDecoder<'a>

§

impl<'a> Unpin for EntityDecoder<'a>

§

impl<'a> UnsafeUnpin for EntityDecoder<'a>

§

impl<'a> UnwindSafe for EntityDecoder<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.