Skip to content

Add Seek and RecordIndex for random record access - #283

Merged
chrisrichards merged 1 commit into
mainfrom
feature/seek-and-record-index
Jul 6, 2026
Merged

Add Seek and RecordIndex for random record access#283
chrisrichards merged 1 commit into
mainfrom
feature/seek-and-record-index

Conversation

@chrisrichards

Copy link
Copy Markdown
Member

Summary

Reading has been strictly forward-only — no way to jump to a record by index, and no way to tell which record was just read. This adds:

  • DbfTable.Seek(int recordIndex) — positions the table at the given zero-based record (dataStart + recordIndex * RecordLength). Also exposed as DbfDataReader.Seek. Negative indexes throw ArgumentOutOfRangeException; non-seekable streams throw NotSupportedException; seeking past the last record is allowed and simply makes the next Read() return false (deliberately not validated against Header.RecordCount, which is unreliable in real-world files).
  • DbfRecord.RecordIndex — zero-based index of the record most recently read, -1 before any read. Also exposed as DbfDataReader.RecordIndex. The value is derived from the stream position rather than a counter, so it stays correct across seeks and across multiple DbfRecords reading interleaved from one table. With SkipDeletedRecords, it reports the file index of the record actually surfaced.
  • The offset math accounts for streams where the DBF content starts at a non-zero position: the table captures the stream position at construction and bases all record offsets on it.

Design notes

  • Zero-based throughout, so Seek(record.RecordIndex) + Read re-reads the current record — one consistent convention. (xBase record numbers are traditionally 1-based; a future CDX index integration will convert in one documented place.)
  • This is the groundwork step for CDX index support from the fork analysis: index searches yield record numbers that need Seek to become rows.

Test plan

New SeekTests (8 tests): sequential RecordIndex tracking (-1 → 0..13), seek-then-read matches sequential values, rewind after full scan, seek past end reads nothing, negative index throws, non-seekable stream throws, DBF embedded at a non-zero stream offset seeks correctly, and the DbfDataReader-level Seek/RecordIndex path. Full suite: 112 passed, 1 skipped (pre-existing WIP skip).

🤖 Generated with Claude Code

Reading was strictly forward-only: there was no way to jump to a
record by index or to tell which record had just been read.

DbfTable.Seek(recordIndex) positions the stream at the given
zero-based record using the header's HeaderLength and RecordLength,
and DbfRecord.RecordIndex reports the zero-based index of the record
most recently read (-1 before any read). The index is derived from
the stream position rather than a counter, so it stays correct across
seeks and interleaved readers. DbfDataReader exposes both.

The offset math accounts for streams where the DBF content starts at
a non-zero position (the table captures the stream position at
construction), and seeking past the last record simply causes the
next read to return no record. This is groundwork for CDX index
support, where index searches yield record numbers that need to be
turned into rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

@chrisrichards
chrisrichards merged commit fffaa44 into main Jul 6, 2026
3 checks passed
@chrisrichards
chrisrichards deleted the feature/seek-and-record-index branch July 6, 2026 10:28
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.

1 participant