| 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: <pgid=%d>\n", m.root.root) |
| fmt.Fprintf(w, "Freelist: <pgid=%d>\n", m.freelist) |
| fmt.Fprintf(w, "HWM: <pgid=%d>\n", m.pgid) |
| fmt.Fprintf(w, "Txn ID: %d\n", m.txid) |
| fmt.Fprintf(w, "Checksum: %016x\n", m.checksum) |
| fmt.Fprintf(w, "\n") |
| } |