WEBSITE ALPHA · AI-assisted, source-reviewed documentation · Full-stack docs reconciled 2026-08-26
64x64base

x64 Capacity Math

A lesson and matrix for why DBF-style systems change when records, offsets, memos, indexes, and APIs move from 32-bit to 64-bit.

This page is a lesson first and a specification matrix second. The important question is not only "what is the maximum?" The real question is:

Which part of the system still decides the maximum?

Current explanatory diagrams

Diagram attachments: DIAG-X64GEOM-005, DIAG-X64SELFDESC-006

Trinity headers

x64 self-describing DBF

Current sources:

  • docs/manuals/assets/diagrams/trinity_headers_v1.svg
  • docs/manuals/assets/diagrams/x64_self_describing_dbf_v1.svg

A table format can put a 64-bit record count in the header, but the system is not truly x64 all the way if navigation, locks, memo pointers, index payloads, or GUI APIs still pass record numbers through 16-bit or 32-bit slots.

Why x64 all the way?

Classic DBF-family files are beautifully simple: a header, field descriptors, fixed-length records, optional memo sidecars, and indexes. That simplicity also means the math is visible. If a header field is 16 bits wide, its maximum is bounded by 2^16 - 1. If a record counter is 32 bits wide, its natural ceiling is 2^32 - 1, and many products impose lower practical limits for file size, locking, compatibility, or runtime memory.

Going x64 changes the shape of the possible database:

WidthUnsigned maximumWhat it means in a DBF-style engine
8-bit255Classic one-byte field lengths and flags.
16-bit65,535Header length, record length, field offsets, and compact index limits in many classic paths.
32-bit4,294,967,295Record numbers, file offsets, memo blocks, lock offsets, and index payloads in many older engines.
64-bit18,446,744,073,709,551,615Large record counts, file offsets, memo object ids, index page addresses, and future table metadata.

The lesson is not that every table should contain trillions of records. The lesson is that each old counter becomes a choke point unless it is widened through the whole stack.

For x64base, it helps to distinguish three different ceilings:

Ceiling typeCurrent meaning in x64base
64-bit design fieldThe x64 extension can store wide counts, offsets, and row geometry.
16-bit compatible mirrorThe VFP-style compatibility header still mirrors header size and record size through 16-bit fields, and those mirrors may saturate at 65535.
Parser / construction limitSome creation surfaces still impose narrower convenience limits, such as the current CREATE X64 single-character-field cap of 4096, even though the runtime can now honor wider total row geometry.

These are not the same thing, and the matrix below should not pretend they are.

The choke-point chain

For a DBF-style runtime to be truly 64-bit, the following layers need to agree:

  1. Table header - record count, record length, header length, and data-start offset.
  2. Record navigation - GOTO, SKIP, RECNO, EOF/BOF, append, delete, and scan loops.
  3. Record locking - lock-file names, lock offsets, owner records, and stale-lock cleanup.
  4. Memo references - in-row memo reference width and sidecar object/block addressing.
  5. Index payloads - key entries must carry wide record numbers and wide page or object offsets.
  6. Expression/runtime APIs - scalar functions, commands, GUI bindings, and scripting need wide-safe values.
  7. Diagnostics and manuals - reports must say whether a value is a design target, a header capability, or a runtime-proven limit.

If any one of those layers still clamps a value to 16 or 32 bits, the system may have an x64 file header but not an x64 operating envelope.

Record-number widening status (public build, 2026-07)

The record-number widening lane (internal name RECNO64) removed the 32-bit choke points listed above. The table records the current state in the public build; prior states are preserved in this site's version history.

Choke-point layerCurrent state
Table header64-bit record count, length, and data-start via the *64 accessors.
Record navigation64-bit positioning (gotoRec64); GO/GOTO/SKIP/RECNO/TOP/BOTTOM/FIRST/LAST/NEXT/PRIOR regression-checked (CURSOR, INDEX_X64).
Record locking64-bit — lock API and lock-file record keys widened from 32-bit.
Memo references64-bit DTX object ids, offsets, and append offsets.
Index payloadsActive CDX/LMDB path is 64-bit (index hooks + keyed-seek decoders); classic CNX and legacy .inx kept 32-bit by design with a capability report.
Expression / runtime APIs64-bit through the *64 accessors; the classic recno()/recLength()/cpr() accessors now return -1 past their 32-bit range (2026-07) instead of clamping to INT_MAX, so a legacy consumer sees "out of range" rather than a wrong record.
Diagnostics and manualsThis page and the DBF-64 specification separate design target from runtime-proven limit.

