| Value | Meaning |
|---|---|
EDB_OK |
Success |
EDB_ERROR |
General failure (invalid/corrupt header, I/O verification failed, invalid params) |
EDB_OUT_OF_RANGE |
recno is less than 1 or refers to a never-used slot |
EDB_TABLE_FULL |
Table cannot hold another record |
EDB_DELETED |
recno refers to a deleted (tombstoned) slot |
EDB_CORRUPT |
A record or header CRC failed verification |
EDB_NEEDS_MIGRATION |
A legacy v1/v2 file — migrate offline with tools/edb_migrate.py |
Alias for byte*. Cast structs with the EDB_REC macro:
MyRecord rec;
db.appendRec(EDB_REC rec);EDB(EDB_Write_Handler *write_byte, EDB_Read_Handler *read_byte);
EDB(EDB_Write_Buffer *write_buffer, EDB_Read_Buffer *read_buffer);Creates a new v3 database at head_ptr.
- Sets
n_recs = 0. - Writes and read-verifies the header.
- Returns
EDB_ERRORfor invalid parameters or failed verification.
Opens an existing database.
- Detects legacy v1/v2 headers.
- Validates flag, sizes, and
n_recs <= limit(). - Returns
EDB_ERRORfor corrupt or unrecognized headers.
Appends a record into a free slot (reused or newly grown), O(1). The optional out_recno receives
the slot index for immediate readRec / updateRec / deleteRec — not a durable logical id.
On normal tables, returns EDB_TABLE_FULL when full. On ring tables, when full the oldest slot
is overwritten in place and EDB_OK is returned (ring mode never returns EDB_TABLE_FULL).
Reads record recno into caller-provided memory (rec_size bytes) and verifies its CRC. Returns
EDB_OUT_OF_RANGE (never-used id), EDB_DELETED (tombstoned), or EDB_CORRUPT (CRC mismatch).
Overwrites an existing live record, O(1) (no header write). Returns EDB_OUT_OF_RANGE/EDB_DELETED.
v3 behavior: allocates a free slot like appendRec; the recno argument is ignored and
positional order is not preserved. Prefer appendRec. (Kept for source compatibility.)
Tombstones the record's slot, O(1). The slot may be reused by a later appendRec with different
data. Returns EDB_ERROR on ring tables (append-only; use clear() to wipe). Returns
EDB_OUT_OF_RANGE/EDB_DELETED for invalid ids on normal tables.
Iterate live records in slot index order, skipping tombstones; return 0 when exhausted.
Use these instead of looping 1..count() (slots can be sparse after deletes). On ring tables,
prefer fifoFirstRec() / fifoNextRec() for chronological order.
for (unsigned long r = db.firstRec(); r != 0; r = db.nextRec(r)) {
db.readRec(r, EDB_REC rec);
}Returns true if recno is an allocated, non-tombstoned slot.
Reconciles count() and rebuilds the free list from tombstones (reclaims slots leaked by a crash).
Does not move live records. Returns EDB_ERROR on ring tables (use clear() instead).
Enables fixed-capacity ring FIFO mode: append-only, overwrites the oldest record when full.
deleteRec and compact() return EDB_ERROR. Reset the log with clear() (examples often wrap
this in a local deleteAll() helper).
Returns whether ring FIFO mode is active.
Iterate live records in FIFO order (oldest first). Before the ring has wrapped, order matches
firstRec() / nextRec(). After wrap, starts at the oldest slot (the next slot to be overwritten).
Enables monotonic record_id in payload bytes [0..3] on each appendRec (requires rec_size >= 4).
Your struct should reserve those bytes (e.g. struct { uint32_t id; ... }). Existing live records
with non-zero ids in that field are respected when enabling.
Returns whether stable record ids are active for this table.
Returns the stable record_id for a live slot when stable ids are enabled.
Linear scan for a live record by stable id; sets out_recno to the current slot address.
Returns the stored record size (bytes) of the open table.
Defers the header publish so each appendRec costs only the slot write (~11 writes instead of 59):
db.beginBatch();
for (...) db.appendRec(EDB_REC rec);
db.endBatch();A crash mid-batch never corrupts the table: beginBatch() sets a dirty bit, and the next open()
rebuilds count(), the slot high-water mark, and the free list from the slot region, then clears it
— no records are lost. deleteRec, clear, and compact return EDB_ERROR while a batch is open,
and ring tables cannot be batched. See BENCHMARK.md.
Re-reads the header and recreates the table with the same table_size and rec_size, resetting
count() to 0 and clearing ring head / next_record_id when those modes are enabled. Writes a fresh
v3 header.
This is a destructive logical wipe: it does not securely erase old record bytes from EEPROM/flash/SD — stale data may remain until overwritten. It is not a substitute for migrating a legacy file to v3 when you need to preserve records (use MIGRATION.md instead).
Returns the number of stored records.
Returns the maximum number of records that fit in the table. Returns 0 if rec_size == 0.
Returns the byte offset in storage where this table's header begins (the value passed to open() or create()).
Returns the allocated table size in bytes for the currently open table.
Returns the byte offset where the next table can start without overlapping:
#define TABLE_A_SIZE 512
#define TABLE_B_SIZE 256
const unsigned long TABLE_A_HEAD = 0;
const unsigned long TABLE_B_HEAD = EDB::nextTableOffset(TABLE_A_HEAD, TABLE_A_SIZE);Calls open(head_ptr) and, if that returns EDB_ERROR, calls create() with the same parameters. Useful when initializing several tables at boot.
head_ptr is a byte address in EEPROM, SD, or other storage — not a table index. Do not use create(1, ...), create(2, ...) unless those addresses are intentionally spaced.
Each instance keeps its own header in memory after open() or create():
EDB dbEvents(&writer, &reader);
EDB dbConfig(&writer, &reader);
dbEvents.openOrCreate(EVENTS_HEAD, EVENTS_TABLE_SIZE, sizeof(EventRecord));
dbConfig.openOrCreate(CONFIG_HEAD, CONFIG_TABLE_SIZE, sizeof(ConfigRecord));
dbEvents.appendRec(...); // count() applies to events only
dbConfig.appendRec(...); // count() applies to config onlyEDB db(&writer, &reader);
db.open(EVENTS_HEAD);
db.appendRec(...);
db.open(CONFIG_HEAD);
db.appendRec(...);You must call open() before operating on each table. count() always reflects the currently open table only.
1.0.x headers declared extern EDB edb. Sketches that define EDB edb(&writer, &reader); continue to work. For multiple tables, use separate EDB instances (or call open() before each table). Opt out of the legacy declaration in 2.0.0 with #define EDB_NO_GLOBAL before #include <EDB.h>.
recnois a 1-based slot address for I/O and iteration — not a durable logical record id. Putuint32_t id(orenableStableIds()) in your struct for anything you must find again later.- Valid
recnofor read/update/delete: allocated slots1throughn_slotsthat are live (seeisLive()). After deletes, do not assume1..count()covers all live rows — usefirstRec/nextRec. recno == 0always returnsEDB_OUT_OF_RANGE.
Optional compile-time flags (define before #include or via -D in build properties). See ENCRYPTION.md.
| Flag | Default | Purpose |
|---|---|---|
EDB_VERIFY_ON_READ |
1 |
Verify each record's CRC on read; 0 = write-only integrity (min read CPU) |
EDB_HEADER_REDUNDANT |
1 |
Store the header twice for atomic updates; 0 = single header (discouraged) |
EDB_WRITE_IF_DIFFERENT |
1 |
On byte handlers, skip writes whose stored value is already correct (cuts appendRec from 59 to 18 writes). Set 0 for RAM/FRAM backends where reads cost as much as writes. See BENCHMARK.md |
EDB_ENABLE_CRYPTO |
off | Include EDB_Crypto.h (ChaCha20-Poly1305 record encryption) |
EDB_CRYPTO_DEVICE_AUTONOMOUS |
off | On-device wrapped-key extension helpers |
EDB_NO_GLOBAL |
off | Omit legacy extern EDB edb |
EDB_TEST |
off | Test hooks (native tests only) |
Bridge sketch flags (examples/EDB_SerialBridge/config.h): EDB_BRIDGE_ENABLE_TRANSPORT_CRYPTO, EDB_BRIDGE_MAX_LINE, EDB_BRIDGE_ENABLE_BASE64.
The EDB Gateway exposes the same semantics over HTTP. Encrypted tables use stored_rec_size = plaintext_rec_size + 16 at create() time.