package common import ( "fmt" "os" "sort" "unsafe" ) const PageHeaderSize = unsafe.Sizeof(Page{}) const MinKeysPerPage = 2 const BranchPageElementSize = unsafe.Sizeof(branchPageElement{}) const LeafPageElementSize = unsafe.Sizeof(leafPageElement{}) const pgidSize = unsafe.Sizeof(Pgid(0)) const ( BranchPageFlag = 0x01 LeafPageFlag = 0x02 MetaPageFlag = 0x04 FreelistPageFlag = 0x10 ) const ( BucketLeafFlag = 0x01 ) type Pgid uint64 type Page struct { id Pgid flags uint16 count uint16 overflow uint32 } func NewPage(id Pgid, flags, count uint16, overflow uint32) *Page { return &Page{ id: id, flags: flags, count: count, overflow: overflow, } } // Typ returns a human-readable page type string used for debugging. func (p *Page) Typ() string { if p.IsBranchPage() { return "branch" } else if p.IsLeafPage() { return "leaf" } else if p.IsMetaPage() { return "meta" } else if p.IsFreelistPage() { return "freelist" } return fmt.Sprintf("unknown<%02x>", p.flags) } func (p *Page) IsBranchPage() bool { return p.flags == BranchPageFlag } func (p *Page) IsLeafPage() bool { return p.flags == LeafPageFlag } func (p *Page) IsMetaPage() bool { return p.flags == MetaPageFlag } func (p *Page) IsFreelistPage() bool { return p.flags == FreelistPageFlag } // Meta returns a pointer to the metadata section of the page. func (p *Page) Meta() *Meta { return (*Meta)(UnsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))) } func (p *Page) FastCheck(id Pgid) { Assert(p.id == id, "Page expected to be: %v, but self identifies as %v", id, p.id) // Only one flag of page-type can be set. Assert(p.IsBranchPage() || p.IsLeafPage() || p.IsMetaPage() || p.IsFreelistPage(), "page %v: has unexpected type/flags: %x", p.id, p.flags) } // LeafPageElement retrieves the leaf node by index func (p *Page) LeafPageElement(index uint16) *leafPageElement { return (*leafPageElement)(UnsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p), LeafPageElementSize, int(index))) } // LeafPageElements retrieves a list of leaf nodes. func (p *Page) LeafPageElements() []leafPageElement { if p.count == 0 { return nil } data := UnsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p)) elems := unsafe.Slice((*leafPageElement)(data), int(p.count)) return elems } // BranchPageElement retrieves the branch node by index func (p *Page) BranchPageElement(index uint16) *branchPageElement { return (*branchPageElement)(UnsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p), unsafe.Sizeof(branchPageElement{}), int(index))) } // BranchPageElements retrieves a list of branch nodes. func (p *Page) BranchPageElements() []branchPageElement { if p.count == 0 { return nil } data := UnsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p)) elems := unsafe.Slice((*branchPageElement)(data), int(p.count)) return elems } func (p *Page) FreelistPageCount() (int, int) { Assert(p.IsFreelistPage(), fmt.Sprintf("can't get freelist page count from a non-freelist page: %2x", p.flags)) // If the page.count is at the max uint16 value (64k) then it's considered // an overflow and the size of the freelist is stored as the first element. var idx, count = 0, int(p.count) if count == 0xFFFF { idx = 1 c := *(*Pgid)(UnsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))) count = int(c) if count < 0 { panic(fmt.Sprintf("leading element count %d overflows int", c)) } } return idx, count } func (p *Page) FreelistPageIds() []Pgid { Assert(p.IsFreelistPage(), fmt.Sprintf("can't get freelist page IDs from a non-freelist page: %2x", p.flags)) idx, count := p.FreelistPageCount() if count == 0 { return nil } data := UnsafeIndex(unsafe.Pointer(p), unsafe.Sizeof(*p), pgidSize, idx) ids := unsafe.Slice((*Pgid)(data), count) return ids } // dump writes n bytes of the page to STDERR as hex output. func (p *Page) hexdump(n int) { buf := UnsafeByteSlice(unsafe.Pointer(p), 0, 0, n) fmt.Fprintf(os.Stderr, "%x\n", buf) } func (p *Page) PageElementSize() uintptr { if p.IsLeafPage() { return LeafPageElementSize } return BranchPageElementSize } func (p *Page) Id() Pgid { return p.id } func (p *Page) SetId(target Pgid) { p.id = target } func (p *Page) Flags() uint16 { return p.flags } func (p *Page) SetFlags(v uint16) { p.flags = v } func (p *Page) Count() uint16 { return p.count } func (p *Page) SetCount(target uint16) { p.count = target } func (p *Page) Overflow() uint32 { return p.overflow } func (p *Page) SetOverflow(target uint32) { p.overflow = target } func (p *Page) String() string { return fmt.Sprintf("ID: %d, Type: %s, count: %d, overflow: %d", p.id, p.Typ(), p.count, p.overflow) } type Pages []*Page func (s Pages) Len() int { return len(s) } func (s Pages) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s Pages) Less(i, j int) bool { return s[i].id < s[j].id } // branchPageElement represents a node on a branch page. type branchPageElement struct { pos uint32 ksize uint32 pgid Pgid } func (n *branchPageElement) Pos() uint32 { return n.pos } func (n *branchPageElement) SetPos(v uint32) { n.pos = v } func (n *branchPageElement) Ksize() uint32 { return n.ksize } func (n *branchPageElement) SetKsize(v uint32) { n.ksize = v } func (n *branchPageElement) Pgid() Pgid { return n.pgid } func (n *branchPageElement) SetPgid(v Pgid) { n.pgid = v } // Key returns a byte slice of the node key. func (n *branchPageElement) Key() []byte { return UnsafeByteSlice(unsafe.Pointer(n), 0, int(n.pos), int(n.pos)+int(n.ksize)) } // leafPageElement represents a node on a leaf page. type leafPageElement struct { flags uint32 pos uint32 ksize uint32 vsize uint32 } func NewLeafPageElement(flags, pos, ksize, vsize uint32) *leafPageElement { return &leafPageElement{ flags: flags, pos: pos, ksize: ksize, vsize: vsize, } } func (n *leafPageElement) Flags() uint32 { return n.flags } func (n *leafPageElement) SetFlags(v uint32) { n.flags = v } func (n *leafPageElement) Pos() uint32 { return n.pos } func (n *leafPageElement) SetPos(v uint32) { n.pos = v } func (n *leafPageElement) Ksize() uint32 { return n.ksize } func (n *leafPageElement) SetKsize(v uint32) { n.ksize = v } func (n *leafPageElement) Vsize() uint32 { return n.vsize } func (n *leafPageElement) SetVsize(v uint32) { n.vsize = v } // Key returns a byte slice of the node key. func (n *leafPageElement) Key() []byte { i := int(n.pos) j := i + int(n.ksize) return UnsafeByteSlice(unsafe.Pointer(n), 0, i, j) } // Value returns a byte slice of the node value. func (n *leafPageElement) Value() []byte { i := int(n.pos) + int(n.ksize) j := i + int(n.vsize) return UnsafeByteSlice(unsafe.Pointer(n), 0, i, j) } func (n *leafPageElement) IsBucketEntry() bool { return n.flags&uint32(BucketLeafFlag) != 0 } func (n *leafPageElement) Bucket() *InBucket { if n.IsBucketEntry() { return LoadBucket(n.Value()) } else { return nil } } // PageInfo represents human readable information about a page. type PageInfo struct { ID int Type string Count int OverflowCount int } type Pgids []Pgid func (s Pgids) Len() int { return len(s) } func (s Pgids) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s Pgids) Less(i, j int) bool { return s[i] < s[j] } // Merge returns the sorted union of a and b. func (a Pgids) Merge(b Pgids) Pgids { // Return the opposite slice if one is nil. if len(a) == 0 { return b } if len(b) == 0 { return a } merged := make(Pgids, len(a)+len(b)) Mergepgids(merged, a, b) return merged } // Mergepgids copies the sorted union of a and b into dst. // If dst is too small, it panics. func Mergepgids(dst, a, b Pgids) { if len(dst) < len(a)+len(b) { panic(fmt.Errorf("mergepgids bad len %d < %d + %d", len(dst), len(a), len(b))) } // Copy in the opposite slice if one is nil. if len(a) == 0 { copy(dst, b) return } if len(b) == 0 { copy(dst, a) return } // Merged will hold all elements from both lists. merged := dst[:0] // Assign lead to the slice with a lower starting value, follow to the higher value. lead, follow := a, b if b[0] < a[0] { lead, follow = b, a } // Continue while there are elements in the lead. for len(lead) > 0 { // Merge largest prefix of lead that is ahead of follow[0]. n := sort.Search(len(lead), func(i int) bool { return lead[i] > follow[0] }) merged = append(merged, lead[:n]...) if n >= len(lead) { break } // Swap lead and follow. lead, follow = follow, lead[n:] } // Append what's left in follow. _ = append(merged, follow...) }