The decisive measurement — resolving a distinct record beyond 2^31 — has been performed two ways: a synthetic boundary unit test (dottalkpp_recno64_boundary_test, where recno64/recCount64/recLength64 resolve distinct values at INT32_MAX, +1, +2, UINT32_MAX, UINT32_MAX+1 while the legacy accessors return -1), and a real sparse-file end-to-end test (dottalkpp_recno64_sparse_e2e_test) that sets an x64 table's record_count to 2^31+2, writes real records at recno 1 / 2^31+1 / 2^31+2 into a sparse file — the ~18 GiB gap is a filesystem hole, a few KB physical — reopens through the engine, and reads the two records past 2^31 distinctly off disk (in ~0.5 s). What has not been done is materializing a fully populated multi-billion-row table, which is a data-volume / performance question (see the Pinocchio benchmarks), not an addressing-correctness one. The record-number path itself is proven past 2^31.

x64base current truth

x64base already has real x64 structure in the local source tree, but the honest documentation must separate format direction from current runtime limit.

Where these limits live (and why they are adjustable)

MAX_AREA, MAX_FIELDS, the record-size guardrails, and the name-length ceilings are engineering choices, not format-forced values — the DBF_64 format could represent larger, and these numbers are deliberately round. They are currently centralized as compile-time constants in include/xbase.hpp (with the table-buffer bitset width in include/cli/table_state.hpp), so raising one is a single-point edit plus a rebuild. A natural future direction is a proper configuration layer — runtime-tunable profile parameters in the style of enterprise systems, or an xbase.meta descriptor — so a deployment could set its own ceilings for MAX_RECORDS, MAX_RECORD_SIZE, MAX_FIELDS, and MAX_AREAS without a recompile. The values below are the current constants in the public build (2026-07); earlier builds carried the prior values.

Capacity itemCurrent x64base evidenceDocumentation status
Work areas / open table slotsinclude/xbase.hpp defines MAX_AREA = 512 (raised from 256 in 2026-07).Public build; raised from 256 (2026-07).
Field countinclude/xbase.hpp defines MAX_FIELDS = 256 (raised from 128 in 2026-07; the table-buffer kMaxFields bitsets were already 256, so this only relaxes two guard checks).Public build; raised from 128 (2026-07).
Classic/VFP record countClassic header uses signed 32-bit mirror; VFP header has a uint32_t num_recs, but some current paths mirror through signed 32-bit APIs.Classic-compatible, with current runtime chokepoints.
x64 record countinclude/xbase_64.hpp stores LargeHeaderExtension::record_count as uint64_t; DbArea keeps _rec_count64 and exposes recCount64(). In the public build (2026-07): navigation and command paths — GOTO/GO/SKIP/RECNO/TOP/BOTTOM/FIRST/LAST/NEXT/PRIOR — position through 64-bit gotoRec64/recno64.64-bit header state exists; navigation/command paths are widened and pass the CURSOR (x32/CNX) and INDEX_X64 (v64/CDX/LMDB) regression suites. The classic recno()/recLength()/recCount()/cpr() accessors now return -1 past their 32-bit range (2026-07; no longer clamp to INT_MAX), and both a synthetic unit test and a sparse-file end-to-end test read distinct records at recno 2^31+1/+2 off disk. A fully populated multi-billion-row table is a volume question, not proven here.
Header/data-start sizex64 extension stores data_start_64 as uint64_t, validator/open now accept a saturated 16-bit compatibility mirror through x64_compatible_u16_matches(), and runtime read/write paths consume dataStart64() directly.Wide geometry is active; compatibility mirror still exists but no longer defines live x64 geometry.
Record sizex64 extension stores record_size_64 as uint64_t, validator/open now accept a saturated 16-bit compatibility mirror through x64_compatible_u16_matches(), and runtime read/write paths consume recLength64() directly.Wide geometry is active; compatibility mirror still exists but no longer defines live x64 geometry.
Table namesx64 naming policy allows up to 256-byte table names (ceiling raised from 128 in 2026-07; default stays 128).Public build ceiling 256; default stays 128.
Field namesx64 naming policy allows up to 256-byte field names (ceiling raised from 128 in 2026-07; default stays 128) with 10-byte descriptor fallback tokens.Public build ceiling 256; default stays 128; fallback remains classic-compatible.
Field length metadataX64FieldMetaEntry::field_length is uint32_t; FieldDef.length is also uint32_t; current CREATE X64 allows x64 character fields above 255 bytes, but one C field is still capped at 4096 in the parser.Wider field-length metadata exists; current parser/construction limits are narrower than the x64 design lane.
x64 memo referencesx64 memo fields use an 8-byte object-id slot; DTX memo object ids, offsets, payload bytes, and append offsets are uint64_t.Source-evidenced DTX/x64 memo architecture.
Index record numbersinclude/xindex/key_common.hpp defines RecNo = std::uint64_t; the active LMDB/CDX backend stores the record number as 8 bytes little-endian. In the public build (2026-07): the index-hook apply_replace chain and the CDX keyed-seek decoders (decode_recno_from_kv_, seekRecnoUserKey) were widened to 64-bit; classic CNX and legacy .inx remain 32-bit by design and report their ceiling through IIndexBackend::maxRecordNumber().Active path (CDX/LMDB) carries 64-bit record numbers; legacy 32-bit formats are retained by design with an explicit capability report rather than silent truncation.

