github.com/bhojpur/cache@v0.0.4/pkg/memory/page.go (about) 1 package memory 2 3 // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 import ( 24 "fmt" 25 "os" 26 "sort" 27 "unsafe" 28 ) 29 30 const pageHeaderSize = int(unsafe.Offsetof(((*page)(nil)).ptr)) 31 32 const minKeysPerPage = 2 33 34 const branchPageElementSize = int(unsafe.Sizeof(branchPageElement{})) 35 const leafPageElementSize = int(unsafe.Sizeof(leafPageElement{})) 36 37 const ( 38 branchPageFlag = 0x01 39 leafPageFlag = 0x02 40 metaPageFlag = 0x04 41 freelistPageFlag = 0x10 42 ) 43 44 const ( 45 bucketLeafFlag = 0x01 46 ) 47 48 type pgid uint64 49 50 type page struct { 51 id pgid 52 flags uint16 53 count uint16 54 overflow uint32 55 ptr uintptr 56 } 57 58 // typ returns a human readable page type string used for debugging. 59 func (p *page) typ() string { 60 if (p.flags & branchPageFlag) != 0 { 61 return "branch" 62 } else if (p.flags & leafPageFlag) != 0 { 63 return "leaf" 64 } else if (p.flags & metaPageFlag) != 0 { 65 return "meta" 66 } else if (p.flags & freelistPageFlag) != 0 { 67 return "freelist" 68 } 69 return fmt.Sprintf("unknown<%02x>", p.flags) 70 } 71 72 // meta returns a pointer to the metadata section of the page. 73 func (p *page) meta() *meta { 74 return (*meta)(unsafe.Pointer(&p.ptr)) 75 } 76 77 // leafPageElement retrieves the leaf node by index 78 func (p *page) leafPageElement(index uint16) *leafPageElement { 79 n := &((*[0x7FFFFFF]leafPageElement)(unsafe.Pointer(&p.ptr)))[index] 80 return n 81 } 82 83 // leafPageElements retrieves a list of leaf nodes. 84 func (p *page) leafPageElements() []leafPageElement { 85 if p.count == 0 { 86 return nil 87 } 88 return ((*[0x7FFFFFF]leafPageElement)(unsafe.Pointer(&p.ptr)))[:] 89 } 90 91 // branchPageElement retrieves the branch node by index 92 func (p *page) branchPageElement(index uint16) *branchPageElement { 93 return &((*[0x7FFFFFF]branchPageElement)(unsafe.Pointer(&p.ptr)))[index] 94 } 95 96 // branchPageElements retrieves a list of branch nodes. 97 func (p *page) branchPageElements() []branchPageElement { 98 if p.count == 0 { 99 return nil 100 } 101 return ((*[0x7FFFFFF]branchPageElement)(unsafe.Pointer(&p.ptr)))[:] 102 } 103 104 // dump writes n bytes of the page to STDERR as hex output. 105 func (p *page) hexdump(n int) { 106 buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:n] 107 fmt.Fprintf(os.Stderr, "%x\n", buf) 108 } 109 110 type pages []*page 111 112 func (s pages) Len() int { return len(s) } 113 func (s pages) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 114 func (s pages) Less(i, j int) bool { return s[i].id < s[j].id } 115 116 // branchPageElement represents a node on a branch page. 117 type branchPageElement struct { 118 pos uint32 119 ksize uint32 120 pgid pgid 121 } 122 123 // key returns a byte slice of the node key. 124 func (n *branchPageElement) key() []byte { 125 buf := (*[maxAllocSize]byte)(unsafe.Pointer(n)) 126 return (*[maxAllocSize]byte)(unsafe.Pointer(&buf[n.pos]))[:n.ksize] 127 } 128 129 // leafPageElement represents a node on a leaf page. 130 type leafPageElement struct { 131 flags uint32 132 pos uint32 133 ksize uint32 134 vsize uint32 135 } 136 137 // key returns a byte slice of the node key. 138 func (n *leafPageElement) key() []byte { 139 buf := (*[maxAllocSize]byte)(unsafe.Pointer(n)) 140 return (*[maxAllocSize]byte)(unsafe.Pointer(&buf[n.pos]))[:n.ksize:n.ksize] 141 } 142 143 // value returns a byte slice of the node value. 144 func (n *leafPageElement) value() []byte { 145 buf := (*[maxAllocSize]byte)(unsafe.Pointer(n)) 146 return (*[maxAllocSize]byte)(unsafe.Pointer(&buf[n.pos+n.ksize]))[:n.vsize:n.vsize] 147 } 148 149 // PageInfo represents human readable information about a page. 150 type PageInfo struct { 151 ID int 152 Type string 153 Count int 154 OverflowCount int 155 } 156 157 type pgids []pgid 158 159 func (s pgids) Len() int { return len(s) } 160 func (s pgids) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 161 func (s pgids) Less(i, j int) bool { return s[i] < s[j] } 162 163 // merge returns the sorted union of a and b. 164 func (a pgids) merge(b pgids) pgids { 165 // Return the opposite slice if one is nil. 166 if len(a) == 0 { 167 return b 168 } 169 if len(b) == 0 { 170 return a 171 } 172 merged := make(pgids, len(a)+len(b)) 173 mergepgids(merged, a, b) 174 return merged 175 } 176 177 // mergepgids copies the sorted union of a and b into dst. 178 // If dst is too small, it panics. 179 func mergepgids(dst, a, b pgids) { 180 if len(dst) < len(a)+len(b) { 181 panic(fmt.Errorf("mergepgids bad len %d < %d + %d", len(dst), len(a), len(b))) 182 } 183 // Copy in the opposite slice if one is nil. 184 if len(a) == 0 { 185 copy(dst, b) 186 return 187 } 188 if len(b) == 0 { 189 copy(dst, a) 190 return 191 } 192 193 // Merged will hold all elements from both lists. 194 merged := dst[:0] 195 196 // Assign lead to the slice with a lower starting value, follow to the higher value. 197 lead, follow := a, b 198 if b[0] < a[0] { 199 lead, follow = b, a 200 } 201 202 // Continue while there are elements in the lead. 203 for len(lead) > 0 { 204 // Merge largest prefix of lead that is ahead of follow[0]. 205 n := sort.Search(len(lead), func(i int) bool { return lead[i] > follow[0] }) 206 merged = append(merged, lead[:n]...) 207 if n >= len(lead) { 208 break 209 } 210 211 // Swap lead and follow. 212 lead, follow = follow, lead[n:] 213 } 214 215 // Append what's left in follow. 216 _ = append(merged, follow...) 217 }