package common import ( "fmt" "hash/fnv" "io" "unsafe" "go.etcd.io/bbolt/errors" ) type Meta struct { magic uint32 version uint32 pageSize uint32 flags uint32 root InBucket freelist Pgid pgid Pgid txid Txid checksum uint64 } // Validate checks the marker bytes and version of the meta page to ensure it matches this binary. func (m *Meta) Validate() error { if m.magic != Magic { return errors.ErrInvalid } else if m.version != Version { return errors.ErrVersionMismatch } else if m.checksum != m.Sum64() { return errors.ErrChecksum } return nil } // Copy copies one meta object to another. func (m *Meta) Copy(dest *Meta) { *dest = *m } // Write writes the meta onto a page. func (m *Meta) Write(p *Page) { if m.root.root >= m.pgid { panic(fmt.Sprintf("root bucket pgid (%d) above high water mark (%d)", m.root.root, m.pgid)) } else if m.freelist >= m.pgid && m.freelist != PgidNoFreelist { // TODO: reject pgidNoFreeList if !NoFreelistSync panic(fmt.Sprintf("freelist pgid (%d) above high water mark (%d)", m.freelist, m.pgid)) } // Page id is either going to be 0 or 1 which we can determine by the transaction ID. p.id = Pgid(m.txid % 2) p.SetFlags(MetaPageFlag) // Calculate the checksum. m.checksum = m.Sum64() m.Copy(p.Meta()) } // Sum64 generates the checksum for the meta. func (m *Meta) Sum64() uint64 { var h = fnv.New64a() _, _ = h.Write((*[unsafe.Offsetof(Meta{}.checksum)]byte)(unsafe.Pointer(m))[:]) return h.Sum64() } func (m *Meta) Magic() uint32 { return m.magic } func (m *Meta) SetMagic(v uint32) { m.magic = v } func (m *Meta) Version() uint32 { return m.version } func (m *Meta) SetVersion(v uint32) { m.version = v } func (m *Meta) PageSize() uint32 { return m.pageSize } func (m *Meta) SetPageSize(v uint32) { m.pageSize = v } func (m *Meta) Flags() uint32 { return m.flags } func (m *Meta) SetFlags(v uint32) { m.flags = v } func (m *Meta) SetRootBucket(b InBucket) { m.root = b } func (m *Meta) RootBucket() *InBucket { return &m.root } func (m *Meta) Freelist() Pgid { return m.freelist } func (m *Meta) SetFreelist(v Pgid) { m.freelist = v } func (m *Meta) IsFreelistPersisted() bool { return m.freelist != PgidNoFreelist } func (m *Meta) Pgid() Pgid { return m.pgid } func (m *Meta) SetPgid(id Pgid) { m.pgid = id } func (m *Meta) Txid() Txid { return m.txid } func (m *Meta) SetTxid(id Txid) { m.txid = id } func (m *Meta) IncTxid() { m.txid += 1 } func (m *Meta) DecTxid() { m.txid -= 1 } func (m *Meta) Checksum() uint64 { return m.checksum } func (m *Meta) SetChecksum(v uint64) { m.checksum = v } func (m *Meta) Print(w io.Writer) { fmt.Fprintf(w, "Version: %d\n", m.version) fmt.Fprintf(w, "Page Size: %d bytes\n", m.pageSize) fmt.Fprintf(w, "Flags: %08x\n", m.flags) fmt.Fprintf(w, "Root: \n", m.root.root) fmt.Fprintf(w, "Freelist: \n", m.freelist) fmt.Fprintf(w, "HWM: \n", m.pgid) fmt.Fprintf(w, "Txn ID: %d\n", m.txid) fmt.Fprintf(w, "Checksum: %016x\n", m.checksum) fmt.Fprintf(w, "\n") }