This is the exact reason the project says x64base implements an x64 DBF-style architecture with 64-bit record addressing proven end to end, while staying candid that a fully populated multi-billion-row table is a volume question — not something these boundary proofs establish.

Current boundary proof

The current canonical proof is the x64 matrix/metrics boundary canary:

  • dottalkpp/data/scripts/canaries/x64_matrix_metrics_boundary_canary.dts

It proves two separate x64 row-geometry cases:

  • a disposable x64 table with a record length of 36,865 bytes
  • a disposable x64 table with a record length of 69,633 bytes

That second case matters because it crosses the old 65,535 compatibility-mirror ceiling. The mirror may saturate, but the runtime now honors the wide x64 metrics from the extension instead of shrinking live geometry back to a 16-bit active path.

Ecosystem capacity matrix

The values below are deliberately conservative. They compare published or source-evidenced limits, not sales claims.

Capacity / specificationx64base / DotTalk++ WorkbenchVisual FoxPro 9dBASE / BDE dBASEHarbour / xHarbour DBF-familyAlaska Xbase++ DBFDBEPython DBF libraries
Primary intentEducational-first open architecture and x64 DBF-style research systemMature desktop database/runtimeCommercial DBF database/tooling lineageOpen Clipper/xBase compiler/runtime ecosystemCommercial modernization/runtime platformUtility libraries for reading/writing DBF files
Open table/work areas512 work areas (MAX_AREA, raised from 256 in 2026-07; public build 512)65,535 tables open at one time, memory/file-handle limited256-512 open dBASE tables per system depending BDE generationImplementation/runtime dependentRuntime/DBE dependentNot a work-area engine
Records per tablex64 header stores a 64-bit record count; navigation and command paths were widened to 64-bit (2026-07) and pass the CURSOR and INDEX_X64 regression suites. The classic recno() accessor now returns -1 past 2^31 - 1 (no longer clamps); proven by a unit test and a sparse-file end-to-end test that reads distinct records at recno 2^31+1/+2 off disk. A fully populated multi-billion-row table is a volume question, not proven here.1 billion1 billionOften described by DBF structure as up to 2^32 - 1 records, implementation dependent(lock offset - header - 1) / record sizeFollows target DBF dialect and library implementation
DBF/table file sizeWide-offset design target exists, and open/read/write now honor wide x64 geometry; broader whole-file-scale proof is still pending across the full stack2 GB table file2 GB table fileFormat/backend discussions cite much larger theoretical DBF ceilings, but product/backend dependentDefault lock-offset model around 1 GB unless configuredUsually inherits classic/VFP file limits and Python/runtime memory constraints
Record sizex64 extension stores a 64-bit row size; a 16 MiB hard ceiling and 64 KiB soft advisory were added in 2026-07. The compatibility mirror may saturate at 65,535, and the current canary proves live rows at 36,865 and 69,633 bytes. CREATE X64 still caps one C field at 4096.65,500 characters per record4,000 bytes dBASE 4; 32,767 bytes dBASE for WindowsOften tied to 16-bit record-size structure, around 65,535 bytesFixed-length DBF rows; character fields up to 64 KBDialect dependent
Fields per table256 (MAX_FIELDS, raised from 128 in 2026-07; public build 256)255, or 254 when nullable-field overhead applies255 dBASE 4; 1024 dBASE for WindowsBackend/dialect dependentListed as not limited by DBFDBE table, but practical DBF/runtime limits applyDialect/library dependent
Character field widthWider x64 metadata exists and current CREATE allows x64 character fields beyond 255 bytes, but practical width is still bounded by the same row-size mirrors254254Dialect/backend dependent; some readers note extended 16-bit character widthsCharacter values max 64 KBLibrary/dialect dependent; dbfread documents up to 65,535 for reused length bytes
Free-table field names10-byte fallback descriptor token; x64 metadata supports up to 256-byte names (public build 256; default 128)10 chars free table; 128 chars in database-contained tabledBASE/BDE table and field name size listed as 31 characters in general limitsClassic DBF-compatible fallback usually short namesDBF/Clipper-compatible rulesDialect/library dependent
Memo file / object scaleDTX uses 64-bit object ids, offsets, payload sizes, and append offsets; proof lane active2 GB FPT fileMemo/Blob limits product dependent; BDE Paradox lists 256 MB BLOB, dBASE memo limits vary by formatBackend dependent; public discussions cite large DBT/FPT/SMT ceilingsMemo text length and memo file size limited by system resources in DBFDBE docsDialect/library dependent
Index key / index file scaleCDX/LMDB active with 64-bit record numbers (index hooks and keyed-seek decoders widened, 2026-07); classic CNX and legacy .inx kept 32-bit by design with a capability reportCompact index key 240 bytes; non-compact 100 bytes47 MDX tags; 220-character key expressionBackend dependent: NTX/CDX/other RDDsRequires ORDER component such as NTXDBE/CDXDBEUsually no native persistent index engine
Locking modelRecord locks exist; the record-lock API and lock-file record keys were widened from 32-bit to 64-bit (2026-07)Product/runtime managed100 record locks per dBASE table in listed BDE limitsRuntime/RDD dependentDBF file size tied to record-lock offset modelNot a multi-user DBF engine
InternationalizationMessage catalog and locale lanes in progressCode pages/collation settingsBDE/codepage dependentEcosystem/runtime dependentOEM/ANSI conversion through SET CHARSETPython ecosystem dependent
SQL / relational supportSQL bridge, relation graph, workspaces, and DDL lanes active with caveatsSQL SELECT/update support in VFP runtimeSQL through BDE/toolingLibraries and driversDatabase engines and migration toolingPython DB/CSV/DBF tooling rather than native xBase work areas

Student exercise: find the hidden 32-bit ceiling

Take any proposed x64 feature and ask these questions:

QuestionWhy it matters
Is the value stored on disk as 64-bit?If not, the file format itself is the ceiling.
Is the value kept in memory as 64-bit?If not, large values are lost after open.
Do commands accept and print the value as 64-bit?If not, scripts and diagnostics lie.
Do locks, indexes, and memos carry the same width?If not, mutation and navigation break before storage does.
Do GUI/TUI/API bindings expose the wide value?If not, the engine may be wider than the application surface.

For x64base, that exercise produces real engineering lanes:

  • keep wide x64 geometry authoritative even when compatibility mirrors saturate.
  • audit remaining create/export/utility surfaces that still emit or assume classic-sized geometry.
  • audit every int32_t and uint32_t record-number path.
  • prove x64 record navigation, append, delete, replace, lock, and scan behavior.
  • prove index backends with 64-bit record payloads.
  • keep fallback descriptor tokens for DBF interoperability while preserving x64 vector names.
  • make the manuals report current, proven, planned, and design-target limits separately.

Source notes