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  }