Transações de grafo de objetos com cópia na escrita

THotPDF provides nested savepoints for loaded or assembled indirect-object graphs without cloning the document at transaction start

Transaction lifecycle

PDF.BeginGraphTransaction('metadata edit');
try
  Info.AddStringValue('Producer', 'HotPDF');
  PDF.CommitGraphTransaction;
except
  PDF.RollbackGraphTransaction;
  raise;
end;

BeginGraphTransaction indexes the current indirect roots and installs mutation observers but creates no snapshots; the first write to an existing indirect root in that frame creates one snapshot, and later writes to the same root reuse it

CommitGraphTransaction keeps the current graph and discards or transfers snapshots without cloning it; RollbackGraphTransaction restores root objects in place, removes objects appended after the savepoint, and restores object-number allocation

Nested transactions are independent savepoints; an inner rollback returns to the inner entry state, while an inner commit retains enough history for a later outer rollback

Automatic and explicit write preparation

THPDFDictionaryObject.AddValue, ReplaceValue, and DeleteValue, THPDFArrayObject.AddObject e SetIndexedItem, and THPDFStreamObject.Length changes notify the enclosing indirect root automatically

Public scalar fields and raw stream bytes cannot intercept assignment; call PrepareLoadedObjectForWrite before assigning Value, changing public object flags, replacing graph pointers, or writing through THPDFStreamObject.Stream

PDF.BeginGraphTransaction('raw stream edit');
try
  PDF.PrepareLoadedObjectForWrite(StreamObject);
  StreamObject.Stream.Size:= 0;
  StreamObject.Stream.WriteBuffer(Bytes[0], Length(Bytes));
  StreamObject.Length:= Length(Bytes);
  PDF.CommitGraphTransaction;
except
  PDF.RollbackGraphTransaction;
  raise;
end;

Memory and performance

Snapshot memory grows with touched indirect roots rather than total document size; large stream payloads spill to owned temporary files after GraphTransactionMemoryThreshold, whose default is 16 MiB

Rollback swaps saved dictionary entries, array items, and stream storage back into the existing root, avoiding a second deep copy; the root pointer remains stable, but pointers to direct descendants must be reacquired after rollback

GetGraphTransactionStatistics devolve Active, nesting Depth, SnapshotCount, AddedObjectCount, resident and spilled snapshot bytes, and peak resident snapshot bytes

THPDFBeforeMutationEvent is the low-level observer signature used to route container and lifecycle mutations to the enclosing indirect-root snapshot

Scope

Transactions cover the indirect-object graph, lifecycle state, appended roots, and object-number allocation; caller-owned pointers, loaded-page ordering arrays, rendered caches, and other facade side state are not journalled

Do not remove, reorder, or replace existing entries in GetLoadedIndirectObjects while a transaction is active; use graph container methods and reacquire direct descendants after rollback