package mdb import ( "bytes" "encoding/json" "sync/atomic" "git.crumpington.com/public/jldb/mdb/change" ) type Snapshot struct { parent atomic.Pointer[Snapshot] // The Snapshot's version is incremented each time it's cloned. version uint64 // The snapshot's seqNum is set when it becomes active (read-only).] seqNum int64 timestampMS int64 collections map[uint64]any // Map from collection ID to *collectionState[T]. changes []change.Change } func newSnapshot() *Snapshot { return &Snapshot{ collections: map[uint64]any{}, changes: []change.Change{}, } } func (s *Snapshot) addCollection(id uint64, c any) { s.collections[id] = c } func (s *Snapshot) writable() bool { return s.parent.Load() != nil } func (s *Snapshot) setReadOnly() { s.parent.Store(nil) s.changes = s.changes[:0] } func (s *Snapshot) store(cID, iID uint64, item any) { change := s.appendChange(cID, iID) change.Store = true buf := bytes.NewBuffer(change.Data[:0]) if err := json.NewEncoder(buf).Encode(item); err != nil { panic(err) } change.Data = buf.Bytes() } func (s *Snapshot) delete(cID, iID uint64) { change := s.appendChange(cID, iID) change.Store = false } func (s *Snapshot) appendChange(cID, iID uint64) *change.Change { if len(s.changes) == cap(s.changes) { s.changes = append(s.changes, change.Change{}) } else { s.changes = s.changes[:len(s.changes)+1] } change := &s.changes[len(s.changes)-1] change.CollectionID = cID change.ItemID = iID change.Store = false change.ClearPageIDs = change.ClearPageIDs[:0] change.WritePageIDs = change.WritePageIDs[:0] change.Data = change.Data[:0] return change } func (s *Snapshot) begin() *Snapshot { c := s.clone() c.changes = c.changes[:0] return c } func (s *Snapshot) clone() *Snapshot { collections := make(map[uint64]any, len(s.collections)) for k, v := range s.collections { collections[k] = v } c := &Snapshot{ version: s.version + 1, collections: collections, changes: s.changes[:], } c.parent.Store(s) return c } func (s *Snapshot) rollback() *Snapshot { parent := s.parent.Load() if parent == nil { return nil } // Don't throw away allocated changes. parent.changes = s.changes[:len(parent.changes)] return parent }