1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! The STEP HEADER-skip used by [`super::EntityScanner`] to position past the
//! HEADER section before it starts looking for entities. Split out of
//! `scanner.rs` to keep that file under its module-size budget — this piece
//! is self-contained (one function, no scanner state) and has no reason to
//! live inline.
/// Locate the byte offset of the first character after `DATA;` (skipping the
/// STEP HEADER section). Returns 0 if the marker isn't found — partial files
/// without a HEADER still scan from the top.
///
/// Scanning the HEADER for entities is unsafe: the HEADER is a free-form
/// STEP record that legally contains arbitrary characters inside quoted
/// strings (filenames, descriptions). CATIA emits `FILE_NAME('…\X0\2#.ifc'…)`,
/// and a tokenizer that anchors on `#` will latch onto the in-string `#`,
/// flip `find_entity_end`'s quote parity, and drop the rest of the file.
/// See issue #654.
///
/// Quote-aware: the marker is only matched outside `'…'` strings, since a
/// HEADER field could legally contain the literal text `DATA;` in a
/// description or filename. Escaped single quotes (`''`) are treated as a
/// pair of in-string characters per ISO 10303-21.
///
/// Comment-aware for the same reason: ISO 10303-21 allows a `/* … */`
/// comment wherever whitespace is allowed, the HEADER included, so a
/// commented-out `DATA;` is not the marker either. Matching one used to end
/// the search inside the comment, and every `#N=…` written after it in that
/// comment was then scanned as a real record.
///
/// An unterminated `/*` in the header gets the same answer as a missing
/// marker, 0: everything from that `/` on is inside the comment, so there is
/// no `DATA;` left to find. Answering 0 rather than skipping past the
/// comment is also what keeps the condition REPORTED — the scan then starts
/// at the top, [`super::EntityScanner::next_entity`] meets the same
/// unterminated `/*`, and [`super::lexical::skip_step_comment`] refusing it
/// marks `malformed_record_start` through the one channel #3699 added. It is
/// also what a headerless partial file needs: `#1=IFCWALL($); /* oops` has
/// real records BEFORE the bad comment, and they still scan.
pub