Skip to content

Give feature pointers a long-name type and add lookups by long name - #35

Merged
philliphoff merged 2 commits into
mainfrom
claude/feature-long-names
Oct 12, 2026
Merged

philliphoff merged 2 commits into
mainfrom
claude/feature-long-names

Conversation

@philliphoff

Copy link
Copy Markdown
Owner

Summary

A feature-to-feature pointer (FFPT) names its target only by long name (AGEN, FIDN, FIDS). Until now it was stored as an S57RecordName with RCNM and RCID left at zero. That had three consequences:

  • A pointer's name never equalled the target feature's record name.
  • ReferencingFeatures was keyed by names that no feature had.
  • The library gave no way to follow a pointer. FIDN, an unsigned 32-bit value, was also cast to int.

This PR changes that:

  • New S57LongName type. A readonly record struct holding AgencyCode, FeatureId (uint) and FeatureSubdivision.
  • S57FeaturePointer.Name is now an S57LongName.
  • LongName property on S57RecordName, S57FeatureRecord and S57TypedFeature. It is null when a record has no FOID field.
  • Chart lookups: S57Chart.FeaturesByLongName, GetFeature(S57LongName) and GetReferencingFeatures(S57LongName). ReferencingFeatures is now keyed by long name.
  • Document lookup: S57Document.GetFeatureRecord(S57LongName). It scans every record, while the chart's lookup uses an index.
  • Docs: a new section in docs/reading-s57-charts.md on following relationships between features, and a note in the s57-enc skill.

Memory cost. The long-name index is a read-only Dictionary instead of an ImmutableDictionary. With ImmutableDictionary, building the chart allocated 5.4% more and the chart retained 4.8% more. With Dictionary, the chart's retained memory went back inside its tolerance, and building it allocates 3.2% more. I raised the S57ChartFromDocument and S57ChartRetained budgets to the measured values: 1,063,752 and 1,178,960 bytes.

The reader's output is unchanged.

  • The fingerprint helper formats a pointer's long name exactly as it did before, so the recorded hashes still hold.
  • Locally, all 24 fingerprints of the synthetic cells and of 19 real NOAA cells and updates match the baseline taken before Read ISO 8211 subfields without allocating per value #32.
  • In nine NOAA cells, all 662 feature pointers resolve with GetFeature.

Standards alignment

  • ISO/IEC 8211 (iso8211)
  • S-57 records, topology, updates or exchange sets (s57-enc)
  • Legacy chart viewer, maintenance only (chart-viewer)
  • N/A: build, CI, docs or tooling only

Standard section references cited in code or docs:

S-57 Part 3, 4.3 (feature object identifier) and 4.6 (feature-to-feature relationships), on S57LongName and S57FeaturePointer.Name.

Tests

  • Added or updated xUnit tests under tests/. New tests cover:
    • the reader decoding FFPT into S57LongName, including a FIDN above int.MaxValue;
    • GetFeatureRecord(S57LongName);
    • a feature without FOID having no long name;
    • chart pointer resolution, referencing features, and features without FOID being excluded.
  • Test input is built in memory; no real ENC cells are committed
  • dotnet test --configuration Release passes locally. Unit tests: 528 pass. Budget tests: 14 pass.

Documentation

  • Updated the affected package's src/<project>/README.md. Not needed: it doesn't cover feature pointers.
  • Updated guides under docs/ if user-facing behavior changed
  • New public APIs have XML doc comments
  • docfx docfx.json --warningsAsErrors passes, if docs changed

Dependencies

  • No new NuGet dependencies, OR versions added to Directory.Packages.props (not in the .csproj)
  • gh-advisory-database security check run for any new dependency. Not applicable: no new dependencies.

Breaking changes

These are binary and source breaks to EncDotNet.S57, recorded in src/EncDotNet.S57/CompatibilitySuppressions.xml for 0.8.0:

  • S57FeaturePointer.Name is S57LongName (was S57RecordName).
  • S57Chart.ReferencingFeatures is IReadOnlyDictionary<S57LongName, IReadOnlyList<S57TypedFeature>> (was keyed by S57RecordName).

Impact on EncDotNet.S100: S57ToS101Translator calls Lnam(fp.Name) in four places. The Lnam(S57RecordName) helper needs an S57LongName overload, or a change of parameter type. Its (ushort)feat.RecordName.AgencyCode / (uint)…FeatureId casts can use feat.LongName instead, but don't have to.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YagvELyJKBANk61DyFGUNJ


Generated by Claude Code

Breaking: S57FeaturePointer.Name is now S57LongName (was S57RecordName),
and S57Chart.ReferencingFeatures is keyed by S57LongName.

An FFPT pointer names its target by long name (AGEN, FIDN, FIDS) only,
but it was an S57RecordName with RCNM and RCID left zero, so a pointer's
name never matched a feature's record name and the library offered no
way to follow it. FIDN, an unsigned 32-bit value, was also cast to int.

- Add S57LongName, a readonly record struct of AGEN, FIDN and FIDS.
- Add LongName to S57RecordName, S57FeatureRecord and S57TypedFeature;
  it is null for a record without a FOID field.
- Add S57Chart.FeaturesByLongName, GetFeature(S57LongName) and
  GetReferencingFeatures(S57LongName), and
  S57Document.GetFeatureRecord(S57LongName).
- The long-name index is a read-only Dictionary rather than an
  ImmutableDictionary, which kept the chart's retained memory within its
  budget. Building a chart allocates 3.2% more for it, so the
  S57ChartFromDocument and S57ChartRetained budgets rise to the measured
  values.
- The break is recorded in CompatibilitySuppressions.xml for 0.8.0.

The reader's output is otherwise unchanged: the fingerprint helper
formats a pointer's long name as before, and the synthetic and local
real-cell fingerprints match. In nine NOAA cells, all 662 feature
pointers resolve with GetFeature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YagvELyJKBANk61DyFGUNJ
@philliphoff
philliphoff merged commit cf087ff into main Oct 12, 2026
12 checks passed
@philliphoff
philliphoff deleted the claude/feature-long-names branch October 12, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants