Compare commits

...
97 Commits
Author SHA1 Message Date
Clifford Yapp ed686ee1d9 Merge pull request #487 from starseeker/develop
Address compiler warnings
2026-09-10 09:56:05 -04:00
Clifford Yapp 79926118c5 Merge pull request #486 from attixray/sc-fix-uninitialized-member-reads
Initialise two members that are read before they are written
2026-09-10 09:37:15 -04:00
Clifford Yapp 0f8c5eed76 Merge pull request #485 from attixray/sc-fix-header-scan-hang
Stop FindHeaderSection spinning forever on a file with no header
2026-09-10 09:36:30 -04:00
Clifford Yapp 9d47461e32 Merge pull request #484 from attixray/sc-fix-lazy-null-and-uninitialised-reads
lazy: refuse an instance whose data section failed to index, and three uninitialised reads around it
2026-09-10 09:35:56 -04:00
Clifford Yapp e480b0bbd1 Address compiler warnings 2026-09-10 13:22:16 +00:00
Attila Prokai 40bef08602 Terminate TypeDescriptor::_altname on construction
_altname is a char[BUFSIZ+1] that neither constructor touches, so CurrName()
and the AltNameOf paths can read uninitialised heap until something calls
AltName(). Start it empty.
2026-08-21 19:50:43 +02:00
Attila Prokai ee5c3dbfd2 Stop FindHeaderSection spinning on a file with no header
getline(buf, BUFSIZ, ';') on a stream whose next BUFSIZ bytes contain no
semicolon sets failbit and leaves buf unchanged. The scan loop only tests
eof(), which never arrives once failbit is set, so it never terminates - hand
it any binary file and it hangs instead of reporting that the file is not
STEP. Treat a failed extraction the same as end of file.
2026-08-21 19:50:43 +02:00
Attila Prokai 2e0b644c99 Initialise _null in aggregate nodes
STEPnode::_null had no default: STEPnode has no constructor, so a node built
by a subclass that does not assign it starts out with whatever was on the
heap, and is_null() then answers at random. Give STEPnode a constructor that
starts nodes null.

EntityNode::StrToVal() and ::STEPread() additionally have to set it per
outcome - they resolve an entity reference and must report null exactly when
the reference did not resolve. Before this, a node whose reference failed to
resolve could still report itself non-null.
2026-08-21 19:50:43 +02:00
Attila Prokai 18f7b43c99 lazy: refuse an instance whose data section failed to index
A data section indexes its instances from its own constructor, through
addLazyInstance(), under the section id it WILL be given - and
lazyFileReader::initP21() registers the section only afterwards, and only
if its scan succeeded. A section that fails part way therefore leaves
every instance it had already indexed naming a section that does not
exist.

typeFromFile() then indexes _dataSections with it unconditionally, and
loadInstance() does the same behind an assert() that Release compiles
out. On an empty vector that is an access violation. sourceRecord()
already makes exactly this test; the other two now make it too and answer
0 with a diagnostic, as they already do for an instance found in no
section.

The instances stay in the index - totalInstanceCount() still counts them
and getInstances() still lists them. What changes is that asking for one
of them fails the way every other unloadable instance fails.

Reachable from a thirteen-line file, added as test/lazy_broken_section.stp
and checked by the lazy_index test: a DATA section holding one instance
and then a line that is not one. Without the two changes below it, that
test dies with SIGSEGV inside typeFromFile().

Note that the new break in loadInstance() leaves `inst` null, and
isNilSTEPentity( 0 ) is false, so the null would be cached as a loaded
instance were it not for the guard added in "lazy: do not cache a null
instance as a loaded one" earlier in this branch. With the bounds test
alone the process survives the two calls and then dies on the way out.
2026-08-21 19:50:42 +02:00
Attila Prokai b5e0082f56 lazy: null the reference list the invalid-instance path deletes
nextInstance()'s "invalid instance, so clear everything" branch deletes
i.refs but leaves the pointer, while componentTypes two lines below is
deleted and nulled, and i.name is assigned 0 right after. The struct is
returned by value with the stale pointer still in it.

Nothing frees it twice today: the same branch zeroes loc.begin, and both
callers - the data section constructor's scan loop and indexScope() -
stop on that before looking at anything else. So this is the same kind of
uniformity fix as the two commits before it rather than a live bug, and
it costs one line to stop being a question.
2026-08-21 19:50:42 +02:00
Attila Prokai c4d4373280 lazy: initialise namedLazyInstance::name with the rest of the struct
nextInstance() explicitly initialises every member of the local
namedLazyInstance except `name`, and the assignment it does get is conditional:

    if( _file.good() ) i.name = getDelimitedKeyword( ";( /\\" );

Nothing is wrong today. When that guard is false the closing "invalid instance"
branch is reached on the same condition and assigns `i.name = 0` before the
struct is returned, so the uninitialised value is never observed - but that is a
control-flow argument spanning forty lines, not an invariant the code states.

It also stops holding the moment getDelimitedKeyword() can fail softly. It
cannot upstream, because it abort()s on a keyword with no delimiter after it; a
caller that would rather report bad input than terminate the host process has to
make it return 0, and then `i.name` is read while uninitialised.

One line, and the struct is uniformly initialised where it is declared.
2026-08-21 19:50:42 +02:00
Attila Prokai 54c89ed13e lazy: do not cache a null instance as a loaded one
isNilSTEPentity( 0 ) returns false, so `if( !isNilSTEPentity( inst ) )` in
loadInstance() is TRUE for a null pointer, and every branch of the switch above
it can leave one:

  * case 0 and default leave `inst` at the value returned by
    _instancesLoaded.find(), which is 0 on a cache miss;
  * case 1 sets `inst = 0` explicitly on the cancellation path added for batch
    loading.

A null therefore reaches the success arm and is inserted into _instancesLoaded,
while _loadedInstanceCount, _materializations, _cacheHighWater and the resident
source-byte accounting are all advanced for an instance that was never
materialized. The lazyRefs construction that follows is harmless - its init()
rejects two null arguments - so this is accounting corruption rather than a
crash.

It does not settle either. releaseBatch() skips the entry with `if( !inst )
continue;` BEFORE the `--_loadedInstanceCount` and the erase from
_batchOwnedInstances, so the phantom can never be evicted: the counter stays
high for the life of the manager and _batchOwnedInstances grows across batches.
cacheStatistics() reports the drift.

Guard the pointer as well as the nil sentinel. A null then takes the existing
else arm, which already emits LAZY_DIAGNOSTIC_ERROR "SDAI instance
materialization failed" - the right diagnostic for what happened.
2026-08-21 19:50:42 +02:00
Clifford Yapp 9baa5dadaa Merge pull request #482 from attixray/sc-fix-static-lib-link-interface
Fix the static library build: a generator expression leaks into the link line
2026-08-21 11:20:34 -04:00
Attila Prokai e7c70802d4 cldai: name the static dependencies instead of composing them
"cmake: declare target link interfaces explicitly" (0b950dd1) made SC_ADDLIB
publish its LINK_LIBRARIES as PUBLIC. That is the right change, but it is not
compatible with the way src/cldai/CMakeLists.txt names its static dependency:

    SC_ADDLIB(stepdai-static STATIC SOURCES ${DAI_SRCS}
              LINK_LIBRARIES $<JOIN:${_libdeps},-static >-static)

_libdeps holds one entry, steputils, so the generator expression is an elaborate
way of writing steputils-static. While the dependency was linked privately it
expanded once, on the link line of stepdai-static, and was correct. Published as
PUBLIC it lands in INTERFACE_LINK_LIBRARIES and is evaluated again in every
consumer, where it comes back out mangled and reaches the linker verbatim:

    LINK : fatal error LNK1104: cannot open file $<JOIN:steputils,-static.obj

p21read_sdai_<schema> is the first consumer to hit it, so with BUILD_STATIC_LIBS=ON
no schema executable links. Reproduced on develop tip - nothing after 0b950dd1
touches either file, so the static build is broken there too.

Building the list with a foreach costs nothing, reads the same, and keeps working
if _libdeps ever grows. The shared branch is untouched; this is inside
if(BUILD_STATIC_LIBS).
2026-08-19 13:16:28 +02:00
Clifford Yapp 8a228eb2c8 Merge pull request #481 from starseeker/string_pool
Fixes for Clang compilation
2026-08-07 13:48:39 -04:00
Clifford Yapp 18e16bc6ed Fixes for Clang compilation
Some of the strings being generated with ap242 were bigger than
the compiler could guarantee to support, and there were a few other
minor adjustments.
2026-08-07 14:20:19 +00:00
Clifford Yapp c5d6b10a9a Merge pull request #480 from starseeker/improve_output
Add an option for an alternative STEPcode generated output API
2026-08-04 09:27:48 -04:00
Clifford Yapp 9726d00caa Defer lazy load errors to diagnostic callbacks
When an embedding application installs a structured diagnostic callback,
do not also print the same materialization errors to stderr.  This keeps
large batches of unsupported companion-schema entities from flooding
converter output while preserving legacy standalone diagnostics.
2026-08-03 13:17:07 -04:00
Clifford Yapp d962ea0381 Bound lazy schema warnings per file
Materializing each instance repeated the same FILE_SCHEMA warning.
Keep a thread-safe claim flag on the lazy file reader so concurrent
loads and multiple DATA sections report the ambiguity once per input
file.
2026-08-03 12:47:01 -04:00
Clifford Yapp 5a37441843 Make ASan schema generation build-safe
Disable leak detection only while the instrumented exp2cxx build tool
generates schema sources.  LeakSanitizer cannot run under common tracers
and sandboxes, which otherwise makes an ASan build fail before its tests
can start.
2026-08-03 07:02:40 -04:00
Clifford Yapp deefe3442d Expose lazy complex component types
Preserve the component keyword vector already parsed while indexing a
complex Part 21 instance, and expose it through a read-only lazy manager
API.  This lets clients interpret complex instances without reparsing raw
source text or materializing the SDAI graph.
2026-08-03 00:40:19 -04:00
Clifford Yapp 2d8ad4ba0c Fix undefined behavior in packed Judy keys
Judy nodes pack variable-width keys at byte offsets which are not
guaranteed to meet judyvalue alignment. Access these keys and caller
byte buffers through memcpy so lazy indexing is portable and visible
to optimizers without undefined behavior.

Also mask the radix iterator shift at a word boundary. Without the
mask, the final byte requests a shift by 64 on 64-bit builds.
2026-08-02 22:39:42 -04:00
Clifford Yapp b5aea76eed Skip missing lazy dependencies in batches
Reference validation already reports targets absent from DATA sections.
Do not add those IDs to a materialization closure, where they cannot
be loaded and only produce duplicate or misleading limit errors.

Cover the closure behavior with the existing missing-reference fixture.
2026-08-02 21:38:08 -04:00
Clifford Yapp e0e60f1277 Add sanitizer build modes
Provide explicit ASan, UBSan, and TSan options with compile and
link probes.  Keep thread instrumentation in an incompatible,
standalone configuration and preserve frame pointers for useful
diagnostics.
2026-08-02 21:27:11 -04:00
Clifford Yapp 14a16f18b0 Fixes found while testing
- Correct ownership of stack-backed and allocated entity attributes.
- Typed accessor delegation for redefined attributes.
- Correct destruction when an attribute is redefined to a different type.
- Complete aggregate descriptor initialization.
- The API-v2 exp2python link dependency.
- Regression tests covering those cases.
2026-08-02 12:34:45 -04:00
Clifford Yapp aba9111ff6 Make generated output content-stable
Write generated files to sibling temporary files and replace a target
only when content differs.  Track owned paths in a versioned manifest
and remove obsolete generated files without touching unrelated files.
Strip generator source paths from output and add regression coverage.

Treat nonliteral aggregate bounds as expression records instead of
reading an inactive integer union member.  This also removes process
addresses that made packed AP242 output nondeterministic.

On AP242e4, an identical generation changes zero of ten generated
source/header timestamps and leaves the 390-byte manifest unchanged.
The following CMake build falls from 2.01 to 0.06 seconds and peak RSS
from 118,108 to 18,432 KiB.  Generation remains 0.40 seconds.

The NIST AP242e4 sample still creates and validates all 10,609
instances with no errors or warnings and writes the same 949,685-byte
semantic result.
2026-08-02 07:42:16 -04:00
Clifford Yapp 5b0085612f Add structural metadata profile for API v2
Keep full descriptor metadata as the default and add an opt-in
structural profile for conversion-oriented late-bound API v2 users.
Strip descriptive and constraint text while retaining the complete
runtime descriptor graph.  Pass the profile through CMake and correct
the scanner's compact late-bound file lists.

AP242e4 full-to-structural measurements:

Generated output falls from 54,839 lines and 3,304,007 bytes to
29,993 lines and 1,525,897 bytes.  The packed schema source shrinks
68.1%.  The schema DSO falls from 2,178,296 to 720,120 bytes, and peak
compile RSS drops 6.8%.  File count and 49 ELF relocations are
unchanged.

The NIST AP242e4 sample still creates and validates all 10,609
instances with no errors or warnings and writes the same 949,685-byte
semantic result.  Twenty read/write runs improve from 4.76 to 4.71
seconds and peak RSS falls from 82,276 to 78,900 KiB.
2026-08-02 07:27:08 -04:00
Clifford Yapp 0c81351efd Use generic late-bound values and bounded headers
Construct SELECT, enumeration, and aggregate values from descriptors so
compact output needs no generated type classes or factories.  Preserve
enumeration elements and renamed schema items in the packed image.

Make each public schema header independently includable through the small
SchemaModule interface.  Keep runtime and model declarations internal.

AP242e4 output falls from 865 to 10 files and clean compile/link time
falls from 18.71 to 1.21 seconds.  The schema DSO falls from 3,503,352
to 2,178,296 bytes, with ELF relocations falling from 9,620 to 49.
2026-08-02 07:16:26 -04:00
Clifford Yapp 87bfb7929d Pack the complete API v2 descriptor graph
Replace late-bound descriptor procedures with validated, offset-based
schema and complex metadata images.  Use an explicit per-load context
for reentrant module construction while retaining the version 1
compatibility path.

AP242e4 generated output falls from 9,060,249 to 4,865,657 bytes and
clean compile/link time falls from 41.49 to 18.71 seconds.  The schema
DSO falls from 7,470,304 to 3,503,352 bytes and ELF relocations fall
from 33,221 to 9,620.
2026-08-02 07:04:54 -04:00
Clifford Yapp 43a95a6cf4 Pack API v2 schema module images
Replace generated SchemaModuleSlot pointer arrays with a versioned,
16-byte SchemaModuleImage.  Shared initialization helpers capture
entity, type, and attribute descriptors in stable generated-ID order
and transfer them to SchemaModule without one relocated pointer per
descriptor.

Retain the slot-based initialization entry point for existing API v2
output, and leave API v1 generation unchanged.

For AP242e4, generated output falls from 123,552 to 117,046 lines
(-5.3%) and from 9,804,680 to 9,060,249 bytes (-7.6%).  The schema
module source falls from 808,907 to 64,367 bytes (-92.0%).

The schema DSO falls from 7,728,384 to 7,470,304 bytes (-3.3%), while
ELF relocations fall from 39,719 to 33,221 (-16.4%).  Comparable clean
compile times are unchanged, and paired read/write runs improve by 0.6%.

The real file still creates and validates 10,609 instances with zero
errors and warnings.  Its 949,685-byte rewrite differs only by its
timestamp.
2026-08-02 06:15:25 -04:00
Clifford Yapp 99855789c2 Precompute late-bound entity attribute layouts
Resolve each flattened inherited attribute layout once after schema
initialization.  Late-bound instance construction now follows descriptor
and index records directly instead of recursively traversing supertypes
and repeating derived and redefined attribute name searches.

Keep the cache outside EntityDescriptor so its object layout and API v1
ABI remain unchanged.  Invalidate cached subtype layouts when descriptor
graphs are edited, and retain lazy preparation for non-module callers.

For the AP242e4 sample's most common entity, constructing 1,531,000
DIRECTION instances improved from 0.84s to 0.63s (-25.0%); user time
improved from 0.82s to 0.61s (-25.6%).  Peak RSS increased by 0.9% and
libstepcore grew by 1.6%.  Twenty full read/write runs remained at 4.9s.

The real file still created and validated 10,609 instances with zero
errors and warnings.  Its 949,685-byte rewrite changed only by its
timestamp.
2026-08-02 06:15:25 -04:00
Clifford Yapp 05c6adb716 Complete compact schema modules for API v2
Add stable entity, type, and attribute IDs through SchemaModule.  Make
late-bound compatibility names opt-in, table-drive rules and complex
metadata, and emit entity metadata directly into bounded chunks.

AP242e4 late-bound output changed as follows:

* generated files: 5,719 to 905 (-84.2%)
* generated lines: 254,220 to 123,552 (-51.4%)
* generated bytes: 13,942,523 to 9,804,680 (-29.7%)
* generation wall time: 2.74s to 0.56s (-79.6%)
* compile/link wall time: 157.68s to 37.02s (-76.5%)
* compile/link peak RSS: 593,320 KiB to 199,156 KiB (-66.4%)
* shared library size: 9,956,640 to 7,716,096 bytes (-22.5%)
* read/write wall time: 0.26s to 0.25s

ELF relocations increased from 28,843 to 39,719 due to descriptor-slot
tables.  The packed schema image stage is intended to remove this cost.

The real AP242 sample created and validated 10,609 instances with zero
errors and warnings.  Rewritten output matched except for its timestamp.
2026-08-02 06:15:25 -04:00
Clifford Yapp 6705c0f62b Implemented API v2 table-driven initialization and opt-in late-bound mode
- API v2 table-driven schema, entity, type, attribute, and inheritance
  initialization via include/clstepcore/ schemaInit.h.

- --late-bound/-T, producing descriptor-backed entities without
  generated early-bound entity classes.

- CMake controls:
    - SC_EXP2CXX_API_VERSION=2
    - SC_EXP2CXX_LATE_BOUND=ON

- Runtime late-bound construction and owned attribute storage in
  src/clstepcore/entityDescriptor.cc and src/clstepcore/STEPattribute.cc.

- Documentation and regression coverage, including incompatible option
  handling.

AP242e4 generated-output results:

 Mode                      Lines       Bytes
━━━━━━━━━━━━━━━━━━━━  ━━━━━━━━━━━  ━━━━━━━━━━
 Default v1            1,210,640    57.57 MB
────────────────────  ───────────  ──────────
 API v2 early-bound      509,001    25.20 MB
────────────────────  ───────────  ──────────
 API v2 late-bound       254,123    13.94 MB

The late-bound AP242 library compiled under C++11 and successfully
processed the BRL-CAD NIST sample: 10,609 instances
created and validated, with zero errors and warnings.
2026-08-02 06:15:25 -04:00
Clifford Yapp 6e18e57380 Implemented the first major API v2 scalability feature
Opt in with either:

exp2cxx --api-version 2 schema.exp

or:

cmake -DSC_EXP2CXX_API_VERSION=2 ...

Version 1 remains the default, and the regression test verifies default
output is byte-identical to explicit v1
output.

API v2 now provides:

- Compact SELECT classes backed by shared descriptor-driven runtime code
  instead of repeated generated parsing, assignment, and serialization
  bodies.

- Generic Set* and typed getter APIs in
  include/clstepcore/sdaiSelect.h:109.
- Dependency-bounded entity headers and source-owned dependencies in
  src/exp2cxx/classes_entity.c:968.
- Out-of-line entity creators.
- Unity chunks that no longer include the all-entity header.
- C++11 output; C++17 did not offer enough additional benefit for this
  tranche.

The command-line handling is in src/exp2cxx/fedex_main.c:85, while
compact SELECT generation begins in src/exp2cxx/selects.c:702.

Measured using the BRL-CAD ap242e4.exp, Release builds, eight parallel
jobs, and 64:8 unity chunks:

 Metric                           API v1    Final API v2    Reduction
━━━━━━━━━━━━━━━━━━━━━━━━  ━━━━━━━━━━━━━━━  ━━━━━━━━━━━━━━  ━━━━━━━━━━━
 Generated C++ lines           1,210,640         522,445        56.8%
────────────────────────  ───────────────  ──────────────  ───────────
 Generated bytes              57,566,051      27,202,644        52.7%
────────────────────────  ───────────────  ──────────────  ───────────
 Schema build wall time         183.22 s         88.31 s        51.8%
────────────────────────  ───────────────  ──────────────  ───────────
 Compiler CPU time              641.53 s        378.49 s        41.0%
────────────────────────  ───────────────  ──────────────  ───────────
 Peak RSS                  1,035,112 KiB     916,412 KiB        11.5%
────────────────────────  ───────────────  ──────────────  ───────────
 Object-file bytes            70,472,952      40,031,088        43.2%
────────────────────────  ───────────────  ──────────────  ───────────
 Shared-library text          34,361,400      19,420,467        43.5%
────────────────────────  ───────────────  ──────────────  ───────────
 Generation wall time             1.52 s          0.69 s        54.6%
2026-08-02 06:15:25 -04:00
Clifford Yapp 3d74790546 Merge pull request #478 from starseeker/upstream/lazy-part21-indexing
Various additions and changes to the stepcode cllazyfile code in response to testing it out with some fairly heavy duty conversion work.
2026-08-02 04:59:34 -04:00
Clifford Yapp 84fa417d0d Whoopes, missed header changes 2026-08-02 03:35:10 -04:00
Clifford Yapp e7b231ca07 Correct AP209/AP214 crashes
The 35 AP209/AP214 crashes came from inverse resolution inspecting an
instance still being materialized. The loader now defers such inverse
passes until explicit attributes are complete, avoids partial duplicate
inverse population, and guards invalid/null referrers. See
src/cllazyfile/lazyInstMgr.cc:301 and src/cllazyfile/lazyRefs.h:90.
2026-08-02 03:13:55 -04:00
Clifford Yapp ced6a1d84b Merge pull request #477 from starseeker/upstream/schema-generation-chunking
Add ability to produce smaller individual schema outputs
2026-08-01 23:11:15 -04:00
Clifford Yapp 1d35f711ce Merge pull request #476 from starseeker/upstream/redefined-attribute-serialization
stepcore: serialize redefined attributes before derived markers
2026-08-01 23:09:53 -04:00
Clifford Yapp 92549337e2 Merge pull request #475 from starseeker/upstream/parser-modern-lemon
express: support modern Lemon allocator callbacks
2026-08-01 23:08:39 -04:00
Clifford Yapp ec07e4e6bd Merge pull request #479 from starseeker/develop
cmake: declare target link interfaces explicitly
2026-08-01 23:08:04 -04:00
Clifford Yapp 0e091f0de7 lazy: index Edition 1 SCOPE records
Handle nested SCOPE constructs and export lists while indexing Part 21
records, preserving scope-owned source records and their references.
Add scoped-record coverage.
2026-08-01 22:27:46 -04:00
Clifford Yapp 6040858231 exp2cxx: tune entity and type chunk limits independently
Schemas commonly contain many more entity implementations than type
implementations. Accept N:N chunk limits and use smaller defaults so
both kinds of generated unity files stay manageable.
2026-08-01 22:27:46 -04:00
Clifford Yapp a267616a30 lazy: stop batch materialization promptly on cancellation
Check cancellation while finding dependencies, pinning records, and
resolving recursive references. Discard an object if cancellation
interrupts its materialization.
2026-08-01 22:27:46 -04:00
Clifford Yapp 3113afd2a4 exp2cxx: generate bounded schema unity chunks
Generate schema source manifests and bounded unity translation
units, then make schema targets depend on the generated absolute
paths. This avoids oversized compiler inputs while retaining
unity-build performance.
2026-08-01 22:27:46 -04:00
Clifford Yapp 9005894a16 lazy: allow explicit materialization type aliases
Permit callers to map known, attribute-compatible source keywords
during object creation while preserving the original keyword in the
lazy index.
2026-08-01 22:27:46 -04:00
Clifford Yapp d69363b85e lazy: expose exact indexed source records
Retain record extents during indexing and provide stream-safe access
to an instance's original Part 21 record without materializing it.
2026-08-01 22:27:46 -04:00
Clifford Yapp 6d7e383ed2 lazy: account for resident source record bytes
Track indexed record bounds and expose the source-byte footprint of
materialized instances in cache statistics. Avoid promoting batch-owned
cache hits during reference resolution.
2026-08-01 22:27:46 -04:00
Clifford Yapp 66e696471c lazy: detect cyclic instance materialization
Track in-progress instance loads and report recursive references
instead of recursing indefinitely through a cyclic Part 21 graph.
2026-08-01 22:27:46 -04:00
Clifford Yapp 844a380d4c lazy: recognize Part 21 comments while scanning
Skip comments as opaque text during token searches so quoted text
and string-control sequences within a comment cannot affect indexing.
2026-08-01 22:27:46 -04:00
Clifford Yapp 9b233ac6ca lazy: add indexed Part 21 instance materialization
Add a lazy Part 21 reader and instance manager that index records
and resolve SDAI instances on demand. Include diagnostics, cache
management, reference indexes, and baseline coverage for lazy reads.
2026-08-01 22:27:46 -04:00
Clifford Yapp 780ed87cbb stepcore: serialize redefined attributes before derived markers
A redefined attribute supplies the inherited physical-file slot even
when that original attribute is derived. Prefer the redefinition
consistently during formatting and STEP writing, remove debug output,
and cover the behavior with unit tests.
2026-08-01 22:27:25 -04:00
Clifford Yapp 7a19e69532 express: support modern Lemon allocator callbacks
Modern Lemon accepts allocator callbacks with a context parameter. Adapt
the standard C allocator functions so generated EXPRESS parsers remain
compatible.
2026-08-01 22:27:25 -04:00
Clifford Yapp 0b950dd18e cmake: declare target link interfaces explicitly
Use modern target_link_libraries scopes so libraries publish their
transitive requirements and executables keep theirs private. This
preserves schema test linkage with current CMake versions.
2026-08-01 22:26:29 -04:00
Clifford Yapp 7836a9ec77 Merge pull request #474 from mumbricht/fix_static_libs
Fix static libs
2026-07-25 22:29:09 -04:00
Clifford Yapp 3bfac87072 Merge pull request #470 from starseeker/treat
Support new version of AP242 STEP schema
2026-07-25 22:07:48 -04:00
Clifford Yapp bf80813ee5 Clear some build warnings 2026-07-25 12:21:26 -04:00
Clifford Yapp 801bed19e1 do more verbose ctest reporting 2026-07-25 12:16:24 -04:00
Clifford Yapp 4ee1286a18 Adjustments for test exp files 2026-07-25 11:29:30 -04:00
Clifford Yapp d1d13d5bce fixes for test_resolve 2026-07-25 11:01:20 -04:00
Clifford Yapp 8b777842d3 Adjustment for test_expr building 2026-07-25 10:32:47 -04:00
Clifford Yapp 29a0de453d Suppress a C4251 warning 2026-07-25 10:00:16 -04:00
Marc Umbricht 0e548f7f1b added SC_STATIC flag for windows static builds 2026-05-02 21:28:00 +02:00
Marc Umbricht ebf495a74a fixed SHARED vs STATIC lib logic in multiple locations 2026-05-01 16:53:48 +02:00
Marc Umbricht e81cd61d65 propagated syntax fix to SC_ADDLIB 2026-05-01 13:31:57 +02:00
Marc Umbricht 19b8148e73 fixed parsing error in SC_ADDEXEC and SC_ADDLIB macros 2026-05-01 12:51:23 +02:00
Clifford Yapp 57a929cd71 Add adjustment per suggestion from TRThurman to make SCHEMA_CMLIST idempotent 2026-03-29 22:05:23 -04:00
Copilotandstarseeker e1de4558e6 Add functional tests for AP242 styled_item with both narrowing approaches (#16)
Add AP242 styled_item functional test infrastructure
Test both vanilla AP242 schema (needs flow-sensitive narrowing)
and modded schema that is more strictly compliant.

Both schema tests use same STEP file

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2026-02-09 15:42:50 -05:00
Clifford Yapp 966fb9d323 Remove md implementation files 2026-02-09 09:25:30 -05:00
Clifford Yapp 7a038d11ed Remove testing files 2026-02-09 09:23:53 -05:00
Clifford Yapp 4d03347df6 Put TREAT modded ap242 into a separate directory 2026-02-09 09:22:17 -05:00
Copilotandstarseeker befb85f2f9 Add TREAT-based AP242 schema variant for standards compliance comparison (#15)
* Initial plan

* Add documentation and clarify TYPEis_aggregate as shallow check

Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>

* Add test for aggregate unwrapping with typedef chains

Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>

* Add TREAT-based corrected AP242 schema and comparison documentation

Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>

* Add AP242 schema README and update documentation with comparison references

Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2026-02-09 09:16:56 -05:00
Copilotandstarseeker aa79dbfeec Implement flow-sensitive SELECT narrowing for TYPEOF guards in AND expressions (#14)
Implement flow-sensitive narrowing - ap242 schema can now be read

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2026-02-09 09:16:53 -05:00
Copilotandstarseeker a01c338005 Implement TREAT expression for SELECT type narrowing per ISO 10303-11 (#13)
* Add TREAT keyword and grammar infrastructure
* Complete TREAT infrastructure and test schema (parser generation pending)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2026-02-09 09:16:36 -05:00
Copilotandstarseeker 975a74d36b Fix PE056: Deep aggregate unwrapping for QUERY resolution (#12)
* Implement deep aggregate unwrapping for QUERY resolution (PE056 fix)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2026-02-09 09:16:36 -05:00
Copilotandstarseeker 93369ce4aa Add baseline test for PE056 error in AP242 schema parsing (#10)
* Initial plan

* Add baseline instrumentation test for PE056 in AP242 schema

Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2026-02-09 09:16:36 -05:00
Clifford Yapp 8cc8fc7590 Set up the newer (non-working) ap242 exp file 2026-02-09 09:16:36 -05:00
Clifford Yapp 74b6fe4575 Merge pull request #469 from starseeker/cmaketweaks
Complete CMake variable migration, fix schema library installation
2026-02-09 09:14:53 -05:00
Copilot aa595c9c91 Complete CMake variable migration, fix schema library installation
Clean up stray SC_BUILD_STATIC_LIBS and SC_BUILD_TYPE variables.
Install schema libs
Fix linking when doing static builds
2026-02-06 18:08:10 -05:00
Clifford Yapp e79c15dae8 Updates for compiling in newer compilers/environments
Various fixes for building with new compilers, update Github workflows for new BRL-CAD compilation setup, one behavior correction - rather than trying to use a NULL pointer deference to crash in an error case (bad practice and undefined behavior anyway) return an error attribute calling applications can deal with gracefully.

Merges pull request #466 from starseeker/buildfixes
2026-02-06 11:13:40 -05:00
Clifford Yapp 66aec9749e Whoops, old fixed versions of ubuntu 2026-02-06 10:38:33 -05:00
Clifford Yapp 11694e8bf9 Helps to spell the variable name correctly... 2026-02-06 09:54:45 -05:00
Clifford Yapp fb00dd424f Release build config on Windows 2026-02-06 09:11:21 -05:00
Clifford Yapp c9c1bb99da Remove stray -B options 2026-02-06 08:31:00 -05:00
Clifford Yapp b3fc4258c8 More yml adjustments 2026-02-05 17:22:06 -05:00
Clifford Yapp 58732df22c Use snprintf instead of sprintf. There are probably other string handling modernizations to do here... 2026-02-05 17:16:23 -05:00
Clifford Yapp a74c755f1e More yml adjustments 2026-02-05 15:14:50 -05:00
Clifford Yapp 5e2ac7ea72 syntax error 2025-12-09 09:49:07 -05:00
Clifford Yapp 391f630af6 Try adjusting BRL-CAD check logic 2025-12-08 14:57:04 -05:00
Clifford Yapp 4025beba46 fix BRL-CAD url 2025-12-05 17:07:18 -05:00
Clifford Yapp c2dc4f13fa Merge pull request #3 from starseeker/copilot/debug-test-exppp-supertype-andor
Fix test_exppp_supertype_andor CMake version inconsistency
2025-12-05 16:53:59 -05:00
copilot-swe-agent[bot]andstarseeker 9880ca87bc Update CMake version requirement for consistency
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2025-12-05 21:33:31 +00:00
copilot-swe-agent[bot] 6ff7580814 Initial plan 2025-12-05 21:15:28 +00:00
Clifford Yapp 845c3fdc34 Fix null pointer dereference in STEPattributeList operator[]
Improves error handling in STEPattributeList operator[] - return an
error attribute and set error state rather than trying to rely on
crashing via a NULL deference.  That's undefined behavior, so it
was not guaranteed - this requires the calling code to check the
return, but is *much* cleaner.

Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2025-12-05 14:01:54 -05:00
copilot-swe-agent[bot]andstarseeker 6cbc18b87a Fix legacy C style warnings - add void to parameterless function declarations
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2025-12-05 14:01:32 -05:00
copilot-swe-agent[bot]andstarseeker aa3b89affb Fix strdup implicit declaration errors for clang 18.1.3 compatibility
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2025-12-05 14:01:21 -05:00
copilot-swe-agent[bot]andstarseeker 6bbd46007c Address build issues in judySArray and judyS2Array
Co-authored-by: starseeker <238416+starseeker@users.noreply.github.com>
2025-12-05 14:01:08 -05:00
182 changed files with 127298 additions and 38828 deletions
+65 -56
View File
@@ -44,11 +44,11 @@ jobs:
- name: Test
run: |
cd build && ctest -j1 .
cd build && ctest --verbose -j1 .
linux:
name: Ubuntu Latest GCC
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
@@ -86,11 +86,11 @@ jobs:
- name: Test
run: |
cd build && ctest -j1 . -C Release
cd build && ctest --verbose -j1 . -C Release
linux_clang:
name: Ubuntu Latest Clang
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
@@ -128,7 +128,7 @@ jobs:
- name: Test
run: |
cd build && ctest -j1 . -C Release
cd build && ctest --verbose -j1 . -C Release
osx:
@@ -160,62 +160,68 @@ jobs:
- name: Test
run: |
cd build && ctest -j1 . -C Release
cd build && ctest --verbose -j1 . -C Release
brlcad_linux:
name: BRL-CAD Linux step-g Test
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
fail-fast: true
steps:
- name: Setup - CMake
uses: lukka/get-cmake@latest
- name: Setup - Ninja
uses: seanmiddleditch/gha-setup-ninja@master
- name: Setup - System
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get update
# Install dev tools
sudo apt-get install re2c lemon
sudo apt-get install re2c astyle xsltproc libxml2-utils
sudo apt-get install xserver-xorg-dev libx11-dev libxi-dev libxext-dev libglu1-mesa-dev libfontconfig-dev
sudo apt-get install zlib1g-dev libpng-dev libjpeg-dev libtiff-dev libeigen3-dev libgdal-dev libassimp-dev libopencv-dev
sudo apt-get clean
- name: Clone bext
run: |
git clone https://github.com/BRL-CAD/bext.git
cd bext/stepcode
cmake -E rm -r stepcode
git clone --depth 1 https://github.com/stepcode/stepcode.git -b develop
cd ../..
- name: Build bext
run: |
cmake -E make_directory bext_build
cmake -S bext -B bext_build -DCMAKE_BUILD_TYPE=Release -DUSE_GDAL=OFF -DUSE_TCL=OFF -DUSE_QT=OFF -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}\bext_output
cmake --build bext_build --config Release -j2
# We only need the build ouputs (bext_output) after this point, so
# clean up to save space for the main BRL-CAD build.
cmake -E rm -rf bext
cmake -E rm -rf bext_build
- name: Checkout
run: |
git clone --depth 1 https://github.com/BRL-CAD/brlcad.git -b main
cd brlcad/src/other/ext && rm -rf stepcode
git clone --depth 1 https://github.com/stepcode/stepcode.git -b develop
# Ordinarily BRL-CAD keeps track of what files are supposed to be
# present in the repository. In this case we're not interested in
# updating the list to the working stepcode filelist, so use an empty
# list instead
echo "set(stepcode_ignore_files)" > stepcode.dist
cd ../../../../
- name: Configure
run: |
export PATH=$ENV{GITHUB_WORKSPACE}:$PATH
cd brlcad
cmake -S . -G Ninja -B build -DENABLE_ALL=ON -DCMAKE_BUILD_TYPE=Release -DEXT_BUILD_VERBOSE=ON
cmake -E make_directory brlcad_build
cd brlcad_build
cmake -S ../brlcad -DBRLCAD_EXT_DIR=${{ github.workspace }}\bext_output -DBRLCAD_ENABLE_GDAL=OFF -DBRLCAD_ENABLE_TCL=OFF -DBRLCAD_ENABLE_QT=OFF -G Ninja
cd ..
- name: Build
run: |
export PATH=$ENV{GITHUB_WORKSPACE}:$PATH
cd brlcad
cmake --build build --config Release --target step-g
cd brlcad_build
cmake --build . --target step-g
cd ..
- name: Test
run: |
export PATH=$ENV{GITHUB_WORKSPACE}:$PATH
cd brlcad/build
./bin/step-g ../db/nist/NIST_MBE_PMI_3.stp -o nist3.g
cd ../..
cd brlcad_build
./bin/step-g ../brlcad/db/nist/NIST_MBE_PMI_3.stp -o nist3.g
cd ..
brlcad_windows:
@@ -227,9 +233,6 @@ jobs:
- name: Setup - CMake
uses: lukka/get-cmake@latest
- name: Setup - Ninja
uses: seanmiddleditch/gha-setup-ninja@master
- name: Add github workspace to path
# https://github.community/t/deprecated-add-path/136621
run: echo "$ENV{GITHUB_WORKSPACE}" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
@@ -240,38 +243,44 @@ jobs:
- name: Add cl.exe to PATH
uses: ilammy/msvc-dev-cmd@v1
- name: Clone bext
run: |
git clone https://github.com/BRL-CAD/bext.git
cd bext/stepcode
cmake -E rm -r stepcode
git clone --depth 1 https://github.com/stepcode/stepcode.git -b develop
cd ../..
- name: Build bext
shell: powershell
run: |
cmake -E make_directory bext_build
cmake -S bext -B bext_build -DCMAKE_BUILD_TYPE=Release -DUSE_GDAL=OFF -DUSE_TCL=OFF -DUSE_QT=OFF -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}\bext_output
cmake --build bext_build --config Release -j2
# We only need the build ouputs (bext_output) after this point, so
# clean up to save space for the main BRL-CAD build.
cmake -E rm -rf bext
cmake -E rm -rf bext_build
- name: Checkout
run: |
git clone --depth 1 https://github.com/BRL-CAD/brlcad.git -b main
cd brlcad/src/other/ext
cmake -E rm -r stepcode
git clone --depth 1 https://github.com/stepcode/stepcode.git -b develop
# Ordinarily BRL-CAD keeps track of what files are supposed to be
# present in the repository. In this case we're not interested in
# updating the list to the working stepcode filelist, so use an empty
# list instead
echo "set(stepcode_ignore_files)" > stepcode.dist
cd ../../../../
- name: Configure
run: |
cd brlcad && cmake -S . -B build -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_CXX_COMPILER="cl.exe" -DSC_ENABLE_TESTING=ON
cd ..
# We do the following in order to help ensure files are "flushed"
# to disk before compilation is attempted
# https://superuser.com/a/1553374/1286142
powershell Write-VolumeCache C
powershell Write-VolumeCache D
cmake -E make_directory brlcad_build
cmake -S brlcad -B brlcad_build -DCMAKE_BUILD_TYPE=Release -DBRLCAD_EXT_DIR=${{ github.workspace }}\bext_output -DBRLCAD_ENABLE_GDAL=OFF -DBRLCAD_ENABLE_TCL=OFF -DBRLCAD_ENABLE_QT=OFF -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_CXX_COMPILER="cl.exe"
- name: Build
run: |
cd brlcad/build
ninja -j1 -v step-g
cd ../..
cd brlcad_build
cmake --build . --config Release --target step-g
cd ..
- name: Test
run: |
cd brlcad/build
./bin/step-g.exe ../db/nist/NIST_MBE_PMI_3.stp -o nist3.g
cd ../..
cd brlcad_build
./bin/step-g.exe ../brlcad/db/nist/NIST_MBE_PMI_3.stp -o nist3.g
cd ..
+4
View File
@@ -1,5 +1,6 @@
# Common build directories used in the source tree
/build
/build-*
/.build
# Temporary files
@@ -19,3 +20,6 @@
# Tool specific patterns
*.kdev4
.kdev_include_paths
# Parser generator intermediate files
*.out
+117 -1
View File
@@ -129,6 +129,18 @@ if (SC_ENABLE_COVERAGE AND ${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "-fprofile-arcs -ftest-coverage" CACHE STRING "Extra linker flags required by code coverage" FORCE)
endif (SC_ENABLE_COVERAGE AND ${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
option(SC_ENABLE_ADDRESS_SANITIZER "Enable AddressSanitizer instrumentation" OFF)
option(SC_ENABLE_UNDEFINED_SANITIZER "Enable UndefinedBehaviorSanitizer instrumentation" OFF)
option(SC_ENABLE_THREAD_SANITIZER "Enable ThreadSanitizer instrumentation" OFF)
if(SC_ENABLE_THREAD_SANITIZER AND
(SC_ENABLE_ADDRESS_SANITIZER OR SC_ENABLE_UNDEFINED_SANITIZER))
message(FATAL_ERROR
"SC_ENABLE_THREAD_SANITIZER must use a separate build from "
"AddressSanitizer and UndefinedBehaviorSanitizer"
)
endif()
option(SC_BUILD_EXPRESS_ONLY "Only build express parser." OFF)
mark_as_advanced(SC_BUILD_EXPRESS_ONLY)
@@ -155,6 +167,111 @@ include(SC_Regenerate)
# create config.h header
include(SC_Config_Headers)
# Apply sanitizer instrumentation after configure-time feature tests so their
# try-compile executables do not depend on sanitizer runtime behavior. Keep
# link flags explicit for CMake 3.12 compatibility (add_link_options was added
# in CMake 3.13).
if(SC_ENABLE_ADDRESS_SANITIZER OR
SC_ENABLE_UNDEFINED_SANITIZER OR
SC_ENABLE_THREAD_SANITIZER)
if(MSVC)
message(FATAL_ERROR "STEPcode sanitizer builds are not yet supported with MSVC")
endif()
include(CMakePushCheckState)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
set(_sc_sanitizer_compile_options)
set(_sc_sanitizer_link_options)
function(sc_require_sanitizer FLAG RESULT_PREFIX)
cmake_push_check_state()
set(CMAKE_REQUIRED_FLAGS "${FLAG}")
list(APPEND CMAKE_REQUIRED_LIBRARIES "${FLAG}")
check_c_source_compiles("int main(void) { return 0; }" ${RESULT_PREFIX}_C_SUPPORTED)
check_cxx_source_compiles("int main() { return 0; }" ${RESULT_PREFIX}_CXX_SUPPORTED)
cmake_pop_check_state()
if(NOT ${RESULT_PREFIX}_C_SUPPORTED OR NOT ${RESULT_PREFIX}_CXX_SUPPORTED)
message(FATAL_ERROR
"STEPcode sanitizer mode was requested, but the C/C++ compiler and "
"linker do not support ${FLAG}"
)
endif()
endfunction()
if(SC_ENABLE_ADDRESS_SANITIZER)
sc_require_sanitizer("-fsanitize=address" SC_ASAN)
list(APPEND _sc_sanitizer_compile_options -fsanitize=address)
list(APPEND _sc_sanitizer_link_options -fsanitize=address)
# exp2cxx is executed during the build to generate schema bindings. It
# remains address-instrumented, but leak checking a short-lived build
# tool is both outside the test scope and unsupported under common build
# tracers/sandboxes. Limit this environment to schema generation; CTest
# and installed tools retain the user's normal ASan settings.
set(SC_SCHEMA_GENERATOR_ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0")
endif()
if(SC_ENABLE_UNDEFINED_SANITIZER)
sc_require_sanitizer("-fsanitize=undefined" SC_UBSAN)
cmake_push_check_state()
set(CMAKE_REQUIRED_FLAGS "-fsanitize=undefined")
list(APPEND CMAKE_REQUIRED_LIBRARIES "-fsanitize=undefined")
check_c_compiler_flag(
"-fno-sanitize-recover=undefined"
SC_UBSAN_C_NO_RECOVER_SUPPORTED
)
check_cxx_compiler_flag(
"-fno-sanitize-recover=undefined"
SC_UBSAN_CXX_NO_RECOVER_SUPPORTED
)
cmake_pop_check_state()
if(NOT SC_UBSAN_C_NO_RECOVER_SUPPORTED OR
NOT SC_UBSAN_CXX_NO_RECOVER_SUPPORTED)
message(FATAL_ERROR
"STEPcode's fail-fast UndefinedBehaviorSanitizer mode was requested, "
"but the C/C++ compiler does not support "
"-fno-sanitize-recover=undefined"
)
endif()
list(APPEND _sc_sanitizer_compile_options
-fsanitize=undefined
-fno-sanitize-recover=undefined
)
list(APPEND _sc_sanitizer_link_options -fsanitize=undefined)
endif()
if(SC_ENABLE_THREAD_SANITIZER)
sc_require_sanitizer("-fsanitize=thread" SC_TSAN)
list(APPEND _sc_sanitizer_compile_options -fsanitize=thread)
list(APPEND _sc_sanitizer_link_options -fsanitize=thread)
endif()
check_c_compiler_flag("-fno-omit-frame-pointer" SC_SANITIZER_C_FRAME_POINTER_SUPPORTED)
check_cxx_compiler_flag("-fno-omit-frame-pointer" SC_SANITIZER_CXX_FRAME_POINTER_SUPPORTED)
if(SC_SANITIZER_C_FRAME_POINTER_SUPPORTED)
list(APPEND _sc_sanitizer_compile_options
$<$<COMPILE_LANGUAGE:C>:-fno-omit-frame-pointer>
)
endif()
if(SC_SANITIZER_CXX_FRAME_POINTER_SUPPORTED)
list(APPEND _sc_sanitizer_compile_options
$<$<COMPILE_LANGUAGE:CXX>:-fno-omit-frame-pointer>
)
endif()
add_compile_options(${_sc_sanitizer_compile_options})
string(JOIN " " _sc_sanitizer_link_flags ${_sc_sanitizer_link_options})
foreach(_sc_link_type EXE SHARED MODULE)
set(
CMAKE_${_sc_link_type}_LINKER_FLAGS
"${CMAKE_${_sc_link_type}_LINKER_FLAGS} ${_sc_sanitizer_link_flags}"
)
endforeach()
endif()
if(NOT DEFINED SC_SDAI_ADDITIONAL_EXES_SRCS)
set(SC_SDAI_ADDITIONAL_EXES_SRCS "" CACHE STRING "Source files for additional executables to be linked with SDAI libs")
endif(NOT DEFINED SC_SDAI_ADDITIONAL_EXES_SRCS)
@@ -230,4 +347,3 @@ endforeach(_msg ${CONFIG_END_MESSAGES})
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+1 -1
View File
@@ -5,7 +5,7 @@ This repo was moved and renamed in May 2012. Previously, it was at
http://github.com/mpictor/StepClassLibrary
Some are duplicates of changes recorded in the BRL-CAD repo:
http://brlcad.svn.sourceforge.net/viewvc/brlcad/brlcad/trunk/src/other/step/?view=log
https://github.com/BRL-CAD/brlcad/tree/158beae4556d94821938368951546384cba53107/src/other/ext/stepcode
Pre-BRL-CAD changes:
************************************************************************
+7 -1
View File
@@ -51,7 +51,13 @@ Installation
-DSC_BUILD_SCHEMAS=ALL
- Added to make use of ctest easier. If SC_BUILD_SCHEMAS == ALL,
then CMake adds each *.exp file found in data/.
-DSC_BUILD_TYPE=Release
-DSC_EXP2CXX_API_VERSION=2
- opts in to compact descriptor-driven SELECTs and dependency-bounded
entity headers in the generated C++ API.
- clients include the entity headers they use (for example,
entity/SdaiProduct.h); schema.h no longer expands every entity class.
- version 1 remains the default for source compatibility.
-DCMAKE_BUILD_TYPE=Release
- this causes binaries to be built without debugging information
- without this, cmake defaults to a Debug build
+41 -11
View File
@@ -27,17 +27,29 @@ endmacro(P21_TESTS sfile)
# create p21read_sdai_*, lazy_sdai_*, any exes listed in SC_SDAI_ADDITIONAL_EXES_SRCS
macro(SCHEMA_EXES)
RELATIVE_PATH_TO_TOPLEVEL(${CMAKE_CURRENT_SOURCE_DIR} RELATIVE_PATH_COMPONENT)
SC_ADDEXEC(p21read_${PROJECT_NAME} SOURCES "${RELATIVE_PATH_COMPONENT}/src/test/p21read/p21read.cc;${RELATIVE_PATH_COMPONENT}/src/test/p21read/sc_benchmark.cc" LINK_LIBRARIES ${PROJECT_NAME} stepdai stepcore stepeditor steputils TESTABLE)
if(BUILD_SHARED_LIBS)
SC_ADDEXEC(p21read_${PROJECT_NAME} SOURCES "${RELATIVE_PATH_COMPONENT}/src/test/p21read/p21read.cc;${RELATIVE_PATH_COMPONENT}/src/test/p21read/sc_benchmark.cc" LINK_LIBRARIES ${PROJECT_NAME} stepdai stepcore stepeditor steputils TESTABLE)
elseif(BUILD_STATIC_LIBS)
SC_ADDEXEC(p21read_${PROJECT_NAME} SOURCES "${RELATIVE_PATH_COMPONENT}/src/test/p21read/p21read.cc;${RELATIVE_PATH_COMPONENT}/src/test/p21read/sc_benchmark.cc" LINK_LIBRARIES ${PROJECT_NAME}-static stepeditor-static stepcore-static stepdai-static steputils-static TESTABLE)
endif()
if(NOT WIN32)
SC_ADDEXEC(lazy_${PROJECT_NAME} SOURCES "${RELATIVE_PATH_COMPONENT}/src/cllazyfile/lazy_test.cc;${RELATIVE_PATH_COMPONENT}/src/cllazyfile/sc_benchmark.cc" LINK_LIBRARIES ${PROJECT_NAME} steplazyfile stepdai stepcore stepeditor steputils TESTABLE)
if(BUILD_SHARED_LIBS)
SC_ADDEXEC(lazy_${PROJECT_NAME} SOURCES "${RELATIVE_PATH_COMPONENT}/src/cllazyfile/lazy_test.cc;${RELATIVE_PATH_COMPONENT}/src/cllazyfile/sc_benchmark.cc" LINK_LIBRARIES ${PROJECT_NAME} steplazyfile stepdai stepcore stepeditor steputils TESTABLE)
elseif(BUILD_STATIC_LIBS)
SC_ADDEXEC(lazy_${PROJECT_NAME} SOURCES "${RELATIVE_PATH_COMPONENT}/src/cllazyfile/lazy_test.cc;${RELATIVE_PATH_COMPONENT}/src/cllazyfile/sc_benchmark.cc" LINK_LIBRARIES ${PROJECT_NAME}-static steplazyfile-static stepeditor-static stepcore-static stepdai-static steputils-static TESTABLE)
endif()
endif(NOT WIN32)
#add user-defined executables
foreach(src ${SC_SDAI_ADDITIONAL_EXES_SRCS})
get_filename_component(name ${src} NAME_WE)
get_filename_component(path ${src} ABSOLUTE)
SC_ADDEXEC(${name}_${PROJECT_NAME} SOURCES ${src} LINK_LIBRARIES ${PROJECT_NAME} stepdai stepcore stepeditor steputils TESTABLE)
if(BUILD_SHARED_LIBS)
SC_ADDEXEC(${name}_${PROJECT_NAME} SOURCES ${src} LINK_LIBRARIES ${PROJECT_NAME} stepdai stepcore stepeditor steputils TESTABLE)
elseif(BUILD_STATIC_LIBS)
SC_ADDEXEC(${name}_${PROJECT_NAME} SOURCES ${src} LINK_LIBRARIES ${PROJECT_NAME}-static stepcore-static stepdai-static stepeditor-static steputils-static TESTABLE)
#set_target_properties(${name}_${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "${${PROJECT_NAME}_COMPILE_FLAGS} -I${path}")
endif()
endforeach(src ${SC_SDAI_ADDITIONAL_EXES_SRCS})
ENDMACRO(SCHEMA_EXES)
@@ -77,25 +89,40 @@ macro(SCHEMA_TARGETS expFile schemaName sourceFiles)
# schema scanner comes up with a short schema name for PROJECT() (which sets ${PROJECT_NAME})
message(STATUS "Will generate ${${PROJECT_NAME}_file_count} C++ files for ${PROJECT_NAME}.")
add_custom_target(generate_cpp_${PROJECT_NAME} DEPENDS exp2cxx ${expFile} ${sourceFiles} SOURCES ${sourceFiles})
set(_schema_generated_files)
foreach(_schema_source ${sourceFiles})
list(APPEND _schema_generated_files "${CMAKE_CURRENT_SOURCE_DIR}/${_schema_source}")
endforeach()
add_custom_target(generate_cpp_${PROJECT_NAME} DEPENDS exp2cxx ${expFile} ${_schema_generated_files} SOURCES ${_schema_generated_files})
# this calls a cmake script because it doesn't seem to be possible
# to divert stdout, stderr in cmake except via execute_process
add_custom_command(OUTPUT ${sourceFiles}
add_custom_command(OUTPUT ${_schema_generated_files}
COMMAND ${CMAKE_COMMAND} -DEXE=\"$<TARGET_FILE:exp2cxx>\" -DEXP=\"${expFile}\"
-DONESHOT=\"${SC_GENERATE_CXX_ONESHOT}\" -DSDIR=\"${CMAKE_CURRENT_LIST_DIR}\"
-DCHUNK_SIZE=\"${SC_EXP2CXX_CHUNK_SIZE}\"
-DAPI_VERSION=\"${SC_EXP2CXX_API_VERSION}\"
-DLATE_BOUND=\"${SC_EXP2CXX_LATE_BOUND}\"
-DCOMPAT_NAMES=\"${SC_EXP2CXX_COMPAT_NAMES}\"
-DMETADATA_PROFILE=\"${SC_EXP2CXX_METADATA}\"
-DGENERATOR_ENVIRONMENT=\"${SC_SCHEMA_GENERATOR_ENVIRONMENT}\"
-P ${SC_CMAKE_DIR}/SC_Run_exp2cxx.cmake
DEPENDS exp2cxx ${expFile}
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
COMMENT "[exp2cxx] Generating ${${PROJECT_NAME}_file_count} C++ files for ${PROJECT_NAME}."
)
include_directories(
set(_schema_include_dirs
${CMAKE_CURRENT_SOURCE_DIR} ${SC_SOURCE_DIR}/src/cldai ${SC_SOURCE_DIR}/src/cleditor
${SC_SOURCE_DIR}/src/clutils ${SC_SOURCE_DIR}/src/clstepcore ${SC_SOURCE_DIR}/src/cllazyfile
${SC_SOURCE_DIR}/src/cllazyfile/judy/src
)
# if testing is enabled, "TESTABLE" sets property EXCLUDE_FROM_ALL and prevents installation
# Schema libraries should be installed by default
if(BUILD_SHARED_LIBS)
SC_ADDLIB(${PROJECT_NAME} SHARED SOURCES ${sourceFiles} LINK_LIBRARIES stepdai stepcore stepeditor steputils TESTABLE)
SC_ADDLIB(${PROJECT_NAME} SHARED SOURCES ${_schema_generated_files} LINK_LIBRARIES stepdai stepcore stepeditor steputils)
add_dependencies(${PROJECT_NAME} generate_cpp_${PROJECT_NAME})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${_schema_include_dirs})
if(${PROJECT_NAME}_unity_build)
target_compile_definitions(${PROJECT_NAME} PRIVATE SC_SDAI_UNITY_BUILD)
endif()
if(WIN32)
target_compile_definitions("${PROJECT_NAME}" PRIVATE SC_SCHEMA_DLL_EXPORTS)
if(MSVC)
@@ -110,10 +137,14 @@ macro(SCHEMA_TARGETS expFile schemaName sourceFiles)
endif()
endif()
if($CACHE{SC_BUILD_STATIC_LIBS})
SC_ADDLIB(${PROJECT_NAME}-static STATIC SOURCES ${sourceFiles} LINK_LIBRARIES stepdai-static stepcore-static stepeditor-static steputils-static TESTABLE)
if(BUILD_STATIC_LIBS)
SC_ADDLIB(${PROJECT_NAME}-static STATIC SOURCES ${_schema_generated_files} LINK_LIBRARIES stepcore-static stepdai-static stepeditor-static steputils-static)
add_dependencies(${PROJECT_NAME}-static generate_cpp_${PROJECT_NAME})
target_include_directories(${PROJECT_NAME}-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${_schema_include_dirs})
target_compile_definitions("${PROJECT_NAME}-static" PRIVATE SC_STATIC)
if(${PROJECT_NAME}_unity_build)
target_compile_definitions(${PROJECT_NAME}-static PRIVATE SC_SDAI_UNITY_BUILD)
endif()
if(MSVC)
target_compile_options("${PROJECT_NAME}-static" PRIVATE "/bigobj")
endif()
@@ -133,4 +164,3 @@ endmacro(SCHEMA_TARGETS expFile schemaName sourceFiles)
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+17 -2
View File
@@ -1,10 +1,26 @@
# if oneshot is true, don't run exp2cxx if source files exist. if schema.cc exists, assume others do
if(NOT DEFINED API_VERSION OR "${API_VERSION}" STREQUAL "")
set(API_VERSION 1)
endif()
if(NOT DEFINED METADATA_PROFILE OR "${METADATA_PROFILE}" STREQUAL "")
set(METADATA_PROFILE full)
endif()
if(ONESHOT AND EXISTS "${SDIR}/schema.cc")
message("WARNING: SC_GENERATE_CXX_ONESHOT is enabled. If generated code has been modified, it will NOT be rewritten!")
message("This is ONLY for debugging STEPcode internals!")
else()
execute_process(COMMAND ${EXE} ${EXP}
set(_exp2cxx_mode_args)
if(LATE_BOUND)
list(APPEND _exp2cxx_mode_args --late-bound)
endif()
if(COMPAT_NAMES)
list(APPEND _exp2cxx_mode_args --compat-names)
endif()
list(APPEND _exp2cxx_mode_args --metadata ${METADATA_PROFILE})
execute_process(COMMAND ${CMAKE_COMMAND} -E env ${GENERATOR_ENVIRONMENT}
${EXE} -k ${CHUNK_SIZE} -V ${API_VERSION} ${_exp2cxx_mode_args} ${EXP}
WORKING_DIRECTORY ${SDIR}
RESULT_VARIABLE _res
OUTPUT_FILE exp2cxx_stdout.txt
@@ -22,4 +38,3 @@ endif()
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+21 -9
View File
@@ -10,15 +10,23 @@ macro(SC_ADDEXEC execname)
add_executable(${execname} ${${_arg_prefix}_SOURCES})
if(BUILD_STATIC_LIBS)
target_compile_definitions(${execname} PRIVATE SC_STATIC)
endif()
if(DEFINED "${_arg_prefix}_LINK_LIBRARIES")
foreach(_lib ${${_arg_prefix}_LINK_LIBRARIES})
if($CACHE{SC_STATIC_UTILS})
if(NOT $<TARGET_PROPERTY:${_lib},TYPE> STREQUAL "STATIC_LIBRARY")
get_property(_lib_type TARGET ${_lib} PROPERTY TYPE)
if(NOT _lib_type STREQUAL "STATIC_LIBRARY")
message(SEND_ERROR "SC_ADDEXEC usage error - expected STATIC LINK_LIBRARIES targets (${_lib})")
endif()
endif()
target_link_libraries(${execname} ${_lib})
# Executables consume their dependencies but do not publish an
# interface to downstream targets.
target_link_libraries(${execname} PRIVATE ${_lib})
endforeach()
target_link_libraries(${execname} PRIVATE ${${_arg_prefix}_LINK_LIBRARIES})
endif()
if(NOT ${_arg_prefix}_NO_INSTALL AND NOT ${_arg_prefix}_TESTABLE)
@@ -37,7 +45,7 @@ endmacro()
macro(SC_ADDLIB _addlib_target)
set(_addlib_opts SHARED STATIC NO_INSTALL TESTABLE)
set(_addlib_multikw SOURCES LINK_LIBRARIES)
string(TOUPPER "SC_ADDLIB_${libname}_ARG" _arg_prefix)
string(TOUPPER "SC_ADDLIB_${_addlib_target}_ARG" _arg_prefix)
cmake_parse_arguments(${_arg_prefix} "${_addlib_opts}" "" "${_addlib_multikw}" ${ARGN})
if(NOT DEFINED ${_arg_prefix}_SOURCES)
@@ -64,20 +72,25 @@ macro(SC_ADDLIB _addlib_target)
if(DEFINED ${_arg_prefix}_LINK_LIBRARIES)
foreach(_lib ${${_arg_prefix}_LINK_LIBRARIES})
if(${_arg_prefix}_STATIC AND TARGET ${_lib})
get_property(_libtype TARGET ${_lib} PROPERTY TYPE)
if(NOT ${_libtype} STREQUAL "STATIC_LIBRARY")
message(SEND_ERROR "SC_ADDLIB usage error - expected (static) LINK_LIBRARIES targets (${_lib})")
get_property(_libtype TARGET ${_lib} PROPERTY TYPE)
if(NOT ${_libtype} STREQUAL "STATIC_LIBRARY")
message(SEND_ERROR "SC_ADDLIB usage error - expected (static) LINK_LIBRARIES targets (${_lib})")
endif()
endif()
target_link_libraries(${_addlib_target} ${_lib})
# Schema libraries are consumed directly by generated test programs.
# Publish their link requirements explicitly; the legacy unscoped
# signature does not reliably provide a transitive interface with
# current CMake versions.
target_link_libraries(${_addlib_target} PUBLIC ${_lib})
endforeach()
target_link_libraries(${_addlib_target} ${_lib})
endif()
if(NOT ${_arg_prefix}_NO_INSTALL AND NOT ${_arg_prefix}_TESTABLE)
install(TARGETS ${_addlib_target}
RUNTIME DESTINATION ${BIN_DIR}
LIBRARY DESTINATION ${LIB_DIR}
ARCHIVE DESTINATION ${LIB_DIR}
ARCHIVE DESTINATION ${LIB_DIR}
)
endif()
endmacro()
@@ -88,4 +101,3 @@ endmacro()
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+79 -15
View File
@@ -35,6 +35,7 @@ extern "C" {
#endif
}
#include <algorithm>
#include <string>
#include <sstream>
#include <iomanip>
@@ -50,6 +51,35 @@ using std::ofstream;
using std::cerr;
using std::cout;
static unsigned long entityChunkSize = 256;
static unsigned long typeChunkSize = 256;
static bool
parseChunkSizes( const char * value )
{
if( !value || !value[0] ) {
return false;
}
char * end = 0;
const unsigned long entities = strtoul( value, &end, 10 );
if( !end || entities == 0 ) {
return false;
}
unsigned long types = entities;
if( *end == ':' ) {
char * typeEnd = 0;
types = strtoul( end + 1, &typeEnd, 10 );
if( !end[1] || !typeEnd || *typeEnd || types == 0 ) {
return false;
}
} else if( *end ) {
return false;
}
entityChunkSize = entities;
typeChunkSize = types;
return true;
}
/** \return true for types that exp2cxx won't generate code for */
bool notGenerated( const Type t ) {
switch( TYPEget_body( t )->type ) {
@@ -186,20 +216,34 @@ void writeLists( const char * schemaName, stringstream & eh, stringstream & ei,
cmLists << "# targets, logic, etc are within a set of macros shared by all schemas" << endl;
cmLists << "include(${SC_CMAKE_DIR}/SC_CXX_schema_macros.cmake)" << endl;
// * 2 for headers, + 10 other files
cmLists << "set(" << shortName << "_file_count " << ( ( ecount + tcount ) * 2 ) + 10 << ")" << endl << endl;
const int entityChunks = std::max( 1, ( ecount + static_cast<int>( entityChunkSize ) - 1 ) / static_cast<int>( entityChunkSize ) );
const int typeChunks = std::max( 1, ( tcount + static_cast<int>( typeChunkSize ) - 1 ) / static_cast<int>( typeChunkSize ) );
cmLists << "if(SC_EXP2CXX_LATE_BOUND)" << endl;
cmLists << " set(" << shortName << "_file_count 10)" << endl;
cmLists << "else()" << endl;
cmLists << " set(" << shortName << "_file_count " <<
( ecount + tcount ) * 2 + entityChunks + typeChunks + 14 << ")" << endl;
cmLists << "endif()" << endl << endl;
cmLists << "PROJECT(" << shortName << ")" << endl;
cmLists << "# list headers so they can be installed - entity, type, misc" << endl;
cmLists << "set(" << shortName << "_entity_hdrs" << endl;
cmLists << "if(SC_EXP2CXX_LATE_BOUND)" << endl;
cmLists << " set(" << shortName << "_entity_hdrs)" << endl;
cmLists << "else()" << endl;
cmLists << " set(" << shortName << "_entity_hdrs" << endl;
cmLists << eh.str();
cmLists << " )" << endl << endl;
cmLists << " )" << endl;
cmLists << "endif()" << endl << endl;
cmLists << "set(" << shortName << "_type_hdrs" << endl;
cmLists << "if(SC_EXP2CXX_LATE_BOUND)" << endl;
cmLists << " set(" << shortName << "_type_hdrs)" << endl;
cmLists << "else()" << endl;
cmLists << " set(" << shortName << "_type_hdrs" << endl;
cmLists << th.str();
cmLists << " )" << endl << endl;
cmLists << " )" << endl;
cmLists << "endif()" << endl << endl;
cmLists << "set(" << shortName << "_misc_hdrs" << endl;
cmLists << " Sdaiclasses.h schema.h" << endl;
@@ -224,11 +268,26 @@ void writeLists( const char * schemaName, stringstream & eh, stringstream & ei,
cmLists << "# unity build: #include small .cc files to reduce the number" << endl;
cmLists << "# of translation units that must be compiled" << endl;
cmLists << "if(SC_UNITY_BUILD)" << endl << " # turns off include statements within type and entity .cc's - the unity T.U.'s include a unity header" << endl;
cmLists << " add_definitions( -DSC_SDAI_UNITY_BUILD)" << endl;
cmLists << " set(" << shortName << "_entity_impls Sdai" << schema_upper << "_unity_entities.cc)" << endl;
cmLists << " set(" << shortName << "_type_impls Sdai" << schema_upper << "_unity_types.cc)" << endl;
cmLists << "else(SC_UNITY_BUILD)" << endl;
cmLists << "if(SC_EXP2CXX_LATE_BOUND)" << endl;
cmLists << " set(" << shortName << "_unity_build FALSE)" << endl;
cmLists << " set(" << shortName << "_entity_impls)" << endl;
cmLists << " set(" << shortName << "_type_impls)" << endl;
cmLists << "elseif(SC_UNITY_BUILD)" << endl;
cmLists << " # bounded compact chunks; definitions are target-local" << endl;
cmLists << " set(" << shortName << "_unity_build TRUE)" << endl;
cmLists << " set(" << shortName << "_entity_impls" << endl;
for( int chunk = 1; chunk <= entityChunks; ++chunk ) {
cmLists << " Sdai" << schema_upper << "_unity_entities_" <<
std::setfill( '0' ) << std::setw( 4 ) << chunk << ".cc" << endl;
}
cmLists << " )" << endl;
cmLists << " set(" << shortName << "_type_impls" << endl;
for( int chunk = 1; chunk <= typeChunks; ++chunk ) {
cmLists << " Sdai" << schema_upper << "_unity_types_" <<
std::setfill( '0' ) << std::setw( 4 ) << chunk << ".cc" << endl;
}
cmLists << " )" << endl;
cmLists << "else()" << endl;
cmLists << " set(" << shortName << "_entity_impls" << endl;
cmLists << ei.str();
cmLists << " )" << endl << endl;
@@ -236,12 +295,13 @@ void writeLists( const char * schemaName, stringstream & eh, stringstream & ei,
cmLists << " set(" << shortName << "_type_impls" << endl;
cmLists << ti.str();
cmLists << " )" << endl;
cmLists << "endif(SC_UNITY_BUILD)" << endl << endl;
cmLists << "endif()" << endl << endl;
cmLists << "set( " << shortName << "_misc_impls" << endl;
cmLists << " SdaiAll.cc compstructs.cc schema.cc" << endl;
cmLists << " Sdai" << schema_upper << ".cc" << endl;
cmLists << " Sdai" << schema_upper << ".init.cc )" << endl << endl;
cmLists << " Sdai" << schema_upper << ".init.cc" << endl;
cmLists << " Sdai" << schema_upper << ".sources.cmake )" << endl << endl;
cmLists << "set(schema_target_files ${" << shortName << "_entity_impls} " << "${" << shortName << "_type_impls} " << "${" << shortName << "_misc_impls})" << endl;
cmLists << "SCHEMA_TARGETS(\"" << input_filename << "\" \"" << schemaName << "\"" << endl;
@@ -330,14 +390,18 @@ int main( int argc, char ** argv ) {
DictionaryEntry de;
/* copied from fedex.c */
Express model;
if( ( argc != 2 ) || ( strlen( argv[1] ) < 1 ) ) {
fprintf( stderr, "\nUsage: %s file.exp\nOutput: a CMakeLists.txt to build the schema,", argv[0] );
if( ( argc != 2 && argc != 3 ) || ( strlen( argv[1] ) < 1 ) ) {
fprintf( stderr, "\nUsage: %s file.exp [entity-chunk-size[:type-chunk-size]]\nOutput: a CMakeLists.txt to build the schema,", argv[0] );
fprintf( stderr, " containing file names for entities, types, etc\n" );
fprintf( stderr, "also prints (to stdout) the absolute path to the directory CMakeLists.txt was created in\n" );
exit( EXIT_FAILURE );
}
EXPRESSprogram_name = argv[0];
input_filename = argv[1];
if( argc == 3 && !parseChunkSizes( argv[2] ) ) {
fprintf( stderr, "chunk sizes must be positive integers in N or N:N form\n" );
exit( EXIT_FAILURE );
}
EXPRESSinitialize();
+49 -2
View File
@@ -18,6 +18,44 @@ else(NOT DEFINED SC_UNITY_BUILD)
message( STATUS "Respecting user-defined SC_UNITY_BUILD value of ${SC_UNITY_BUILD}.")
endif(NOT DEFINED SC_UNITY_BUILD)
if(NOT DEFINED SC_EXP2CXX_CHUNK_SIZE)
set(SC_EXP2CXX_CHUNK_SIZE "64:8" CACHE STRING "Maximum entity:type objects in generated unity translation units")
endif()
if(NOT DEFINED SC_EXP2CXX_API_VERSION)
set(SC_EXP2CXX_API_VERSION "1")
endif()
set(SC_EXP2CXX_API_VERSION "${SC_EXP2CXX_API_VERSION}" CACHE STRING "Generated C++ API version (1 or 2)")
set_property(CACHE SC_EXP2CXX_API_VERSION PROPERTY STRINGS 1 2)
if(NOT SC_EXP2CXX_API_VERSION MATCHES "^[12]$")
message(FATAL_ERROR "SC_EXP2CXX_API_VERSION must be 1 or 2")
endif()
option(SC_EXP2CXX_LATE_BOUND "Generate descriptor-backed entities without early-bound C++ entity classes" OFF)
if(SC_EXP2CXX_LATE_BOUND AND NOT SC_EXP2CXX_API_VERSION STREQUAL "2")
message(FATAL_ERROR "SC_EXP2CXX_LATE_BOUND requires SC_EXP2CXX_API_VERSION=2")
endif()
option(SC_EXP2CXX_COMPAT_NAMES "Retain generated entity aliases in late-bound output" OFF)
if(SC_EXP2CXX_COMPAT_NAMES AND NOT SC_EXP2CXX_LATE_BOUND)
message(FATAL_ERROR "SC_EXP2CXX_COMPAT_NAMES requires SC_EXP2CXX_LATE_BOUND=ON")
endif()
if(NOT DEFINED SC_EXP2CXX_METADATA)
set(SC_EXP2CXX_METADATA "full")
endif()
set(SC_EXP2CXX_METADATA "${SC_EXP2CXX_METADATA}" CACHE STRING
"Generated schema metadata profile (full or structural)")
set_property(CACHE SC_EXP2CXX_METADATA PROPERTY STRINGS full structural)
if(NOT SC_EXP2CXX_METADATA MATCHES "^(full|structural)$")
message(FATAL_ERROR "SC_EXP2CXX_METADATA must be full or structural")
endif()
if(SC_EXP2CXX_METADATA STREQUAL "structural" AND
NOT SC_EXP2CXX_LATE_BOUND)
message(FATAL_ERROR
"SC_EXP2CXX_METADATA=structural requires SC_EXP2CXX_LATE_BOUND=ON")
endif()
# --- variables ---
# SC_ROOT: SC root dir
@@ -82,7 +120,7 @@ endif(WIN32)
# SCHEMA_FILE - path to the schema
# TODO should we have a result variable to return schema name(s) found?
macro(SCHEMA_CMLIST SCHEMA_FILE)
execute_process(COMMAND ${SCANNER_OUT_DIR}/schema_scanner ${SCHEMA_FILE}
execute_process(COMMAND ${SCANNER_OUT_DIR}/schema_scanner ${SCHEMA_FILE} ${SC_EXP2CXX_CHUNK_SIZE}
WORKING_DIRECTORY ${SC_BINARY_DIR}/schemas
RESULT_VARIABLE _ss_stat
OUTPUT_VARIABLE _ss_out
@@ -97,7 +135,16 @@ macro(SCHEMA_CMLIST SCHEMA_FILE)
string(STRIP "${_ss_out}" _ss_stripped)
string(REGEX REPLACE "\\\n" ";" _list ${_ss_stripped})
foreach(_dir ${_list})
add_subdirectory(${_dir} ${_dir}) #specify source and binary dirs as the same
get_property(_already_added GLOBAL PROPERTY _SC_SCHEMA_DIRS)
list(FIND _already_added "${_dir}" _idx)
if(${_idx} EQUAL -1)
set_property(GLOBAL APPEND PROPERTY _SC_SCHEMA_DIRS "${_dir}")
# Keep generated targets under the top-level binary tree. Passing the
# absolute generated-source path as both arguments creates absolute
# make target names with newer CMake.
get_filename_component(_schema_dir_name "${_dir}" NAME)
add_subdirectory("${_dir}" "${SC_BINARY_DIR}/schema-targets/${_schema_dir_name}")
endif()
endforeach(_dir ${_ss_out})
# configure_file forces cmake to run again if the schema has been modified
#if multiple schemas in one file, _schema is the last one printed.
+1 -1
View File
@@ -9,7 +9,7 @@ set(CTEST_INITIAL_CACHE "
SITE:STRING=${CTEST_SITE}
BUILDNAME:STRING=${CTEST_BUILD_NAME}
SC_ENABLE_TESTING:BOOL=ON
SC_BUILD_TYPE:STRING=Debug
CMAKE_BUILD_TYPE:STRING=Debug
")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
# AP242 TREAT Schema File
This directory contains an altered version of the AP242 (ISO 10303-242) schema.
It is corrected to avoid the non-standard parsing requirement of the original
by using explicit TREAT expression for type narrowing
## The Difference
Both files are identical except for a single line at line 34149 in the `styled_item` entity WHERE rule WR3:
**Original (242_mim_lf.exp)**:
```express
QUERY(it <* item | ...)
```
**TREAT version (242_mim_lf_treat.exp)**:
```express
QUERY(it <* TREAT(item AS set_representation_item) | ...)
```
### Historical Context
The original AP242 schema contains a WHERE rule that:
1. Checks if `item` (a SELECT type) is a `SET_REPRESENTATION_ITEM`
2. Then tries to iterate over `item` in a QUERY expression
This pattern requires implicit type narrowing, which is not part of the ISO 10303-11 standard.
### STEPcode's Solution
STEPcode implements **flow-sensitive type narrowing** as an extension:
- Recognizes TYPEOF guard patterns like `'Type' IN TYPEOF(var)` in AND expressions
- Automatically narrows the type for subsequent uses
- Makes the original schema parse successfully
### Standards-Compliant Alternative
The TREAT version uses the ISO 10303-11 TREAT expression:
- `TREAT(item AS set_representation_item)` explicitly narrows the type
- No implicit narrowing needed
- Works in any compliant EXPRESS parser
## Testing
Both versions parse successfully and produce identical output:
```bash
# Run comparison test
./test/compare_ap242_versions.sh
```
Expected output:
```
✓ Both versions parse successfully
✓ Both versions generate the same number of files
```
For detailed comparison, see `doc/ap242-comparison.md`.
## Related Documentation
- `doc/ap242-comparison.md` - Detailed comparison and analysis
- `doc/express-type-unwrapping.md` - TREAT and narrowing documentation
- `src/express/test/README_AP242_BASELINE.md` - Historical PE056 error info
+165
View File
@@ -0,0 +1,165 @@
# AP242 Schema Comparison: Flow-Sensitive Narrowing vs TREAT Expression
## Overview
This document compares two versions of the AP242 schema to demonstrate the difference between STEPcode's non-standard flow-sensitive narrowing extension and the ISO 10303-11 compliant TREAT expression.
## Schema Versions
### 1. Original Version (`242_mim_lf.exp`)
- **Status**: Uses implicit flow-sensitive narrowing (non-standard extension)
- **Location**: `data/ap242/242_mim_lf.exp`
- **Size**: 54,756 lines
- **Problematic code**: Line 34149 in `styled_item` entity, WR3 WHERE rule
### 2. TREAT Version (`242_mim_lf_treat.exp`)
- **Status**: Standards-compliant using TREAT expression
- **Location**: `data/ap242/242_mim_lf_treat.exp`
- **Size**: 54,756 lines
- **Fix applied**: Line 34149 - explicit TREAT expression
## The Problem
In the `styled_item` entity at line ~34148, the WHERE rule WR3 contains:
```express
WR3: ('AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.MAPPED_ITEM' IN TYPEOF(item)) OR
('AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.GEOMETRIC_REPRESENTATION_ITEM' IN TYPEOF(item)) OR
(('AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.SET_REPRESENTATION_ITEM' IN TYPEOF(item)) AND
(SIZEOF(QUERY(it <* item | ...)) = 0));
```
### Issue
- `item` is of type `styled_item_target`, which is a SELECT type
- The SELECT contains `set_representation_item` (which is an aggregate type)
- The code checks `'SET_REPRESENTATION_ITEM' IN TYPEOF(item)` (a TYPEOF guard)
- Then tries to iterate over `item` in a QUERY: `QUERY(it <* item | ...)`
### Why This Was Problematic
According to strict ISO 10303-11 standards, you cannot iterate over a SELECT type directly in a QUERY expression, even if there's a preceding TYPEOF guard. The guard and the QUERY are separate expressions in an AND clause.
## The Solutions
### Solution 1: Flow-Sensitive Narrowing (Non-Standard Extension)
STEPcode implements a **flow-sensitive type narrowing** extension that:
1. Recognizes the pattern `'TypeName' IN TYPEOF(var)` in AND expressions
2. Automatically narrows the type of `var` to `TypeName` for subsequent expressions in the AND
3. Allows `QUERY(it <* item | ...)` to work because `item` is implicitly narrowed to `set_representation_item`
**Code (original, line 34149)**:
```express
QUERY(it <* item | ...)
```
**How it works**:
- The AND expression LHS has the guard: `'SET_REPRESENTATION_ITEM' IN TYPEOF(item)`
- The RHS has the QUERY: `SIZEOF(QUERY(it <* item | ...)) = 0`
- STEPcode's narrowing recognizes the pattern and narrows `item` to `set_representation_item`
- The QUERY can now iterate over the narrowed type
### Solution 2: TREAT Expression (Standards-Compliant)
The ISO 10303-11 standard provides the TREAT expression for explicit type narrowing:
**Code (TREAT version, line 34149)**:
```express
QUERY(it <* TREAT(item AS set_representation_item) | ...)
```
**How it works**:
- `TREAT(item AS set_representation_item)` explicitly narrows the SELECT type to the member type
- The QUERY can iterate over the result of TREAT, which is guaranteed to be `set_representation_item`
- No implicit narrowing needed - the intent is explicit in the code
## The Exact Difference
```diff
--- data/ap242/242_mim_lf.exp
+++ data/ap242/242_mim_lf_treat.exp
@@ -34146,7 +34146,7 @@
('AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.GEOMETRIC_REPRESENTATION_ITEM' IN TYPEOF(item)) OR
(('AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.SET_REPRESENTATION_ITEM' IN TYPEOF(item)) AND
(SIZEOF(QUERY(it
- <* item
+ <* TREAT(item AS set_representation_item)
| NOT (('AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.MAPPED_ITEM' IN TYPEOF(it)) OR
('AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.GEOMETRIC_REPRESENTATION_ITEM' IN
TYPEOF(it))))) =
```
**Change**: Single line change from `<* item` to `<* TREAT(item AS set_representation_item)`
## Testing Results
### Original Version (with Flow-Sensitive Narrowing)
```bash
./build/bin/exp2cxx data/ap242/242_mim_lf.exp /tmp/ap242_original_test
```
**Result**: ✅ Parses successfully, generates 5,626 C++ files
### TREAT Version (Standards-Compliant)
```bash
./build/bin/exp2cxx data/ap242/242_mim_lf_treat.exp /tmp/ap242_treat_test
```
**Result**: ✅ Parses successfully, generates 5,626 C++ files
### Output Comparison
Both versions generate identical output:
- Same number of entities
- Same entity relationships
- Same multiple inheritance warnings
- Identical "Finished writing files" status
## Verification
To verify both versions produce consistent output:
```bash
# Parse original version
mkdir -p /tmp/ap242_original
./build/bin/exp2cxx data/ap242/242_mim_lf.exp /tmp/ap242_original
# Parse TREAT version
mkdir -p /tmp/ap242_treat
./build/bin/exp2cxx data/ap242/242_mim_lf_treat.exp /tmp/ap242_treat
# Compare generated headers (should be identical except for file references)
diff /tmp/ap242_original/SdaiAP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.h \
/tmp/ap242_treat/SdaiAP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF.h
```
## Implications
### For STEPcode Users
- **Current**: Both versions work due to flow-sensitive narrowing extension
- **Future**: TREAT version is more portable if other EXPRESS parsers are used
- **Recommended**: Use TREAT for new schemas to ensure standards compliance
### For STEPcode Development
- Flow-sensitive narrowing is a valuable usability extension
- TREAT expression provides standards-compliant alternative
- Both should continue to be supported for compatibility
### For Standards Compliance
- Original AP242 schema uses pattern that requires extension
- TREAT version is fully ISO 10303-11 compliant
- TREAT makes intent explicit and improves code clarity
## Related Documentation
- `doc/express-type-unwrapping.md` - Full documentation of TREAT and narrowing features
- `src/express/test/README_AP242_BASELINE.md` - Historical PE056 error documentation
- Test schemas:
- `test/unitary_schemas/flow_narrowing_test.exp` - Flow-sensitive narrowing examples
- `test/unitary_schemas/minimal_treat_test.exp` - TREAT expression examples
## Conclusion
This comparison demonstrates:
1. ✅ STEPcode's flow-sensitive narrowing successfully handles the original AP242 schema
2. ✅ TREAT expression provides the standards-compliant alternative
3. ✅ Both approaches produce identical, correct output
4. ✅ The single-line change makes the code more explicit and portable
The availability of both approaches gives users flexibility while maintaining standards compliance when needed.
+251
View File
@@ -0,0 +1,251 @@
# AP242 API v2 Output Benchmarks
This file records comparable measurements for the staged API v2 output
work. It is intended to make changes in generated output cost visible in
the commits which introduce them.
## Method
The measurements were made on 2026-08-02 with GCC 13.3.0 and C++11.
The schema under test was BRL-CAD's `ap242e4.exp` conversion schema.
The real exchange-file workload was the NIST STC 09 ASME AP242 edition
4 sample, `nist_stc_09_asme1_ap242-e4.stp`.
The generator used `--late-bound`, the default 64-entity and 8-type chunk
limits, and a release STEPcode build. Compilation used `-O2 -fPIC` and two
concurrent compiler processes. Generated-output counts include `.cc`, `.h`,
and `.cmake` files through directory depth two. The baseline is commit
`fa2758c5` before recommendations 1--4.
## Results
| Measurement | Baseline | Recommendations 1--4 | Change |
|---|---:|---:|---:|
| Generated files | 5,719 | 905 | -84.2% |
| Generated lines | 254,220 | 123,552 | -51.4% |
| Generated bytes | 13,942,523 | 9,804,680 | -29.7% |
| Entity leaf files | 4,814 | 0 | -100.0% |
| Generation wall time | 2.74 s | 0.56 s | -79.6% |
| Generation peak RSS | 109,024 KiB | 109,280 KiB | +0.2% |
| Compile/link wall time | 157.68 s | 37.02 s | -76.5% |
| Compile/link user time | 277.75 s | 66.32 s | -76.1% |
| Compile/link peak RSS | 593,320 KiB | 199,156 KiB | -66.4% |
| Schema shared library | 9,956,640 B | 7,716,096 B | -22.5% |
| ELF relocations | 28,843 | 39,719 | +37.7% |
| Function symbols | 15,641 | 13,542 | -13.4% |
| Read/write wall time | 0.26 s | 0.25 s | -3.8% |
| Read/write peak RSS | 88,732 KiB | 85,040 KiB | -4.2% |
The relocation increase is caused by the stable module-ID tables referring
to descriptor slots. It is recorded here because the packed schema image
stage is expected to remove most of that cost.
## Recommendations 1--4
This stage adds stable entity, type, and attribute IDs through `SchemaModule`,
makes compatibility entity names opt-in for late-bound output, moves schema
rules and complex-entity support to runtime-consumed tables, and writes late
entity metadata directly to bounded chunks instead of creating 4,814 leaf
files.
The real AP242 exchange-file check created and validated 10,609 instances
with zero errors and zero warnings. Its 949,685-byte rewritten Part 21 file
matched the baseline apart from the expected `FILE_NAME` timestamp.
## Recommendation 5
This stage resolves each late-bound entity's flattened attribute layout once
after schema initialization. Instance construction then follows descriptor
and index records directly instead of recursively walking supertypes and
repeating attribute-name searches. The cache is external to
`EntityDescriptor`, so its object layout and the API v1 ABI are unchanged.
The construction benchmark uses the 1,531 `DIRECTION` instances in the real
exchange file and repeats that population 1,000 times. This isolates the
work changed by this stage while retaining a type and frequency from the
AP242 workload. Results are the median of three runs.
| Measurement | Recommendations 1--4 | Recommendation 5 | Change |
|---|---:|---:|---:|
| 1,531,000 entity constructions | 0.84 s | 0.63 s | -25.0% |
| Construction user time | 0.82 s | 0.61 s | -25.6% |
| Construction peak RSS | 68,956 KiB | 69,600 KiB | +0.9% |
| 20 full read/write runs | 4.90 s | 4.91 s | no measurable change |
| Full read/write peak RSS | 85,164 KiB | 85,732 KiB | +0.7% |
| `libstepcore` size | 640,936 B | 651,312 B | +1.6% |
The complete exchange-file check still created and validated 10,609
instances with zero errors and zero warnings. Its rewritten file remained
949,685 bytes and differed from the previous stage only in the expected
`FILE_NAME` timestamp. Generated schema file counts, source size, and the
schema shared library are unchanged because this is a shared-runtime change.
## Recommendation 6
This stage replaces the generated `SchemaModuleSlot` pointer arrays with a
16-byte, relocation-free `SchemaModuleImage`. Shared initialization helpers
capture descriptor pointers in stable generated-ID order and transfer those
vectors to `SchemaModule` after initialization. The older slot-based entry
point remains available for existing generated API v2 code.
Source and binary measurements compare recommendation 5 with the packed
image. Compile times use matching clean CMake builds with two compiler
processes. Read/write times are medians from paired 20-run batches.
| Measurement | Recommendation 5 | Recommendation 6 | Change |
|---|---:|---:|---:|
| Generated files | 905 | 905 | unchanged |
| Generated lines | 123,552 | 117,046 | -5.3% |
| Generated bytes | 9,804,680 | 9,060,249 | -7.6% |
| Schema module source | 808,907 B | 64,367 B | -92.0% |
| Schema module source lines | 7,280 | 773 | -89.4% |
| Compile/link wall time | 41.38 s | 41.49 s | no measurable change |
| Compile/link peak RSS | 208,312 KiB | 208,188 KiB | -0.1% |
| Schema shared library | 7,728,384 B | 7,470,304 B | -3.3% |
| ELF relocations | 39,719 | 33,221 | -16.4% |
| 20 full read/write runs | 5.10 s | 5.07 s | -0.6% |
| Full read/write peak RSS | 85,736 KiB | 85,572 KiB | -0.2% |
| `libstepcore` size | 651,312 B | 661,088 B | +1.5% |
The image removes 6,498 per-descriptor pointer relocations. The shared
runtime grows by 9,776 bytes once, while each AP242 schema library is 258,080
bytes smaller. Generated API v1 output and the conventional public entity
classes remain unchanged.
The complete exchange-file check again created and validated 10,609
instances with zero errors and zero warnings. Its 949,685-byte rewrite
differed from recommendation 5 only in the expected `FILE_NAME` timestamp.
## Recommendation 7
This stage replaces the remaining generated descriptor procedures with one
bounded, offset-based schema image. The shared loader validates every array,
string, range, descriptor reference, schema binding, and complex-entity tree
before it changes the registry. It then constructs the descriptor graph with
an explicit per-load context; the process-global capture path remains only for
older generated API v2 code. Complex-entity metadata uses the same packed
representation and validation model.
Source and binary measurements compare recommendation 6 with the complete
descriptor image. Compile times use clean CMake builds with two compiler
processes. Read/write times use matching 20-run batches.
| Measurement | Recommendation 6 | Recommendation 7 | Change |
|---|---:|---:|---:|
| Generated files | 905 | 865 | -4.4% |
| Generated lines | 117,046 | 86,400 | -26.2% |
| Generated bytes | 9,060,249 | 4,865,657 | -46.3% |
| Entity initialization chunks | 38 | 0 | -100.0% |
| Compile/link wall time | 41.49 s | 18.71 s | -54.9% |
| Compile/link peak RSS | 208,188 KiB | 208,528 KiB | +0.2% |
| Schema shared library | 7,470,304 B | 3,503,352 B | -53.1% |
| ELF relocations | 33,221 | 9,620 | -71.0% |
| 20 full read/write runs | 5.07 s | 4.97 s | -2.0% |
| Full read/write peak RSS | 85,572 KiB | 82,916 KiB | -3.1% |
The image removes the 2,407 generated entity initialization functions and
23,601 additional relocations. The generated schema library is 3,966,952
bytes smaller, while its existing typed SELECT and enumeration factories
remain available for this transitional stage.
The complete exchange-file check created and validated 10,609 instances with
zero errors and zero warnings. Its 949,685-byte rewrite retained the same
semantic result as recommendation 6.
## Recommendations 8 and 9
This stage makes the compact late-bound output independent of generated type
classes. Shared descriptor-driven implementations now construct SELECT,
enumeration, and aggregate values, including nested values and BOOLEAN or
LOGICAL aliases. Enumeration elements and renamed USE or REFERENCE items are
retained in the packed image. The conventional early-bound API v1 and API v2
paths still emit their schema-specific type classes.
The generated schema header is now a dependency-bounded consumer interface.
It contains stable entity, type, and attribute IDs plus `SchemaModule` lookup
functions, and includes only `schemaModule.h`. Runtime and model-construction
declarations remain in the internal `schema.h` umbrella.
| Measurement | Recommendation 7 | Recommendations 8--9 | Change |
|---|---:|---:|---:|
| Generated files | 865 | 10 | -98.8% |
| Generated lines | 86,400 | 54,839 | -36.5% |
| Generated bytes | 4,865,657 | 3,303,993 | -32.1% |
| Type leaf files | 802 | 0 | -100.0% |
| Type initialization chunks | 51 | 0 | -100.0% |
| Compile/link wall time | 18.71 s | 1.21 s | -93.5% |
| Compile/link peak RSS | 208,528 KiB | 126,580 KiB | -39.3% |
| Schema shared library | 3,503,352 B | 2,178,296 B | -37.8% |
| ELF relocations | 9,620 | 49 | -99.5% |
| Exported function symbols | 4,647 | 8 | -99.8% |
| Public header preprocessed bytes | 2,384,191 | 635,945 | -73.3% |
| 20 full read/write runs | 4.97 s | 4.91 s | -1.2% |
| Full read/write peak RSS | 82,916 KiB | 82,304 KiB | -0.7% |
The complete exchange-file check again created and validated 10,609
instances with zero errors and zero warnings, and wrote the same 949,685-byte
semantic result. Runtime unit coverage separately exercises generic SELECT,
enumeration, aggregate, malformed-image, and packed rename behavior.
## Recommendation 10
This stage adds an opt-in `structural` metadata profile for late-bound API
version 2. The default `full` profile is unchanged. Structural images keep
the descriptor graph needed to construct, read, write, and inspect entity,
attribute, type, aggregate, enumeration, SELECT, inverse, and renamed values.
They omit EXPRESS descriptions, WHERE and UNIQUE rules, schema rule and
algorithm text, supertype statements, derived expressions, and symbolic
aggregate-bound text.
The source and library measurements below compare matching fresh output from
the two profiles. Compile times use clean CMake builds with one compiler
process; read/write times use matching 20-run batches.
| Measurement | Full metadata | Structural metadata | Change |
|---|---:|---:|---:|
| Generated files | 10 | 10 | unchanged |
| Generated lines | 54,839 | 29,993 | -45.3% |
| Generated bytes | 3,304,007 | 1,525,897 | -53.8% |
| Packed schema source | 2,612,413 B | 834,303 B | -68.1% |
| Generation wall time | 0.48 s | 0.41 s | -14.6% |
| Compile/link wall time | 2.08 s | 2.03 s | -2.4% |
| Compile/link peak RSS | 126,784 KiB | 118,220 KiB | -6.8% |
| Schema shared library | 2,178,296 B | 720,120 B | -66.9% |
| ELF relocations | 49 | 49 | unchanged |
| 20 full read/write runs | 4.76 s | 4.71 s | -1.1% |
| Full read/write peak RSS | 82,276 KiB | 78,900 KiB | -4.1% |
The structural profile created and validated all 10,609 instances in the
real exchange file with zero errors and zero warnings. It wrote the same
949,685-byte semantic result as the full profile.
## Recommendation 11
This stage writes each generated file to a sibling temporary file and replaces
the destination only when its content changes. A compact generated-file
manifest supports safe cleanup when an option change makes old entity, type,
or unity files obsolete; files not named by the previous manifest are left
alone. The manifest itself is also content-stable.
The deterministic-output audit also found that nonliteral aggregate bounds
were being read through the integer member of the expression union. Packed
images now emit those bounds as expression records, eliminating process-address
values from generated AP242 source as well as correcting the metadata kind.
| Measurement after identical AP242 generation | Rewrite all | Stable output | Change |
|---|---:|---:|---:|
| Changed schema source/header timestamps | 10 of 10 | 0 of 10 | -100.0% |
| Changed manifest timestamps | n/a | 0 of 1 | stable |
| Generation wall time | 0.41 s | 0.40 s | no overhead |
| Following CMake build wall time | 2.01 s | 0.06 s | -97.0% |
| Following build peak RSS | 118,108 KiB | 18,432 KiB | -84.4% |
The AP242 manifest is 390 bytes. Switching the focused regression schema
from early-bound API version 2 to compact late-bound output removes all stale
entity, type, and unity files and their empty directories while preserving an
unrelated file in the output directory.
The real exchange-file check still created and validated all 10,609 instances
with zero errors and zero warnings. Its rewritten result remained 949,685
bytes.
+367
View File
@@ -0,0 +1,367 @@
# EXPRESS Type Unwrapping and Narrowing
## Overview
This document describes the type unwrapping and narrowing features implemented in STEPcode's EXPRESS compiler. These features enable proper type checking and semantic analysis for aggregate types and SELECT type narrowing.
**Note:** Some features described here are extensions beyond the ISO 10303-11 standard and are explicitly marked as such.
## Deep Aggregate Unwrapping
### Background
In EXPRESS, aggregate types (ARRAY, LIST, SET, BAG) can be nested and wrapped in type definitions. For semantic analysis and type checking, the compiler often needs to "unwrap" these layers to access the underlying element type.
### Shallow vs Deep Type Checking
#### Shallow Aggregate Check: `TYPEis_aggregate`
The macro `TYPEis_aggregate(t)` performs a **shallow** check by testing if `t->u.type->body->base` is non-NULL. This indicates the type has a base type, which is characteristic of aggregate types.
```c
#define TYPEis_aggregate(t) ((t)->u.type->body->base)
```
**Important:** This is a shallow check and does not follow type definition chains. It returns true if the immediate type structure has a base type.
**Use when:**
- You need a quick check if a type is directly an aggregate
- You're working with the immediate type structure
- Performance is critical and deep unwrapping isn't needed
#### Deep Aggregate Unwrapping: `TYPEget_nonaggregate_base_type`
The function `TYPEget_nonaggregate_base_type(Type t)` performs **deep unwrapping** by iteratively following the chain of aggregate base types until reaching a non-aggregate type.
```express
TYPE int_list = LIST OF INTEGER;
TYPE named_int_list = int_list; -- typedef chain
TYPE set_of_lists = SET OF named_int_list;
```
For `set_of_lists`:
- `TYPEis_aggregate()` → true (immediate check)
- `TYPEget_nonaggregate_base_type()` → INTEGER (unwraps through SET → LIST → INTEGER)
**Use when:**
- Comparing element types across different aggregate wrappers
- Type resolution requires the ultimate element type
- Semantic checking needs to validate deep type compatibility
### Deep Unwrapping Implementation
```c
Type TYPEget_nonaggregate_base_type( Type t ) {
while( TYPEis_aggregate( t ) ) {
t = t->u.type->body->base;
}
return t;
}
```
This function safely handles:
- Nested aggregates (SET OF LIST OF ...)
- Type definition chains
- Mixed aggregate types
### Aggregate Type Comparison: `TYPE_retrieve_aggregate`
For SELECT types containing multiple aggregate members, `TYPE_retrieve_aggregate()` validates that all aggregate members share the same element type.
```express
TYPE item_a = SET OF INTEGER;
TYPE item_b = LIST OF INTEGER;
TYPE my_select = SELECT(item_a, item_b); -- Valid: both have INTEGER elements
```
The function:
1. Iterates through SELECT members
2. Identifies which are aggregates
3. Unwraps typedef chains to find element types
4. Returns common element type or NULL if incompatible
## TREAT Expression Semantics
### ISO 10303-11 Standard Syntax
The TREAT expression is defined in ISO 10303-11 as:
```express
TREAT(expression AS type_identifier)
```
**Purpose:** Explicitly narrow a SELECT type to one of its member types.
### STEPcode Implementation
STEPcode implements TREAT as specified in ISO 10303-11 with the following semantics:
#### Type Resolution
1. **Operand Resolution:** The expression being narrowed is resolved first
```c
EXPresolve( e->e.op1, s, Type_Dont_Care );
operand_type = e->e.op1->return_type;
```
2. **Target Type Lookup:** The target type identifier is resolved via scope lookup
```c
target_type = (Type)SCOPEfind( s, e->symbol.name, SCOPE_FIND_TYPE );
```
3. **Type Compatibility:** The implementation validates that narrowing is semantically valid
- For SELECT operands: Verifies target is a member type
- For other types: Allows permissively (may emit warnings)
4. **Return Type:** The expression's return type becomes the target type
```c
return target_type; /* Not Type_Generic */
```
#### Error Handling
**Clear errors with location information:**
- **Missing operand:** `SYNTAX` error - "TREAT expression missing operand"
- **Missing target type:** `SYNTAX` error - "TREAT expression missing target type"
- **Undefined type:** `UNDEFINED_TYPE` error with the type name
- **Type mismatch:** Currently permissive (no error, future: may warn)
All errors use `ERRORreport_with_symbol()` which provides:
- File name and line number
- Symbol context
- Descriptive error message
#### Example Usage
```express
TYPE attachment = SELECT(nail, screw);
END_TYPE;
FUNCTION get_length(item : attachment) : REAL;
LOCAL
n : nail;
END_LOCAL;
IF ('EXAMPLE.NAIL' IN TYPEOF(item)) THEN
n := TREAT(item AS nail); -- Narrows SELECT to nail
RETURN n.length;
END_IF;
RETURN 0.0;
END_FUNCTION;
```
### Grammar Integration
The TREAT syntax is integrated into the EXPRESS parser grammar as a unary expression:
```yacc
unary_expression ::= TOK_TREAT TOK_LEFT_PAREN expression TOK_AS
TOK_IDENTIFIER TOK_RIGHT_PAREN
```
Key points:
- `TOK_TREAT`: Recognized as a keyword
- Creates unary expression with `OP_TREAT` operator
- Operand stored in `op1`
- Target type name stored in `symbol` field
## Flow-Sensitive SELECT Narrowing
**Extension Status:** This is a **non-standard extension** that enhances type safety for SELECT types.
### Background
EXPRESS WHERE clauses often use TYPEOF guards to check SELECT types before accessing member-specific attributes. Flow-sensitive narrowing automatically narrows SELECT types within the scope of such guards.
### Pattern Recognition
The narrowing system recognizes this pattern:
```express
'TypeName' IN TYPEOF(variable)
```
When this pattern appears in an AND conjunction, the variable is narrowed to `TypeName` for subsequent expressions in the conjunction.
### Implementation Architecture
#### Refinement Context
A **refinement context** tracks active type narrowings during expression resolution:
```c
typedef struct RefinementContext_ {
Refinement refinements; /* Linked list of variable → type mappings */
struct RefinementContext_ *previous; /* Stack of contexts */
} *RefinementContext;
RefinementContext active_refinements = NULL; /* Thread-local global */
```
#### Pattern Matching
The function `match_typeof_guard()` identifies guard patterns:
1. Expression is `IN` operator
2. Left operand is string literal (type name)
3. Right operand is `TYPEOF(identifier)`
4. Identifier resolves to a variable
#### Refinement Collection
The function `collect_refinements_from_conjunction()` recursively walks AND expressions:
1. Recursively descends into AND nodes
2. Matches TYPEOF guard patterns at leaves
3. Validates narrowing is semantically correct:
- Variable must have SELECT type
- Target type must be SELECT member (validated via `is_select_member()`)
4. Returns linked list of valid refinements
#### Application
In `EXPresolve_op_and()`:
```c
Type EXPresolve_op_and(Expression e, Scope s) {
EXPresolve(e->e.op1, s, Type_Dont_Care); /* Resolve LHS normally */
Refinement refs = collect_refinements_from_conjunction(e->e.op1, s);
if (refs) {
/* Create temporary context */
struct RefinementContext_ ctx;
ctx.refinements = refs;
ctx.previous = active_refinements;
active_refinements = &ctx;
EXPresolve(e->e.op2, s, Type_Dont_Care); /* Resolve RHS with narrowing */
/* Restore previous context */
active_refinements = ctx.previous;
free_refinements(refs);
} else {
EXPresolve(e->e.op2, s, Type_Dont_Care);
}
return Type_Logical;
}
```
### Behavior Scope
**What is narrowed:**
- Variables with SELECT type
- Within RHS of AND expressions
- Where LHS contains TYPEOF guard
**What is NOT narrowed:**
- OR expressions (conservative - doesn't extract from disjunction)
- NOT expressions (conservative - doesn't extract from negation)
- Other boolean operators
- Variables not guarded by TYPEOF
**Rationale:** Conservative approach prevents false narrowing in cases where the guard may not hold.
### Limitations
1. **Single-level context:** Only immediate AND RHS is narrowed, not deeply nested scopes
2. **No cross-statement narrowing:** Limited to expression-level scope
3. **AND-only:** Does not propagate through OR/NOT (conservative)
4. **Pattern-based:** Only recognizes exact `'Type' IN TYPEOF(var)` pattern
### Example
```express
ENTITY test_entity;
item : target; -- target is SELECT(set_representation_item, simple_int)
WHERE
wr1: ('SCHEMA.SET_REPRESENTATION_ITEM' IN TYPEOF(item)) AND
(SIZEOF(item) > 0); -- item narrowed to set_representation_item
END_ENTITY;
```
Without narrowing, `SIZEOF(item)` would fail (can't take SIZEOF of SELECT). With narrowing, `item` is known to be an aggregate, so `SIZEOF` is valid.
## Memory Management
### Refinement Lifecycle
1. **Collection:** `collect_refinements_from_conjunction()` allocates refinement nodes
2. **Usage:** Active during RHS expression resolution only
3. **Cleanup:** `free_refinements()` called after RHS resolution completes
### Context Stack Model
The implementation uses a **stack-based context model** rather than deep copying:
- `RefinementContext` has `previous` pointer forming a stack
- New contexts pushed onto stack with `active_refinements = &ctx`
- Restored with `active_refinements = ctx.previous`
- No deep copying of refinement data
**Memory safety:**
- Refinement nodes allocated individually
- Freed immediately after use
- No dangling pointers (stack-local contexts)
- No memory leaks (verified by code inspection)
## Testing
### Test Coverage
**TREAT expression tests:**
- `test/unitary_schemas/minimal_treat_test.exp` - Basic TREAT usage
- `test/unitary_schemas/test_treat_real.exp` - Nested SELECT types
- `test/unitary_schemas/treat_expression.exp` - TREAT in WHERE clauses
**Flow-sensitive narrowing tests:**
- `test/unitary_schemas/flow_narrowing_test.exp` - AND-based narrowing
- `test/unitary_schemas/select_aggregate_test.exp` - SELECT of aggregates
**Production schema comparison:**
- `data/ap242/242_mim_lf.exp` - Original AP242 with flow-sensitive narrowing
- `data/ap242/242_mim_lf_treat.exp` - TREAT-based standards-compliant version
- `test/compare_ap242_versions.sh` - Automated comparison script
See `doc/ap242-comparison.md` for detailed analysis of the AP242 schema comparison.
### AP242 Compatibility
The implementation is validated against the AP242 schema, ensuring:
- Large-scale schema parsing succeeds
- Complex type hierarchies handled correctly
- Production schema compatibility
**AP242 Schema Versions:**
- `data/ap242/242_mim_lf.exp` - Original schema using flow-sensitive narrowing extension
- `data/ap242/242_mim_lf_treat.exp` - Standards-compliant version using TREAT expression
Both versions parse successfully and produce identical output. See `doc/ap242-comparison.md` for detailed analysis.
## Standards Compliance Notes
### ISO 10303-11 Compliance
**Fully compliant:**
- TREAT expression syntax and semantics
- Aggregate type definitions
- SELECT type definitions
- Standard type resolution rules
**Extensions (non-standard):**
- Flow-sensitive type narrowing in AND expressions
- Permissive TREAT type checking (warns vs errors)
### Extension Philosophy
STEPcode's extensions follow these principles:
1. **Conservative:** Only apply when semantically safe
2. **Permissive:** Prefer warnings over errors where reasonable
3. **Explicit:** Document extensions clearly
4. **Compatible:** Don't break standard-compliant schemas
Extensions are designed to enhance usability while maintaining backward compatibility with ISO 10303-11 compliant EXPRESS schemas.
+33 -1
View File
@@ -45,6 +45,11 @@ EXPRESS code into C++ source code. The C++ source code is generated
in files in the directory in which exp2cxx was run. The files are
named according to the schema names in the input EXPRESS file.
Generated files are replaced only when their content changes. exp2cxx records
the files it owns in `.exp2cxx-manifest`; a later run removes obsolete files
from that manifest while leaving unrelated files in the output directory
untouched.
.SH OPTIONS
.TP
.B -s|S
@@ -57,6 +62,34 @@ Generates C++ classes for use with CORBA (Orbix).
.B -l|L
Prints logging code in the generated C++ classes.
.TP
.B -k entity-chunk-size[:type-chunk-size]
Limits the number of generated schema objects in each deterministic unity
translation unit. A single value applies to both entity and type chunks.
.TP
.B -V 1|2, --api-version 1|2
Selects the generated C++ API. Version 1 is the source-compatible default.
Version 2 emits compact SELECT classes, table-driven schema initialization, and
dependency-bounded entity headers backed by shared runtime code. Version 2
clients include the individual entity headers they use; schema.h does not
include every entity class definition.
.TP
.B -T, --late-bound
Selects API version 2 and omits generated early-bound C++ entity classes.
Entity instances are constructed at runtime from schema descriptors. This mode
is intended for conversion and inspection applications which do not require the
conventional generated entity API.
.TP
.B --compat-names
Retains the generated entity-name aliases in late-bound output. Normal
late-bound API v2 output uses schema module IDs instead and omits these aliases.
.TP
.B --metadata full|structural
Selects the generated schema metadata profile. Full is the default and
retains EXPRESS descriptions, constraints, and executable schema text.
Structural is available with late-bound API version 2; it retains the entity,
attribute, type, aggregate, enumeration, SELECT, inverse, and rename graph
needed by conversion tools while omitting descriptive and constraint text.
.TP
.B -v
Produces a version description.
.TP
@@ -161,4 +194,3 @@ appropriate file to download).
.SH BUGS
See fedex(1).
-1
View File
@@ -31,7 +31,6 @@ set(SC_IS_SUBBUILD TRUE)
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif(NOT DEFINED CMAKE_BUILD_TYPE)
set(SC_BUILD_TYPE ${CMAKE_BUILD_TYPE})
# Path to STEPcode
set(STEPCODE_BUILD_DIR ${CMAKE_BINARY_DIR}/sc CACHE PATH "STEPcode build subdir")
@@ -4,9 +4,9 @@ ExternalProject_Add( STEPCODE
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
-DSC_BUILD_TYPE=Debug
-DCMAKE_BUILD_TYPE=Debug
-DSC_BUILD_SCHEMAS=ap203/ap203.exp
-DSC_BUILD_STATIC_LIBS=ON
-DBUILD_STATIC_LIBS=ON
-DSC_PYTHON_GENERATOR=OFF
-DSC_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)
+1 -1
View File
@@ -5,6 +5,7 @@ set(LAZY_HDRS
p21HeaderSectionReader.h
lazyDataSectionReader.h
lazyInstMgr.h
lazySupport.h
lazyTypes.h
sectionReader.h
instMgrHelper.h
@@ -24,4 +25,3 @@ install(FILES ${LAZY_HDRS}
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+1 -4
View File
@@ -27,12 +27,9 @@ class SC_LAZYFILE_EXPORT headerSectionReader: public sectionReader {
}
virtual ~headerSectionReader() {
//FIXME delete each instance?! maybe add to clear, since it iterates over everything already
//enum clearHow { rawData, deletePointers }
_headerInstances->clear();
_headerInstances->clear( true );
delete _headerInstances;
}
};
#endif //HEADERSECTIONREADER_H
+3 -2
View File
@@ -31,7 +31,9 @@ class SC_LAZYFILE_EXPORT mgrNodeHelper: public MgrNodeBase {
_id = id;
}
inline SDAI_Application_instance * GetSTEPentity() {
return _lim->loadInstance( _id, true );
/* Attribute resolution must not convert a batch-owned cache hit
* into a process-lifetime retained instance. */
return _lim->loadInstance( _id, true, false );
}
};
@@ -58,4 +60,3 @@ class SC_LAZYFILE_EXPORT instMgrAdapter: public InstMgrBase {
#endif //INSTMGRHELPER_H
+2 -2
View File
@@ -25,8 +25,8 @@ struct judyl2KVpair {
/** A judyL2 array maps JudyKey's to multiple JudyValue's, similar to std::multimap.
* Internally, this is a judyL array of std::vector< JudyValue >.
* The first template parameter must be the same size as a void*
* \param JudyKey the type of the key, i.e. uint64_t, etc
* \param JudyValue the type of the value, i.e. int, pointer-to-object, etc. With judyL2Array, the size of this value can vary.
* \tparam JudyKey the type of the key, i.e. uint64_t, etc
* \tparam JudyValue the type of the value, i.e. int, pointer-to-object, etc. With judyL2Array, the size of this value can vary.
*/
template< typename JudyKey, typename JudyValue >
class judyL2Array {
+4 -4
View File
@@ -24,8 +24,8 @@ struct judylKVpair {
* a JudyValue. Each cell must be set to a non-zero value by the caller.
*
* Both template parameters must be the same size as a void*
* \param JudyKey the type of the key, i.e. uint64_t, pointer-to-object, etc
* \param JudyValue the type of the value
* \tparam JudyKey the type of the key, i.e. uint64_t, pointer-to-object, etc
* \tparam JudyValue the type of the value
*/
template< typename JudyKey, typename JudyValue >
class judyLArray {
@@ -159,8 +159,8 @@ class judyLArray {
* getLastValue() will return the entry before the one that was deleted
* \sa isEmpty()
*/
bool removeEntry( JudyKey * key ) {
if( judy_slot( _judyarray, key, _depth * JUDY_key_size ) ) {
bool removeEntry( JudyKey key ) {
if( judy_slot( _judyarray, reinterpret_cast<const unsigned char *>( &key ), _depth * JUDY_key_size ) ) {
_lastSlot = ( JudyValue * ) judy_del( _judyarray );
return true;
} else {
+3 -3
View File
@@ -25,7 +25,7 @@ struct judys2KVpair {
/** A judyS2 array maps a set of strings to multiple JudyValue's, similar to std::multimap.
* Internally, this is a judyS array of std::vector< JudyValue >.
* \param JudyValue the type of the value, i.e. int, pointer-to-object, etc.
* \tparam JudyValue the type of the value, i.e. int, pointer-to-object, etc.
*/
template< typename JudyValue >
class judyS2Array {
@@ -51,8 +51,8 @@ class judyS2Array {
explicit judyS2Array( const judyS2Array< JudyValue > & other ): _maxKeyLen( other._maxKeyLen ), _success( other._success ) {
_judyarray = judy_clone( other._judyarray );
_buff = new unsigned char[_maxKeyLen];
strncpy( _buff, other._buff, _maxKeyLen );
_buff[ _maxKeyLen ] = '\0'; //ensure that _buff is null-terminated, since strncpy won't necessarily do so
strncpy( reinterpret_cast< char * >( _buff ), reinterpret_cast< const char * >( other._buff ), _maxKeyLen - 1 );
_buff[ _maxKeyLen - 1 ] = '\0'; //ensure that _buff is null-terminated
find( _buff ); //set _lastSlot
}
+2 -2
View File
@@ -39,8 +39,8 @@ class judySArray {
explicit judySArray( const judySArray< JudyValue > & other ): _maxKeyLen( other._maxKeyLen ), _success( other._success ) {
_judyarray = judy_clone( other._judyarray );
_buff = new unsigned char[_maxKeyLen];
strncpy( _buff, other._buff, _maxKeyLen );
_buff[ _maxKeyLen ] = '\0'; //ensure that _buff is null-terminated, since strncpy won't necessarily do so
strncpy( reinterpret_cast< char * >( _buff ), reinterpret_cast< const char * >( other._buff ), _maxKeyLen - 1 );
_buff[ _maxKeyLen - 1 ] = '\0'; //ensure that _buff is null-terminated
find( _buff ); //set _lastSlot
}
+18 -1
View File
@@ -1,6 +1,7 @@
#ifndef LAZYFILEREADER_H
#define LAZYFILEREADER_H
#include <atomic>
#include <vector>
#include <string>
#include <cstdlib>
@@ -32,11 +33,14 @@ class SC_LAZYFILE_EXPORT lazyFileReader {
lazyInstMgr * _parent;
headerSectionReader * _header;
std::ifstream _file;
std::atomic<bool> _schemaWarningClaimed;
#ifdef _MSC_VER
#pragma warning( pop )
#endif
fileTypeEnum _fileType;
fileID _fileID;
lazyFileOffset _fileSize;
bool _valid;
void initP21();
@@ -48,6 +52,12 @@ class SC_LAZYFILE_EXPORT lazyFileReader {
fileID ID() const {
return _fileID;
}
lazyFileOffset fileSize() const {
return _fileSize;
}
bool valid() const {
return _valid;
}
instancesLoaded_t * getHeaderInstances();
lazyFileReader( std::string fname, lazyInstMgr * i, fileID fid );
@@ -60,8 +70,15 @@ class SC_LAZYFILE_EXPORT lazyFileReader {
return _parent;
}
/** Return true only to the first caller reporting a FILE_SCHEMA
* selection warning for this file. Instance materialization may
* occur concurrently and span multiple DATA sections. */
bool claimSchemaWarning() {
return !_schemaWarningClaimed.exchange( true,
std::memory_order_relaxed );
}
bool needKW( const char * kw );
};
#endif //LAZYFILEREADER_H
+121 -12
View File
@@ -1,13 +1,17 @@
#ifndef LAZYINSTMGR_H
#define LAZYINSTMGR_H
#include <cctype>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <assert.h>
#include "cllazyfile/lazyDataSectionReader.h"
#include "cllazyfile/lazyFileReader.h"
#include "cllazyfile/lazyTypes.h"
#include "cllazyfile/lazySupport.h"
#include "clstepcore/Registry.h"
#include "sc_export.h"
@@ -19,6 +23,7 @@
class Registry;
class instMgrAdapter;
class lazyRefs;
class SC_LAZYFILE_EXPORT lazyInstMgr {
protected:
@@ -61,15 +66,51 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
lazyFileReaderVec_t _files;
/** All indexed DATA instance IDs in deterministic file order. */
instanceRefs _allInstances;
Registry * _headerRegistry, * _mainRegistry;
bool _ownsMainRegistry;
ErrorDescriptor * _errors;
unsigned long _lazyInstanceCount, _loadedInstanceCount;
uint64_t _cacheHighWater, _cacheHits, _cacheMisses, _materializations, _evictions;
uint64_t _activeBatches;
uint64_t _residentSourceBytes, _sourceBytesHighWater;
int _longestTypeNameLen;
std::string _longestTypeName;
std::map<instanceID, size_t> _pinCounts;
std::map<instanceID, uint64_t> _instanceSourceBytes;
std::map<instanceID, std::vector<std::string> > _instanceComponentTypes;
std::map<std::string, std::string> _materializationTypeAliases;
std::set<instanceID> _batchOwnedInstances;
std::set<instanceID> _permanentlyLoadedInstances;
std::set<instanceID> _instancesLoading;
std::set<instanceID> _deferredInverseInstances;
size_t _batchLoadDepth;
LazyProgressCallback _progressCallback;
LazyCancellationCallback _cancellationCallback;
LazyDiagnosticCallback _diagnosticCallback;
std::map<std::string, uint64_t> _diagnosticCounts;
bool _cancelled;
instMgrAdapter * _ima;
friend class LazyInstanceBatch;
friend class lazyRefs;
void releaseBatch( const std::vector<instanceID> & instances );
void resolveDeferredInverses();
bool isMaterializing( instanceID id ) const {
return _instancesLoading.count( id ) != 0;
}
void deferInverseResolution( instanceID id ) {
_deferredInverseInstances.insert( id );
}
SDAI_Application_instance * cachedInstance( instanceID id );
std::vector<instanceID> dependencyClosure( const std::vector<instanceID> & roots );
#ifdef _MSC_VER
#pragma warning( pop )
#endif
@@ -77,13 +118,34 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
public:
lazyInstMgr();
~lazyInstMgr();
void openFile( std::string fname );
bool openFile( std::string fname );
void addLazyInstance( namedLazyInstance inst );
InstMgrBase * getAdapter() {
return ( InstMgrBase * ) _ima;
}
/** Register an explicit source-keyword substitution used only when
* materializing an SDAI object. The lazy type index retains the
* original Part 21 keyword. This is intended for known,
* attribute-compatible exporter aliases, not arbitrary recovery. */
void setMaterializationTypeAlias( std::string source, const std::string & target ) {
for( std::string::iterator c = source.begin(); c != source.end(); ++c ) {
*c = static_cast<char>( toupper( static_cast<unsigned char>( *c ) ) );
}
_materializationTypeAliases[source] = target;
}
std::string materializationType( std::string source ) const {
std::string key = source;
for( std::string::iterator c = key.begin(); c != key.end(); ++c ) {
*c = static_cast<char>( toupper( static_cast<unsigned char>( *c ) ) );
}
std::map<std::string, std::string>::const_iterator alias =
_materializationTypeAliases.find( key );
return alias == _materializationTypeAliases.end() ? source : alias->second;
}
instanceRefs_t * getFwdRefs() {
return & _fwdInstanceRefs;
}
@@ -91,19 +153,33 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
instanceRefs_t * getRevRefs() {
return & _revInstanceRefs;
}
LazyInstanceIdView instancesByType( std::string type, bool caseSensitive = false );
LazyInstanceIdView allInstances() const {
return LazyInstanceIdView( &_allInstances );
}
LazyInstanceIdView forwardReferences( instanceID id );
LazyInstanceIdView reverseReferences( instanceID id );
/** Copy the exact indexed source record for an instance. Returns an
* empty string when the ID is missing or ambiguous. */
std::string sourceRecord( instanceID id );
/** Return the Part 21 component keywords of a complex instance in
* source order. Ordinary and unknown instances return an empty
* vector. */
const std::vector<std::string> & componentTypes( instanceID id ) const;
/// returns a vector containing the instances that match `type`
instanceTypes_t::cvector * getInstances( std::string type, bool caseSensitive = false ) { /*const*/
if( !caseSensitive ) {
std::string::iterator it = type.begin();
for( ; it != type.end(); ++it ) {
*it = toupper( *it );
*it = static_cast<char>( toupper( static_cast<unsigned char>( *it ) ) );
}
}
return _instanceTypes->find( type.c_str() );
}
/// get the number of instances of a certain type
unsigned int countInstances( std::string type ) {
instanceTypes_t::cvector * v = _instanceTypes->find( type.c_str() );
unsigned int countInstances( std::string type, bool caseSensitive = false ) {
instanceTypes_t::cvector * v = getInstances( type, caseSensitive );
if( !v ) {
return 0;
}
@@ -123,6 +199,28 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
return _loadedInstanceCount;
}
LazyCacheStatistics cacheStatistics() const;
void setProgressCallback( const LazyProgressCallback & callback ) {
_progressCallback = callback;
}
void setCancellationCallback( const LazyCancellationCallback & callback ) {
_cancellationCallback = callback;
}
void setDiagnosticCallback( const LazyDiagnosticCallback & callback ) {
_diagnosticCallback = callback;
}
bool cancelled() const {
return _cancelled;
}
void observeScan( fileID file, lazyFileOffset offset, lazyFileOffset fileSize );
void emitDiagnostic( LazyDiagnostic diagnostic );
uint64_t diagnosticCount( const std::string & key ) const;
const std::map<std::string, uint64_t> & diagnosticCounts() const {
return _diagnosticCounts;
}
void validateReferences();
/// get the number of data sections that have been identified
unsigned int countDataSections() {
return _dataSections.size();
@@ -130,7 +228,9 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
///builds the registry using the given initFunct
const Registry * initRegistry( CF_init initFunct ) {
setRegistry( new Registry( initFunct ) );
assert( _mainRegistry == 0 );
_mainRegistry = new Registry( initFunct );
_ownsMainRegistry = true;
return _mainRegistry;
}
@@ -138,6 +238,7 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
void setRegistry( Registry * reg ) {
assert( _mainRegistry == 0 );
_mainRegistry = reg;
_ownsMainRegistry = false;
}
const Registry * getHeaderRegistry() const {
@@ -165,8 +266,12 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
/** returns a pointer to an instance, loading it if necessary.
* \param id the instance number to look for
* \param reSeek if true, reset file position to current position when done. only necessary when loading an instance with dependencies; excessive use will cause a performance hit
* \param promoteCached if true, a cached instance requested outside a batch is retained for legacy callers
*/
SDAI_Application_instance * loadInstance( instanceID id, bool reSeek = false );
SDAI_Application_instance * loadInstance( instanceID id, bool reSeek = false, bool promoteCached = true );
LazyInstanceBatch loadBatch( instanceID root );
LazyInstanceBatch loadBatch( const std::vector<instanceID> & roots );
//list all instances that one instance depends on (recursive)
instanceSet * instanceDependencies( instanceID id );
@@ -183,11 +288,16 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
std::cerr << "Error at " << __FILE__ << ":" << __LINE__ << " - multiple instances (" << cv->size() << ") with one instanceID (" << id << ") not supported yet." << std::endl;
return 0;
}
positionAndSection ps = cv->at( 0 );
//extract p, s, call
long int off = ps & 0xFFFFFFFFFFFFULL;
sectionID sid = ps >> 48;
return _dataSections[sid]->getType( off );
instancePosition pos = cv->at( 0 );
//a data section indexes its instances from its own constructor and is
//registered only afterwards, and only if it succeeded - so an instance
//can name a section that was never registered. Same test sourceRecord()
//already makes.
if( pos.section >= _dataSections.size() || !_dataSections[pos.section] ) {
std::cerr << "Error at " << __FILE__ << ":" << __LINE__ << " - instanceID " << id << " names data section " << pos.section << ", which failed to index." << std::endl;
return 0;
}
return _dataSections[pos.section]->getType( pos.begin );
}
std::cerr << "Error at " << __FILE__ << ":" << __LINE__ << " - instanceID " << id << " not found." << std::endl;
return 0;
@@ -218,4 +328,3 @@ class SC_LAZYFILE_EXPORT lazyInstMgr {
};
#endif //LAZYINSTMGR_H
@@ -7,12 +7,13 @@
class SC_LAZYFILE_EXPORT lazyP21DataSectionReader: public lazyDataSectionReader {
protected:
/** Index the instances in an Edition 1 SCOPE and leave the stream at
* the owning entity's record. */
bool indexScope();
public:
lazyP21DataSectionReader( lazyFileReader * parent, std::ifstream & file, std::streampos start, sectionID sid );
void findSectionStart() {
_sectionStart = findNormalString( "DATA", true );
}
void findSectionStart();
/** gets information (start, end, name, etc) about the next
* instance in the file and returns it in a namedLazyInstance
* \sa p21HeaderSectionReader::nextInstance()
@@ -22,4 +23,3 @@ class SC_LAZYFILE_EXPORT lazyP21DataSectionReader: public lazyDataSectionReader
};
#endif //LAZYP21DATASECTIONREADER_H
+131
View File
@@ -0,0 +1,131 @@
#ifndef LAZYSUPPORT_H
#define LAZYSUPPORT_H
#include <cstddef>
#include <functional>
#include <iosfwd>
#include <string>
#include <vector>
#include "cllazyfile/lazyTypes.h"
#include "sc_export.h"
class lazyInstMgr;
class SDAI_Application_instance;
enum LazyDiagnosticSeverity {
LAZY_DIAGNOSTIC_INFO,
LAZY_DIAGNOSTIC_WARNING,
LAZY_DIAGNOSTIC_ERROR,
LAZY_DIAGNOSTIC_FATAL
};
struct SC_LAZYFILE_EXPORT LazyDiagnostic {
LazyDiagnosticSeverity severity;
instanceID entity;
std::string type;
lazyFileOffset offset;
uint64_t line;
std::string attribute;
std::string message;
uint64_t occurrences;
LazyDiagnostic(): severity( LAZY_DIAGNOSTIC_INFO ), entity( 0 ), offset( 0 ),
line( 0 ), occurrences( 1 ) {}
};
struct SC_LAZYFILE_EXPORT LazyScanProgress {
fileID file;
lazyFileOffset offset;
lazyFileOffset fileSize;
uint64_t instancesScanned;
LazyScanProgress(): file( 0 ), offset( 0 ), fileSize( 0 ), instancesScanned( 0 ) {}
};
struct SC_LAZYFILE_EXPORT LazyCacheStatistics {
uint64_t instancesScanned;
uint64_t instancesLoaded;
uint64_t instancesPinned;
uint64_t cacheHighWater;
uint64_t cacheHits;
uint64_t cacheMisses;
uint64_t materializations;
uint64_t evictions;
uint64_t activeBatches;
uint64_t dataSections;
uint64_t residentSourceBytes;
uint64_t sourceBytesHighWater;
bool cancelled;
LazyCacheStatistics(): instancesScanned( 0 ), instancesLoaded( 0 ), instancesPinned( 0 ),
cacheHighWater( 0 ), cacheHits( 0 ), cacheMisses( 0 ), materializations( 0 ),
evictions( 0 ), activeBatches( 0 ), dataSections( 0 ), residentSourceBytes( 0 ),
sourceBytesHighWater( 0 ), cancelled( false ) {}
};
typedef std::function<void ( const LazyScanProgress & )> LazyProgressCallback;
typedef std::function<bool ()> LazyCancellationCallback;
typedef std::function<void ( const LazyDiagnostic & )> LazyDiagnosticCallback;
/** A non-owning, zero-copy view of an indexed instance-ID vector.
* The view remains valid until another file is scanned by the manager.
*/
class SC_LAZYFILE_EXPORT LazyInstanceIdView {
public:
typedef instanceRefs::const_iterator const_iterator;
LazyInstanceIdView(): _ids( 0 ) {}
explicit LazyInstanceIdView( const instanceRefs * ids ): _ids( ids ) {}
const_iterator begin() const;
const_iterator end() const;
size_t size() const;
bool empty() const;
instanceID operator[]( size_t index ) const;
private:
const instanceRefs * _ids;
};
/** A dependency closure materialized and pinned for a bounded operation.
* STEPcode parsing remains single-threaded. Detach immutable conversion data
* before releasing the batch or passing work to another thread.
*/
class SC_LAZYFILE_EXPORT LazyInstanceBatch {
public:
LazyInstanceBatch();
~LazyInstanceBatch();
LazyInstanceBatch( LazyInstanceBatch && other );
LazyInstanceBatch & operator=( LazyInstanceBatch && other );
LazyInstanceBatch( const LazyInstanceBatch & ) = delete;
LazyInstanceBatch & operator=( const LazyInstanceBatch & ) = delete;
bool valid() const;
void release();
const std::vector<instanceID> & roots() const;
const std::vector<instanceID> & instances() const;
SDAI_Application_instance * get( instanceID id ) const;
private:
friend class lazyInstMgr;
LazyInstanceBatch( lazyInstMgr * manager, const std::vector<instanceID> & roots,
const std::vector<instanceID> & instances );
lazyInstMgr * _manager;
std::vector<instanceID> _roots;
std::vector<instanceID> _instances;
};
/** Adapter preserving a stream-oriented diagnostic presentation. */
class SC_LAZYFILE_EXPORT LazyTextDiagnosticAdapter {
public:
explicit LazyTextDiagnosticAdapter( std::ostream & stream );
void operator()( const LazyDiagnostic & diagnostic ) const;
private:
std::ostream * _stream;
};
#endif // LAZYSUPPORT_H
+10 -4
View File
@@ -6,6 +6,7 @@
#include <iostream>
#include <vector>
#include <set>
#include <string>
#ifdef HAVE_STDINT_H
#include <stdint.h>
@@ -29,6 +30,7 @@ enum fileTypeEnum { Part21, Part28 };
// enum loadingEnum { immediate, lazy };
typedef uint64_t instanceID; ///< the number assigned to an instance in the file
typedef uint64_t lazyFileOffset; ///< byte offset in an exchange file
typedef uint16_t sectionID; ///< globally unique index of a sectionReader in a sectionReaderVec_t
typedef uint16_t fileID; ///< the index of a lazyFileReader in a lazyFileReaderVec_t. Can be inferred from a sectionID
@@ -42,7 +44,10 @@ typedef uint16_t fileID; ///< the index of a lazyFileReader in a lazyFileRe
* section = ( ps >> 48 );
* TODO: Also 8 bits of flags? Would allow files of 2^40 bytes, or ~1TB.
*/
typedef uint64_t positionAndSection;
typedef struct {
lazyFileOffset begin;
sectionID section;
} instancePosition;
typedef std::vector< instanceID > instanceRefs;
@@ -55,7 +60,8 @@ typedef std::set< instanceID > instanceSet;
* situations, so the information should be kept up-to-date.
*/
typedef struct {
long begin; ///< this is the result of tellg() before reading the instanceID; there may be whitespace or comments, but nothing else.
lazyFileOffset begin; ///< byte offset before the instance ID; whitespace or comments may precede it.
lazyFileOffset end; ///< byte offset immediately after the terminating semicolon.
instanceID instance;
sectionID section;
/* bool modified; */ /* this will be useful when writing instances - if an instance is
@@ -67,6 +73,7 @@ typedef struct {
lazyInstanceLoc loc;
const char * name;
instanceRefs * refs;
std::vector<std::string> * componentTypes; ///< compositional types for a complex instance
} namedLazyInstance;
// instanceRefs - map between an instanceID and instances that refer to it
@@ -80,7 +87,7 @@ typedef judyLArray< instanceID, SDAI_Application_instance * > instancesLoaded_t;
// instanceStreamPos - map instance id to a streampos and data section
// there could be multiple instances with the same ID, but in different files (or different sections of the same file?)
typedef judyL2Array< instanceID, positionAndSection > instanceStreamPos_t;
typedef judyL2Array< instanceID, instancePosition > instanceStreamPos_t;
// data sections
@@ -93,4 +100,3 @@ typedef std::vector< lazyFileReader * > lazyFileReaderVec_t;
// NOTE not useful? typedef std::vector< lazyInstance > lazyInstanceVec_t;
#endif //LAZYTYPES_H
+32 -6
View File
@@ -46,12 +46,31 @@ class SC_LAZYFILE_EXPORT sectionReader {
*/
std::streampos findNormalString( const std::string & str, bool semicolon = false );
/** Skip from immediately after a comment's opening slash and star
* through its closing star and slash. Part 21 comments are opaque:
* apostrophes and string-control text inside them have no syntax.
*/
bool skipComment();
/** Skip whitespace and Part 21 comments, leaving the next syntactic
* token unread. */
bool skipTokenSeparators();
/** Skip an Edition 1 SCOPE construct, including nested scopes and its
* optional export list. The stream must initially point at '&' and
* is left at the owning entity's record. */
bool skipScope();
/** Skip an optional scope export list at the current stream
* position. */
bool skipScopeExportList();
/** Get a keyword ending with one of delimiters.
*/
const char * getDelimitedKeyword( const char * delimiters );
/** Seek to the end of the current instance */
std::streampos seekInstanceEnd( instanceRefs ** refs );
std::streampos seekInstanceEnd( instanceRefs ** refs, std::vector<std::string> * componentTypes = 0 );
/// operator>> is very slow?!
inline void skipWS() {
@@ -63,9 +82,15 @@ class SC_LAZYFILE_EXPORT sectionReader {
STEPcomplex * CreateSubSuperInstance( const Registry * reg, instanceID fileid, Severity & sev );
public:
SDAI_Application_instance * getRealInstance( const Registry * reg, long int begin, instanceID instance,
SDAI_Application_instance * getRealInstance( const Registry * reg, lazyFileOffset begin, instanceID instance,
const std::string & typeName = "", const std::string & schName = "", bool header = false );
/** Return one indexed source record without materializing it. The
* stream position is restored before returning. This is intended
* for lightweight adapters which can detach scalar/reference data
* directly from Part 21 source. */
std::string sourceRecord( lazyFileOffset begin, uint64_t length );
sectionID ID() const {
return _sectionID;
}
@@ -91,13 +116,15 @@ class SC_LAZYFILE_EXPORT sectionReader {
/** returns the type string for an instance, read straight from the file
* if this function changes, probably need to change nextInstance() as well
* don't check errors - they would have been encountered during the initial file scan, and the file is still open so it can't have been modified */
const char * getType( long int offset ) {
const char * getType( lazyFileOffset offset ) {
if( offset <= 0 ) {
return 0;
}
_file.seekg( offset );
_file.seekg( static_cast<std::streamoff>( offset ) );
readInstanceNumber();
skipWS();
if( !skipTokenSeparators() ) return 0;
if( _file.peek() == '&' && !skipScope() ) return 0;
if( !skipTokenSeparators() ) return 0;
return getDelimitedKeyword( ";( /\\" );
}
@@ -112,4 +139,3 @@ class SC_LAZYFILE_EXPORT sectionReader {
};
#endif //SECTIONREADER_H
+2 -1
View File
@@ -31,6 +31,8 @@ set(CORE_HDRS
realTypeDescriptor.h
Registry.h
schRename.h
schemaInit.h
schemaModule.h
sdai.h
sdaiApplication_instance.h
sdaiSelect.h
@@ -68,4 +70,3 @@ install(FILES ${CORE_HDRS}
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+6 -1
View File
@@ -4,6 +4,8 @@
#include "clstepcore/STEPaggregate.h"
#include <sc_export.h>
class SelectTypeDescriptor;
/** \file STEPaggrSelect.h
* classes SelectAggregate, SelectNode
*/
@@ -14,6 +16,9 @@
** This is a minimal representation for a collection of SDAI_Select
*/
class SC_CORE_EXPORT SelectAggregate : public STEPaggregate {
protected:
const SelectTypeDescriptor * _select_type;
public:
virtual Severity ReadValue( istream & in, ErrorDescriptor * err,
const TypeDescriptor * elem_type,
@@ -24,7 +29,7 @@ public:
virtual SingleLinkNode * NewNode();
virtual STEPaggregate & ShallowCopy( const STEPaggregate & );
SelectAggregate();
explicit SelectAggregate( const SelectTypeDescriptor * select_type = 0 );
virtual ~SelectAggregate();
};
typedef SelectAggregate * SelectAggregateH;
+5
View File
@@ -88,6 +88,11 @@ class SC_CORE_EXPORT STEPnode : public SingleLinkNode {
protected:
int _null;
/// nodes must start out null; several StrToVal/STEPread paths leave
/// _null untouched on failure and would otherwise read uninitialised.
STEPnode() : _null( 1 ) {
}
public:
int is_null() {
return _null;
+9
View File
@@ -112,6 +112,7 @@ class SC_CORE_EXPORT STEPattribute {
char SkipBadAttr( istream & in, char * StopChars );
void AddErrorInfo();
void STEPwriteError( ostream& out, unsigned int line, const char* desc );
void DeleteOwnedPtr();
public:
void incrRefCount() {
@@ -232,6 +233,14 @@ class SC_CORE_EXPORT STEPattribute {
memset( & ptr, 0, sizeof( ptr ) );
}
~STEPattribute();
/** Construct storage owned by this attribute from its descriptor.
*
* Generated early-bound classes normally pass a pointer to one of
* their data members to the typed constructors below. Late-bound
* instances have no generated data members, so this constructor
* allocates the corresponding value object instead.
*/
explicit STEPattribute( const class AttrDescriptor & d );
// INTEGER
STEPattribute( const class AttrDescriptor & d, SDAI_Integer * p );
// BINARY
+1 -1
View File
@@ -48,7 +48,7 @@ public:
void AssignAggrCreator( AggregateCreator f = 0 );
STEPaggregate * CreateAggregate();
STEPaggregate * CreateAggregate() const;
AggrTypeDescriptor( );
AggrTypeDescriptor( SDAI_Integer b1, SDAI_Integer b2,
+21
View File
@@ -24,6 +24,9 @@ class Registry;
*/
class SC_CORE_EXPORT EntityDescriptor : public TypeDescriptor {
private:
void InvalidateLateBoundLayout() const;
protected:
SDAI_LOGICAL _abstractEntity;
@@ -57,6 +60,22 @@ class SC_CORE_EXPORT EntityDescriptor : public TypeDescriptor {
virtual ~EntityDescriptor();
/** Create an instance of this entity.
*
* Uses the generated early-bound creator when one is installed and
* otherwise constructs a descriptor-backed late-bound instance.
*/
SDAI_Application_instance * CreateEntity() const;
/** Resolve the inherited attribute layout used by late-bound
* instances.
*
* SchemaModule calls this once after descriptor initialization.
* CreateEntity also calls it as a fallback for independently built
* descriptor graphs.
*/
void PrepareLateBoundLayout() const;
void InitIAttrs( Registry & reg, const char * schNm );
const char * GenerateExpress( std::string & buf ) const;
@@ -134,10 +153,12 @@ class SC_CORE_EXPORT EntityDescriptor : public TypeDescriptor {
}
void AddSupertype( EntityDescriptor * ed ) {
InvalidateLateBoundLayout();
_supertypes.AddNode( ed );
}
void AddExplicitAttr( AttrDescriptor * ad ) {
InvalidateLateBoundLayout();
_explicitAttr.AddNode( ad );
}
+8 -2
View File
@@ -1,6 +1,8 @@
#ifndef ENUMTYPEDESCRIPTOR_H
#define ENUMTYPEDESCRIPTOR_H
#include <stddef.h>
#include "typeDescriptor.h"
typedef SDAI_Enum * ( * EnumCreator )();
@@ -15,16 +17,20 @@ class SC_CORE_EXPORT EnumTypeDescriptor : public TypeDescriptor {
CreateNewEnum = f;
}
SDAI_Enum * CreateEnum();
SDAI_Enum * CreateEnum() const;
EnumTypeDescriptor( ) { }
EnumTypeDescriptor( const char * nm, PrimitiveType ft,
Schema * origSchema, const char * d,
EnumCreator f = 0 );
virtual ~EnumTypeDescriptor() { }
virtual ~EnumTypeDescriptor();
};
SC_CORE_EXPORT void RegisterEnumDescriptorElements(
const EnumTypeDescriptor & descriptor,
const char * const * elements, size_t count );
/** \class EnumerationTypeDescriptor
* FIXME not implemented
*
+7
View File
@@ -21,7 +21,14 @@ public:
Entity__set_var _entities; // not implemented
Where_rule__list_var _where_rules;
Schema_ptr _parent_schema;
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable: 4251 )
#endif
std::string _rule_text; // non-SDAI
#ifdef _MSC_VER
#pragma warning( pop )
#endif
Global_rule();
Global_rule( const char * n, Schema_ptr parent_sch, const std::string & rt );
+362
View File
@@ -0,0 +1,362 @@
#ifndef SCHEMAINIT_H
#define SCHEMAINIT_H
#include <stddef.h>
#include "sc_export.h"
#include "clstepcore/attrDescriptor.h"
#include "clstepcore/aggrTypeDescriptor.h"
#include "clstepcore/dictSchema.h"
#include "clstepcore/entityDescriptor.h"
#include "clstepcore/enumTypeDescriptor.h"
#include "clstepcore/selectTypeDescriptor.h"
#include "clstepcore/schemaModule.h"
class Derived_attribute;
class Inverse_attribute;
class ComplexCollect;
class Registry;
/** Compact records used by API v2 generated schema initialization. */
struct SC_CORE_EXPORT SchemaInitRecord {
Schema ** slot;
const char * name;
ModelContentsCreator modelContentsCreator;
};
struct SC_CORE_EXPORT EntityDescriptorInitRecord {
EntityDescriptor ** slot;
const char * name;
Schema ** schemaSlot;
Logical abstractEntity;
Logical externalMapping;
Creator creator;
};
struct SC_CORE_EXPORT EntitySupertypeInitRecord {
EntityDescriptor * supertype;
};
enum TypeDescriptorInitKind {
TypeDescriptorInit_Base,
TypeDescriptorInit_Enum,
TypeDescriptorInit_Select,
TypeDescriptorInit_Aggregate,
TypeDescriptorInit_Array,
TypeDescriptorInit_List,
TypeDescriptorInit_Set,
TypeDescriptorInit_Bag
};
struct SC_CORE_EXPORT TypeDescriptorInitRecord {
void * slot;
TypeDescriptorInitKind kind;
const char * name;
PrimitiveType fundamentalType;
Schema ** schemaSlot;
const char * description;
int uniqueElements;
SelectCreator selectCreator;
EnumCreator enumCreator;
AggregateCreator aggregateCreator;
TypeDescriptorInitRecord( TypeDescriptor ** descriptorSlot,
const char * typeName, PrimitiveType type,
Schema ** schema, const char * typeDescription );
TypeDescriptorInitRecord( EnumTypeDescriptor ** descriptorSlot,
const char * typeName, PrimitiveType type,
Schema ** schema, const char * typeDescription,
EnumCreator creator );
TypeDescriptorInitRecord( SelectTypeDescriptor ** descriptorSlot,
int unique, const char * typeName,
PrimitiveType type, Schema ** schema,
const char * typeDescription,
SelectCreator creator );
TypeDescriptorInitRecord( AggrTypeDescriptor ** descriptorSlot,
const char * typeName, PrimitiveType type,
Schema ** schema, const char * typeDescription,
AggregateCreator creator );
TypeDescriptorInitRecord( ArrayTypeDescriptor ** descriptorSlot,
const char * typeName, PrimitiveType type,
Schema ** schema, const char * typeDescription,
AggregateCreator creator );
TypeDescriptorInitRecord( ListTypeDescriptor ** descriptorSlot,
const char * typeName, PrimitiveType type,
Schema ** schema, const char * typeDescription,
AggregateCreator creator );
TypeDescriptorInitRecord( SetTypeDescriptor ** descriptorSlot,
const char * typeName, PrimitiveType type,
Schema ** schema, const char * typeDescription,
AggregateCreator creator );
TypeDescriptorInitRecord( BagTypeDescriptor ** descriptorSlot,
const char * typeName, PrimitiveType type,
Schema ** schema, const char * typeDescription,
AggregateCreator creator );
};
/**
* Attribute descriptor record. The overloaded constructors preserve the
* concrete type of generated descriptor slots without templates or casts in
* generated translation units.
*/
struct SC_CORE_EXPORT AttributeInitRecord {
AttrDescriptor ** attributeSlot;
Derived_attribute ** derivedSlot;
Inverse_attribute ** inverseSlot;
const char * name;
const TypeDescriptor * domain;
Logical optional;
Logical unique;
AttrType_Enum attrType;
const char * initializer;
const char * invertedAttribute;
const char * invertedEntity;
AttributeInitRecord( AttrDescriptor ** slot, const char * attrName,
const TypeDescriptor * domainType, Logical isOptional,
Logical isUnique, AttrType_Enum type,
const char * init = 0 );
AttributeInitRecord( Derived_attribute ** slot, const char * attrName,
const TypeDescriptor * domainType, Logical isOptional,
Logical isUnique, AttrType_Enum type,
const char * init = 0 );
AttributeInitRecord( Inverse_attribute ** slot, const char * attrName,
const TypeDescriptor * domainType, Logical isOptional,
Logical isUnique, const char * invertedAttr,
const char * invertedEnt );
};
enum ComplexNodeInitKind {
ComplexNodeInit_Simple,
ComplexNodeInit_And,
ComplexNodeInit_Or,
ComplexNodeInit_AndOr
};
struct SC_CORE_EXPORT ComplexNodeInitRecord {
ComplexNodeInitKind kind;
const char * name;
size_t firstChild;
size_t nextSibling;
};
struct SC_CORE_EXPORT ComplexListInitRecord {
size_t rootNode;
};
struct SC_CORE_EXPORT GlobalRuleInitRecord {
const char * name;
const char * text;
};
enum SchemaImageDescriptorRefKind {
SchemaImageRef_None,
SchemaImageRef_Entity,
SchemaImageRef_Type,
SchemaImageRef_Builtin,
SchemaImageRef_Aggregate
};
struct SC_CORE_EXPORT SchemaImageDescriptorRef {
uint32_t kind;
uint32_t schema;
uint32_t index;
};
struct SC_CORE_EXPORT SchemaImageSchemaRecord {
uint32_t name;
uint32_t firstEntity;
uint32_t entityCount;
uint32_t firstType;
uint32_t typeCount;
};
struct SC_CORE_EXPORT SchemaImageEntityRecord {
uint32_t schema;
uint32_t name;
uint32_t flags;
uint32_t supertypeStatement;
uint32_t firstSupertype;
uint32_t supertypeCount;
uint32_t firstAttribute;
uint32_t attributeCount;
uint32_t firstWhereRule;
uint32_t whereRuleCount;
uint32_t firstUniqueRule;
uint32_t uniqueRuleCount;
};
enum SchemaImageEntityFlags {
SchemaImageEntity_Abstract = 1u << 0,
SchemaImageEntity_ExternalMapping = 1u << 1
};
struct SC_CORE_EXPORT SchemaImageTypeRecord {
uint32_t schema;
uint32_t name;
uint32_t description;
uint32_t fundamentalType;
uint32_t descriptorKind;
int32_t uniqueElements;
SchemaImageDescriptorRef referent;
uint32_t aggregate;
uint32_t firstSelectElement;
uint32_t selectElementCount;
uint32_t firstWhereRule;
uint32_t whereRuleCount;
uint32_t firstEnumElement;
uint32_t enumElementCount;
};
struct SC_CORE_EXPORT SchemaImageAttributeRecord {
uint32_t name;
SchemaImageDescriptorRef domain;
uint32_t optional;
uint32_t unique;
uint32_t attrType;
uint32_t initializer;
uint32_t invertedAttribute;
uint32_t invertedEntity;
};
struct SC_CORE_EXPORT SchemaImageAggregateRecord {
uint32_t schema;
uint32_t description;
uint32_t fundamentalType;
SchemaImageDescriptorRef referent;
uint32_t bound1Type;
int32_t bound1;
uint32_t bound1Text;
uint32_t bound2Type;
int32_t bound2;
uint32_t bound2Text;
uint32_t optionalElements;
uint32_t uniqueElements;
};
struct SC_CORE_EXPORT SchemaImageRuleRecord {
uint32_t text;
};
enum SchemaImageSchemaTextKind {
SchemaImageText_GlobalRule,
SchemaImageText_Function,
SchemaImageText_Procedure
};
struct SC_CORE_EXPORT SchemaImageSchemaTextRecord {
uint32_t schema;
uint32_t kind;
uint32_t name;
uint32_t text;
};
struct SC_CORE_EXPORT SchemaImageRenameRecord {
SchemaImageDescriptorRef descriptor;
uint32_t schema;
uint32_t name;
};
struct SC_CORE_EXPORT SchemaImageSchemaBinding {
Schema ** schemaSlot;
ModelContentsCreator modelContentsCreator;
SchemaModule * module;
};
struct SC_CORE_EXPORT SchemaImageTypeBinding {
void * slot;
SelectCreator selectCreator;
EnumCreator enumCreator;
AggregateCreator aggregateCreator;
};
enum SchemaLoadError {
SchemaLoad_Ok,
SchemaLoad_UnsupportedVersion,
SchemaLoad_TruncatedImage,
SchemaLoad_InvalidOffset,
SchemaLoad_InvalidString,
SchemaLoad_InvalidReference,
SchemaLoad_CountMismatch,
SchemaLoad_MissingBinding,
SchemaLoad_ModuleFailure
};
struct SC_CORE_EXPORT SchemaLoadResult {
SchemaLoadError error;
uint32_t record;
bool Succeeded() const {
return error == SchemaLoad_Ok;
}
const char * Message() const;
};
SC_CORE_EXPORT SchemaLoadResult InitializeSchemaFromImage(
Registry & registry, const SchemaModuleImage & image,
const SchemaImageSchemaBinding * schemaBindings,
size_t schemaBindingCount,
const SchemaImageTypeBinding * typeBindings,
size_t typeBindingCount, SchemaLoadContext & context );
enum PackedComplexImageVersion {
PackedComplexImageVersion_1 = 1
};
struct SC_CORE_EXPORT PackedComplexImage {
uint32_t version;
uint32_t byteSize;
uint32_t stringOffset;
uint32_t stringBytes;
uint32_t nodeOffset;
uint32_t nodeCount;
uint32_t listOffset;
uint32_t listCount;
};
struct SC_CORE_EXPORT PackedComplexNode {
uint32_t kind;
uint32_t name;
uint32_t firstChild;
uint32_t nextSibling;
};
struct SC_CORE_EXPORT PackedComplexList {
uint32_t rootNode;
};
SC_CORE_EXPORT ComplexCollect * InitializePackedComplexSupport(
const PackedComplexImage & image, SchemaLoadResult * result = 0 );
SC_CORE_EXPORT void InitializeSchemas( Registry & reg,
const SchemaInitRecord * records,
size_t count );
SC_CORE_EXPORT void InitializeSchemaModuleImages(
const SchemaInitRecord * records, size_t count );
SC_CORE_EXPORT void InitializeEntityDescriptors(
const EntityDescriptorInitRecord * records, size_t count );
SC_CORE_EXPORT void InitializeTypeDescriptors(
const TypeDescriptorInitRecord * records, size_t count );
SC_CORE_EXPORT void InitializeTypeMetadata(
Registry & reg, Schema & schema, TypeDescriptor & type,
const TypeDescriptor * referent,
const TypeDescriptor * const * selectElements, size_t selectElementCount );
SC_CORE_EXPORT void InitializeEntityMetadata(
Registry & reg, EntityDescriptor & entity, Schema & schema,
const EntitySupertypeInitRecord * supertypes, size_t supertypeCount,
const AttributeInitRecord * attributes, size_t attributeCount );
SC_CORE_EXPORT void InitializeWhereRules(
TypeDescriptor & owner, const char * const * rules, size_t count );
SC_CORE_EXPORT void InitializeUniquenessRules(
EntityDescriptor & owner, const char * const * rules, size_t count );
SC_CORE_EXPORT void InitializeGlobalRules(
Schema & schema, const GlobalRuleInitRecord * rules, size_t count );
SC_CORE_EXPORT void InitializeFunctions(
Schema & schema, const char * const * functions, size_t count );
SC_CORE_EXPORT void InitializeProcedures(
Schema & schema, const char * const * procedures, size_t count );
SC_CORE_EXPORT ComplexCollect * InitializeComplexSupport(
const ComplexNodeInitRecord * nodes, size_t nodeCount,
const ComplexListInitRecord * lists, size_t listCount );
#endif
+171
View File
@@ -0,0 +1,171 @@
#ifndef SCHEMAMODULE_H
#define SCHEMAMODULE_H
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include "sc_export.h"
class AttrDescriptor;
class EntityDescriptor;
class Schema;
class TypeDescriptor;
enum SchemaModuleSlotKind {
SchemaModuleSlot_Entity,
SchemaModuleSlot_Type,
SchemaModuleSlot_EnumType,
SchemaModuleSlot_SelectType,
SchemaModuleSlot_AggregateType,
SchemaModuleSlot_ArrayType,
SchemaModuleSlot_ListType,
SchemaModuleSlot_SetType,
SchemaModuleSlot_BagType,
SchemaModuleSlot_Attribute,
SchemaModuleSlot_DerivedAttribute,
SchemaModuleSlot_InverseAttribute
};
struct SC_CORE_EXPORT SchemaModuleSlot {
const void * slot;
SchemaModuleSlotKind kind;
};
enum SchemaModuleImageVersion {
SchemaModuleImageVersion_1 = 1,
SchemaModuleImageVersion_2 = 2
};
enum SchemaModuleImageFlags {
SchemaModuleImage_FullMetadata = 0,
SchemaModuleImage_StructuralMetadata = 1u << 0
};
/**
* Relocation-free generated header for a schema module image.
*
* Version 1 records only stable generated-ID counts. Version 2 describes
* one bounded block of offset-based schemas, descriptors, metadata, and
* strings. Runtime loading validates the complete block before constructing
* descriptors or changing a Registry.
*/
struct SC_CORE_EXPORT SchemaModuleImage {
uint32_t version;
uint32_t entityCount;
uint32_t typeCount;
uint32_t attributeCount;
/* Version 2 fields. Version 1 initializers leave these zero. */
uint32_t byteSize;
uint32_t flags;
uint32_t schemaCount;
uint32_t stringBytes;
uint32_t schemaOffset;
uint32_t entityOffset;
uint32_t typeOffset;
uint32_t attributeOffset;
uint32_t aggregateCount;
uint32_t aggregateOffset;
uint32_t referenceCount;
uint32_t referenceOffset;
uint32_t ruleCount;
uint32_t ruleOffset;
uint32_t schemaTextCount;
uint32_t schemaTextOffset;
uint32_t enumElementCount;
uint32_t enumElementOffset;
uint32_t stringOffset;
uint32_t reserved;
uint64_t fingerprint;
uint32_t renameCount;
uint32_t renameOffset;
};
class SchemaModule;
/**
* Per-load descriptor collection used by packed API v2 schema images.
*
* Keeping this state explicit makes independent schema loads reentrant and
* avoids the process-global capture map retained for version-1 images.
*/
class SC_CORE_EXPORT SchemaLoadContext {
class Impl;
Impl * _impl;
SchemaLoadContext( const SchemaLoadContext & );
SchemaLoadContext & operator=( const SchemaLoadContext & );
friend class SchemaModule;
public:
SchemaLoadContext();
~SchemaLoadContext();
void Begin( Schema & schema );
void RecordEntity( Schema & schema, const EntityDescriptor * entity );
void RecordType( Schema & schema, const TypeDescriptor * type );
void RecordAttribute( Schema & schema, const AttrDescriptor * attribute );
};
SC_CORE_EXPORT void RecordSchemaModuleEntity(
Schema & schema, const EntityDescriptor * entity );
SC_CORE_EXPORT void RecordSchemaModuleType(
Schema & schema, const TypeDescriptor * type );
SC_CORE_EXPORT void RecordSchemaModuleAttribute(
Schema & schema, const AttrDescriptor * attribute );
SC_CORE_EXPORT void BeginSchemaModuleImage( Schema & schema );
/**
* Stable API v2 view of a generated schema.
*
* Generated EntityId, TypeId, and AttributeId enumerations are converted to
* indexes before calling this class. Keeping the public lookup API separate
* from generated descriptor globals allows the underlying representation to
* move from C++ objects to packed schema data without another API break.
*/
class SC_CORE_EXPORT SchemaModule {
Schema * _schema;
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable: 4251 )
#endif
std::vector<const EntityDescriptor *> _entities;
std::vector<const TypeDescriptor *> _types;
std::vector<const AttrDescriptor *> _attributes;
uint64_t _fingerprint;
#ifdef _MSC_VER
#pragma warning( pop )
#endif
public:
SchemaModule();
~SchemaModule();
void Initialize(
Schema & schema,
const EntityDescriptor * const * entities, size_t entityCount,
const TypeDescriptor * const * types, size_t typeCount,
const AttrDescriptor * const * attributes, size_t attributeCount );
void InitializeFromSlots(
Schema & schema,
const SchemaModuleSlot * entities, size_t entityCount,
const SchemaModuleSlot * types, size_t typeCount,
const SchemaModuleSlot * attributes, size_t attributeCount );
void InitializeFromImage( Schema & schema,
const SchemaModuleImage & image );
bool InitializeFromContext( Schema & schema, SchemaLoadContext & context,
uint64_t fingerprint );
bool IsInitialized() const;
const Schema & GetSchema() const;
const EntityDescriptor * Entity( size_t id ) const;
const TypeDescriptor * Type( size_t id ) const;
const AttrDescriptor * Attribute( size_t id ) const;
size_t EntityCount() const;
size_t TypeCount() const;
size_t AttributeCount() const;
uint64_t Fingerprint() const;
};
#endif
+45 -7
View File
@@ -17,7 +17,13 @@
/**
** \file sdaiSelect.h class definition for the select superclass SDAI_Select.
**/
class STEPaggregate;
class SC_CORE_EXPORT SDAI_Select {
private:
class Storage;
Storage * _storage;
protected:
const SelectTypeDescriptor * _type;
const TypeDescriptor * underlying_type;
@@ -36,8 +42,11 @@ class SC_CORE_EXPORT SDAI_Select {
int IsUnique( const BASE_TYPE bt ) const;
virtual const TypeDescriptor * AssignEntity( SDAI_Application_instance * se ) = 0;
virtual SDAI_Select * NewSelect() = 0;
virtual const TypeDescriptor * AssignEntity( SDAI_Application_instance * se );
virtual SDAI_Select * NewSelect();
void * ValueAddress();
const void * ValueAddress() const;
public:
Severity severity() const;
Severity severity( Severity );
@@ -47,7 +56,7 @@ class SC_CORE_EXPORT SDAI_Select {
void ClearError();
// clears error
virtual BASE_TYPE ValueType() const = 0;
virtual BASE_TYPE ValueType() const;
// constructors
SDAI_Select( const SelectTypeDescriptor * s = 0,
@@ -72,28 +81,57 @@ class SC_CORE_EXPORT SDAI_Select {
virtual void STEPwrite_verbose( ostream & out = cout, const char * = 0 )
const;
virtual void STEPwrite_content( ostream & out, const char * = 0 ) const = 0;
virtual void STEPwrite_content( ostream & out, const char * = 0 ) const;
Severity StrToVal( const char * val, const char * selectType,
ErrorDescriptor * err, InstMgrBase * instances = 0 );
virtual Severity StrToVal_content( const char *,
InstMgrBase * instances = 0 ) = 0;
InstMgrBase * instances = 0 );
Severity STEPread( istream & in, ErrorDescriptor * err,
InstMgrBase * instances = 0, const char * utype = 0,
int addFileId = 0, const char * = NULL );
// abstract function
// Descriptor-driven default; API v1 SELECTs may override it.
virtual Severity STEPread_content( istream & in = cin,
InstMgrBase * instances = 0,
const char * utype = 0,
int addFileId = 0,
const char * currSch = 0 ) = 0;
const char * currSch = 0 );
//windows complains if operator= is pure virtual, perhaps because the impl is not in the lib with the definition
//linux has a regression if the pure virtual operator= is commented out
virtual SDAI_Select & operator =( const SDAI_Select & other );
SDAI_Select & operator =( const SDAI_Select * other );
SDAI_Select * Clone() const;
// Descriptor-driven value API used by generated API v2 classes and
// by applications that do not need per-choice early-bound members.
bool SetInteger( const TypeDescriptor *, SDAI_Integer );
bool SetReal( const TypeDescriptor *, SDAI_Real );
bool SetString( const TypeDescriptor *, const SDAI_String & );
bool SetBinary( const TypeDescriptor *, const SDAI_Binary & );
bool SetEnum( const TypeDescriptor *, const SDAI_Enum & );
bool SetSelect( const TypeDescriptor *, const SDAI_Select & );
bool SetAggregate( const TypeDescriptor *, const STEPaggregate & );
bool SetEntity( SDAI_Application_instance * );
SDAI_Integer * IntegerValue();
const SDAI_Integer * IntegerValue() const;
SDAI_Real * RealValue();
const SDAI_Real * RealValue() const;
SDAI_String * StringValue();
const SDAI_String * StringValue() const;
SDAI_Binary * BinaryValue();
const SDAI_Binary * BinaryValue() const;
SDAI_Enum * EnumValue();
const SDAI_Enum * EnumValue() const;
SDAI_Select * SelectValue();
const SDAI_Select * SelectValue() const;
STEPaggregate * AggregateValue();
const STEPaggregate * AggregateValue() const;
SDAI_Application_instance * EntityValue() const;
//FIXME set_null always returns true. why not void?!
bool set_null();
+1 -1
View File
@@ -19,7 +19,7 @@ public:
CreateNewSelect = f;
}
SDAI_Select * CreateSelect();
SDAI_Select * CreateSelect() const;
SelectTypeDescriptor( int b, const char * nm, PrimitiveType ft,
Schema * origSchema,
+1 -1
View File
@@ -58,7 +58,7 @@ struct freelist_head {
#endif
};
char * nnew();
char * nnew(void);
#include "error.h"
+3 -4
View File
@@ -93,11 +93,10 @@
/* function pointer types */
/**************************/
typedef void ( *voidFuncptr )();
typedef int ( *intFuncptr )();
typedef void ( *voidFuncptr )(void);
typedef int ( *intFuncptr )(void);
/* Option index - can we get rid of this? */
extern SC_EXPRESS_EXPORT int sc_optind;
#endif /* BASIC_H */
+1
View File
@@ -108,6 +108,7 @@ extern SC_EXPRESS_EXPORT char * KW_SUPERTYPE;
extern SC_EXPRESS_EXPORT char * KW_TAN;
extern SC_EXPRESS_EXPORT char * KW_THEN;
extern SC_EXPRESS_EXPORT char * KW_TO;
extern SC_EXPRESS_EXPORT char * KW_TREAT;
extern SC_EXPRESS_EXPORT char * KW_TRUE;
extern SC_EXPRESS_EXPORT char * KW_TYPE;
extern SC_EXPRESS_EXPORT char * KW_TYPEOF;
+1 -1
View File
@@ -83,7 +83,7 @@ typedef enum {
OP_MINUS, OP_MOD, OP_NEGATE,
OP_NOT, OP_NOT_EQUAL, OP_OR,
OP_PLUS, OP_REAL_DIV, OP_SUBCOMPONENT,
OP_TIMES, OP_XOR, OP_UNKNOWN,
OP_TIMES, OP_TREAT, OP_XOR, OP_UNKNOWN,
OP_LAST /**< must be last - used only to size tables */
} Op_Code;
+1 -1
View File
@@ -3,6 +3,6 @@
#include "sc_export.h"
SC_EXPRESS_EXPORT void FACTORYinitialize();
SC_EXPRESS_EXPORT void FACTORYinitialize(void);
#endif /* __FACTORY_H_ */
+1 -1
View File
@@ -3,6 +3,6 @@
#include "sc_export.h"
SC_EXPRESS_EXPORT void MEMORYinitialize();
SC_EXPRESS_EXPORT void MEMORYinitialize(void);
#endif // __MEMORY_H
+19
View File
@@ -47,6 +47,25 @@
extern SC_EXPRESS_EXPORT int print_objects_while_running;
/*************************/
/* Refinement structures */
/*************************/
/* Maximum length for type name lookups */
#define MAX_TYPE_NAME_LENGTH 512
/** Structure to track type refinements from TYPEOF guards in AND expressions */
typedef struct Refinement_ {
Variable variable; /**< the variable being refined */
Type refined_type; /**< the narrowed type */
struct Refinement_ *next; /**< next refinement in the chain */
} *Refinement;
/** Context for passing refinements through expression resolution */
typedef struct RefinementContext_ {
Refinement refinements; /**< linked list of active refinements */
} *RefinementContext;
/* macros */
/* cheaper doing the check here, then inside the function call. Return */
+4
View File
@@ -237,6 +237,9 @@ extern SC_EXPRESS_EXPORT struct freelist_head TYPEBODY_fl;
#define TYPEis_oneof(t) ((t)->u.type->body->type == oneof_)
#define TYPEis_entity(t) ((t)->u.type->body->type == entity_)
#define TYPEis_enumeration(t) ((t)->u.type->body->type == enumeration_)
/** Shallow aggregate check - returns true if type has a base (characteristic of aggregates).
* Does NOT follow type definition chains. For deep unwrapping through typedef chains,
* use TYPEget_nonaggregate_base_type() or TYPEget_aggregate_base(). */
#define TYPEis_aggregate(t) ((t)->u.type->body->base)
#define TYPEis_aggregate_raw(t) ((t)->u.type->body->type == aggregate_)
#define TYPEis_array(t) ((t)->u.type->body->type == array_)
@@ -303,6 +306,7 @@ extern SC_EXPRESS_EXPORT void TYPEcleanup( void );
extern SC_EXPRESS_EXPORT bool TYPEinherits_from( Type, enum type_enum );
extern SC_EXPRESS_EXPORT Type TYPEget_nonaggregate_base_type( Type );
extern SC_EXPRESS_EXPORT Type TYPEget_aggregate_base( Type );
extern SC_EXPRESS_EXPORT Type TYPEcreate_user_defined_type( Type, Scope, struct Symbol_ * );
extern SC_EXPRESS_EXPORT Type TYPEcreate_user_defined_tag( Type, Scope, struct Symbol_ * );
+1 -1
View File
@@ -15,7 +15,7 @@ BUILDNAME:STRING=${CTEST_BUILD_NAME}
SC_ENABLE_TESTING:BOOL=ON
SC_ENABLE_COVERAGE:BOOL=ON
SC_BUILD_SCHEMAS:STRING=ALL
SC_BUILD_TYPE:STRING=Debug
CMAKE_BUILD_TYPE:STRING=Debug
")
set(LCOV_OUT "${CTEST_BINARY_DIRECTORY}/lcov_html")
+1 -1
View File
@@ -9,7 +9,7 @@ set(CTEST_INITIAL_CACHE "
SITE:STRING=${CTEST_SITE}
BUILDNAME:STRING=${CTEST_BUILD_NAME}
SC_ENABLE_TESTING:BOOL=ON
SC_BUILD_TYPE:STRING=Debug
CMAKE_BUILD_TYPE:STRING=Debug
")
+10 -1
View File
@@ -27,7 +27,16 @@ if(BUILD_SHARED_LIBS)
endif()
if(BUILD_STATIC_LIBS)
SC_ADDLIB(stepdai-static STATIC SOURCES ${DAI_SRCS} LINK_LIBRARIES $<JOIN:${_libdeps},-static >-static)
# Spell the static dependency names out rather than composing them with a
# generator expression. Since 0b950dd1 SC_ADDLIB publishes LINK_LIBRARIES as
# PUBLIC, so anything given here lands in INTERFACE_LINK_LIBRARIES and is
# re-evaluated in each consumer - where a nested $<JOIN:...> comes back out
# mangled and reaches the linker verbatim.
set(_static_libdeps)
foreach(_libdep ${_libdeps})
list(APPEND _static_libdeps ${_libdep}-static)
endforeach()
SC_ADDLIB(stepdai-static STATIC SOURCES ${DAI_SRCS} LINK_LIBRARIES ${_static_libdeps})
endif()
# Local Variables:
+3 -3
View File
@@ -121,17 +121,17 @@ Severity SDAI_Binary::ReadBinary( istream & in, ErrorDescriptor * err, int Assig
if( !validDelimiters ) {
err->GreaterSeverity( SEVERITY_WARNING );
if( needDelims )
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
"Binary value missing double quote delimiters.\n" );
else
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
"Mismatched double quote delimiters for binary.\n" );
err->AppendToDetailMsg( messageBuf );
err->AppendToUserMsg( messageBuf );
}
} else {
err->GreaterSeverity( SEVERITY_WARNING );
sprintf( messageBuf, "Invalid binary value.\n" );
snprintf( messageBuf, sizeof(messageBuf), "Invalid binary value.\n" );
err->AppendToDetailMsg( messageBuf );
err->AppendToUserMsg( messageBuf );
}
+6 -6
View File
@@ -211,10 +211,10 @@ Severity SDAI_LOGICAL::ReadEnum( istream & in, ErrorDescriptor * err, int Assign
if( !validDelimiters ) {
err->GreaterSeverity( SEVERITY_WARNING );
if( needDelims )
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
"Enumerated value has invalid period delimiters.\n" );
else
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
"Mismatched period delimiters for enumeration.\n" );
err->AppendToDetailMsg( messageBuf );
err->AppendToUserMsg( messageBuf );
@@ -241,7 +241,7 @@ Severity SDAI_LOGICAL::ReadEnum( istream & in, ErrorDescriptor * err, int Assign
} else {
in.putback( c );
err->GreaterSeverity( SEVERITY_WARNING );
sprintf( messageBuf, "Invalid enumeration value.\n" );
snprintf( messageBuf, sizeof(messageBuf), "Invalid enumeration value.\n" );
err->AppendToDetailMsg( messageBuf );
err->AppendToUserMsg( messageBuf );
}
@@ -489,10 +489,10 @@ Severity SDAI_Enum::ReadEnum( istream & in, ErrorDescriptor * err, int AssignVal
if( !validDelimiters ) {
err->GreaterSeverity( SEVERITY_WARNING );
if( needDelims )
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
"Enumerated value has invalid period delimiters.\n" );
else
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
"Mismatched period delimiters for enumeration.\n" );
err->AppendToDetailMsg( messageBuf );
err->AppendToUserMsg( messageBuf );
@@ -519,7 +519,7 @@ Severity SDAI_Enum::ReadEnum( istream & in, ErrorDescriptor * err, int AssignVal
} else {
in.putback( c );
err->GreaterSeverity( SEVERITY_WARNING );
sprintf( messageBuf, "Invalid enumeration value.\n" );
snprintf( messageBuf, sizeof(messageBuf), "Invalid enumeration value.\n" );
err->AppendToDetailMsg( messageBuf );
err->AppendToUserMsg( messageBuf );
}
+1 -1
View File
@@ -21,7 +21,7 @@ if(BUILD_SHARED_LIBS)
endif()
if(BUILD_STATIC_LIBS)
SC_ADDLIB(stepeditor-static STATIC SOURCES ${EDITOR_SRCS} LINK_LIBRARIES stepcore-static stepdai-static steputils-static)
SC_ADDLIB(stepeditor-static STATIC SOURCES ${EDITOR_SRCS} LINK_LIBRARIES steputils-static stepdai-static stepcore-static)
endif()
# Local Variables:
+18 -9
View File
@@ -528,7 +528,7 @@ int STEPfile::ReadData1( istream & in ) {
} // end while loop
if( _entsNotCreated ) {
sprintf( buf,
snprintf( buf, sizeof(buf),
"STEPfile Reading File: Unable to create %d instances.\n\tIn first pass through DATA section. Check for invalid entity types.\n",
_entsNotCreated );
_error.AppendToUserMsg( buf );
@@ -647,7 +647,7 @@ int STEPfile::ReadData2( istream & in, bool useTechCor ) {
} // end while loop
if( _entsInvalid ) {
sprintf( buf,
snprintf( buf, sizeof(buf),
"%s \n\tTotal instances: %d \n\tInvalid instances: %d \n\tIncomplete instances (includes invalid instances): %d \n\t%s: %d.\n",
"Second pass complete - instance summary:", total_instances,
_entsInvalid, _entsIncomplete, "Warnings",
@@ -743,6 +743,15 @@ int STEPfile::FindHeaderSection( istream & in ) {
return 0;
}
in.getline( buf, BUFSIZ, ';' ); // reads but does not store the ;
if( in.fail() ) {
// A file with no ';' in its first BUFSIZ bytes - a binary file that
// is not STEP at all, say - sets failbit and leaves buf unchanged.
// eof() then never becomes true and the loop above spins forever.
_error.AppendToUserMsg(
"Error: Unable to find HEADER section. File not read.\n" );
_error.GreaterSeverity( SEVERITY_INPUT_ERROR );
return 0;
}
}
return 1;
}
@@ -1321,7 +1330,7 @@ SDAI_Application_instance * STEPfile::ReadInstance( istream & in, ostream & out,
}
} else {
if( node->CurrState() == completeSE ) {
sprintf( errbuf, "WARNING in WORKING FILE: changing instance #%d state from completeSE to incompleteSE.\n", fileid );
snprintf( errbuf, sizeof(errbuf), "WARNING in WORKING FILE: changing instance #%d state from completeSE to incompleteSE.\n", fileid );
_error.AppendToUserMsg( errbuf );
if( _fileType != WORKING_SESSION ) {
node->ChangeState( incompleteSE );
@@ -1643,7 +1652,7 @@ Severity STEPfile::AppendFile( istream * in, bool useTechCor ) {
}
SetFileType( WORKING_SESSION );
} else {
sprintf( errbuf,
snprintf( errbuf, sizeof(errbuf),
"Faulty input at beginning of file. \"ISO-10303-21;\" or"
" \"STEP_WORKING_SESSION;\" expected. File not read: %s\n",
( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) );
@@ -1658,14 +1667,14 @@ Severity STEPfile::AppendFile( istream * in, bool useTechCor ) {
rval = ReadHeader( *in );
cout << "\nHEADER read:";
if( rval < SEVERITY_WARNING ) {
sprintf( errbuf,
snprintf( errbuf, sizeof(errbuf),
"Error: non-recoverable error in reading header section. "
"There were %d errors encountered. Rest of file is ignored.\n",
_errorCount );
_error.AppendToUserMsg( errbuf );
return rval;
} else if( rval != SEVERITY_NULL ) {
sprintf( errbuf, " %d ERRORS\t %d WARNINGS\n\n",
snprintf( errbuf, sizeof(errbuf), " %d ERRORS\t %d WARNINGS\n\n",
_errorCount, _warningCount );
cout << errbuf;
} else {
@@ -1683,7 +1692,7 @@ Severity STEPfile::AppendFile( istream * in, bool useTechCor ) {
cout << "\nFIRST PASS complete: " << total_insts
<< " instances created.\n";
sprintf( errbuf,
snprintf( errbuf, sizeof(errbuf),
" %d ERRORS\t %d WARNINGS\n\n",
_errorCount, _warningCount );
cout << errbuf;
@@ -1727,7 +1736,7 @@ Severity STEPfile::AppendFile( istream * in, bool useTechCor ) {
//check for "ENDSEC;"
ReadTokenSeparator( *in2 );
if( total_insts != valid_insts ) {
sprintf( errbuf, "%d invalid instances in file: %s\n",
snprintf( errbuf, sizeof(errbuf), "%d invalid instances in file: %s\n",
total_insts - valid_insts, ( ( FileName().compare( "-" ) == 0 ) ? "standard input" : FileName().c_str() ) );
_error.AppendToUserMsg( errbuf );
CloseInputFile( in2 );
@@ -1736,7 +1745,7 @@ Severity STEPfile::AppendFile( istream * in, bool useTechCor ) {
cout << "\nSECOND PASS complete: " << valid_insts
<< " instances valid.\n";
sprintf( errbuf,
snprintf( errbuf, sizeof(errbuf),
" %d ERRORS\t %d WARNINGS\n\n",
_errorCount, _warningCount );
_error.AppendToUserMsg( errbuf );
+3 -3
View File
@@ -179,7 +179,7 @@ istream * STEPfile::OpenInputFile( const std::string filename ) {
} else {
if( SetFileName( filename ).empty() && ( filename.compare( "-" ) != 0 ) ) {
char msg[BUFSIZ+1];
sprintf( msg, "Unable to find file for input: \'%s\'. File not read.\n", filename.c_str() );
snprintf( msg, sizeof(msg), "Unable to find file for input: \'%s\'. File not read.\n", filename.c_str() );
_error.AppendToUserMsg( msg );
_error.GreaterSeverity( SEVERITY_INPUT_ERROR );
return( 0 );
@@ -196,7 +196,7 @@ istream * STEPfile::OpenInputFile( const std::string filename ) {
if( !in || !( in -> good() ) ) {
char msg[BUFSIZ+1];
sprintf( msg, "Unable to open file for input: \'%s\'. File not read.\n", filename.c_str() );
snprintf( msg, sizeof(msg), "Unable to open file for input: \'%s\'. File not read.\n", filename.c_str() );
_error.AppendToUserMsg( msg );
_error.GreaterSeverity( SEVERITY_INPUT_ERROR );
return ( 0 );
@@ -231,7 +231,7 @@ ofstream * STEPfile::OpenOutputFile( std::string filename ) {
} else {
if( SetFileName( filename ).empty() ) {
char msg[BUFSIZ+1];
sprintf( msg, "can't find file: %s\nFile not written.\n", filename.c_str() );
snprintf( msg, sizeof(msg), "can't find file: %s\nFile not written.\n", filename.c_str() );
_error.AppendToUserMsg( msg );
_error.GreaterSeverity( SEVERITY_INPUT_ERROR );
}
+24 -3
View File
@@ -2,6 +2,7 @@ set(LAZY_SRCS
lazyDataSectionReader.cc
lazyFileReader.cc
lazyInstMgr.cc
lazySupport.cc
p21HeaderSectionReader.cc
sectionReader.cc
lazyP21DataSectionReader.cc
@@ -13,22 +14,27 @@ include_directories(
${CMAKE_SOURCE_DIR}/include/stepcode
)
set(_libdeps stepcore stepdai steputils stepeditor)
if(BUILD_SHARED_LIBS)
set(_libdeps stepcore stepdai steputils stepeditor)
SC_ADDLIB(steplazyfile SHARED SOURCES ${LAZY_SRCS} LINK_LIBRARIES ${_libdeps})
if(WIN32)
target_compile_definitions(steplazyfile PRIVATE SC_LAZYFILE_DLL_EXPORTS)
endif()
SC_ADDEXEC(lazy_test SOURCES "lazy_test.cc;sc_benchmark.cc" LINK_LIBRARIES steplazyfile stepeditor ${_libdeps} NO_INSTALL)
target_compile_definitions(lazy_test PRIVATE NO_REGISTRY)
endif()
if(BUILD_STATIC_LIBS)
set(_libdeps stepcore-static stepdai-static steputils-static stepeditor-static)
SC_ADDLIB(steplazyfile-static STATIC SOURCES ${LAZY_SRCS} LINK_LIBRARIES ${_libdeps})
if(NOT BUILD_SHARED_LIBS)
SC_ADDEXEC(lazy_test SOURCES "lazy_test.cc;sc_benchmark.cc" LINK_LIBRARIES steplazyfile-static stepeditor-static ${_libdeps} NO_INSTALL)
target_compile_definitions(lazy_test PRIVATE NO_REGISTRY)
endif()
endif()
SC_ADDEXEC(lazy_test SOURCES "lazy_test.cc;sc_benchmark.cc" LINK_LIBRARIES steplazyfile stepeditor NO_INSTALL)
target_compile_definitions(lazy_test PRIVATE NO_REGISTRY)
# Local Variables:
# tab-width: 8
@@ -37,3 +43,18 @@ target_compile_definitions(lazy_test PRIVATE NO_REGISTRY)
# End:
# ex: shiftwidth=2 tabstop=8
if(SC_ENABLE_TESTING)
if(BUILD_SHARED_LIBS)
set(_lazy_index_libs steplazyfile stepeditor stepcore stepdai steputils)
else()
set(_lazy_index_libs steplazyfile-static stepeditor-static
stepcore-static stepdai-static steputils-static)
endif()
SC_ADDEXEC(lazy_index_test SOURCES lazy_index_test.cc
LINK_LIBRARIES ${_lazy_index_libs} NO_INSTALL)
add_test(NAME lazy_index
COMMAND lazy_index_test
${CMAKE_CURRENT_SOURCE_DIR}/test/lazy_index.stp
${CMAKE_CURRENT_SOURCE_DIR}/test/lazy_scope.stp
${CMAKE_CURRENT_SOURCE_DIR}/test/lazy_broken_section.stp)
endif()
+61 -17
View File
@@ -65,6 +65,29 @@
#include <string.h>
#include <stdio.h>
/*
* Judy keys are byte-packed: adjacent slots may start at an address that is
* not suitably aligned for judyvalue. Callers are likewise permitted to
* supply byte buffers without judyvalue alignment. Copy through an aligned
* local value rather than dereferencing a cast pointer. Zero-filling any
* bytes not requested also makes partial loads deterministic.
*/
static judyvalue
judy_load_value( const unsigned char * source, unsigned int size )
{
judyvalue value = 0;
memcpy( &value, source, size );
return value;
}
static void
judy_store_value( unsigned char * destination, judyvalue value,
unsigned int size )
{
memcpy( destination, &value, size );
}
#if defined(STANDALONE) || defined(ASKITIS)
extern unsigned int MaxMem;
@@ -313,7 +336,6 @@ void judy_free( Judy * judy, void * block, int type ) {
// assemble key from current path
unsigned int judy_key( Judy * judy, unsigned char * buff, unsigned int max ) {
judyvalue * dest = ( judyvalue * )buff;
unsigned int len = 0, idx = 0, depth;
int slot, off, type;
judyvalue value;
@@ -333,7 +355,8 @@ unsigned int judy_key( Judy * judy, unsigned char * buff, unsigned int max ) {
if( judy->depth )
if( !( len & JUDY_key_mask ) ) {
dest[depth] = 0;
judy_store_value( buff + depth * JUDY_key_size, 0,
JUDY_key_size );
}
switch( type ) {
@@ -350,9 +373,14 @@ unsigned int judy_key( Judy * judy, unsigned char * buff, unsigned int max ) {
base = ( unsigned char * )( judy->stack[idx].next & JUDY_mask );
if( judy->depth ) {
value = *( judyvalue * )( base + slot * keysize );
value = judy_load_value( base + slot * keysize,
JUDY_key_size );
value &= JudyMask[keysize];
dest[depth++] |= value;
value |= judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
judy_store_value( buff + depth * JUDY_key_size, value,
JUDY_key_size );
depth++;
len += keysize;
if( depth < judy->depth ) {
@@ -383,7 +411,13 @@ unsigned int judy_key( Judy * judy, unsigned char * buff, unsigned int max ) {
case JUDY_radix:
if( judy->depth ) {
dest[depth] |= ( judyvalue )slot << ( JUDY_key_size - ( ++len & JUDY_key_mask ) ) * 8;
value = judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
value |= ( judyvalue )slot <<
( ( JUDY_key_size - ++len ) &
JUDY_key_mask ) * 8;
judy_store_value( buff + depth * JUDY_key_size, value,
JUDY_key_size );
if( !( len & JUDY_key_mask ) ) {
depth++;
}
@@ -419,7 +453,6 @@ unsigned int judy_key( Judy * judy, unsigned char * buff, unsigned int max ) {
// find slot & setup cursor
JudySlot * judy_slot( Judy * judy, const unsigned char * buff, unsigned int max ) {
judyvalue * src = ( judyvalue * )buff;
int slot, size, keysize, tst, cnt;
JudySlot next = *judy->root;
judyvalue value, test = 0;
@@ -463,7 +496,9 @@ JudySlot * judy_slot( Judy * judy, const unsigned char * buff, unsigned int max
value = 0;
if( judy->depth ) {
value = src[depth++];
value = judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
depth++;
off |= JUDY_key_mask;
off++;
value &= JudyMask[keysize];
@@ -478,7 +513,8 @@ JudySlot * judy_slot( Judy * judy, const unsigned char * buff, unsigned int max
// find slot > key
while( slot-- ) {
test = *( judyvalue * )( base + slot * keysize );
test = judy_load_value( base + slot * keysize,
JUDY_key_size );
#if BYTE_ORDER == BIG_ENDIAN
test >>= 8 * ( JUDY_key_size - keysize );
#else
@@ -509,7 +545,9 @@ JudySlot * judy_slot( Judy * judy, const unsigned char * buff, unsigned int max
table = ( JudySlot * )( next & JUDY_mask ); // outer radix
if( judy->depth ) {
slot = ( src[depth] >> ( (( JUDY_key_size - ++off) & JUDY_key_mask ) * 8 ) ) & 0xff;
value = judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
slot = ( value >> ( (( JUDY_key_size - ++off) & JUDY_key_mask ) * 8 ) ) & 0xff;
} else if( off < max ) {
slot = buff[off++];
} else {
@@ -1224,10 +1262,9 @@ void judy_splitspan( Judy * judy, JudySlot * next, unsigned char * base ) {
// judy_cell: add string to judy array
JudySlot * judy_cell( Judy * judy, const unsigned char * buff, unsigned int max ) {
judyvalue * src = ( judyvalue * )buff;
int size, idx, slot, cnt, tst;
JudySlot * next = judy->root;
judyvalue test, value;
judyvalue test = 0, value;
unsigned int off = 0, start;
JudySlot * table;
JudySlot * node;
@@ -1261,7 +1298,9 @@ JudySlot * judy_cell( Judy * judy, const unsigned char * buff, unsigned int max
value = 0;
if( judy->depth ) {
value = src[depth++];
value = judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
depth++;
off |= JUDY_key_mask;
off++;
value &= JudyMask[keysize];
@@ -1276,7 +1315,8 @@ JudySlot * judy_cell( Judy * judy, const unsigned char * buff, unsigned int max
// find slot > key
while( slot-- ) {
test = *( judyvalue * )( base + slot * keysize );
test = judy_load_value( base + slot * keysize,
JUDY_key_size );
#if BYTE_ORDER == BIG_ENDIAN
test >>= 8 * ( JUDY_key_size - keysize );
#else
@@ -1378,7 +1418,9 @@ JudySlot * judy_cell( Judy * judy, const unsigned char * buff, unsigned int max
table = ( JudySlot * )( *next & JUDY_mask ); // outer radix
if( judy->depth ) {
slot = ( src[depth] >> ( ( (JUDY_key_size - ++off) & JUDY_key_mask ) * 8 ) ) & 0xff;
value = judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
slot = ( value >> ( ( (JUDY_key_size - ++off) & JUDY_key_mask ) * 8 ) ) & 0xff;
} else if( off < max ) {
slot = buff[off++];
} else {
@@ -1464,7 +1506,8 @@ JudySlot * judy_cell( Judy * judy, const unsigned char * buff, unsigned int max
// fill in slot 0 with bytes of key
if( judy->depth ) {
value = src[depth];
value = judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
#if BYTE_ORDER != BIG_ENDIAN
memcpy( base, &value, keysize ); // copy new key into slot
#else
@@ -1542,7 +1585,9 @@ JudySlot * judy_cell( Judy * judy, const unsigned char * buff, unsigned int max
// fill in slot 0 with bytes of key
*( judyvalue * )base = src[depth];
value = judy_load_value( buff + depth * JUDY_key_size,
JUDY_key_size );
judy_store_value( base, value, JUDY_key_size );
if( judy->level < judy->max ) {
judy->level++;
@@ -1562,4 +1607,3 @@ JudySlot * judy_cell( Judy * judy, const unsigned char * buff, unsigned int max
#endif
return next;
}
+2 -2
View File
@@ -6,7 +6,7 @@
lazyDataSectionReader::lazyDataSectionReader( lazyFileReader * parent, std::ifstream & file,
std::streampos start, sectionID sid ):
sectionReader( parent, file, start, sid ) {
_sectionIdentifier = ""; //FIXME set _sectionIdentifier from the data section identifier (2002 rev of Part 21), if present
_sectionIdentifier = ""; //FIXME retain the data section identifier (2002 revision of Part 21)
_error = false;
_completelyLoaded = false;
}
+49 -6
View File
@@ -19,20 +19,44 @@ void lazyFileReader::initP21() {
}
_parent->registerDataSection( r );
if( _parent->cancelled() ) break;
//check for new data section (DATA) or end of file (END-ISO-10303-21;)
while( isspace( _file.peek() ) && _file.good() ) {
_file.ignore( 1 );
for( ;; ) {
while( isspace( _file.peek() ) && _file.good() ) _file.ignore( 1 );
if( _file.peek() != '/' ) break;
std::streampos comment = _file.tellg();
_file.get();
if( _file.peek() != '*' ) {
_file.seekg( comment );
break;
}
_file.get();
int previous = 0;
int current = 0;
while( _file.good() ) {
current = _file.get();
if( previous == '*' && current == '/' ) break;
previous = current;
}
}
std::streampos nextSection = _file.tellg();
if( needKW( "END-ISO-10303-21;" ) ) {
break;
} else if( !needKW( "DATA" ) ) {
}
_file.clear();
_file.seekg( nextSection );
if( !needKW( "DATA" ) ) {
std::cerr << "Corrupted file - did not find new data section (\"DATA\") or end of file (\"END-ISO-10303-21;\") at offset " << _file.tellg() << std::endl;
break;
}
_file.clear();
_file.seekg( nextSection );
}
}
bool lazyFileReader::needKW( const char * kw ) {
std::streampos start = _file.tellg();
const char * c = kw;
bool found = true;
while( *c ) {
@@ -42,6 +66,10 @@ bool lazyFileReader::needKW( const char * kw ) {
}
c++;
}
if( !found ) {
_file.clear();
_file.seekg( start );
}
return found;
}
@@ -49,11 +77,26 @@ instancesLoaded_t * lazyFileReader::getHeaderInstances() {
return _header->getInstances();
}
lazyFileReader::lazyFileReader( std::string fname, lazyInstMgr * i, fileID fid ): _fileName( fname ), _parent( i ), _fileID( fid ) {
lazyFileReader::lazyFileReader( std::string fname, lazyInstMgr * i, fileID fid ): _fileName( fname ), _parent( i ),
_header( 0 ), _schemaWarningClaimed( false ), _fileID( fid ),
_fileSize( 0 ), _valid( false ) {
_file.open( _fileName.c_str(), std::ios::binary );
_file.imbue( std::locale::classic() );
_file.unsetf( std::ios_base::skipws );
assert( _file.is_open() && _file.good() );
if( !_file.is_open() || !_file.good() ) {
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_FATAL;
diagnostic.message = "unable to open exchange file: " + _fileName;
_parent->emitDiagnostic( diagnostic );
return;
}
_file.seekg( 0, std::ios::end );
std::streampos end = _file.tellg();
if( end != std::streampos( -1 ) ) {
_fileSize = static_cast<lazyFileOffset>( static_cast<std::streamoff>( end ) );
}
_file.seekg( 0, std::ios::beg );
detectType();
switch( _fileType ) {
@@ -67,9 +110,9 @@ lazyFileReader::lazyFileReader( std::string fname, lazyInstMgr * i, fileID fid )
std::cerr << "Reached default case, " << __FILE__ << ":" << __LINE__ << std::endl;
abort();
}
_valid = true;
}
lazyFileReader::~lazyFileReader() {
delete _header;
}
+434 -40
View File
@@ -8,22 +8,35 @@
#include "clstepcore/sdaiApplication_instance.h"
#include <algorithm>
#include <climits>
#include <sstream>
lazyInstMgr::lazyInstMgr() {
_headerRegistry = new Registry( HeaderSchemaInit );
_instanceTypes = new instanceTypes_t( 255 ); //NOTE arbitrary max of 255 chars for a type name
_lazyInstanceCount = 0;
_loadedInstanceCount = 0;
_cacheHighWater = 0;
_cacheHits = 0;
_cacheMisses = 0;
_materializations = 0;
_evictions = 0;
_activeBatches = 0;
_residentSourceBytes = 0;
_sourceBytesHighWater = 0;
_batchLoadDepth = 0;
_cancelled = false;
_longestTypeNameLen = 0;
_mainRegistry = 0;
_ownsMainRegistry = false;
_errors = new ErrorDescriptor();
_ima = new instMgrAdapter( this );
}
lazyInstMgr::~lazyInstMgr() {
delete _headerRegistry;
delete _errors;
delete _ima;
//loop over files, sections, instances; delete header instances
// Keep registries and the adapter alive until their instances are gone.
_instancesLoaded.clear( true );
lazyFileReaderVec_t::iterator fit = _files.begin();
for( ; fit != _files.end(); ++fit ) {
delete *fit;
@@ -32,8 +45,12 @@ lazyInstMgr::~lazyInstMgr() {
for( ; sit != _dataSections.end(); ++sit ) {
delete *sit;
}
_instancesLoaded.clear();
_instanceStreamPos.clear();
delete _instanceTypes;
delete _ima;
if( _ownsMainRegistry ) delete _mainRegistry;
delete _headerRegistry;
delete _errors;
}
sectionID lazyInstMgr::registerDataSection( lazyDataSectionReader * sreader ) {
@@ -44,24 +61,44 @@ sectionID lazyInstMgr::registerDataSection( lazyDataSectionReader * sreader ) {
void lazyInstMgr::addLazyInstance( namedLazyInstance inst ) {
_lazyInstanceCount++;
assert( inst.loc.begin > 0 && inst.loc.instance > 0 );
const bool duplicate = _instanceStreamPos.find( inst.loc.instance ) != 0;
int len = strlen( inst.name );
if( len > _longestTypeNameLen ) {
_longestTypeNameLen = len;
_longestTypeName = inst.name;
}
_instanceTypes->insert( inst.name, inst.loc.instance );
/* store 16 bits of section id and 48 of instance offset into one 64-bit int
** TODO: check and warn if anything is lost (in calling code?)
** does 32bit need anything special?
**
** create conversion class?
** could then initialize conversion object with number of bits
** also a good place to check for data loss
*/
positionAndSection ps = inst.loc.section;
ps <<= 48;
ps |= ( inst.loc.begin & 0xFFFFFFFFFFFFULL );
_instanceStreamPos.insert( inst.loc.instance, ps );
if( inst.componentTypes ) {
if( !duplicate && !inst.componentTypes->empty() ) {
_instanceComponentTypes[inst.loc.instance] = *inst.componentTypes;
}
std::vector<std::string>::const_iterator type = inst.componentTypes->begin();
for( ; type != inst.componentTypes->end(); ++type ) {
_instanceTypes->insert( type->c_str(), inst.loc.instance );
if( static_cast<int>( type->size() ) > _longestTypeNameLen ) {
_longestTypeNameLen = type->size();
_longestTypeName = *type;
}
}
delete inst.componentTypes;
}
if( !duplicate ) _allInstances.push_back( inst.loc.instance );
instancePosition pos;
pos.begin = inst.loc.begin;
pos.section = inst.loc.section;
if( duplicate ) {
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_WARNING;
diagnostic.entity = inst.loc.instance;
diagnostic.type = inst.name;
diagnostic.offset = inst.loc.begin;
diagnostic.message = "duplicate instance identifier";
emitDiagnostic( diagnostic );
}
_instanceStreamPos.insert( inst.loc.instance, pos );
if( !duplicate && inst.loc.end >= inst.loc.begin ) {
_instanceSourceBytes[inst.loc.instance] = inst.loc.end - inst.loc.begin;
}
if( inst.refs ) {
if( inst.refs->size() > 0 ) {
@@ -72,10 +109,103 @@ void lazyInstMgr::addLazyInstance( namedLazyInstance inst ) {
//reverse refs
_revInstanceRefs.insert( *it, inst.loc.instance );
}
} else {
delete inst.refs;
}
delete inst.refs;
}
}
const std::vector<std::string> & lazyInstMgr::componentTypes( instanceID id ) const {
static const std::vector<std::string> empty;
std::map<instanceID, std::vector<std::string> >::const_iterator found =
_instanceComponentTypes.find( id );
return found == _instanceComponentTypes.end() ? empty : found->second;
}
LazyInstanceIdView lazyInstMgr::instancesByType( std::string type, bool caseSensitive ) {
if( !caseSensitive ) {
std::string::iterator it = type.begin();
for( ; it != type.end(); ++it ) {
*it = static_cast<char>( toupper( static_cast<unsigned char>( *it ) ) );
}
}
return LazyInstanceIdView( _instanceTypes->find( type.c_str() ) );
}
LazyInstanceIdView lazyInstMgr::forwardReferences( instanceID id ) {
return LazyInstanceIdView( _fwdInstanceRefs.find( id ) );
}
LazyInstanceIdView lazyInstMgr::reverseReferences( instanceID id ) {
return LazyInstanceIdView( _revInstanceRefs.find( id ) );
}
std::string lazyInstMgr::sourceRecord( instanceID id ) {
instanceStreamPos_t::cvector * positions = _instanceStreamPos.find( id );
std::map<instanceID, uint64_t>::const_iterator bytes = _instanceSourceBytes.find( id );
if( !positions || positions->size() != 1 || bytes == _instanceSourceBytes.end() ||
bytes->second == 0 ) {
return std::string();
}
const instancePosition & position = positions->front();
if( position.section >= _dataSections.size() || !_dataSections[position.section] ) {
return std::string();
}
return _dataSections[position.section]->sourceRecord( position.begin, bytes->second );
}
void lazyInstMgr::observeScan( fileID file, lazyFileOffset offset, lazyFileOffset fileSize ) {
if( _cancelled ) return;
if( _cancellationCallback && _cancellationCallback() ) {
_cancelled = true;
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_INFO;
diagnostic.offset = offset;
diagnostic.message = "scan cancelled";
emitDiagnostic( diagnostic );
return;
}
if( _progressCallback && ( _lazyInstanceCount == 1 || ( _lazyInstanceCount % 4096 ) == 0 || offset >= fileSize ) ) {
LazyScanProgress progress;
progress.file = file;
progress.offset = offset;
progress.fileSize = fileSize;
progress.instancesScanned = _lazyInstanceCount;
_progressCallback( progress );
}
}
void lazyInstMgr::emitDiagnostic( LazyDiagnostic diagnostic ) {
std::ostringstream key;
key << diagnostic.type << '#' << diagnostic.entity << ':' << diagnostic.attribute << ':' << diagnostic.message;
uint64_t & count = _diagnosticCounts[key.str()];
++count;
diagnostic.occurrences = count;
if( _diagnosticCallback && count == 1 ) {
_diagnosticCallback( diagnostic );
}
}
uint64_t lazyInstMgr::diagnosticCount( const std::string & key ) const {
std::map<std::string, uint64_t>::const_iterator found = _diagnosticCounts.find( key );
return found == _diagnosticCounts.end() ? 0 : found->second;
}
LazyCacheStatistics lazyInstMgr::cacheStatistics() const {
LazyCacheStatistics stats;
stats.instancesScanned = _lazyInstanceCount;
stats.instancesLoaded = _loadedInstanceCount;
stats.instancesPinned = _pinCounts.size();
stats.cacheHighWater = _cacheHighWater;
stats.cacheHits = _cacheHits;
stats.cacheMisses = _cacheMisses;
stats.materializations = _materializations;
stats.evictions = _evictions;
stats.activeBatches = _activeBatches;
stats.dataSections = _dataSections.size();
stats.residentSourceBytes = _residentSourceBytes;
stats.sourceBytesHighWater = _sourceBytesHighWater;
stats.cancelled = _cancelled;
return stats;
}
unsigned long lazyInstMgr::getNumTypes() const {
@@ -93,7 +223,7 @@ unsigned long lazyInstMgr::getNumTypes() const {
return n ;
}
void lazyInstMgr::openFile( std::string fname ) {
bool lazyInstMgr::openFile( std::string fname ) {
//don't want to hold a lock for the entire time we're reading the file.
//create a place in the vector and remember its location, then free lock
///FIXME begin atomic op
@@ -101,58 +231,323 @@ void lazyInstMgr::openFile( std::string fname ) {
_files.push_back( (lazyFileReader * ) 0 );
///FIXME end atomic op
lazyFileReader * lfr = new lazyFileReader( fname, this, i );
if( !lfr->valid() ) {
delete lfr;
_files.pop_back();
return false;
}
_files[i] = lfr;
validateReferences();
/// TODO resolve inverse attr references
//between instances, or eDesc --> inst????
return true;
}
SDAI_Application_instance * lazyInstMgr::loadInstance( instanceID id, bool reSeek ) {
void lazyInstMgr::validateReferences() {
instanceRefs_t::cpair current = _fwdInstanceRefs.begin();
while( current.value ) {
instanceRefs_t::cvector::const_iterator ref = current.value->begin();
for( ; ref != current.value->end(); ++ref ) {
if( !_instanceStreamPos.find( *ref ) ) {
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = current.key;
const char * type = typeFromFile( current.key );
if( type ) diagnostic.type = type;
instanceStreamPos_t::cvector * positions = _instanceStreamPos.find( current.key );
if( positions && !positions->empty() ) diagnostic.offset = positions->front().begin;
std::ostringstream message;
message << "reference to missing instance #" << *ref;
diagnostic.message = message.str();
emitDiagnostic( diagnostic );
}
}
current = _fwdInstanceRefs.next();
}
}
SDAI_Application_instance * lazyInstMgr::loadInstance( instanceID id, bool reSeek, bool promoteCached ) {
if( _batchLoadDepth && ( _cancelled ||
( _cancellationCallback && _cancellationCallback() ) ) ) {
_cancelled = true;
return 0;
}
assert( _mainRegistry && "Main registry has not been initialized. Do so with initRegistry() or setRegistry()." );
std::streampos oldPos;
positionAndSection ps;
sectionID sid;
instancePosition pos = instancePosition();
SDAI_Application_instance * inst = _instancesLoaded.find( id );
if( inst ) {
++_cacheHits;
if( promoteCached && _batchLoadDepth == 0 ) _permanentlyLoadedInstances.insert( id );
return inst;
}
++_cacheMisses;
if( id > static_cast<instanceID>( INT_MAX ) ) {
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = id;
diagnostic.message = "instance ID exceeds the current SDAI materialization limit";
emitDiagnostic( diagnostic );
return 0;
}
const std::pair<std::set<instanceID>::iterator, bool> loading_result = _instancesLoading.insert( id );
if( !loading_result.second ) {
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = id;
const char * type = typeFromFile( id );
if( type ) diagnostic.type = type;
instanceStreamPos_t::cvector * positions = _instanceStreamPos.find( id );
if( positions && !positions->empty() ) {
diagnostic.offset = positions->front().begin;
}
diagnostic.message = "cyclic dependency encountered while materializing instance";
emitDiagnostic( diagnostic );
return 0;
}
/* sectionReader materializes referenced instances recursively. Keep an
* explicit in-progress set because an instance is not cacheable until its
* STEPread() has completed. */
class LoadingGuard {
public:
LoadingGuard( std::set<instanceID> & loading, instanceID id )
: _loading( loading ), _id( id ), _active( true ) {}
~LoadingGuard() { release(); }
void release() {
if( _active ) {
_loading.erase( _id );
_active = false;
}
}
private:
std::set<instanceID> & _loading;
instanceID _id;
bool _active;
} loading_guard( _instancesLoading, id );
instanceStreamPos_t::cvector * cv;
if( 0 != ( cv = _instanceStreamPos.find( id ) ) ) {
switch( cv->size() ) {
case 0:
std::cerr << "Instance #" << id << " not found in any section." << std::endl;
if( !_diagnosticCallback ) {
std::cerr << "Instance #" << id
<< " not found in any section." << std::endl;
}
break;
case 1:
long int off;
ps = cv->at( 0 );
off = ps & 0xFFFFFFFFFFFFULL;
sid = ps >> 48;
assert( _dataSections.size() > sid );
if( reSeek ) {
oldPos = _dataSections[sid]->tellg();
pos = cv->at( 0 );
//not an assert: a section that failed part way through its scan has
//already indexed the instances before the error under its id, and is
//then never registered. Those entries are bad input, not a programming
//error, and in Release the assert was compiled out and this indexed an
//empty vector. Same test sourceRecord() already makes.
if( pos.section >= _dataSections.size() || !_dataSections[pos.section] ) {
if( !_diagnosticCallback ) {
std::cerr << "Instance #" << id << " names data section "
<< pos.section << ", which failed to index." << std::endl;
}
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = id;
diagnostic.offset = pos.begin;
diagnostic.message = "instance belongs to a DATA section that failed to index";
emitDiagnostic( diagnostic );
break;
}
inst = _dataSections[sid]->getRealInstance( _mainRegistry, off, id );
if( reSeek ) {
_dataSections[sid]->seekg( oldPos );
oldPos = _dataSections[pos.section]->tellg();
}
inst = _dataSections[pos.section]->getRealInstance( _mainRegistry, pos.begin, id );
if( reSeek ) {
_dataSections[pos.section]->seekg( oldPos );
}
/* A recursive reference load may observe cancellation after
* this instance began materializing. Never cache that
* partially resolved SDAI object. */
if( _batchLoadDepth && ( _cancelled ||
( _cancellationCallback && _cancellationCallback() ) ) ) {
_cancelled = true;
if( !isNilSTEPentity( inst ) ) delete inst;
inst = 0;
}
break;
default:
std::cerr << "Instance #" << id << " exists in multiple sections. This is not yet supported." << std::endl;
if( !_diagnosticCallback ) {
std::cerr << "Instance #" << id
<< " exists in multiple sections. This is not yet supported."
<< std::endl;
}
{
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = id;
diagnostic.message = "instance identifier occurs in multiple DATA sections";
emitDiagnostic( diagnostic );
}
break;
}
if( !isNilSTEPentity( inst ) ) {
/* Explicit attributes are now complete. Inverse discovery may
* safely revisit this instance without tripping the materialization
* cycle detector. */
loading_guard.release();
/* isNilSTEPentity( 0 ) is false, so a null has to be tested for
* separately - every branch of the switch above can leave one. */
if( inst && !isNilSTEPentity( inst ) ) {
_instancesLoaded.insert( id, inst );
_loadedInstanceCount++;
lazyRefs lr( this, inst );
lazyRefs::referentInstances_t insts = lr.result();
++_materializations;
_cacheHighWater = std::max<uint64_t>( _cacheHighWater, _loadedInstanceCount );
std::map<instanceID, uint64_t>::const_iterator source_size = _instanceSourceBytes.find( id );
if( source_size != _instanceSourceBytes.end() ) {
_residentSourceBytes += source_size->second;
_sourceBytesHighWater = std::max<uint64_t>( _sourceBytesHighWater,
_residentSourceBytes );
}
if( _batchLoadDepth ) {
_batchOwnedInstances.insert( id );
} else {
_permanentlyLoadedInstances.insert( id );
lazyRefs lr( this, inst );
lazyRefs::referentInstances_t insts = lr.result();
resolveDeferredInverses();
}
} else {
std::cerr << "Error loading instance #" << id << "." << std::endl;
if( !_diagnosticCallback ) {
std::cerr << "Error loading instance #" << id << "."
<< std::endl;
}
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = id;
diagnostic.offset = pos.begin;
diagnostic.message = "SDAI instance materialization failed";
emitDiagnostic( diagnostic );
}
} else {
std::cerr << "Instance #" << id << " not found in any section." << std::endl;
if( !_diagnosticCallback ) {
std::cerr << "Instance #" << id
<< " not found in any section." << std::endl;
}
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = id;
diagnostic.message = "instance not found in any DATA section";
emitDiagnostic( diagnostic );
}
return inst;
}
void lazyInstMgr::resolveDeferredInverses() {
if( !_instancesLoading.empty() ) return;
while( !_deferredInverseInstances.empty() ) {
std::set<instanceID> deferred;
deferred.swap( _deferredInverseInstances );
std::set<instanceID>::const_iterator id = deferred.begin();
for( ; id != deferred.end(); ++id ) {
SDAI_Application_instance * inst = cachedInstance( *id );
if( inst ) {
lazyRefs refs( this, inst );
}
}
}
}
SDAI_Application_instance * lazyInstMgr::cachedInstance( instanceID id ) {
return _instancesLoaded.find( id );
}
std::vector<instanceID> lazyInstMgr::dependencyClosure( const std::vector<instanceID> & roots ) {
std::set<instanceID> closure( roots.begin(), roots.end() );
std::vector<instanceID> queue( roots.begin(), roots.end() );
size_t current = 0;
while( current < queue.size() ) {
if( _cancelled || ( _cancellationCallback && _cancellationCallback() ) ) {
_cancelled = true;
break;
}
instanceRefs_t::cvector * refs = _fwdInstanceRefs.find( queue[current++] );
if( !refs ) continue;
instanceRefs_t::cvector::const_iterator ref = refs->begin();
for( ; ref != refs->end(); ++ref ) {
/* validateReferences() reports nonexistent targets when the file
* is opened. They cannot be pinned or materialized, and adding
* them to a batch only produces a second, less useful error (or
* an integer-limit error for a malformed maximum-width ID). */
if( !_instanceStreamPos.find( *ref ) ) continue;
if( closure.insert( *ref ).second ) queue.push_back( *ref );
}
}
return std::vector<instanceID>( closure.begin(), closure.end() );
}
LazyInstanceBatch lazyInstMgr::loadBatch( instanceID root ) {
std::vector<instanceID> roots( 1, root );
return loadBatch( roots );
}
LazyInstanceBatch lazyInstMgr::loadBatch( const std::vector<instanceID> & roots ) {
std::vector<instanceID> closure = dependencyClosure( roots );
std::vector<instanceID>::const_iterator id = closure.begin();
for( ; id != closure.end(); ++id ) {
if( _cancelled || ( _cancellationCallback && _cancellationCallback() ) ) {
_cancelled = true;
closure.erase( id, closure.end() );
break;
}
++_pinCounts[*id];
}
++_activeBatches;
++_batchLoadDepth;
for( id = closure.begin(); id != closure.end(); ++id ) {
if( _cancelled || ( _cancellationCallback && _cancellationCallback() ) ) {
_cancelled = true;
break;
}
if( !loadInstance( *id, true ) ) {
LazyDiagnostic diagnostic;
diagnostic.severity = LAZY_DIAGNOSTIC_ERROR;
diagnostic.entity = *id;
diagnostic.message = "dependency batch could not materialize instance";
emitDiagnostic( diagnostic );
}
}
--_batchLoadDepth;
return LazyInstanceBatch( this, roots, closure );
}
void lazyInstMgr::releaseBatch( const std::vector<instanceID> & instances ) {
std::vector<instanceID> evict;
std::vector<instanceID>::const_iterator id = instances.begin();
for( ; id != instances.end(); ++id ) {
std::map<instanceID, size_t>::iterator pin = _pinCounts.find( *id );
if( pin == _pinCounts.end() ) continue;
if( --pin->second == 0 ) {
_pinCounts.erase( pin );
if( _batchOwnedInstances.count( *id ) && !_permanentlyLoadedInstances.count( *id ) ) {
evict.push_back( *id );
}
}
}
std::vector<SDAI_Application_instance *> deleted;
for( id = evict.begin(); id != evict.end(); ++id ) {
SDAI_Application_instance * inst = _instancesLoaded.find( *id );
if( !inst ) continue;
_instancesLoaded.removeEntry( *id );
_batchOwnedInstances.erase( *id );
deleted.push_back( inst );
--_loadedInstanceCount;
std::map<instanceID, uint64_t>::const_iterator source_size = _instanceSourceBytes.find( *id );
if( source_size != _instanceSourceBytes.end() ) {
_residentSourceBytes = source_size->second <= _residentSourceBytes ?
_residentSourceBytes - source_size->second : 0;
}
++_evictions;
}
std::vector<SDAI_Application_instance *>::iterator inst = deleted.begin();
for( ; inst != deleted.end(); ++inst ) delete *inst;
if( _activeBatches ) --_activeBatches;
}
instanceSet * lazyInstMgr::instanceDependencies( instanceID id ) {
instanceSet * checkedDependencies = new instanceSet();
@@ -182,4 +577,3 @@ instanceSet * lazyInstMgr::instanceDependencies( instanceID id ) {
return checkedDependencies;
}
+92 -5
View File
@@ -3,13 +3,60 @@
#include "cllazyfile/lazyP21DataSectionReader.h"
#include "cllazyfile/lazyInstMgr.h"
void lazyP21DataSectionReader::findSectionStart() {
std::streampos keywordEnd = findNormalString( "DATA" );
if( keywordEnd == std::streampos( -1 ) ) {
_sectionStart = -1;
return;
}
_file.seekg( keywordEnd );
skipWS();
int depth = 0;
int current = _file.get();
if( current == ';' ) {
_sectionStart = _file.tellg();
return;
}
if( current != '(' ) {
_sectionStart = -1;
return;
}
depth = 1;
while( depth && _file.good() ) {
current = _file.get();
if( current == '\'' ) {
_file.seekg( _file.tellg() - std::streampos( 1 ) );
GetLiteralStr( _file, _lazyFile->getInstMgr()->getErrorDesc() );
} else if( current == '/' && _file.peek() == '*' ) {
findNormalString( "*/" );
} else if( current == '(' ) {
++depth;
} else if( current == ')' ) {
--depth;
}
}
skipWS();
if( depth == 0 && _file.get() == ';' ) {
_sectionStart = _file.tellg();
} else {
_sectionStart = -1;
}
}
lazyP21DataSectionReader::lazyP21DataSectionReader( lazyFileReader * parent, std::ifstream & file,
std::streampos start, sectionID sid ):
lazyDataSectionReader( parent, file, start, sid ) {
findSectionStart();
if( _sectionStart == std::streampos( -1 ) ) {
_error = true;
return;
}
namedLazyInstance nl;
while( nl = nextInstance(), ( ( nl.loc.begin > 0 ) && ( nl.name != 0 ) ) ) {
parent->getInstMgr()->addLazyInstance( nl );
parent->getInstMgr()->observeScan( parent->ID(),
static_cast<lazyFileOffset>( static_cast<std::streamoff>( _file.tellg() ) ), parent->fileSize() );
if( parent->getInstMgr()->cancelled() ) return;
}
if( sectionReader::_error->severity() <= SEVERITY_WARNING ) {
@@ -48,27 +95,67 @@ const namedLazyInstance lazyP21DataSectionReader::nextInstance() {
std::streampos end = -1;
namedLazyInstance i;
i.name = 0;
i.refs = 0;
i.componentTypes = 0;
i.loc.section = 0;
i.loc.begin = _file.tellg();
i.loc.end = 0;
std::streampos start = _file.tellg();
i.loc.begin = start == std::streampos( -1 ) ? 0 :
static_cast<lazyFileOffset>( static_cast<std::streamoff>( start ) );
i.loc.instance = readInstanceNumber();
if( ( _file.good() ) && ( i.loc.instance > 0 ) ) {
skipWS();
if( !skipTokenSeparators() ) {
_file.setstate( std::ios::failbit );
}
if( _file.good() && _file.peek() == '&' && !indexScope() ) {
_file.setstate( std::ios::failbit );
}
if( _file.good() && !skipTokenSeparators() ) {
_file.setstate( std::ios::failbit );
}
i.loc.section = _sectionID;
i.name = getDelimitedKeyword( ";( /\\" );
if( _file.good() ) i.name = getDelimitedKeyword( ";( /\\" );
if( _file.good() ) {
end = seekInstanceEnd( & i.refs );
if( i.name[0] == '\0' ) i.componentTypes = new std::vector<std::string>;
end = seekInstanceEnd( & i.refs, i.componentTypes );
if( end != std::streampos( -1 ) ) {
i.loc.end = static_cast<lazyFileOffset>( static_cast<std::streamoff>( end ) );
}
}
}
if( ( i.loc.instance == 0 ) || ( !_file.good() ) || ( end == ( std::streampos ) - 1 ) ) {
//invalid instance, so clear everything
_file.seekg( i.loc.begin );
i.loc.begin = -1;
i.loc.begin = 0;
if( i.refs ) {
delete i.refs;
i.refs = 0;
}
if( i.componentTypes ) {
delete i.componentTypes;
i.componentTypes = 0;
}
i.name = 0;
}
return i;
}
bool lazyP21DataSectionReader::indexScope() {
if( !skipTokenSeparators() || _file.get() != '&' ) return false;
const std::string scopeKeyword = getDelimitedKeyword( "#/(;\\" );
if( scopeKeyword != "SCOPE" ) return false;
bool haveInstance = false;
while( skipTokenSeparators() && _file.peek() == '#' ) {
namedLazyInstance nested = nextInstance();
if( nested.loc.begin == 0 || !nested.name ) return false;
_lazyFile->getInstMgr()->addLazyInstance( nested );
haveInstance = true;
}
if( !haveInstance || !skipTokenSeparators() ) return false;
const std::string endKeyword = getDelimitedKeyword( "/#(;\\" );
if( endKeyword != "ENDSCOPE" ) return false;
return skipScopeExportList();
}
+28 -3
View File
@@ -87,6 +87,16 @@ class SC_LAZYFILE_EXPORT lazyRefs {
}
//3c - for each item in both _refMap and edL, add it to _referentInstances
potentialReferentInsts( edL );
/* Do not partially populate an inverse attribute and then repeat
* the whole pass. A referrer that is still being parsed cannot
* be inspected reliably, so defer this instance as a unit. */
referentInstances_t::const_iterator pending = _referentInstances.begin();
for( ; pending != _referentInstances.end(); ++pending ) {
if( _lim->isMaterializing( *pending ) ) {
_lim->deferInverseResolution( _id );
return;
}
}
//3d - load each inst
iAstruct ias = invAttr( _inst, ia );
referentInstances_t::iterator insts = _referentInstances.begin();
@@ -98,7 +108,12 @@ class SC_LAZYFILE_EXPORT lazyRefs {
void loadInstIFFreferent( instanceID inst, iAstruct ias, const Inverse_attribute * ia ) {
bool prevLoaded = _lim->isLoaded( inst );
if( _lim->isMaterializing( inst ) ) {
_lim->deferInverseResolution( _id );
return;
}
SDAI_Application_instance * rinst = _lim->loadInstance( inst );
if( !rinst ) return;
bool ref = refersToCurrentInst( ia, rinst );
if( ref ) {
if( ia->inverted_attr_()->IsAggrType() ) {
@@ -108,8 +123,16 @@ class SC_LAZYFILE_EXPORT lazyRefs {
assert( invAttr( _inst, ia ).a == ias.a );
}
EntityAggregate * ea = ias.a;
//TODO check if duplicate
ea->AddNode( new EntityNode( rinst ) );
bool duplicate = false;
EntityNode * existing = static_cast<EntityNode *>( ea->GetHead() );
while( existing ) {
if( existing->node == rinst ) {
duplicate = true;
break;
}
existing = static_cast<EntityNode *>( existing->NextNode() );
}
if( !duplicate ) ea->AddNode( new EntityNode( rinst ) );
} else {
SDAI_Application_instance * ai = ias.i;
if( !ai ) {
@@ -131,8 +154,10 @@ class SC_LAZYFILE_EXPORT lazyRefs {
///3e - check if actually inverse ref
bool refersToCurrentInst( const Inverse_attribute * ia, SDAI_Application_instance * referrer ) {
if( !referrer ) return false;
//find the attr
int rindex = attrIndex( referrer, ia->_inverted_attr_id, ia->_inverted_entity_id );
if( rindex < 0 ) return false;
STEPattribute sa = referrer->attributes[ rindex ];
assert( sa.getADesc()->BaseType() == ENTITY_TYPE );
bool found = false;
@@ -279,7 +304,7 @@ class SC_LAZYFILE_EXPORT lazyRefs {
// 1. find inverse attrs with recursion
getInverseAttrs( ai->eDesc, _iaList );
getInverseAttrs( _inst->eDesc, _iaList );
//2. find reverse refs, map id to type (stop if there are no inverse attrs or no refs)
if( _iaList.size() == 0 || !mapRefsToTypes() ) {
+106
View File
@@ -0,0 +1,106 @@
#include <stdexcept>
#include <utility>
#include <ostream>
#include "cllazyfile/lazySupport.h"
#include "cllazyfile/lazyInstMgr.h"
namespace {
const instanceRefs & emptyIds() {
static const instanceRefs ids;
return ids;
}
}
LazyInstanceIdView::const_iterator LazyInstanceIdView::begin() const {
return _ids ? _ids->begin() : emptyIds().begin();
}
LazyInstanceIdView::const_iterator LazyInstanceIdView::end() const {
return _ids ? _ids->end() : emptyIds().end();
}
size_t LazyInstanceIdView::size() const {
return _ids ? _ids->size() : 0;
}
bool LazyInstanceIdView::empty() const {
return size() == 0;
}
instanceID LazyInstanceIdView::operator[]( size_t index ) const {
if( !_ids || index >= _ids->size() ) {
throw std::out_of_range( "LazyInstanceIdView index" );
}
return ( *_ids )[index];
}
LazyInstanceBatch::LazyInstanceBatch(): _manager( 0 ) {}
LazyInstanceBatch::LazyInstanceBatch( lazyInstMgr * manager,
const std::vector<instanceID> & roots, const std::vector<instanceID> & instances ):
_manager( manager ), _roots( roots ), _instances( instances ) {}
LazyInstanceBatch::~LazyInstanceBatch() {
release();
}
LazyInstanceBatch::LazyInstanceBatch( LazyInstanceBatch && other ):
_manager( other._manager ), _roots( std::move( other._roots ) ),
_instances( std::move( other._instances ) ) {
other._manager = 0;
}
LazyInstanceBatch & LazyInstanceBatch::operator=( LazyInstanceBatch && other ) {
if( this != &other ) {
release();
_manager = other._manager;
_roots = std::move( other._roots );
_instances = std::move( other._instances );
other._manager = 0;
}
return *this;
}
bool LazyInstanceBatch::valid() const {
return _manager != 0;
}
void LazyInstanceBatch::release() {
if( _manager ) {
_manager->releaseBatch( _instances );
_manager = 0;
}
_roots.clear();
_instances.clear();
}
const std::vector<instanceID> & LazyInstanceBatch::roots() const {
return _roots;
}
const std::vector<instanceID> & LazyInstanceBatch::instances() const {
return _instances;
}
SDAI_Application_instance * LazyInstanceBatch::get( instanceID id ) const {
return _manager ? _manager->cachedInstance( id ) : 0;
}
LazyTextDiagnosticAdapter::LazyTextDiagnosticAdapter( std::ostream & stream ): _stream( &stream ) {}
void LazyTextDiagnosticAdapter::operator()( const LazyDiagnostic & diagnostic ) const {
const char * label = "info";
if( diagnostic.severity == LAZY_DIAGNOSTIC_WARNING ) label = "warning";
if( diagnostic.severity == LAZY_DIAGNOSTIC_ERROR ) label = "error";
if( diagnostic.severity == LAZY_DIAGNOSTIC_FATAL ) label = "fatal";
( *_stream ) << label;
if( diagnostic.entity ) ( *_stream ) << " #" << diagnostic.entity;
if( !diagnostic.type.empty() ) ( *_stream ) << " " << diagnostic.type;
if( diagnostic.offset ) ( *_stream ) << " at offset " << diagnostic.offset;
if( diagnostic.line ) ( *_stream ) << " line " << diagnostic.line;
if( !diagnostic.attribute.empty() ) ( *_stream ) << " attribute " << diagnostic.attribute;
( *_stream ) << ": " << diagnostic.message;
if( diagnostic.occurrences > 1 ) ( *_stream ) << " (" << diagnostic.occurrences << " occurrences)";
( *_stream ) << std::endl;
}
+163
View File
@@ -0,0 +1,163 @@
#include <cstdlib>
#include <iostream>
#include <sstream>
#include "cllazyfile/lazyInstMgr.h"
namespace {
void require( bool condition, const char * message ) {
if( !condition ) {
std::cerr << "lazy_index_test: " << message << std::endl;
std::exit( EXIT_FAILURE );
}
}
void emptyRegistryInit( Registry & ) {
}
}
int main( int argc, char ** argv ) {
require( argc == 4, "expected ordinary, scoped and broken-section fixture paths" );
lazyInstMgr manager;
uint64_t progressCalls = 0;
uint64_t diagnosticCalls = 0;
LazyDiagnostic missingReference;
manager.setProgressCallback( [&progressCalls]( const LazyScanProgress & progress ) {
require( progress.fileSize >= progress.offset, "progress offset exceeds file size" );
++progressCalls;
} );
manager.setDiagnosticCallback( [&diagnosticCalls, &missingReference]( const LazyDiagnostic & diagnostic ) {
++diagnosticCalls;
if( diagnostic.entity == 5 ) missingReference = diagnostic;
} );
manager.openFile( argv[1] );
LazyCacheStatistics stats = manager.cacheStatistics();
require( stats.instancesScanned == 5, "wrong scan count" );
require( stats.dataSections == 2, "second DATA section was not indexed" );
require( progressCalls > 0, "progress callback was not called" );
require( manager.instancesByType( "a" ).size() == 1, "case-insensitive type index failed" );
require( manager.allInstances().size() == manager.totalInstanceCount(), "all-instance index count failed" );
require( manager.instancesByType( "C" ).size() == 1, "first complex component not indexed" );
require( manager.instancesByType( "D" ).size() == 1, "second complex component not indexed" );
require( manager.instancesByType( "" ).size() == 1, "complex instance index failed" );
require( manager.componentTypes( 3 ).size() == 2 &&
manager.componentTypes( 3 )[0] == "C" &&
manager.componentTypes( 3 )[1] == "D",
"complex component source order was not preserved" );
require( manager.componentTypes( 1 ).empty() &&
manager.componentTypes( 99 ).empty(),
"ordinary or missing instance has complex components" );
require( diagnosticCalls == 1, "structured diagnostic callback was not bounded" );
require( missingReference.severity == LAZY_DIAGNOSTIC_ERROR && missingReference.offset > 0,
"missing-reference diagnostic lacks structured context" );
require( missingReference.message == "reference to missing instance #99", "wrong missing-reference diagnostic" );
require( manager.forwardReferences( 1 ).size() == 1 && manager.forwardReferences( 1 )[0] == 2,
"forward reference index failed" );
require( manager.reverseReferences( 1 ).size() == 1 && manager.reverseReferences( 1 )[0] == 3,
"reverse reference index failed" );
const std::string firstSource = manager.sourceRecord( 1 );
const std::string secondSource = manager.sourceRecord( 2 );
require( firstSource.find( "#1=A(" ) != std::string::npos &&
!firstSource.empty() && firstSource[firstSource.size() - 1] == ';',
"first exact source record failed" );
require( secondSource.find( "#2=B();" ) != std::string::npos &&
!secondSource.empty() && secondSource[secondSource.size() - 1] == ';',
"second exact source record or stream restoration failed" );
require( manager.sourceRecord( 99 ).empty(),
"missing source record was not empty" );
Registry emptyRegistry( emptyRegistryInit );
manager.setRegistry( &emptyRegistry );
std::ostringstream schemaWarnings;
std::streambuf * originalCerr = std::cerr.rdbuf( schemaWarnings.rdbuf() );
LazyInstanceBatch missingBatch = manager.loadBatch( 5 );
LazyInstanceBatch secondBatch = manager.loadBatch( 1 );
std::cerr.rdbuf( originalCerr );
require( missingBatch.instances().size() == 1 &&
missingBatch.instances()[0] == 5,
"missing reference leaked into materialization closure" );
const std::string warningText =
"Warning - multiple schema names found. Only searching with first one.";
const size_t firstWarning = schemaWarnings.str().find( warningText );
require( firstWarning != std::string::npos &&
schemaWarnings.str().find( warningText, firstWarning + 1 ) ==
std::string::npos,
"multiple FILE_SCHEMA warning was not bounded per file" );
require( schemaWarnings.str().find( "Error loading instance" ) ==
std::string::npos,
"structured materialization diagnostic was also written to stderr" );
secondBatch.release();
missingBatch.release();
lazyInstMgr cancelled;
uint64_t cancellationCalls = 0;
cancelled.setCancellationCallback( [&cancellationCalls]() {
return ++cancellationCalls >= 2;
} );
cancelled.openFile( argv[1] );
stats = cancelled.cacheStatistics();
require( stats.cancelled, "cancellation was not recorded" );
require( stats.instancesScanned == 2, "scan did not stop at cancellation boundary" );
lazyInstMgr scoped;
require( scoped.openFile( argv[2] ), "scoped fixture did not open" );
stats = scoped.cacheStatistics();
require( stats.instancesScanned == 5, "wrong scoped scan count" );
require( scoped.instancesByType( "A" ).size() == 3,
"scoped simple instances were not indexed" );
require( scoped.instancesByType( "B" ).size() == 1,
"nested scoped instance was not indexed" );
require( scoped.instancesByType( "C" ).size() == 1 &&
scoped.instancesByType( "D" ).size() == 1 &&
scoped.instancesByType( "" ).size() == 1,
"scoped complex owner was not indexed" );
require( scoped.forwardReferences( 11 ).size() == 1 &&
scoped.forwardReferences( 11 )[0] == 12,
"nested scoped reference was not indexed" );
require( scoped.forwardReferences( 20 ).size() == 1 &&
scoped.forwardReferences( 20 )[0] == 21,
"complex scope-owner reference was not indexed" );
require( scoped.componentTypes( 20 ).size() == 2 &&
scoped.componentTypes( 20 )[0] == "C" &&
scoped.componentTypes( 20 )[1] == "D",
"scoped complex component order was not preserved" );
const std::string nestedOwner = scoped.sourceRecord( 11 );
const std::string complexOwner = scoped.sourceRecord( 20 );
require( nestedOwner.find( "#11=&SCOPE" ) != std::string::npos &&
nestedOwner.find( "ENDSCOPE /#12/ A(" ) != std::string::npos &&
!nestedOwner.empty() && nestedOwner[nestedOwner.size() - 1] == ';',
"nested scope source record was not preserved" );
require( complexOwner.find( "#20=&SCOPE" ) != std::string::npos &&
complexOwner.find( "ENDSCOPE (C()D(" ) != std::string::npos &&
!complexOwner.empty() && complexOwner[complexOwner.size() - 1] == ';',
"complex scope source record was not preserved" );
/* A DATA section indexes its instances from its own constructor, under the
* id it will be given, and is registered only afterwards and only if the
* scan succeeded. A section that fails part way through therefore leaves
* every instance it had already indexed naming a section that does not
* exist. Asking for one of those must fail like any other unloadable
* instance rather than index an empty vector. */
lazyInstMgr broken;
broken.setRegistry( &emptyRegistry );
uint64_t brokenDiagnostics = 0;
broken.setDiagnosticCallback( [&brokenDiagnostics]( const LazyDiagnostic & ) {
++brokenDiagnostics;
} );
broken.openFile( argv[3] );
stats = broken.cacheStatistics();
require( stats.dataSections == 0, "the failed data section was registered anyway" );
require( broken.totalInstanceCount() == 1,
"the instance indexed before the failure was dropped from the index" );
require( broken.typeFromFile( 1 ) == 0,
"type of an instance in an unregistered section was not refused" );
require( broken.loadInstance( 1 ) == 0,
"instance in an unregistered section was not refused" );
require( brokenDiagnostics > 0,
"refusing the instance produced no diagnostic" );
require( broken.sourceRecord( 1 ).empty(),
"source record of an instance in an unregistered section was not empty" );
return EXIT_SUCCESS;
}
+28 -1
View File
@@ -171,6 +171,34 @@ int main( int argc, char ** argv ) {
#ifndef NO_REGISTRY
if( instWithRef ) {
std::cout << "Number of data section instances fully loaded: " << mgr->loadedInstanceCount() << std::endl;
{
LazyInstanceBatch first = mgr->loadBatch( instWithRef );
LazyInstanceBatch second = mgr->loadBatch( instWithRef );
size_t sharedSize = second.instances().size();
first.release();
if( mgr->loadedInstanceCount() != sharedSize || !second.get( instWithRef ) ) {
std::cerr << "Releasing one batch evicted instances pinned by another batch" << std::endl;
return EXIT_FAILURE;
}
}
if( mgr->loadedInstanceCount() != 0 ) {
std::cerr << "Shared dependency batches did not release their cache" << std::endl;
return EXIT_FAILURE;
}
for( int cycle = 0; cycle < 3; ++cycle ) {
{
LazyInstanceBatch batch = mgr->loadBatch( instWithRef );
if( !batch.valid() || !batch.get( instWithRef ) ) {
std::cerr << "Unable to materialize dependency batch for #" << instWithRef << std::endl;
return EXIT_FAILURE;
}
std::cout << "Batch-pinned instances: " << batch.instances().size() << std::endl;
}
if( mgr->loadedInstanceCount() != 0 ) {
std::cerr << "Dependency batch did not return the cache to its initial size" << std::endl;
return EXIT_FAILURE;
}
}
std::cout << "Loading #" << instWithRef;
SDAI_Application_instance * inst = mgr->loadInstance( instWithRef );
std::cout << " which is of type " << inst->EntityName() << std::endl;
@@ -193,4 +221,3 @@ int main( int argc, char ** argv ) {
delete mgr;
//stats will print from its destructor
}
+9 -3
View File
@@ -34,7 +34,11 @@ const namedLazyInstance p21HeaderSectionReader::nextInstance() {
static instanceID nextFreeInstance = 4; // 1-3 are reserved per 10303-21
i.refs = 0;
i.loc.begin = _file.tellg();
i.componentTypes = 0;
i.loc.end = 0;
std::streampos start = _file.tellg();
i.loc.begin = start == std::streampos( -1 ) ? 0 :
static_cast<lazyFileOffset>( static_cast<std::streamoff>( start ) );
i.loc.section = _sectionID;
skipWS();
if( i.loc.begin <= 0 ) {
@@ -55,12 +59,14 @@ const namedLazyInstance p21HeaderSectionReader::nextInstance() {
assert( strlen( i.name ) > 0 );
std::streampos end = seekInstanceEnd( 0 ); //no references in file header
if( end != std::streampos( -1 ) ) {
i.loc.end = static_cast<lazyFileOffset>( static_cast<std::streamoff>( end ) );
}
if( ( (signed long int)end == -1 ) || ( end >= _sectionEnd ) ) {
//invalid instance, so clear everything
i.loc.begin = -1;
i.loc.begin = 0;
i.name = 0;
}
}
return i;
}
+207 -20
View File
@@ -1,11 +1,16 @@
#ifndef SC_CORE_EXPORT
#define SC_CORE_EXPORT
#endif
#ifndef SC_DAI_EXPORT
#define SC_DAI_EXPORT
#endif
#include <algorithm>
#include <set>
#include <string>
#include <assert.h>
#include <limits.h>
#include <limits>
#ifdef _WIN32
# define strtoull _strtoui64
@@ -35,6 +40,112 @@ sectionReader::~sectionReader() {
delete _error;
}
bool sectionReader::skipComment() {
int previous = 0;
int current;
while( current = _file.get(), _file.good() ) {
if( previous == '*' && current == '/' ) {
return true;
}
previous = current;
}
return false;
}
bool sectionReader::skipTokenSeparators() {
while( _file.good() ) {
skipWS();
if( _file.peek() != '/' ) return _file.good();
const std::streampos slash = _file.tellg();
_file.get();
if( _file.peek() != '*' ) {
_file.seekg( slash );
return _file.good();
}
_file.get(); // consume the opening star
if( !skipComment() ) return false;
}
return false;
}
bool sectionReader::skipScopeExportList() {
if( !skipTokenSeparators() ) return false;
if( _file.peek() != '/' ) return true;
_file.get();
bool haveExport = false;
while( _file.good() ) {
if( !skipTokenSeparators() || _file.get() != '#' ) return false;
bool haveDigit = false;
bool haveNonzeroDigit = false;
while( _file.good() && isdigit( _file.peek() ) ) {
const int digit = _file.get();
haveDigit = true;
if( digit != '0' ) haveNonzeroDigit = true;
}
if( !haveDigit || !haveNonzeroDigit || !skipTokenSeparators() ) return false;
haveExport = true;
const int delimiter = _file.get();
if( delimiter == '/' ) return haveExport;
if( delimiter != ',' ) return false;
}
return false;
}
bool sectionReader::skipScope() {
if( !skipTokenSeparators() || _file.get() != '&' ) return false;
skipWS();
static const char scopeKeyword[] = "SCOPE";
for( size_t i = 0; scopeKeyword[i]; ++i ) {
if( _file.get() != scopeKeyword[i] ) return false;
}
int depth = 1;
while( depth > 0 && _file.good() ) {
const int current = _file.get();
if( current == '\'' ) {
_file.seekg( _file.tellg() - std::streampos( 1 ) );
GetLiteralStr( _file, _lazyFile->getInstMgr()->getErrorDesc() );
continue;
}
if( current == '/' && _file.peek() == '*' ) {
_file.get();
if( !skipComment() ) return false;
continue;
}
if( current == '&' ) {
const std::streampos afterAmpersand = _file.tellg();
skipWS();
std::string keyword;
while( _file.good() && ( isupper( _file.peek() ) ||
isdigit( _file.peek() ) || _file.peek() == '_' ||
_file.peek() == '-' ) ) {
keyword.push_back( static_cast<char>( _file.get() ) );
}
if( keyword == "SCOPE" ) {
++depth;
} else {
_file.seekg( afterAmpersand );
}
continue;
}
if( isupper( current ) ) {
std::string keyword( 1, static_cast<char>( current ) );
while( _file.good() && ( isupper( _file.peek() ) ||
isdigit( _file.peek() ) || _file.peek() == '_' ||
_file.peek() == '-' ) ) {
keyword.push_back( static_cast<char>( _file.get() ) );
}
if( keyword == "ENDSCOPE" ) --depth;
}
}
return depth == 0 && skipScopeExportList();
}
std::streampos sectionReader::findNormalString( const std::string & str, bool semicolon ) {
std::streampos found = -1, startPos = _file.tellg(), nextTry = startPos;
int i = 0, l = str.length();
@@ -59,8 +170,11 @@ std::streampos sectionReader::findNormalString( const std::string & str, bool se
GetLiteralStr( _file, _lazyFile->getInstMgr()->getErrorDesc() );
}
if( ( c == '/' ) && ( _file.peek() == '*' ) ) {
//push past comment
findNormalString( "*/" );
_file.get(); // consume the opening star
if( !skipComment() ) {
return -1;
}
continue;
}
if( str[i] == c ) {
i++;
@@ -88,6 +202,32 @@ std::streampos sectionReader::findNormalString( const std::string & str, bool se
}
std::string sectionReader::sourceRecord( lazyFileOffset begin, uint64_t length ) {
if( begin == 0 || length == 0 ||
length > static_cast<uint64_t>( std::numeric_limits<std::streamsize>::max() ) ||
length > static_cast<uint64_t>( std::numeric_limits<size_t>::max() ) ) {
return std::string();
}
const std::streampos saved = _file.tellg();
_file.clear();
_file.seekg( static_cast<std::streamoff>( begin ) );
if( !_file.good() ) {
_file.clear();
if( saved != std::streampos( -1 ) ) _file.seekg( saved );
return std::string();
}
std::string source( static_cast<size_t>( length ), '\0' );
_file.read( &source[0], static_cast<std::streamsize>( length ) );
const bool complete = static_cast<uint64_t>( _file.gcount() ) == length;
_file.clear();
if( saved != std::streampos( -1 ) ) _file.seekg( saved );
return complete ? source : std::string();
}
//NOTE different behavior than const char * GetKeyword( istream & in, const char * delims, ErrorDescriptor & err ) in read_func.cc
// returns pointer to the contents of a static std::string
const char * sectionReader::getDelimitedKeyword( const char * delimiters ) {
@@ -102,7 +242,10 @@ const char * sectionReader::getDelimitedKeyword( const char * delimiters ) {
str.append( 1, c );
} else if( ( c == '/' ) && ( _file.peek() == '*' ) && ( str.length() == 0 ) ) {
//push past comment
findNormalString( "*/" );
_file.get(); // consume the opening star
if( !skipComment() ) {
break;
}
skipWS();
continue;
} else {
@@ -111,7 +254,7 @@ const char * sectionReader::getDelimitedKeyword( const char * delimiters ) {
}
}
c = _file.peek();
if( !strchr( delimiters, c ) ) {
if( !strchr( delimiters, c ) && !isspace( static_cast<unsigned char>( c ) ) ) {
std::cerr << SC_CURRENT_FUNCTION << ": missing delimiter. Found " << c << ", expected one of " << delimiters << " at end of keyword " << str << ". File offset: " << _file.tellg() << std::endl;
abort();
}
@@ -121,17 +264,22 @@ const char * sectionReader::getDelimitedKeyword( const char * delimiters ) {
/// search forward in the file for the end of the instance. Start position should
/// be the opening parenthesis; otherwise, it is likely to fail.
///NOTE *must* check return value!
std::streampos sectionReader::seekInstanceEnd( instanceRefs ** refs ) {
std::streampos sectionReader::seekInstanceEnd( instanceRefs ** refs, std::vector<std::string> * componentTypes ) {
int c;
int parenDepth = 0;
bool expectComplexType = false;
while( c = _file.get(), _file.good() ) {
switch( c ) {
case '(':
parenDepth++;
if( componentTypes && parenDepth == 1 ) expectComplexType = true;
break;
case '/':
if( _file.peek() == '*' ) {
findNormalString( "*/" );
_file.get(); // consume the opening star
if( !skipComment() ) {
return -1;
}
} else {
return -1;
}
@@ -158,7 +306,8 @@ std::streampos sectionReader::seekInstanceEnd( instanceRefs ** refs ) {
}
break;
case ')':
if( --parenDepth == 0 ) {
if( --parenDepth == 1 && componentTypes ) expectComplexType = true;
if( parenDepth == 0 ) {
skipWS();
if( _file.get() == ';' ) {
return _file.tellg();
@@ -166,7 +315,23 @@ std::streampos sectionReader::seekInstanceEnd( instanceRefs ** refs ) {
_file.seekg( _file.tellg() - std::streampos(1) );
}
}
break;
default:
if( componentTypes && parenDepth == 1 && expectComplexType &&
( isupper( c ) || c == '!' ) ) {
std::string type( 1, static_cast<char>( c ) );
while( _file.good() ) {
int next = _file.get();
if( next == '-' || next == '_' || isupper( next ) || isdigit( next ) ) {
type.push_back( static_cast<char>( next ) );
} else {
_file.putback( static_cast<char>( next ) );
break;
}
}
componentTypes->push_back( type );
expectComplexType = false;
}
break;
}
}
@@ -192,7 +357,10 @@ instanceID sectionReader::readInstanceNumber() {
skipWS();
c = _file.get();
if( ( c == '/' ) && ( _file.peek() == '*' ) ) {
findNormalString( "*/" );
_file.get(); // consume the opening star
if( !skipComment() ) {
return 0;
}
} else {
_file.seekg( _file.tellg() - std::streampos(1) );
}
@@ -257,10 +425,11 @@ instanceID sectionReader::readInstanceNumber() {
/** load an instance and return a pointer to it.
* side effect: recursively loads any instances the specified instance depends upon
*/
SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg, long int begin, instanceID instance,
SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg, lazyFileOffset begin, instanceID instance,
const std::string & typeName, const std::string & schName, bool header ) {
int c;
const char * tName = 0, * sName = 0; //these are necessary since typeName and schName are const
std::string normalizedSchema;
std::string comment;
Severity sev = SEVERITY_NULL;
SDAI_Application_instance * inst = 0;
@@ -273,17 +442,20 @@ SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg
if( fs ) {
StringNode * sn = ( StringNode * ) fs->schema_identifiers_()->GetHead();
if( sn ) {
sName = sn->value.c_str();
if( sn->NextNode() ) {
normalizedSchema = sn->value.c_str();
size_t qualifier = normalizedSchema.find_first_of( " {" );
if( qualifier != std::string::npos ) normalizedSchema.erase( qualifier );
sName = normalizedSchema.c_str();
if( sn->NextNode() && _lazyFile->claimSchemaWarning() ) {
std::cerr << "Warning - multiple schema names found. Only searching with first one." << std::endl;
}
}
} else {
} else if( _lazyFile->claimSchemaWarning() ) {
std::cerr << "Warning - no schema names found; the file is probably invalid. Looking for typeName in any loaded schema." << std::endl;
}
}
_file.seekg( begin );
_file.seekg( static_cast<std::streamoff>( begin ) );
skipWS();
ReadTokenSeparator( _file, &comment );
if( !header ) {
@@ -292,11 +464,11 @@ SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg
skipWS();
ReadTokenSeparator( _file, &comment );
c = _file.peek();
if( c == '&' ) {
if( !skipScope() || !skipTokenSeparators() ) return 0;
c = _file.peek();
}
switch( c ) {
case '&':
std::cerr << "Can't handle scope instances. Skipping #" << instance << ", offset " << _file.tellg() << std::endl;
// sev = CreateScopeInstances( in, &scopelist );
break;
case '(':
inst = CreateSubSuperInstance( reg, instance, sev );
break;
@@ -307,6 +479,11 @@ SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg
if( ( !header ) && ( typeName.size() == 0 ) ) {
tName = getDelimitedKeyword( ";( /\\" );
}
std::string materializationType;
if( !header && tName ) {
materializationType = _lazyFile->getInstMgr()->materializationType( tName );
tName = materializationType.c_str();
}
inst = reg->ObjCreate( tName, sName );
break;
}
@@ -315,7 +492,18 @@ SDAI_Application_instance * sectionReader::getRealInstance( const Registry * reg
inst->AddP21Comment( comment );
}
assert( inst->eDesc );
_file.seekg( begin );
_file.seekg( static_cast<std::streamoff>( begin ) );
if( !header ) {
if( findNormalString( "=" ) == std::streampos( -1 ) ||
!skipTokenSeparators() ) {
delete inst;
return 0;
}
if( _file.peek() == '&' && ( !skipScope() || !skipTokenSeparators() ) ) {
delete inst;
return 0;
}
}
findNormalString( "(" );
_file.seekg( _file.tellg() - std::streampos(1) );
sev = inst->STEPread( instance, 0, _lazyFile->getInstMgr()->getAdapter(), _file, sName, true, false );
@@ -356,7 +544,6 @@ STEPcomplex * sectionReader::CreateSubSuperInstance( const Registry * reg, insta
//TODO still need the schema name
STEPcomplex * sc = new STEPcomplex( ( const_cast<Registry *>( reg ) ), names, ( int ) fileid /*, schnm*/ );
delete[] names;
//TODO also delete contents of typeNames!
for( int i = 0; i < s; i++ ) delete typeNames[i];
return sc;
}
@@ -0,0 +1,13 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('lazy broken section test'),'2;1');
FILE_NAME('lazy_broken_section.stp','2026-08-21T00:00:00',('STEPcode'),('STEPcode'),'','','');
FILE_SCHEMA(('LAZY_TEST_SCHEMA { 1 0 10303 999 }'));
ENDSEC;
/* The DATA section indexes #1 and then fails: no ENDSEC; follows. The
section is therefore never registered, while #1 is already in the
instance index naming it. */
DATA;
#1=A();
not a step instance
END-ISO-10303-21;
+25
View File
@@ -0,0 +1,25 @@
SCHEMA lazy_test_schema;
ENTITY b;
END_ENTITY;
ENTITY a;
opt : OPTIONAL STRING;
label : STRING;
ref : b;
END_ENTITY;
ENTITY c;
END_ENTITY;
ENTITY d
SUBTYPE OF (c);
ref : a;
END_ENTITY;
ENTITY e;
ref : d;
opt : OPTIONAL STRING;
END_ENTITY;
END_SCHEMA;
+17
View File
@@ -0,0 +1,17 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('lazy index test'),'2;1');
FILE_NAME('lazy_index.stp','2026-07-17T00:00:00',('STEPcode'),('STEPcode'),'','','');
FILE_SCHEMA(('LAZY_TEST_SCHEMA { 1 0 10303 999 }','UNUSED_TEST_SCHEMA'));
ENDSEC;
DATA('second',('LAZY_TEST_SCHEMA'));
#1=A($,'escaped quote '' and unicode \X2\03A9\X0\',#2);
#2=B();
ENDSEC;
/* A second data section must not be consumed by lookahead. */
DATA;
#3=(C()D(#1));
#4=E(#3,$);
#5=E(#99,$);
ENDSEC;
END-ISO-10303-21;
+17
View File
@@ -0,0 +1,17 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('lazy scope test'),'1;1');
FILE_NAME('lazy_scope.stp','2026-07-31T00:00:00',('STEPcode'),('STEPcode'),'','','');
FILE_SCHEMA(('LAZY_TEST_SCHEMA { 1 0 10303 999 }'));
ENDSEC;
DATA;
#10=&SCOPE
#11=&SCOPE
#12=B();
ENDSCOPE /#12/ A($,'nested owner',#12);
ENDSCOPE /#11/ A($,'outer owner',#12);
#20=&SCOPE
#21=A($,'complex child',#12);
ENDSCOPE (C()D(#21));
ENDSEC;
END-ISO-10303-21;
+5 -4
View File
@@ -34,6 +34,8 @@ set(CORE_SRCS
read_func.cc
Registry.cc
schRename.cc
schemaInit.cc
schemaModule.cc
sdai.cc
sdaiApplication_instance.cc
sdaiSelect.cc
@@ -66,9 +68,8 @@ include_directories(
${CMAKE_SOURCE_DIR}/include/stepcode
)
set(_libdeps steputils stepdai)
if(BUILD_SHARED_LIBS)
set(_libdeps steputils stepdai)
SC_ADDLIB(stepcore SHARED SOURCES ${CORE_SRCS} LINK_LIBRARIES ${_libdeps})
if(WIN32)
target_compile_definitions(stepcore PRIVATE SC_CORE_DLL_EXPORTS)
@@ -76,7 +77,8 @@ if(BUILD_SHARED_LIBS)
endif()
if(BUILD_STATIC_LIBS)
SC_ADDLIB(stepcore-static STATIC SOURCES ${CORE_SRCS} LINK_LIBRARIES $<JOIN:${_libdeps},-static>-static)
set(_libdeps steputils-static stepdai-static)
SC_ADDLIB(stepcore-static STATIC SOURCES ${CORE_SRCS} LINK_LIBRARIES ${_libdeps})
endif()
if(SC_ENABLE_TESTING)
@@ -89,4 +91,3 @@ endif(SC_ENABLE_TESTING)
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+4 -2
View File
@@ -248,8 +248,10 @@ void Registry::RemoveClones( const EntityDescriptor & e ) {
SDAI_Application_instance * Registry::ObjCreate( const char * nm, const char * schnm, int check_case ) const {
const EntityDescriptor * entd = FindEntity( nm, schnm, check_case );
if( entd ) {
SDAI_Application_instance * se =
( ( EntityDescriptor * )entd ) -> NewSTEPentity();
SDAI_Application_instance * se = entd->CreateEntity();
if( !se ) {
return ENTITY_NULL;
}
// See comment in previous function.
if( entd->AbstractEntity().asInt() == 1 ) {
+8 -2
View File
@@ -91,7 +91,7 @@ Severity EntityAggregate::ReadValue( istream & in, ErrorDescriptor * err,
CheckRemainingInput( in, &errdesc, buf, ",)" );
if( errdesc.severity() < SEVERITY_INCOMPLETE ) {
sprintf( errmsg, " index: %d\n", value_cnt );
snprintf( errmsg, sizeof(errmsg), " index: %d\n", value_cnt );
errdesc.PrependToDetailMsg( errmsg );
err->AppendFromErrorArg( &errdesc );
}
@@ -169,14 +169,17 @@ Severity EntityNode::StrToVal( const char * s, ErrorDescriptor * err,
ErrorDescriptor error;
if( EntityValidLevel( se, elem_type, &error ) == SEVERITY_NULL ) {
node = se;
_null = 0;
} else {
node = S_ENTITY_NULL;
err->AppendToDetailMsg( error.DetailMsg() );
err->AppendToUserMsg( error.UserMsg() );
err->GreaterSeverity( error.severity() );
_null = 1;
}
} else {
node = S_ENTITY_NULL;
_null = 1;
}
return err->severity();
}
@@ -203,14 +206,17 @@ Severity EntityNode::STEPread( istream & in, ErrorDescriptor * err,
ErrorDescriptor error;
if( EntityValidLevel( se, elem_type, &error ) == SEVERITY_NULL ) {
node = se;
_null = 0;
} else {
node = S_ENTITY_NULL;
err->AppendToDetailMsg( error.DetailMsg() );
err->AppendToUserMsg( error.UserMsg() );
err->GreaterSeverity( error.severity() );
_null = 1;
}
} else {
node = S_ENTITY_NULL;
_null = 1;
}
return err->severity();
}
@@ -221,7 +227,7 @@ const char * EntityNode::asStr( std::string & s ) {
return "";
} else { // otherwise return entity id
char tmp [64];
sprintf( tmp, "#%d", node->STEPfile_id );
snprintf( tmp, sizeof(tmp), "#%d", node->STEPfile_id );
s = tmp;
}
return const_cast<char *>( s.c_str() );
+1 -1
View File
@@ -96,7 +96,7 @@ const char * IntNode::asStr( std::string & s ) {
const char * IntNode::STEPwrite( std::string & s, const char * ) {
char tmp[BUFSIZ+1];
if( value != S_INT_NULL ) {
sprintf( tmp, "%ld", value );
snprintf( tmp, sizeof(tmp), "%ld", value );
s = tmp;
} else {
s.clear();
+12 -7
View File
@@ -1,12 +1,13 @@
#include "clstepcore/STEPaggrSelect.h"
#include "clstepcore/typeDescriptor.h"
#include "clstepcore/selectTypeDescriptor.h"
#include <sstream>
/** \file STEPaggrSelect.cc
* implement classes SelectAggregate, SelectNode
*/
SelectAggregate::SelectAggregate() {
SelectAggregate::SelectAggregate( const SelectTypeDescriptor * select_type )
: _select_type( select_type ) {
}
SelectAggregate::~SelectAggregate() {
@@ -87,7 +88,7 @@ Severity SelectAggregate::ReadValue( istream & in, ErrorDescriptor * err,
CheckRemainingInput( in, &errdesc, buf, ",)" );
if( errdesc.severity() < SEVERITY_INCOMPLETE ) {
sprintf( errmsg, " index: %d\n", value_cnt );
snprintf( errmsg, sizeof(errmsg), " index: %d\n", value_cnt );
errdesc.PrependToDetailMsg( errmsg );
err->AppendFromErrorArg( &errdesc );
}
@@ -119,9 +120,10 @@ Severity SelectAggregate::ReadValue( istream & in, ErrorDescriptor * err,
STEPaggregate & SelectAggregate::ShallowCopy( const STEPaggregate & a ) {
Empty();
const SelectNode * tmp = ( const SelectNode * ) a.GetHead();
while( tmp ) {
AddNode( new SelectNode( tmp -> node ) );
AddNode( new SelectNode( tmp->node ? tmp->node->Clone() : 0 ) );
tmp = ( const SelectNode * ) tmp -> NextNode();
}
@@ -136,14 +138,18 @@ STEPaggregate & SelectAggregate::ShallowCopy( const STEPaggregate & a ) {
SingleLinkNode * SelectAggregate::NewNode() {
return new SelectNode();
SDAI_Select * select = _select_type ? _select_type->CreateSelect() : 0;
if( !select && _select_type ) {
select = new SDAI_Select( _select_type );
}
return new SelectNode( select );
}
SelectNode::SelectNode( SDAI_Select * s ) : node( s ) {
}
SelectNode::SelectNode() {
SelectNode::SelectNode() : node( 0 ) {
}
SelectNode::~SelectNode() {
@@ -221,4 +227,3 @@ void SelectNode::STEPwrite( ostream & out ) {
std::string s;
out << asStr( s );
}
+1 -1
View File
@@ -174,7 +174,7 @@ Severity STEPaggregate::ReadValue( istream & in, ErrorDescriptor * err,
CheckRemainingInput( in, &errdesc, buf, ",)" );
if( errdesc.severity() < SEVERITY_INCOMPLETE ) {
sprintf( errmsg, " index: %d\n", value_cnt );
snprintf( errmsg, sizeof(errmsg), " index: %d\n", value_cnt );
errdesc.PrependToDetailMsg( errmsg );
err->AppendFromErrorArg( &errdesc );
}
+230 -29
View File
@@ -392,18 +392,19 @@ const char * STEPattribute::asStr( std::string & str, const char * currSch ) con
str.clear();
// The attribute has been redefined by the attribute pointed
// to by _redefAttr so write the narrowed value in the inherited
// physical-file slot. STEPread and StrToVal use this same precedence.
if( _redefAttr ) {
return _redefAttr->asStr( str, currSch );
}
// The attribute has been derived by a subtype's attribute
if( IsDerived() ) {
str = "*";
return const_cast<char *>( str.c_str() );
}
// The attribute has been redefined by the attribute pointed
// to by _redefAttr so write the redefined value.
if( _redefAttr ) {
return _redefAttr->asStr( str, currSch );
}
if( is_null() ) {
str = "";
return const_cast<char *>( str.c_str() );
@@ -485,18 +486,19 @@ std::string STEPattribute::asStr( const char * currSch ) const {
ostringstream ss;
std::string str;
// The attribute has been redefined by the attribute pointed
// to by _redefAttr so write the narrowed value in the inherited
// physical-file slot. STEPread and StrToVal use this same precedence.
if( _redefAttr ) {
return _redefAttr->asStr( currSch );
}
// The attribute has been derived by a subtype's attribute
if( IsDerived() ) {
str = "*";
return str;
}
// The attribute has been redefined by the attribute pointed
// to by _redefAttr so write the redefined value.
if( _redefAttr ) {
return _redefAttr->asStr( currSch );
}
if( is_null() ) {
return str;
}
@@ -587,17 +589,18 @@ void STEPattribute::STEPwriteError( ostream & out, unsigned int line, const char
*
*/
void STEPattribute::STEPwrite( ostream & out, const char * currSch ) {
// The attribute has been redefined by the attribute pointed
// to by _redefAttr so write the narrowed value in the inherited
// physical-file slot. STEPread and StrToVal use this same precedence.
if( _redefAttr ) {
_redefAttr->STEPwrite( out, currSch );
return;
}
// The attribute has been derived by a subtype's attribute
if( IsDerived() ) {
out << "*";
return;
}
// The attribute has been redefined by the attribute pointed
// to by _redefAttr so write the redefined value.
if( _redefAttr ) {
_redefAttr->STEPwrite( out );
return;
}
if( is_null() ) {
out << "$";
@@ -686,6 +689,10 @@ void STEPattribute::STEPwrite( ostream & out, const char * currSch ) {
void STEPattribute::ShallowCopy( const STEPattribute * sa ) {
if( sa == this ) {
return;
}
DeleteOwnedPtr();
_mustDeletePtr = false;
aDesc = sa->aDesc;
refCount = 0;
@@ -918,6 +925,9 @@ bool STEPattribute::is_null() const {
// these get the attr value
SDAI_Integer * STEPattribute::Integer(){
if( _redefAttr ) {
return _redefAttr->Integer();
}
if( NonRefType() == INTEGER_TYPE ) {
return ptr.i;
}
@@ -925,6 +935,9 @@ SDAI_Integer * STEPattribute::Integer(){
}
SDAI_Real * STEPattribute::Number() {
if( _redefAttr ) {
return _redefAttr->Number();
}
if( NonRefType() == NUMBER_TYPE ) {
return ptr.r;
}
@@ -932,6 +945,9 @@ SDAI_Real * STEPattribute::Number() {
}
SDAI_Real * STEPattribute::Real() {
if( _redefAttr ) {
return _redefAttr->Real();
}
if( NonRefType() == REAL_TYPE ) {
return ptr.r;
}
@@ -939,6 +955,9 @@ SDAI_Real * STEPattribute::Real() {
}
SDAI_Application_instance * STEPattribute::Entity() {
if( _redefAttr ) {
return _redefAttr->Entity();
}
if( NonRefType() == ENTITY_TYPE ) {
return *( ptr.c );
}
@@ -946,6 +965,9 @@ SDAI_Application_instance * STEPattribute::Entity() {
}
SDAI_String * STEPattribute::String() {
if( _redefAttr ) {
return _redefAttr->String();
}
if( NonRefType() == STRING_TYPE ) {
return ptr.S;
}
@@ -953,6 +975,9 @@ SDAI_String * STEPattribute::String() {
}
SDAI_Binary * STEPattribute::Binary() {
if( _redefAttr ) {
return _redefAttr->Binary();
}
if( NonRefType() == BINARY_TYPE ) {
return ptr.b;
}
@@ -960,6 +985,9 @@ SDAI_Binary * STEPattribute::Binary() {
}
STEPaggregate * STEPattribute::Aggregate() {
if( _redefAttr ) {
return _redefAttr->Aggregate();
}
if( ( NonRefType() == AGGREGATE_TYPE ) || ( NonRefType() == ARRAY_TYPE ) || ( NonRefType() == BAG_TYPE )
|| ( NonRefType() == SET_TYPE ) || ( NonRefType() == LIST_TYPE ) ) {
return ptr.a;
@@ -968,6 +996,9 @@ STEPaggregate * STEPattribute::Aggregate() {
}
SDAI_BOOLEAN * STEPattribute::Boolean() {
if( _redefAttr ) {
return _redefAttr->Boolean();
}
if( NonRefType() == BOOLEAN_TYPE ) {
return ( SDAI_BOOLEAN * ) ptr.e;
}
@@ -975,6 +1006,9 @@ SDAI_BOOLEAN * STEPattribute::Boolean() {
}
SDAI_LOGICAL * STEPattribute::Logical() {
if( _redefAttr ) {
return _redefAttr->Logical();
}
if( NonRefType() == LOGICAL_TYPE ) {
return ( SDAI_LOGICAL * ) ptr.e;
}
@@ -982,6 +1016,9 @@ SDAI_LOGICAL * STEPattribute::Logical() {
}
SDAI_Enum * STEPattribute::Enum() {
if( _redefAttr ) {
return _redefAttr->Enum();
}
if( NonRefType() == ENUM_TYPE ) {
return ptr.e;
}
@@ -989,6 +1026,9 @@ SDAI_Enum * STEPattribute::Enum() {
}
SDAI_Select * STEPattribute::Select() {
if( _redefAttr ) {
return _redefAttr->Select();
}
if( NonRefType() == SELECT_TYPE ) {
return ptr.sh;
}
@@ -996,6 +1036,9 @@ SDAI_Select * STEPattribute::Select() {
}
SCLundefined * STEPattribute::Undefined() {
if( _redefAttr ) {
return _redefAttr->Undefined();
}
if( ( NonRefType() != REFERENCE_TYPE ) && ( NonRefType() != GENERIC_TYPE ) ) {
return ptr.u;
}
@@ -1005,6 +1048,10 @@ SCLundefined * STEPattribute::Undefined() {
// these set the attr value
void STEPattribute::Integer( SDAI_Integer * n ) {
if( _redefAttr ) {
_redefAttr->Integer( n );
return;
}
assert( NonRefType() == INTEGER_TYPE );
if( ptr.i ) {
*( ptr.i ) = * n;
@@ -1014,6 +1061,10 @@ void STEPattribute::Integer( SDAI_Integer * n ) {
}
void STEPattribute::Real( SDAI_Real * n ) {
if( _redefAttr ) {
_redefAttr->Real( n );
return;
}
assert( NonRefType() == REAL_TYPE );
if( ptr.r ) {
*( ptr.r ) = * n;
@@ -1023,6 +1074,10 @@ void STEPattribute::Real( SDAI_Real * n ) {
}
void STEPattribute::Number( SDAI_Real * n ) {
if( _redefAttr ) {
_redefAttr->Number( n );
return;
}
assert( NonRefType() == NUMBER_TYPE );
if( ptr.r ) {
*( ptr.r ) = * n;
@@ -1032,6 +1087,10 @@ void STEPattribute::Number( SDAI_Real * n ) {
}
void STEPattribute::String( SDAI_String * str ) {
if( _redefAttr ) {
_redefAttr->String( str );
return;
}
assert( NonRefType() == STRING_TYPE );
if( ptr.S ) {
*( ptr.S ) = * str;
@@ -1041,6 +1100,10 @@ void STEPattribute::String( SDAI_String * str ) {
}
void STEPattribute::Binary( SDAI_Binary * bin ) {
if( _redefAttr ) {
_redefAttr->Binary( bin );
return;
}
assert( NonRefType() == BINARY_TYPE );
if( ptr.b ) {
*( ptr.b ) = * bin;
@@ -1050,15 +1113,25 @@ void STEPattribute::Binary( SDAI_Binary * bin ) {
}
void STEPattribute::Entity( SDAI_Application_instance * ent ) {
if( _redefAttr ) {
_redefAttr->Entity( ent );
return;
}
assert( NonRefType() == ENTITY_TYPE );
if( ptr.c ) {
delete ptr.c;
*( ptr.c ) = ent;
} else {
ptr.c = new (SDAI_Application_instance * );
_mustDeletePtr = true;
*( ptr.c ) = ent;
}
ptr.c = new (SDAI_Application_instance * );
*( ptr.c ) = ent;
}
void STEPattribute::Aggregate( STEPaggregate * aggr ) {
if( _redefAttr ) {
_redefAttr->Aggregate( aggr );
return;
}
assert( ( NonRefType() == AGGREGATE_TYPE ) || ( NonRefType() == ARRAY_TYPE ) || ( NonRefType() == BAG_TYPE )
|| ( NonRefType() == SET_TYPE ) || ( NonRefType() == LIST_TYPE ) );
if( ptr.a ) {
@@ -1069,6 +1142,10 @@ void STEPattribute::Aggregate( STEPaggregate * aggr ) {
}
void STEPattribute::Enum( SDAI_Enum * enu ) {
if( _redefAttr ) {
_redefAttr->Enum( enu );
return;
}
assert( NonRefType() == ENUM_TYPE );
if( ptr.e ) {
ptr.e->set_null();
@@ -1079,6 +1156,10 @@ void STEPattribute::Enum( SDAI_Enum * enu ) {
}
void STEPattribute::Logical( SDAI_LOGICAL * log ) {
if( _redefAttr ) {
_redefAttr->Logical( log );
return;
}
assert( NonRefType() == LOGICAL_TYPE );
if( ptr.e ) {
ptr.e->set_null();
@@ -1089,6 +1170,10 @@ void STEPattribute::Logical( SDAI_LOGICAL * log ) {
}
void STEPattribute::Boolean( SDAI_BOOLEAN * boo ) {
if( _redefAttr ) {
_redefAttr->Boolean( boo );
return;
}
assert( NonRefType() == BOOLEAN_TYPE );
if( ptr.e ) {
ptr.e->set_null();
@@ -1099,6 +1184,10 @@ void STEPattribute::Boolean( SDAI_BOOLEAN * boo ) {
}
void STEPattribute::Select( SDAI_Select * sel ) {
if( _redefAttr ) {
_redefAttr->Select( sel );
return;
}
assert( NonRefType() == SELECT_TYPE );
if( ptr.sh ) {
ptr.sh->set_null();
@@ -1109,6 +1198,10 @@ void STEPattribute::Select( SDAI_Select * sel ) {
}
void STEPattribute::Undefined( SCLundefined * undef ) {
if( _redefAttr ) {
_redefAttr->Undefined( undef );
return;
}
//FIXME is this right, or is the Undefined() above right?
assert( NonRefType() == REFERENCE_TYPE || NonRefType() == UNKNOWN_TYPE );
if( ptr.u ) {
@@ -1266,15 +1359,15 @@ void STEPattribute::AddErrorInfo() {
errStr[0] = '\0';
if( SEVERITY_INPUT_ERROR < _error.severity() &&
_error.severity() < SEVERITY_NULL ) {
sprintf( errStr, " Warning: ATTRIBUTE '%s : %s : %d' - ",
snprintf( errStr, sizeof(errStr), " Warning: ATTRIBUTE '%s : %s : %d' - ",
Name(), TypeName().c_str(), Type() );
_error.PrependToDetailMsg( errStr );
} else if( _error.severity() == SEVERITY_INPUT_ERROR ) {
sprintf( errStr, " Error: ATTRIBUTE '%s : %s : %d' - ",
snprintf( errStr, sizeof(errStr), " Error: ATTRIBUTE '%s : %s : %d' - ",
Name(), TypeName().c_str(), Type() );
_error.PrependToDetailMsg( errStr );
} else if( _error.severity() <= SEVERITY_BUG ) {
sprintf( errStr, " BUG: ATTRIBUTE '%s : %s : %d' - ",
snprintf( errStr, sizeof(errStr), " BUG: ATTRIBUTE '%s : %s : %d' - ",
Name(), TypeName().c_str(), Type() );
_error.PrependToDetailMsg( errStr );
}
@@ -1302,12 +1395,12 @@ char STEPattribute::SkipBadAttr( istream & in, char * StopChars ) {
}
if( in.eof() ) {
_error.GreaterSeverity( SEVERITY_INPUT_ERROR );
sprintf( errStr, " Error: attribute '%s : %s : %d' - %s.\n",
snprintf( errStr, sizeof(errStr), " Error: attribute '%s : %s : %d' - %s.\n",
Name(), TypeName().c_str(), Type(),
"Unexpected EOF when skipping bad attr value" );
_error.AppendToDetailMsg( errStr );
} else {
sprintf( errStr, " Error: attribute '%s : %s : %d' - %s.\n",
snprintf( errStr, sizeof(errStr), " Error: attribute '%s : %s : %d' - %s.\n",
Name(), TypeName().c_str(), Type(), "Invalid value" );
_error.AppendToDetailMsg( errStr );
}
@@ -1364,6 +1457,71 @@ _redefAttr( a._redefAttr ), aDesc( a.aDesc ), refCount( a.refCount ) {
*/
}
STEPattribute::STEPattribute( const class AttrDescriptor & d ) : _derive( false ),
_mustDeletePtr( true ), _redefAttr( 0 ), aDesc( &d ), refCount( 0 ) {
memset( &ptr, 0, sizeof( ptr ) );
switch( d.NonRefType() ) {
case INTEGER_TYPE:
ptr.i = new SDAI_Integer;
break;
case REAL_TYPE:
case NUMBER_TYPE:
ptr.r = new SDAI_Real;
break;
case STRING_TYPE:
ptr.S = new SDAI_String;
break;
case BINARY_TYPE:
ptr.b = new SDAI_Binary;
break;
case ENTITY_TYPE:
ptr.c = new SDAI_Application_instance *;
*ptr.c = 0;
break;
case BOOLEAN_TYPE:
ptr.e = new SDAI_BOOLEAN;
break;
case LOGICAL_TYPE:
ptr.e = new SDAI_LOGICAL;
break;
case ENUM_TYPE: {
const EnumTypeDescriptor * etd =
dynamic_cast<const EnumTypeDescriptor *>( d.NonRefTypeDescriptor() );
ptr.e = etd ? etd->CreateEnum() : 0;
break;
}
case SELECT_TYPE: {
const SelectTypeDescriptor * std =
dynamic_cast<const SelectTypeDescriptor *>( d.NonRefTypeDescriptor() );
ptr.sh = std ? std->CreateSelect() : 0;
if( !ptr.sh && std ) {
ptr.sh = new SDAI_Select( std );
}
break;
}
case AGGREGATE_TYPE:
case ARRAY_TYPE:
case BAG_TYPE:
case SET_TYPE:
case LIST_TYPE: {
const AggrTypeDescriptor * atd =
dynamic_cast<const AggrTypeDescriptor *>( d.NonRefTypeDescriptor() );
ptr.a = atd ? atd->CreateAggregate() : 0;
if( !ptr.a ) {
ptr.a = new GenericAggregate;
}
break;
}
case UNKNOWN_TYPE:
case GENERIC_TYPE:
case REFERENCE_TYPE:
default:
ptr.u = new SCLundefined;
break;
}
}
/// INTEGER
STEPattribute::STEPattribute( const class AttrDescriptor & d, SDAI_Integer * p ): _derive( false ),
_mustDeletePtr( false ), _redefAttr( 0 ), aDesc( &d ), refCount( 0 ) {
@@ -1427,10 +1585,12 @@ _mustDeletePtr( false ), _redefAttr( 0 ), aDesc( &d ), refCount( 0 ) {
assert( &d ); //ensure that the AttrDescriptor is not a null pointer
}
/// the destructor conditionally deletes the object in ptr
STEPattribute::~STEPattribute() {
void STEPattribute::DeleteOwnedPtr() {
if( _mustDeletePtr ) {
switch( NonRefType() ) {
// A redefined attribute delegates value operations to _redefAttr, but
// ptr still holds storage allocated for this attribute's descriptor.
// Use that original type when releasing the owned storage.
switch( aDesc ? aDesc->NonRefType() : UNKNOWN_TYPE ) {
case AGGREGATE_TYPE:
case ARRAY_TYPE: // DAS
case BAG_TYPE: // DAS
@@ -1453,12 +1613,53 @@ STEPattribute::~STEPattribute() {
ptr.e = 0;
}
break;
case ENUM_TYPE:
delete ptr.e;
ptr.e = 0;
break;
case SELECT_TYPE:
delete ptr.sh;
ptr.sh = 0;
break;
case STRING_TYPE:
delete ptr.S;
ptr.S = 0;
break;
case BINARY_TYPE:
delete ptr.b;
ptr.b = 0;
break;
case INTEGER_TYPE:
delete ptr.i;
ptr.i = 0;
break;
case REAL_TYPE:
case NUMBER_TYPE:
delete ptr.r;
ptr.r = 0;
break;
case ENTITY_TYPE:
delete ptr.c;
ptr.c = 0;
break;
case UNKNOWN_TYPE:
case GENERIC_TYPE:
case REFERENCE_TYPE:
delete ptr.u;
ptr.u = 0;
break;
default:
break;
}
_mustDeletePtr = false;
}
}
/// the destructor conditionally deletes the object in ptr
STEPattribute::~STEPattribute() {
DeleteOwnedPtr();
}
/// name is the same even if redefined
const char * STEPattribute::Name() const {
return aDesc->Name();
+6 -2
View File
@@ -41,10 +41,14 @@ STEPattribute & STEPattributeList::operator []( int n ) {
return *( a->attr );
}
// else
// else - error case: return a static error object to avoid undefined behavior
// The error object allows calling code to detect the error condition
static STEPattribute errorAttr;
cerr << "\nERROR in STEP Core library: " << __FILE__ << ":"
<< __LINE__ << "\n" << _POC_ << "\n\n";
return *( STEPattribute * ) 0;
errorAttr.Error().AppendToDetailMsg( "STEPattributeList::operator[] - index out of bounds" );
errorAttr.Error().severity( SEVERITY_BUG );
return errorAttr;
}
int STEPattributeList::list_length() {
+2 -2
View File
@@ -661,7 +661,7 @@ void STEPcomplex::CopyAs( SDAI_Application_instance * se ) {
char errStr[BUFSIZ+1];
cerr << "STEPcomplex::CopyAs() called with non-complex entity: "
<< __FILE__ << __LINE__ << "\n" << _POC_ "\n";
sprintf( errStr,
snprintf( errStr, sizeof(errStr),
"STEPcomplex::CopyAs(): %s - entity #%d.\n",
"Programming ERROR - called with non-complex entity",
STEPfile_id );
@@ -702,7 +702,7 @@ SDAI_Application_instance * STEPcomplex::Replicate() {
char errStr[BUFSIZ+1];
cerr << "STEPcomplex::Replicate() name buffer too small: "
<< __FILE__ << __LINE__ << "\n" << _POC_ "\n";
sprintf( errStr,
snprintf( errStr, sizeof(errStr),
"STEPcomplex::Replicate(): %s - entity #%d.\n",
"Programming ERROR - name buffer too small",
STEPfile_id );
+60 -9
View File
@@ -1,10 +1,59 @@
#include "clstepcore/aggrTypeDescriptor.h"
STEPaggregate * AggrTypeDescriptor::CreateAggregate() {
#include "clstepcore/STEPaggregate.h"
#include "clstepcore/enumTypeDescriptor.h"
#include "clstepcore/selectTypeDescriptor.h"
namespace {
class DescriptorEnumAggregate : public EnumAggregate {
const EnumTypeDescriptor * _enumType;
public:
explicit DescriptorEnumAggregate( const EnumTypeDescriptor * enumType )
: _enumType( enumType ) {
}
virtual SingleLinkNode * NewNode() {
return new EnumNode( _enumType ? _enumType->CreateEnum() : 0 );
}
};
}
STEPaggregate * AggrTypeDescriptor::CreateAggregate() const {
if( CreateNewAggr ) {
return CreateNewAggr();
} else {
return 0;
}
const TypeDescriptor * element = ReferentType();
const TypeDescriptor * concrete =
element ? element->NonRefTypeDescriptor() : 0;
const PrimitiveType type = element ? element->NonRefType() : UNKNOWN_TYPE;
switch( type ) {
case sdaiINSTANCE:
return new EntityAggregate;
case sdaiSELECT:
return new SelectAggregate(
dynamic_cast<const SelectTypeDescriptor *>( concrete ) );
case sdaiENUMERATION:
return new DescriptorEnumAggregate(
dynamic_cast<const EnumTypeDescriptor *>( concrete ) );
case sdaiBOOLEAN:
return new BOOLEANS;
case sdaiLOGICAL:
return new LOGICALS;
case sdaiINTEGER:
return new IntAggregate;
case sdaiREAL:
case sdaiNUMBER:
return new RealAggregate;
case sdaiSTRING:
return new StringAggregate;
case sdaiBINARY:
return new BinaryAggregate;
default:
return new GenericAggregate;
}
}
@@ -13,18 +62,20 @@ void AggrTypeDescriptor::AssignAggrCreator( AggregateCreator f ) {
}
AggrTypeDescriptor::AggrTypeDescriptor( ) :
_uniqueElements( "UNKNOWN_TYPE" ) {
_bound1 = -1;
_bound2 = -1;
_aggrDomainType = 0;
_bound1( -1 ), _bound2( -1 ), _uniqueElements( "UNKNOWN_TYPE" ),
_aggrDomainType( 0 ), CreateNewAggr( 0 ),
_bound1_type( bound_unset ), _bound2_type( bound_unset ),
_bound1_callback( 0 ), _bound2_callback( 0 ) {
}
AggrTypeDescriptor::AggrTypeDescriptor( SDAI_Integer b1,
SDAI_Integer b2,
Logical uniqElem,
TypeDescriptor * aggrDomType )
: _bound1( b1 ), _bound2( b2 ), _uniqueElements( uniqElem ) {
_aggrDomainType = aggrDomType;
: _bound1( b1 ), _bound2( b2 ), _uniqueElements( uniqElem ),
_aggrDomainType( aggrDomType ), CreateNewAggr( 0 ),
_bound1_type( bound_constant ), _bound2_type( bound_constant ),
_bound1_callback( 0 ), _bound2_callback( 0 ) {
}
AggrTypeDescriptor::~AggrTypeDescriptor() {
+2 -2
View File
@@ -79,14 +79,14 @@ void Schema::GenerateExpress( ostream & out ) const {
}
if( !_function_list.empty() ) {
out << "(* *************FUNCTIONS************* *)" << endl;
count = _function_list.size();
count = (int)_function_list.size();
for( i = 0; i < count; i++ ) {
out << endl << _function_list[i] << endl;
}
}
if( !_procedure_list.empty() ) {
out << "(* *************PROCEDURES************* *)" << endl;
count = _procedure_list.size();
count = (int)_procedure_list.size();
for( i = 0; i < count; i++ ) {
out << endl << _procedure_list[i] << endl;
}
+204
View File
@@ -1,10 +1,158 @@
#include <cstring>
#include <limits>
#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include "clstepcore/entityDescriptor.h"
#include "clstepcore/Registry.h"
#include "clstepcore/attrDescriptor.h"
#include "clstepcore/inverseAttribute.h"
#include "clstepcore/SubSuperIterators.h"
#include "clstepcore/STEPattribute.h"
#include "clstepcore/sdaiApplication_instance.h"
struct EntityAttributeLayoutEntry {
explicit EntityAttributeLayoutEntry(
const AttrDescriptor * attributeDescriptor )
: descriptor( attributeDescriptor ), derived( false ),
redefining( std::numeric_limits<size_t>::max() ) {
}
const AttrDescriptor * descriptor;
bool derived;
size_t redefining;
};
class EntityAttributeLayout {
public:
EntityAttributeLayout() : hasRedefinitions( false ) {
}
std::vector<EntityAttributeLayoutEntry> attributes;
bool hasRedefinitions;
};
namespace {
const size_t noAttribute = std::numeric_limits<size_t>::max();
class EntityAttributeLayoutCache {
typedef std::unordered_map<const EntityDescriptor *,
EntityAttributeLayout *> LayoutMap;
LayoutMap _layouts;
std::mutex _mutex;
public:
~EntityAttributeLayoutCache() {
for( LayoutMap::iterator i = _layouts.begin();
i != _layouts.end(); ++i ) {
delete i->second;
}
}
EntityAttributeLayout * Find( const EntityDescriptor * entity ) {
std::lock_guard<std::mutex> lock( _mutex );
LayoutMap::iterator i = _layouts.find( entity );
return i == _layouts.end() ? 0 : i->second;
}
void Insert( const EntityDescriptor * entity,
EntityAttributeLayout * layout ) {
std::lock_guard<std::mutex> lock( _mutex );
LayoutMap::iterator i = _layouts.find( entity );
if( i == _layouts.end() ) {
_layouts[entity] = layout;
} else {
delete layout;
}
}
void Erase( const EntityDescriptor * entity ) {
std::lock_guard<std::mutex> lock( _mutex );
LayoutMap::iterator i = _layouts.find( entity );
if( i != _layouts.end() ) {
delete i->second;
_layouts.erase( i );
}
}
};
EntityAttributeLayoutCache & layoutCache() {
static EntityAttributeLayoutCache cache;
return cache;
}
size_t findAttribute( const EntityAttributeLayout & layout,
const char * name, const char * owner ) {
for( size_t i = 0; i < layout.attributes.size(); ++i ) {
const AttrDescriptor * candidate = layout.attributes[i].descriptor;
if( strcmp( name, candidate->Name() ) == 0 &&
( !owner || candidate->Owner().IsA( owner ) ) ) {
return i;
}
}
return noAttribute;
}
void appendLayout( const EntityDescriptor * ed, EntityAttributeLayout & layout,
std::set<const EntityDescriptor *> & visited ) {
if( !ed || !visited.insert( ed ).second ) {
return;
}
EntityDescItr supers( ed->Supertypes() );
const EntityDescriptor * super = 0;
while( ( super = supers.NextEntityDesc() ) ) {
appendLayout( super, layout, visited );
}
AttrDescItr attrs( ed->ExplicitAttr() );
const AttrDescriptor * ad = 0;
while( ( ad = attrs.NextAttrDesc() ) ) {
const char * separator = strrchr( ad->Name(), '.' );
const char * simpleName = separator ? separator + 1 : ad->Name();
if( ad->AttrType() == AttrType_Deriving ) {
const char * ownerName = ed->Name();
std::string qualifiedOwner;
if( separator ) {
qualifiedOwner.assign( ad->Name(), separator - ad->Name() );
ownerName = qualifiedOwner.c_str();
}
const size_t target =
findAttribute( layout, simpleName, ownerName );
if( target != noAttribute ) {
layout.attributes[target].derived = true;
}
continue;
}
if( ad->AttrType() == AttrType_Inverse ) {
continue;
}
layout.attributes.push_back( EntityAttributeLayoutEntry( ad ) );
if( ad->AttrType() == AttrType_Redefining ) {
const size_t target = findAttribute( layout, simpleName, 0 );
if( target != noAttribute ) {
layout.attributes[target].redefining =
layout.attributes.size() - 1;
layout.hasRedefinitions = true;
}
}
}
}
class LateBoundEntity : public SDAI_Application_instance {
public:
explicit LateBoundEntity( const EntityDescriptor * ed ) {
eDesc = ed;
}
};
}
EntityDescriptor::EntityDescriptor( )
: _abstractEntity( LUnknown ), _extMapping( LUnknown ),
@@ -23,9 +171,65 @@ EntityDescriptor::EntityDescriptor( const char * name, // i.e. char *
}
EntityDescriptor::~EntityDescriptor() {
layoutCache().Erase( this );
delete _uniqueness_rules;
}
SDAI_Application_instance * EntityDescriptor::CreateEntity() const {
if( NewSTEPentity ) {
return NewSTEPentity();
}
PrepareLateBoundLayout();
EntityAttributeLayout * layout = layoutCache().Find( this );
LateBoundEntity * entity = new LateBoundEntity( this );
std::vector<STEPattribute *> attributes;
if( layout->hasRedefinitions ) {
attributes.reserve( layout->attributes.size() );
}
for( size_t i = 0; i < layout->attributes.size(); ++i ) {
const EntityAttributeLayoutEntry & entry = layout->attributes[i];
STEPattribute * attribute = new STEPattribute( *entry.descriptor );
attribute->set_null();
if( entry.derived ) {
attribute->Derive();
}
entity->attributes.push( attribute );
if( layout->hasRedefinitions ) {
attributes.push_back( attribute );
}
}
if( layout->hasRedefinitions ) {
for( size_t i = 0; i < layout->attributes.size(); ++i ) {
const size_t redefining = layout->attributes[i].redefining;
if( redefining != noAttribute ) {
attributes[i]->RedefiningAttr( attributes[redefining] );
}
}
}
return entity;
}
void EntityDescriptor::PrepareLateBoundLayout() const {
if( layoutCache().Find( this ) ) {
return;
}
EntityAttributeLayout * layout = new EntityAttributeLayout;
std::set<const EntityDescriptor *> visited;
appendLayout( this, *layout, visited );
layoutCache().Insert( this, layout );
}
void EntityDescriptor::InvalidateLateBoundLayout() const {
layoutCache().Erase( this );
EntityDescItr subtypes( _subtypes );
const EntityDescriptor * subtype = 0;
while( ( subtype = subtypes.NextEntityDesc() ) ) {
subtype->InvalidateLateBoundLayout();
}
}
// initialize one inverse attr; used in InitIAttrs, below
void initIAttr( Inverse_attribute * ia, Registry & reg, const char * schNm, const char * name ) {
const AttrDescriptor * ad;
+123 -3
View File
@@ -1,5 +1,103 @@
#include "clstepcore/enumTypeDescriptor.h"
#include <cctype>
#include <cstring>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
namespace {
typedef std::vector<std::string> EnumElements;
typedef std::unordered_map<const EnumTypeDescriptor *, EnumElements>
EnumElementMap;
EnumElementMap & enumElementMap() {
static EnumElementMap elements;
return elements;
}
std::mutex & enumElementMutex() {
static std::mutex mutex;
return mutex;
}
EnumElements parseEnumDescription( const char * description ) {
EnumElements result;
if( !description ) {
return result;
}
const char * begin = strchr( description, '(' );
const char * end = strrchr( description, ')' );
if( !begin || !end || begin >= end ) {
return result;
}
++begin;
while( begin < end ) {
while( begin < end &&
( std::isspace( static_cast<unsigned char>( *begin ) ) ||
*begin == ',' ) ) {
++begin;
}
const char * itemEnd = begin;
while( itemEnd < end && *itemEnd != ',' ) {
++itemEnd;
}
const char * trimmed = itemEnd;
while( trimmed > begin &&
std::isspace( static_cast<unsigned char>( trimmed[-1] ) ) ) {
--trimmed;
}
if( trimmed > begin ) {
std::string item( begin, trimmed );
for( size_t i = 0; i < item.size(); ++i ) {
item[i] = static_cast<char>( std::toupper(
static_cast<unsigned char>( item[i] ) ) );
}
result.push_back( item );
}
begin = itemEnd + ( itemEnd < end ? 1 : 0 );
}
return result;
}
EnumElements descriptorElements( const EnumTypeDescriptor & descriptor ) {
std::lock_guard<std::mutex> lock( enumElementMutex() );
EnumElementMap::const_iterator i = enumElementMap().find( &descriptor );
if( i != enumElementMap().end() ) {
return i->second;
}
return parseEnumDescription( descriptor.Description() );
}
class DescriptorEnum : public SDAI_Enum {
std::string _name;
EnumElements _elements;
public:
explicit DescriptorEnum( const EnumTypeDescriptor & descriptor )
: _name( descriptor.Name() ),
_elements( descriptorElements( descriptor ) ) {
nullify();
}
virtual int no_elements() const {
return static_cast<int>( _elements.size() );
}
virtual const char * Name() const {
return _name.c_str();
}
virtual const char * element_at( int index ) const {
return index >= 0 && static_cast<size_t>( index ) < _elements.size() ?
_elements[index].c_str() : "UNSET";
}
};
}
/*
* why have EnumTypeDescriptor + EnumerationTypeDescriptor ???
* this was in ExpDict.cc before splitting it up
@@ -16,12 +114,34 @@ EnumTypeDescriptor::EnumTypeDescriptor( const char * nm, PrimitiveType ft,
: TypeDescriptor( nm, ft, origSchema, d ), CreateNewEnum( f ) {
}
SDAI_Enum * EnumTypeDescriptor::CreateEnum() {
SDAI_Enum * EnumTypeDescriptor::CreateEnum() const {
if( CreateNewEnum ) {
return CreateNewEnum();
} else {
return 0;
}
if( NonRefType() == sdaiBOOLEAN ) {
return new SDAI_BOOLEAN;
}
if( NonRefType() == sdaiLOGICAL ) {
return new SDAI_LOGICAL;
}
return new DescriptorEnum( *this );
}
EnumTypeDescriptor::~EnumTypeDescriptor() {
std::lock_guard<std::mutex> lock( enumElementMutex() );
enumElementMap().erase( this );
}
void RegisterEnumDescriptorElements(
const EnumTypeDescriptor & descriptor,
const char * const * elements, size_t count ) {
EnumElements values;
values.reserve( count );
for( size_t i = 0; i < count; ++i ) {
values.push_back( elements[i] ? elements[i] : "" );
}
std::lock_guard<std::mutex> lock( enumElementMutex() );
enumElementMap()[&descriptor].swap( values );
}
const char * EnumTypeDescriptor::GenerateExpress( std::string & buf ) const {
+5 -5
View File
@@ -115,11 +115,11 @@ InstMgr::VerifyInstances( ErrorDescriptor & err ) {
if( !mn ) {
++errorCount;
if( errorCount == 1 )
sprintf( errbuf,
snprintf( errbuf, sizeof(errbuf),
"VerifyInstances: Unable to verify the following instances: node %d",
i );
else {
sprintf( errbuf, ", node %d", i );
snprintf( errbuf, sizeof(errbuf), ", node %d", i );
}
err.AppendToDetailMsg( errbuf );
@@ -139,18 +139,18 @@ InstMgr::VerifyInstances( ErrorDescriptor & err ) {
}
++errorCount;
if( errorCount == 1 )
sprintf( errbuf,
snprintf( errbuf, sizeof(errbuf),
"VerifyInstances: Unable to verify the following instances: #%d",
se->StepFileId() );
else {
sprintf( errbuf, ", #%d", se->StepFileId() );
snprintf( errbuf, sizeof(errbuf), ", #%d", se->StepFileId() );
}
err.AppendToDetailMsg( errbuf );
}
}
}
if( errorCount ) {
sprintf( errbuf,
snprintf( errbuf, sizeof(errbuf),
"VerifyInstances: %d invalid instances in list.\n",
errorCount );
err.AppendToUserMsg( errbuf );
+5 -5
View File
@@ -153,7 +153,7 @@ std::string WriteReal( SDAI_Real val ) {
// Also use G instead of g since G writes uppercase E (E instead of e
// is also required by Part 21) when scientific notation is used - DAS
sprintf( rbuf, "%.*G", ( int ) RealNumPrecision, val );
snprintf( rbuf, sizeof(rbuf), "%.*G", ( int ) RealNumPrecision, val );
if( !strchr( rbuf, '.' ) ) {
if( strchr( rbuf, 'E' ) || strchr( rbuf, 'e' ) ) {
char * expon = strchr( rbuf, 'E' );
@@ -168,7 +168,7 @@ std::string WriteReal( SDAI_Real val ) {
expon++;
s += expon;
} else {
int rindex = strlen( rbuf );
int rindex = (int)strlen( rbuf );
rbuf[rindex] = '.';
rbuf[rindex + 1] = '\0';
s = rbuf;
@@ -490,7 +490,7 @@ void PushPastImbedAggr( istream & in, std::string & s, ErrorDescriptor * err ) {
}
if( c != ')' ) {
err->GreaterSeverity( SEVERITY_INPUT_ERROR );
sprintf( messageBuf, "Invalid aggregate value.\n" );
snprintf( messageBuf, sizeof(messageBuf), "Invalid aggregate value.\n" );
err->AppendToDetailMsg( messageBuf );
s.append( ")" );
} else {
@@ -518,7 +518,7 @@ void PushPastAggr1Dim( istream & in, std::string & s, ErrorDescriptor * err ) {
while( in.good() && ( c != ')' ) ) {
if( c == '(' ) {
err->GreaterSeverity( SEVERITY_WARNING );
sprintf( messageBuf, "Invalid aggregate value.\n" );
snprintf( messageBuf, sizeof(messageBuf), "Invalid aggregate value.\n" );
err->AppendToDetailMsg( messageBuf );
}
@@ -532,7 +532,7 @@ void PushPastAggr1Dim( istream & in, std::string & s, ErrorDescriptor * err ) {
}
if( c != ')' ) {
err->GreaterSeverity( SEVERITY_INPUT_ERROR );
sprintf( messageBuf, "Invalid aggregate value.\n" );
snprintf( messageBuf, sizeof(messageBuf), "Invalid aggregate value.\n" );
err->AppendToDetailMsg( messageBuf );
s.append( ")" );
} else {
File diff suppressed because it is too large Load Diff
+317
View File
@@ -0,0 +1,317 @@
#include "clstepcore/schemaModule.h"
#include <cassert>
#include <unordered_map>
#include "clstepcore/attrDescriptor.h"
#include "clstepcore/aggrTypeDescriptor.h"
#include "clstepcore/derivedAttribute.h"
#include "clstepcore/dictSchema.h"
#include "clstepcore/entityDescriptor.h"
#include "clstepcore/enumTypeDescriptor.h"
#include "clstepcore/inverseAttribute.h"
#include "clstepcore/selectTypeDescriptor.h"
#include "clstepcore/typeDescriptor.h"
namespace {
struct PendingSchemaModule {
std::vector<const EntityDescriptor *> entities;
std::vector<const TypeDescriptor *> types;
std::vector<const AttrDescriptor *> attributes;
};
class PendingSchemaModules {
typedef std::unordered_map<const Schema *, PendingSchemaModule> ModuleMap;
ModuleMap _modules;
const Schema * _lastSchema;
PendingSchemaModule * _lastModule;
PendingSchemaModule * Find( Schema & schema ) {
if( _lastSchema == &schema ) {
return _lastModule;
}
ModuleMap::iterator i = _modules.find( &schema );
_lastSchema = &schema;
_lastModule = i == _modules.end() ? 0 : &i->second;
return _lastModule;
}
public:
PendingSchemaModules() : _lastSchema( 0 ), _lastModule( 0 ) {
}
void Begin( Schema & schema ) {
PendingSchemaModule & module = _modules[&schema];
module.entities.clear();
module.types.clear();
module.attributes.clear();
_lastSchema = &schema;
_lastModule = &module;
}
void RecordEntity( Schema & schema, const EntityDescriptor * entity ) {
PendingSchemaModule * module = Find( schema );
if( module ) {
module->entities.push_back( entity );
}
}
void RecordType( Schema & schema, const TypeDescriptor * type ) {
PendingSchemaModule * module = Find( schema );
if( module ) {
module->types.push_back( type );
}
}
void RecordAttribute( Schema & schema, const AttrDescriptor * attribute ) {
PendingSchemaModule * module = Find( schema );
if( module ) {
module->attributes.push_back( attribute );
}
}
bool Consume( Schema & schema, PendingSchemaModule & module ) {
ModuleMap::iterator i = _modules.find( &schema );
if( i == _modules.end() ) {
return false;
}
module.entities.swap( i->second.entities );
module.types.swap( i->second.types );
module.attributes.swap( i->second.attributes );
if( _lastSchema == &schema ) {
_lastSchema = 0;
_lastModule = 0;
}
_modules.erase( i );
return true;
}
};
PendingSchemaModules & pendingSchemaModules() {
static PendingSchemaModules modules;
return modules;
}
const TypeDescriptor * typeFromSlot( const SchemaModuleSlot & record ) {
switch( record.kind ) {
case SchemaModuleSlot_EnumType:
return *static_cast<EnumTypeDescriptor * const *>( record.slot );
case SchemaModuleSlot_SelectType:
return *static_cast<SelectTypeDescriptor * const *>( record.slot );
case SchemaModuleSlot_AggregateType:
return *static_cast<AggrTypeDescriptor * const *>( record.slot );
case SchemaModuleSlot_ArrayType:
return *static_cast<ArrayTypeDescriptor * const *>( record.slot );
case SchemaModuleSlot_ListType:
return *static_cast<ListTypeDescriptor * const *>( record.slot );
case SchemaModuleSlot_SetType:
return *static_cast<SetTypeDescriptor * const *>( record.slot );
case SchemaModuleSlot_BagType:
return *static_cast<BagTypeDescriptor * const *>( record.slot );
case SchemaModuleSlot_Type:
default:
return *static_cast<TypeDescriptor * const *>( record.slot );
}
}
const AttrDescriptor * attributeFromSlot( const SchemaModuleSlot & record ) {
switch( record.kind ) {
case SchemaModuleSlot_DerivedAttribute:
return *static_cast<Derived_attribute * const *>( record.slot );
case SchemaModuleSlot_InverseAttribute:
return *static_cast<Inverse_attribute * const *>( record.slot );
case SchemaModuleSlot_Attribute:
default:
return *static_cast<AttrDescriptor * const *>( record.slot );
}
}
void prepareLateBoundLayouts(
const std::vector<const EntityDescriptor *> & entities ) {
for( size_t i = 0; i < entities.size(); ++i ) {
if( entities[i] && !entities[i]->NewSTEPentity ) {
entities[i]->PrepareLateBoundLayout();
}
}
}
}
class SchemaLoadContext::Impl {
public:
typedef std::unordered_map<const Schema *, PendingSchemaModule> ModuleMap;
ModuleMap modules;
};
SchemaLoadContext::SchemaLoadContext() : _impl( new Impl ) {
}
SchemaLoadContext::~SchemaLoadContext() {
delete _impl;
}
void SchemaLoadContext::Begin( Schema & schema ) {
PendingSchemaModule & module = _impl->modules[&schema];
module.entities.clear();
module.types.clear();
module.attributes.clear();
}
void SchemaLoadContext::RecordEntity(
Schema & schema, const EntityDescriptor * entity ) {
_impl->modules[&schema].entities.push_back( entity );
}
void SchemaLoadContext::RecordType(
Schema & schema, const TypeDescriptor * type ) {
_impl->modules[&schema].types.push_back( type );
}
void SchemaLoadContext::RecordAttribute(
Schema & schema, const AttrDescriptor * attribute ) {
_impl->modules[&schema].attributes.push_back( attribute );
}
void BeginSchemaModuleImage( Schema & schema ) {
pendingSchemaModules().Begin( schema );
}
void RecordSchemaModuleEntity( Schema & schema,
const EntityDescriptor * entity ) {
pendingSchemaModules().RecordEntity( schema, entity );
}
void RecordSchemaModuleType( Schema & schema, const TypeDescriptor * type ) {
pendingSchemaModules().RecordType( schema, type );
}
void RecordSchemaModuleAttribute( Schema & schema,
const AttrDescriptor * attribute ) {
pendingSchemaModules().RecordAttribute( schema, attribute );
}
SchemaModule::SchemaModule() : _schema( 0 ), _fingerprint( 0 ) {
}
SchemaModule::~SchemaModule() {
}
void SchemaModule::Initialize(
Schema & schema,
const EntityDescriptor * const * entities, size_t entityCount,
const TypeDescriptor * const * types, size_t typeCount,
const AttrDescriptor * const * attributes, size_t attributeCount ) {
_schema = &schema;
_fingerprint = 0;
_entities.assign( entities, entities + entityCount );
_types.assign( types, types + typeCount );
_attributes.assign( attributes, attributes + attributeCount );
prepareLateBoundLayouts( _entities );
}
void SchemaModule::InitializeFromSlots(
Schema & schema,
const SchemaModuleSlot * entities, size_t entityCount,
const SchemaModuleSlot * types, size_t typeCount,
const SchemaModuleSlot * attributes, size_t attributeCount ) {
_schema = &schema;
_fingerprint = 0;
_entities.clear();
_entities.reserve( entityCount );
for( size_t i = 0; i < entityCount; ++i ) {
_entities.push_back(
*static_cast<EntityDescriptor * const *>( entities[i].slot ) );
}
_types.clear();
_types.reserve( typeCount );
for( size_t i = 0; i < typeCount; ++i ) {
_types.push_back( typeFromSlot( types[i] ) );
}
_attributes.clear();
_attributes.reserve( attributeCount );
for( size_t i = 0; i < attributeCount; ++i ) {
_attributes.push_back( attributeFromSlot( attributes[i] ) );
}
prepareLateBoundLayouts( _entities );
}
void SchemaModule::InitializeFromImage( Schema & schema,
const SchemaModuleImage & image ) {
PendingSchemaModule module;
const bool found = pendingSchemaModules().Consume( schema, module );
const bool valid = found &&
image.version == SchemaModuleImageVersion_1 &&
module.entities.size() == image.entityCount &&
module.types.size() == image.typeCount &&
module.attributes.size() == image.attributeCount;
assert( valid );
if( !valid ) {
_schema = 0;
_entities.clear();
_types.clear();
_attributes.clear();
return;
}
_schema = &schema;
_fingerprint = 0;
_entities.swap( module.entities );
_types.swap( module.types );
_attributes.swap( module.attributes );
prepareLateBoundLayouts( _entities );
}
bool SchemaModule::InitializeFromContext(
Schema & schema, SchemaLoadContext & context, uint64_t fingerprint ) {
SchemaLoadContext::Impl::ModuleMap::iterator i =
context._impl->modules.find( &schema );
if( i == context._impl->modules.end() ) {
return false;
}
_schema = &schema;
_entities.swap( i->second.entities );
_types.swap( i->second.types );
_attributes.swap( i->second.attributes );
_fingerprint = fingerprint;
context._impl->modules.erase( i );
prepareLateBoundLayouts( _entities );
return true;
}
bool SchemaModule::IsInitialized() const {
return _schema != 0;
}
const Schema & SchemaModule::GetSchema() const {
assert( _schema );
return *_schema;
}
const EntityDescriptor * SchemaModule::Entity( size_t id ) const {
return id < _entities.size() ? _entities[id] : 0;
}
const TypeDescriptor * SchemaModule::Type( size_t id ) const {
return id < _types.size() ? _types[id] : 0;
}
const AttrDescriptor * SchemaModule::Attribute( size_t id ) const {
return id < _attributes.size() ? _attributes[id] : 0;
}
size_t SchemaModule::EntityCount() const {
return _entities.size();
}
size_t SchemaModule::TypeCount() const {
return _types.size();
}
size_t SchemaModule::AttributeCount() const {
return _attributes.size();
}
uint64_t SchemaModule::Fingerprint() const {
return _fingerprint;
}
+26 -24
View File
@@ -106,7 +106,7 @@ SDAI_Application_instance * SDAI_Application_instance::Replicate() {
if( IsComplex() ) {
cerr << "STEPcomplex::Replicate() should be called: " << __FILE__
<< __LINE__ << "\n" << _POC_ "\n";
sprintf( errStr,
snprintf( errStr, sizeof(errStr),
"SDAI_Application_instance::Replicate(): %s - entity #%d.\n",
"Programming ERROR - STEPcomplex::Replicate() should be called",
STEPfile_id );
@@ -119,7 +119,10 @@ SDAI_Application_instance * SDAI_Application_instance::Replicate() {
return S_ENTITY_NULL;
}
SDAI_Application_instance * seNew = eDesc->NewSTEPentity();
SDAI_Application_instance * seNew = eDesc->CreateEntity();
if( !seNew ) {
return S_ENTITY_NULL;
}
seNew -> CopyAs( this );
return seNew;
}
@@ -151,7 +154,7 @@ void SDAI_Application_instance::STEPwrite_reference( ostream & out ) {
const char * SDAI_Application_instance::STEPwrite_reference( std::string & buf ) {
char tmp[64];
sprintf( tmp, "#%d", STEPfile_id );
snprintf( tmp, sizeof(tmp), "#%d", STEPfile_id );
buf = tmp;
return const_cast<char *>( buf.c_str() );
}
@@ -419,7 +422,7 @@ const char * SDAI_Application_instance::STEPwrite( std::string & buf, const char
char instanceInfo[BUFSIZ+1];
std::string tmp;
sprintf( instanceInfo, "#%d=%s(", STEPfile_id, StrToUpper( EntityName( currSch ), tmp ) );
snprintf( instanceInfo, sizeof(instanceInfo), "#%d=%s(", STEPfile_id, StrToUpper( EntityName( currSch ), tmp ) );
buf.append( instanceInfo );
int n = attributes.list_length();
@@ -443,7 +446,7 @@ void SDAI_Application_instance::PrependEntityErrMsg() {
if( _error.severity() == SEVERITY_NULL ) {
// if there is not an error already
sprintf( errStr, "\nERROR: ENTITY #%d %s\n", GetFileId(),
snprintf( errStr, sizeof(errStr), "\nERROR: ENTITY #%d %s\n", GetFileId(),
EntityName() );
_error.PrependToDetailMsg( errStr );
}
@@ -464,14 +467,14 @@ void SDAI_Application_instance::STEPread_error( char c, int i, istream & in, con
if( _error.severity() == SEVERITY_NULL ) {
// if there is not an error already
sprintf( errStr, "\nERROR: ENTITY #%d %s\n", GetFileId(),
snprintf( errStr, sizeof(errStr), "\nERROR: ENTITY #%d %s\n", GetFileId(),
EntityName() );
_error.PrependToDetailMsg( errStr );
}
if( ( i >= 0 ) && ( i < attributes.list_length() ) ) { // i is an attribute
Error().GreaterSeverity( SEVERITY_WARNING );
sprintf( errStr, " invalid data before type \'%s\'\n",
snprintf( errStr, sizeof(errStr), " invalid data before type \'%s\'\n",
attributes[i].TypeName().c_str() );
_error.AppendToDetailMsg( errStr );
} else {
@@ -487,7 +490,7 @@ void SDAI_Application_instance::STEPread_error( char c, int i, istream & in, con
_error.AppendToDetailMsg( c );
_error.AppendToDetailMsg( '\n' );
sprintf( errStr, "\nfinished reading #%d\n", STEPfile_id );
snprintf( errStr, sizeof(errStr), "\nfinished reading #%d\n", STEPfile_id );
_error.AppendToDetailMsg( errStr );
return;
}
@@ -552,7 +555,7 @@ Severity SDAI_Application_instance::STEPread( int id, int idIncr,
// set the severity for this entity
_error.GreaterSeverity( severe );
sprintf( errStr, " %s : ", attributes[i].Name() );
snprintf( errStr, sizeof(errStr), " %s : ", attributes[i].Name() );
_error.AppendToDetailMsg( errStr ); // add attr name
_error.AppendToDetailMsg(
"Since using pre-technical corrigendum... missing asterisk for redefined attr.\n" );
@@ -565,9 +568,6 @@ Severity SDAI_Application_instance::STEPread( int id, int idIncr,
if( c == ')' ) { // assume you are at the end so read last char
in >> c;
}
cout << "Entity #" << STEPfile_id
<< " skipping redefined attribute "
<< attributes[i].aDesc->Name() << endl << endl << flush;
}
// increment counter to read following attr since these attrs
// aren't written or read => there won't be a delimiter either
@@ -583,7 +583,7 @@ Severity SDAI_Application_instance::STEPread( int id, int idIncr,
// set the severity for this entity
_error.GreaterSeverity( severe );
sprintf( errStr, " %s : ", attributes[i].Name() );
snprintf( errStr, sizeof(errStr), " %s : ", attributes[i].Name() );
_error.AppendToDetailMsg( errStr ); // add attr name
_error.AppendToDetailMsg( attributes[i].Error().DetailMsg() ); // add attr error
_error.AppendToUserMsg( attributes[i].Error().UserMsg() );
@@ -653,7 +653,7 @@ Severity SDAI_Application_instance::STEPread( int id, int idIncr,
}
}
_error.AppendToDetailMsg( tmp.c_str() );
sprintf( errStr, "\nfinished reading #%d\n", STEPfile_id );
snprintf( errStr, sizeof(errStr), "\nfinished reading #%d\n", STEPfile_id );
_error.AppendToDetailMsg( errStr );
// end of imported code
return _error.severity();
@@ -673,12 +673,14 @@ SDAI_Application_instance * ReadEntityRef( istream & in, ErrorDescriptor * err,
err->AppendToDetailMsg( "Use of @ instead of # to identify entity.\n" );
err->GreaterSeverity( SEVERITY_WARNING );
// no break statement here on purpose
#if defined(__GNUC__) || defined(__clang__)
[[gnu::fallthrough]];
#endif
case '#': {
int id = -1;
in >> id;
if( in.fail() ) { // there's been an error in input
sprintf( errStr, "Invalid entity reference value.\n" );
snprintf( errStr, sizeof(errStr), "Invalid entity reference value.\n" );
err->AppendToDetailMsg( errStr );
err->AppendToUserMsg( errStr );
err->GreaterSeverity( SEVERITY_WARNING );
@@ -692,7 +694,7 @@ SDAI_Application_instance * ReadEntityRef( istream & in, ErrorDescriptor * err,
if( !instances ) {
cerr << "Internal error: " << __FILE__ << __LINE__
<< "\n" << _POC_ "\n";
sprintf( errStr,
snprintf( errStr, sizeof(errStr),
"STEPread_reference(): %s - entity #%d %s.\n",
"BUG - cannot read reference without the InstMgr",
id, "is unknown" );
@@ -714,7 +716,7 @@ SDAI_Application_instance * ReadEntityRef( istream & in, ErrorDescriptor * err,
} else {
cerr << "Internal error: " << __FILE__ << __LINE__
<< "\n" << _POC_ "\n";
sprintf( errStr,
snprintf( errStr, sizeof(errStr),
"STEPread_reference(): %s - entity #%d %s.\n",
"BUG - MgrNode::GetSTEPentity returned NULL pointer",
id, "is unknown" );
@@ -724,7 +726,7 @@ SDAI_Application_instance * ReadEntityRef( istream & in, ErrorDescriptor * err,
return S_ENTITY_NULL;
}
} else {
sprintf( errStr, "Reference to non-existent ENTITY #%d.\n",
snprintf( errStr, sizeof(errStr), "Reference to non-existent ENTITY #%d.\n",
id );
err->AppendToDetailMsg( errStr );
err->AppendToUserMsg( errStr );
@@ -761,7 +763,7 @@ Severity EntityValidLevel( SDAI_Application_instance * se,
if( !ed || ( ed->NonRefType() != ENTITY_TYPE ) ) {
err->GreaterSeverity( SEVERITY_BUG );
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
" BUG: EntityValidLevel() called with %s",
"missing or invalid EntityDescriptor\n" );
err->AppendToUserMsg( messageBuf );
@@ -772,7 +774,7 @@ Severity EntityValidLevel( SDAI_Application_instance * se,
}
if( !se || ( se == S_ENTITY_NULL ) ) {
err->GreaterSeverity( SEVERITY_BUG );
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
" BUG: EntityValidLevel() called with null pointer %s\n",
"for SDAI_Application_instance argument." );
err->AppendToUserMsg( messageBuf );
@@ -800,7 +802,7 @@ Severity EntityValidLevel( SDAI_Application_instance * se,
}
}
err->GreaterSeverity( SEVERITY_WARNING );
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
" Entity #%d exists but is not a %s or descendant.\n",
se->STEPfile_id, ed->Name() );
err->AppendToUserMsg( messageBuf );
@@ -809,7 +811,7 @@ Severity EntityValidLevel( SDAI_Application_instance * se,
}
} else {
err->GreaterSeverity( SEVERITY_BUG );
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
" BUG: EntityValidLevel(): SDAI_Application_instance #%d has a %s",
se->STEPfile_id, "missing or invalid EntityDescriptor\n" );
err->AppendToUserMsg( messageBuf );
@@ -893,7 +895,7 @@ Severity EntityValidLevel( const char * attrValue, // string contain entity ref
SDAI_Application_instance * se = mn->GetSTEPentity();
return EntityValidLevel( se, ed, err );
} else {
sprintf( messageBuf,
snprintf( messageBuf, sizeof(messageBuf),
" Attribute's Entity Reference %s does not exist.\n",
attrValue );
err->AppendToUserMsg( messageBuf );
@@ -908,7 +910,7 @@ Severity EntityValidLevel( const char * attrValue, // string contain entity ref
return err->severity();
}
sprintf( messageBuf, "Invalid attribute entity reference value: '%s'.\n",
snprintf( messageBuf, sizeof(messageBuf), "Invalid attribute entity reference value: '%s'.\n",
attrValue );
err->AppendToUserMsg( messageBuf );
err->AppendToDetailMsg( messageBuf );
+611 -7
View File
@@ -16,33 +16,218 @@
#include <string>
#include "clstepcore/sdai.h"
#include "clstepcore/STEPattribute.h"
#include "clstepcore/STEPaggregate.h"
#include "clstepcore/read_func.h"
#ifdef SC_LOGGING
#include <fstream.h>
extern ofstream * logStream;
#endif
class SDAI_Select::Storage {
public:
const TypeDescriptor * type;
SDAI_Integer integer_value;
SDAI_Real real_value;
SDAI_Application_instance * entity_value;
SDAI_String * string_value;
SDAI_Binary * binary_value;
SDAI_Enum * enum_value;
SDAI_Select * select_value;
STEPaggregate * aggregate_value;
Storage()
: type( 0 ), integer_value( 0 ), real_value( 0.0 ), entity_value( 0 ),
string_value( 0 ), binary_value( 0 ), enum_value( 0 ),
select_value( 0 ), aggregate_value( 0 ) {
}
~Storage() {
Clear();
}
void Clear() {
delete string_value;
delete binary_value;
delete enum_value;
delete select_value;
delete aggregate_value;
type = 0;
integer_value = 0;
real_value = 0.0;
entity_value = 0;
string_value = 0;
binary_value = 0;
enum_value = 0;
select_value = 0;
aggregate_value = 0;
}
bool Reset( const TypeDescriptor * td ) {
Clear();
if( !td ) {
return true;
}
type = td;
const TypeDescriptor * concrete = td->NonRefTypeDescriptor();
switch( td->NonRefType() ) {
case sdaiSTRING:
string_value = new SDAI_String;
break;
case sdaiBINARY:
binary_value = new SDAI_Binary;
break;
case sdaiBOOLEAN:
enum_value = new SDAI_BOOLEAN;
break;
case sdaiLOGICAL:
enum_value = new SDAI_LOGICAL;
break;
case sdaiENUMERATION: {
const EnumTypeDescriptor * enum_type =
dynamic_cast<const EnumTypeDescriptor *>( concrete );
enum_value = enum_type ? enum_type->CreateEnum() : 0;
break;
}
case sdaiSELECT: {
const SelectTypeDescriptor * select_type =
dynamic_cast<const SelectTypeDescriptor *>( concrete );
select_value = select_type ? select_type->CreateSelect() : 0;
if( !select_value && select_type ) {
select_value = new SDAI_Select( select_type );
}
break;
}
case sdaiAGGR:
case ARRAY_TYPE:
case BAG_TYPE:
case SET_TYPE:
case LIST_TYPE: {
const AggrTypeDescriptor * aggregate_type =
dynamic_cast<const AggrTypeDescriptor *>( concrete );
aggregate_value = aggregate_type ? aggregate_type->CreateAggregate() : 0;
break;
}
default:
break;
}
if( ( td->NonRefType() == sdaiENUMERATION && !enum_value ) ||
( td->NonRefType() == sdaiSELECT && !select_value ) ||
( td->IsAggrType() && !aggregate_value ) ) {
Clear();
return false;
}
return true;
}
bool CopyFrom( const Storage & other ) {
if( !Reset( other.type ) ) {
return false;
}
if( !type ) {
return true;
}
switch( type->NonRefType() ) {
case sdaiINTEGER:
integer_value = other.integer_value;
break;
case sdaiREAL:
case sdaiNUMBER:
real_value = other.real_value;
break;
case sdaiINSTANCE:
entity_value = other.entity_value;
break;
case sdaiSTRING:
*string_value = *other.string_value;
break;
case sdaiBINARY:
*binary_value = *other.binary_value;
break;
case sdaiBOOLEAN:
case sdaiLOGICAL:
case sdaiENUMERATION:
*enum_value = *other.enum_value;
break;
case sdaiSELECT:
*select_value = *other.select_value;
break;
case sdaiAGGR:
case ARRAY_TYPE:
case BAG_TYPE:
case SET_TYPE:
case LIST_TYPE:
aggregate_value->ShallowCopy( *other.aggregate_value );
break;
default:
break;
}
return true;
}
void * Address() {
if( !type ) {
return 0;
}
switch( type->NonRefType() ) {
case sdaiINTEGER:
return &integer_value;
case sdaiREAL:
case sdaiNUMBER:
return &real_value;
case sdaiINSTANCE:
return entity_value;
case sdaiSTRING:
return string_value;
case sdaiBINARY:
return binary_value;
case sdaiBOOLEAN:
case sdaiLOGICAL:
case sdaiENUMERATION:
return enum_value;
case sdaiSELECT:
return select_value;
case sdaiAGGR:
case ARRAY_TYPE:
case BAG_TYPE:
case SET_TYPE:
case LIST_TYPE:
return aggregate_value;
default:
return 0;
}
}
};
/**********
(member) functions for the select class SDAI_Select
**********/
SDAI_Select::SDAI_Select( const SelectTypeDescriptor * s,
const TypeDescriptor * td )
: _type( s ), underlying_type( td ) {
: _storage( 0 ), _type( s ), underlying_type( td ),
base_type( td ? td->NonRefType() : UNKNOWN_TYPE ) {
#ifdef SC_LOGGING
*logStream << "Exiting SDAI_Select constructor." << endl;
#endif
}
SDAI_Select::SDAI_Select( const SDAI_Select & other ) {
underlying_type = other.underlying_type;
base_type = other.base_type;
_type = other._type;
SDAI_Select::SDAI_Select( const SDAI_Select & other )
: _storage( 0 ), _type( other._type ),
underlying_type( other.underlying_type ), base_type( other.base_type ),
val( other.val ), _error( other._error ) {
if( other._storage ) {
_storage = new Storage;
_storage->CopyFrom( *other._storage );
}
#ifdef SC_LOGGING
*logStream << "Exiting SDAI_Select constructor." << endl;
#endif
}
SDAI_Select::~SDAI_Select() {
delete _storage;
}
SDAI_Select & SDAI_Select::operator=( const SDAI_Select & other ) {
@@ -52,10 +237,36 @@ SDAI_Select & SDAI_Select::operator=( const SDAI_Select & other ) {
base_type = other.base_type;
underlying_type = other.underlying_type;
val = other.val;
if( other._storage ) {
if( !_storage ) {
_storage = new Storage;
}
_storage->CopyFrom( *other._storage );
} else {
delete _storage;
_storage = 0;
}
}
return *this;
}
SDAI_Select & SDAI_Select::operator=( const SDAI_Select * other ) {
if( other ) {
return operator=( *other );
}
nullify();
return *this;
}
SDAI_Select * SDAI_Select::Clone() const {
SDAI_Select * result = _type ? _type->CreateSelect() : 0;
if( !result ) {
result = new SDAI_Select( _type );
}
*result = *this;
return result;
}
Severity SDAI_Select::severity() const {
return _error.severity();
}
@@ -137,21 +348,265 @@ const TypeDescriptor * SDAI_Select::CurrentUnderlyingType() const {
const TypeDescriptor *
SDAI_Select::SetUnderlyingType( const TypeDescriptor * td ) {
// don\'t do anything if the descriptor is bad
if( !td || !( _type -> CanBe( td ) ) ) {
if( !td || ( _type && !( _type -> CanBe( td ) ) ) ) {
return 0;
}
if( _storage && _storage->type != td ) {
_storage->Clear();
}
base_type = td -> NonRefType();
return underlying_type = td;
}
void * SDAI_Select::ValueAddress() {
if( !underlying_type ) {
return 0;
}
if( !_storage ) {
_storage = new Storage;
}
if( _storage->type != underlying_type && !_storage->Reset( underlying_type ) ) {
Error( "Unable to create storage for SELECT value." );
severity( SEVERITY_BUG );
return 0;
}
return _storage->Address();
}
const void * SDAI_Select::ValueAddress() const {
return const_cast<SDAI_Select *>( this )->ValueAddress();
}
SDAI_Select * SDAI_Select::NewSelect() {
SDAI_Select * result = _type ? _type->CreateSelect() : 0;
return result ? result : new SDAI_Select( _type );
}
const TypeDescriptor *
SDAI_Select::AssignEntity( SDAI_Application_instance * se ) {
if( !_type || !se ) {
return 0;
}
TypeDescItr elements( _type->GetElements() );
const TypeDescriptor * td;
while( ( td = elements.NextTypeDesc() ) ) {
const EntityDescriptor * entity_type =
dynamic_cast<const EntityDescriptor *>( td->NonRefTypeDescriptor() );
if( entity_type && se->IsA( entity_type ) ) {
SetUnderlyingType( td );
if( !_storage ) {
_storage = new Storage;
}
if( !_storage->Reset( td ) ) {
return 0;
}
_storage->entity_value = se;
return td;
}
if( td->NonRefType() == sdaiSELECT && td->CanBe( se->eDesc ) ) {
SetUnderlyingType( td );
SDAI_Select * nested = static_cast<SDAI_Select *>( ValueAddress() );
if( nested && nested->AssignEntity( se ) ) {
return td;
}
}
}
severity( SEVERITY_WARNING );
Error( "Mismatch in underlying type." );
return 0;
}
BASE_TYPE SDAI_Select::ValueType() const {
if( !underlying_type ) {
return UNKNOWN_TYPE;
}
if( underlying_type->NonRefType() == sdaiSELECT ) {
const SDAI_Select * nested =
static_cast<const SDAI_Select *>( ValueAddress() );
return nested ? nested->ValueType() : UNKNOWN_TYPE;
}
return underlying_type->NonRefType();
}
bool SDAI_Select::SetInteger( const TypeDescriptor * td, SDAI_Integer value ) {
if( !td || td->NonRefType() != sdaiINTEGER || !SetUnderlyingType( td ) ) {
return false;
}
SDAI_Integer * target = static_cast<SDAI_Integer *>( ValueAddress() );
if( target ) {
*target = value;
}
return target != 0;
}
bool SDAI_Select::SetReal( const TypeDescriptor * td, SDAI_Real value ) {
if( !td || ( td->NonRefType() != sdaiREAL && td->NonRefType() != sdaiNUMBER ) ||
!SetUnderlyingType( td ) ) {
return false;
}
SDAI_Real * target = static_cast<SDAI_Real *>( ValueAddress() );
if( target ) {
*target = value;
}
return target != 0;
}
bool SDAI_Select::SetString( const TypeDescriptor * td, const SDAI_String & value ) {
if( !td || td->NonRefType() != sdaiSTRING || !SetUnderlyingType( td ) ) {
return false;
}
SDAI_String * target = static_cast<SDAI_String *>( ValueAddress() );
if( target ) {
*target = value;
}
return target != 0;
}
bool SDAI_Select::SetBinary( const TypeDescriptor * td, const SDAI_Binary & value ) {
if( !td || td->NonRefType() != sdaiBINARY || !SetUnderlyingType( td ) ) {
return false;
}
SDAI_Binary * target = static_cast<SDAI_Binary *>( ValueAddress() );
if( target ) {
*target = value;
}
return target != 0;
}
bool SDAI_Select::SetEnum( const TypeDescriptor * td, const SDAI_Enum & value ) {
PrimitiveType kind = td ? td->NonRefType() : UNKNOWN_TYPE;
if( !td || ( kind != sdaiENUMERATION && kind != sdaiBOOLEAN && kind != sdaiLOGICAL ) ||
!SetUnderlyingType( td ) ) {
return false;
}
SDAI_Enum * target = static_cast<SDAI_Enum *>( ValueAddress() );
if( target ) {
*target = value;
}
return target != 0;
}
bool SDAI_Select::SetSelect( const TypeDescriptor * td, const SDAI_Select & value ) {
if( !td || td->NonRefType() != sdaiSELECT || !SetUnderlyingType( td ) ) {
return false;
}
SDAI_Select * target = static_cast<SDAI_Select *>( ValueAddress() );
if( target ) {
*target = value;
}
return target != 0;
}
bool SDAI_Select::SetAggregate( const TypeDescriptor * td, const STEPaggregate & value ) {
if( !td || !td->IsAggrType() || !SetUnderlyingType( td ) ) {
return false;
}
STEPaggregate * target = static_cast<STEPaggregate *>( ValueAddress() );
if( target ) {
target->ShallowCopy( value );
}
return target != 0;
}
bool SDAI_Select::SetEntity( SDAI_Application_instance * value ) {
return AssignEntity( value ) != 0;
}
SDAI_Integer * SDAI_Select::IntegerValue() {
return underlying_type && underlying_type->NonRefType() == sdaiINTEGER ?
static_cast<SDAI_Integer *>( ValueAddress() ) : 0;
}
const SDAI_Integer * SDAI_Select::IntegerValue() const {
return underlying_type && underlying_type->NonRefType() == sdaiINTEGER ?
static_cast<const SDAI_Integer *>( ValueAddress() ) : 0;
}
SDAI_Real * SDAI_Select::RealValue() {
BASE_TYPE kind = underlying_type ? underlying_type->NonRefType() : UNKNOWN_TYPE;
return ( kind == sdaiREAL || kind == sdaiNUMBER ) ?
static_cast<SDAI_Real *>( ValueAddress() ) : 0;
}
const SDAI_Real * SDAI_Select::RealValue() const {
BASE_TYPE kind = underlying_type ? underlying_type->NonRefType() : UNKNOWN_TYPE;
return ( kind == sdaiREAL || kind == sdaiNUMBER ) ?
static_cast<const SDAI_Real *>( ValueAddress() ) : 0;
}
SDAI_String * SDAI_Select::StringValue() {
return underlying_type && underlying_type->NonRefType() == sdaiSTRING ?
static_cast<SDAI_String *>( ValueAddress() ) : 0;
}
const SDAI_String * SDAI_Select::StringValue() const {
return underlying_type && underlying_type->NonRefType() == sdaiSTRING ?
static_cast<const SDAI_String *>( ValueAddress() ) : 0;
}
SDAI_Binary * SDAI_Select::BinaryValue() {
return underlying_type && underlying_type->NonRefType() == sdaiBINARY ?
static_cast<SDAI_Binary *>( ValueAddress() ) : 0;
}
const SDAI_Binary * SDAI_Select::BinaryValue() const {
return underlying_type && underlying_type->NonRefType() == sdaiBINARY ?
static_cast<const SDAI_Binary *>( ValueAddress() ) : 0;
}
SDAI_Enum * SDAI_Select::EnumValue() {
BASE_TYPE kind = underlying_type ? underlying_type->NonRefType() : UNKNOWN_TYPE;
return ( kind == sdaiENUMERATION || kind == sdaiBOOLEAN || kind == sdaiLOGICAL ) ?
static_cast<SDAI_Enum *>( ValueAddress() ) : 0;
}
const SDAI_Enum * SDAI_Select::EnumValue() const {
BASE_TYPE kind = underlying_type ? underlying_type->NonRefType() : UNKNOWN_TYPE;
return ( kind == sdaiENUMERATION || kind == sdaiBOOLEAN || kind == sdaiLOGICAL ) ?
static_cast<const SDAI_Enum *>( ValueAddress() ) : 0;
}
SDAI_Select * SDAI_Select::SelectValue() {
return underlying_type && underlying_type->NonRefType() == sdaiSELECT ?
static_cast<SDAI_Select *>( ValueAddress() ) : 0;
}
const SDAI_Select * SDAI_Select::SelectValue() const {
return underlying_type && underlying_type->NonRefType() == sdaiSELECT ?
static_cast<const SDAI_Select *>( ValueAddress() ) : 0;
}
STEPaggregate * SDAI_Select::AggregateValue() {
return underlying_type && underlying_type->IsAggrType() ?
static_cast<STEPaggregate *>( ValueAddress() ) : 0;
}
const STEPaggregate * SDAI_Select::AggregateValue() const {
return underlying_type && underlying_type->IsAggrType() ?
static_cast<const STEPaggregate *>( ValueAddress() ) : 0;
}
SDAI_Application_instance * SDAI_Select::EntityValue() const {
if( !_storage || !underlying_type || underlying_type->NonRefType() != sdaiINSTANCE ) {
return 0;
}
return _storage->entity_value;
}
bool SDAI_Select::exists() const {
return underlying_type != NULL;
}
void SDAI_Select::nullify() {
underlying_type = 0;
base_type = UNKNOWN_TYPE;
if( _storage ) {
_storage->Clear();
}
}
Severity SDAI_Select::SelectValidLevel( const char * attrValue, ErrorDescriptor * err,
@@ -165,6 +620,155 @@ Severity SDAI_Select::SelectValidLevel( const char * attrValue, ErrorDescriptor
return s;
}
void SDAI_Select::STEPwrite_content( ostream & out, const char * currSch ) const {
const void * value = ValueAddress();
if( !underlying_type || !value ) {
out << "$";
return;
}
switch( underlying_type->NonRefType() ) {
case sdaiINTEGER:
out << *static_cast<const SDAI_Integer *>( value );
break;
case sdaiREAL:
case sdaiNUMBER:
WriteReal( *static_cast<const SDAI_Real *>( value ), out );
break;
case sdaiINSTANCE:
static_cast<SDAI_Application_instance *>(
const_cast<void *>( value ) )->STEPwrite_reference( out );
break;
case sdaiSTRING:
static_cast<const SDAI_String *>( value )->STEPwrite( out );
break;
case sdaiBINARY:
static_cast<const SDAI_Binary *>( value )->STEPwrite( out );
break;
case sdaiBOOLEAN:
case sdaiLOGICAL:
case sdaiENUMERATION:
static_cast<const SDAI_Enum *>( value )->STEPwrite( out );
break;
case sdaiSELECT:
static_cast<const SDAI_Select *>( value )->STEPwrite( out, currSch );
break;
case sdaiAGGR:
case ARRAY_TYPE:
case BAG_TYPE:
case SET_TYPE:
case LIST_TYPE:
static_cast<const STEPaggregate *>( value )->STEPwrite( out, currSch );
break;
default:
out << "$";
break;
}
}
Severity SDAI_Select::STEPread_content( istream & in, InstMgrBase * instances,
const char * utype, int addFileId,
const char * currSch ) {
if( !underlying_type ) {
severity( SEVERITY_BUG );
Error( "Unable to create storage for SELECT value." );
return severity();
}
void * value = ValueAddress();
if( underlying_type->NonRefType() != sdaiINSTANCE && !value ) {
severity( SEVERITY_BUG );
Error( "Unable to create storage for SELECT value." );
return severity();
}
switch( underlying_type->NonRefType() ) {
case sdaiINTEGER:
ReadInteger( *static_cast<SDAI_Integer *>( value ), in, &_error, ")," );
break;
case sdaiREAL:
case sdaiNUMBER:
ReadReal( *static_cast<SDAI_Real *>( value ), in, &_error, ")," );
break;
case sdaiINSTANCE: {
SDAI_Application_instance * entity =
ReadEntityRef( in, &_error, ",)", instances, addFileId );
if( entity && entity != S_ENTITY_NULL && underlying_type->CanBe( entity->eDesc ) ) {
_storage->entity_value = entity;
} else {
Error( "Reference to instance that is not indicated type\n" );
nullify();
return severity( SEVERITY_USERMSG );
}
break;
}
case sdaiSTRING:
static_cast<SDAI_String *>( value )->STEPread( in, &_error );
break;
case sdaiBINARY:
static_cast<SDAI_Binary *>( value )->STEPread( in, &_error );
break;
case sdaiBOOLEAN:
case sdaiLOGICAL:
case sdaiENUMERATION:
static_cast<SDAI_Enum *>( value )->STEPread( in, &_error );
break;
case sdaiSELECT:
static_cast<SDAI_Select *>( value )->STEPread(
in, &_error, instances, utype, addFileId, currSch );
break;
case sdaiAGGR:
case ARRAY_TYPE:
case BAG_TYPE:
case SET_TYPE:
case LIST_TYPE:
static_cast<STEPaggregate *>( value )->STEPread(
in, &_error, underlying_type->AggrElemTypeDescriptor(),
instances, addFileId, currSch );
break;
default:
severity( SEVERITY_WARNING );
Error( "Mismatch in underlying type." );
break;
}
return severity();
}
Severity SDAI_Select::StrToVal_content( const char * str, InstMgrBase * instances ) {
if( !underlying_type ) {
return severity( SEVERITY_BUG );
}
void * value = ValueAddress();
if( underlying_type->NonRefType() != sdaiINSTANCE && !value ) {
return severity( SEVERITY_BUG );
}
switch( underlying_type->NonRefType() ) {
case sdaiSTRING:
return static_cast<SDAI_String *>( value )->StrToVal( str );
case sdaiBINARY:
return static_cast<SDAI_Binary *>( value )->StrToVal( str, &_error );
case sdaiBOOLEAN:
case sdaiLOGICAL:
case sdaiENUMERATION:
return static_cast<SDAI_Enum *>( value )->StrToVal( str, &_error );
case sdaiSELECT:
return static_cast<SDAI_Select *>( value )->StrToVal(
str, underlying_type->Name(), &_error, instances );
case sdaiAGGR:
case ARRAY_TYPE:
case BAG_TYPE:
case SET_TYPE:
case LIST_TYPE:
return static_cast<STEPaggregate *>( value )->StrToVal(
str, &_error, underlying_type->AggrElemTypeDescriptor(), instances );
default: {
istringstream input( str );
return STEPread_content( input, instances );
}
}
}
Severity SDAI_Select::StrToVal( const char * Val, const char * selectType,
ErrorDescriptor * err, InstMgrBase * instances ) {
severity( SEVERITY_NULL );
@@ -580,7 +1184,7 @@ void SDAI_Select::STEPwrite( ostream & out, const char * currSch ) const {
void SDAI_Select::STEPwrite_verbose( ostream & out, const char * currSch ) const {
std::string tmp;
out << StrToUpper( CurrentUnderlyingType()->Name( currSch ), tmp ) << "(";
STEPwrite_content( out );
STEPwrite_content( out, currSch );
out << ")";
}
+3 -2
View File
@@ -1,14 +1,15 @@
#include "clstepcore/selectTypeDescriptor.h"
#include "clstepcore/sdaiSelect.h"
///////////////////////////////////////////////////////////////////////////////
// SelectTypeDescriptor functions
///////////////////////////////////////////////////////////////////////////////
SDAI_Select * SelectTypeDescriptor::CreateSelect() {
SDAI_Select * SelectTypeDescriptor::CreateSelect() const {
if( CreateNewSelect ) {
return CreateNewSelect();
} else {
return 0;
return new SDAI_Select( this );
}
}
+2
View File
@@ -23,7 +23,9 @@ endfunction(add_stepcore_test name libs)
add_stepcore_test("SupertypesIterator" "stepcore;steputils;stepeditor;stepdai") #all these libs are necessary?
add_stepcore_test("operators_STEPattribute" "stepcore;steputils;stepeditor;stepdai")
add_stepcore_test("operators_SDAI_Select" "stepcore;steputils;stepeditor;stepdai")
add_stepcore_test("descriptor_SDAI_Select" "stepcore;steputils;stepeditor;stepdai")
add_stepcore_test("null_attr" "stepcore;steputils;stepeditor;stepdai")
add_stepcore_test("late_bound_entity" "stepcore;steputils;stepeditor;stepdai")
# Local Variables:
# tab-width: 8
@@ -0,0 +1,81 @@
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include "clstepcore/ExpDict.h"
#include "clstepcore/sdaiSelect.h"
static bool expect( bool condition, const char * message ) {
if( !condition ) {
std::cerr << message << std::endl;
}
return condition;
}
int main() {
TypeDescriptor integer_type( "integer_choice", sdaiINTEGER, 0, "INTEGER" );
TypeDescriptor string_type( "string_choice", sdaiSTRING, 0, "STRING" );
SelectTypeDescriptor choice_type(
~( sdaiINTEGER | sdaiSTRING ), "choice", sdaiSELECT, 0,
"SELECT (integer_choice, string_choice)" );
choice_type.Elements().AddNode( &integer_type );
choice_type.Elements().AddNode( &string_type );
bool pass = true;
SDAI_Select value( &choice_type );
pass &= expect( value.SetInteger( &integer_type, 42 ),
"descriptor SELECT rejected an integer choice" );
pass &= expect( value.IntegerValue() && *value.IntegerValue() == 42,
"descriptor SELECT did not retain its integer value" );
std::ostringstream integer_output;
value.STEPwrite_content( integer_output );
pass &= expect( integer_output.str() == "42",
"descriptor SELECT wrote an incorrect integer" );
SDAI_Select copy( value );
pass &= expect( copy.IntegerValue() && *copy.IntegerValue() == 42,
"descriptor SELECT copy lost its integer value" );
ErrorDescriptor error;
pass &= expect( value.StrToVal( "17", "integer_choice", &error ) == SEVERITY_NULL,
"descriptor SELECT could not parse an integer" );
pass &= expect( value.IntegerValue() && *value.IntegerValue() == 17,
"descriptor SELECT parsed the wrong integer" );
SDAI_String text( "descriptor driven" );
pass &= expect( value.SetString( &string_type, text ),
"descriptor SELECT rejected a string choice" );
pass &= expect( value.StringValue() &&
std::string( value.StringValue()->c_str() ) == "descriptor driven",
"descriptor SELECT did not retain its string value" );
std::ostringstream string_output;
value.STEPwrite_content( string_output );
pass &= expect( string_output.str() == "descriptor driven",
"descriptor SELECT wrote an incorrect string" );
EntityDescriptor entity_type( "entity_choice", 0, LFalse, LFalse );
SelectTypeDescriptor entity_choice_type(
~sdaiINSTANCE, "entity_choice_select", sdaiSELECT, 0,
"SELECT (entity_choice)" );
entity_choice_type.Elements().AddNode( &entity_type );
SDAI_Application_instance entity( 99 );
entity.eDesc = &entity_type;
SDAI_Select entity_value( &entity_choice_type );
pass &= expect( entity_value.SetEntity( &entity ),
"descriptor SELECT rejected an entity choice" );
pass &= expect( entity_value.EntityValue() == &entity,
"descriptor SELECT did not retain its entity value" );
std::ostringstream entity_output;
entity_value.STEPwrite_content( entity_output );
pass &= expect( entity_output.str() == "#99",
"descriptor SELECT wrote an incorrect entity reference" );
value.set_null();
pass &= expect( value.is_null(), "descriptor SELECT did not clear its value" );
return pass ? EXIT_SUCCESS : EXIT_FAILURE;
}

Some files were not shown because too many files have changed in this diff Show More