github.com/andy-kimball/arenaskl@v0.0.0-20200617143215-f701008588b9/node.go (about)

     1  /*
     2   * Copyright 2017 Dgraph Labs, Inc. and Contributors
     3   * Modifications copyright (C) 2017 Andy Kimball and Contributors
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package arenaskl
    19  
    20  import (
    21  	"sync/atomic"
    22  )
    23  
    24  type links struct {
    25  	nextOffset uint32
    26  	prevOffset uint32
    27  }
    28  
    29  func (l *links) init(prevOffset, nextOffset uint32) {
    30  	l.nextOffset = nextOffset
    31  	l.prevOffset = prevOffset
    32  }
    33  
    34  type node struct {
    35  	// Immutable fields, so no need to lock to access key.
    36  	keyOffset uint32
    37  	keySize   uint32
    38  
    39  	// Multiple parts of the value are encoded as a single uint64 so that it
    40  	// can be atomically loaded and stored:
    41  	//   value offset: uint32 (bits 0-31)
    42  	//   value size  : uint16 (bits 32-47)
    43  	//   metadata    : uint16 (bits 48-63)
    44  	value uint64
    45  
    46  	// Most nodes do not need to use the full height of the tower, since the
    47  	// probability of each successive level decreases exponentially. Because
    48  	// these elements are never accessed, they do not need to be allocated.
    49  	// Therefore, when a node is allocated in the arena, its memory footprint
    50  	// is deliberately truncated to not include unneeded tower elements.
    51  	//
    52  	// All accesses to elements should use CAS operations, with no need to lock.
    53  	tower [maxHeight]links
    54  }
    55  
    56  func newNode(arena *Arena, height uint32) (nd *node, err error) {
    57  	if height < 1 || height > maxHeight {
    58  		panic("height cannot be less than one or greater than the max height")
    59  	}
    60  
    61  	// Compute the amount of the tower that will never be used, since the height
    62  	// is less than maxHeight.
    63  	unusedSize := (maxHeight - int(height)) * linksSize
    64  
    65  	nodeOffset, err := arena.Alloc(uint32(MaxNodeSize-unusedSize), uint32(unusedSize), Align8)
    66  	if err != nil {
    67  		return
    68  	}
    69  
    70  	nd = (*node)(arena.GetPointer(nodeOffset))
    71  	return
    72  }
    73  
    74  func (n *node) getKey(arena *Arena) []byte {
    75  	return arena.GetBytes(n.keyOffset, n.keySize)
    76  }
    77  
    78  func (n *node) nextOffset(h int) uint32 {
    79  	return atomic.LoadUint32(&n.tower[h].nextOffset)
    80  }
    81  
    82  func (n *node) prevOffset(h int) uint32 {
    83  	return atomic.LoadUint32(&n.tower[h].prevOffset)
    84  }
    85  
    86  func (n *node) casNextOffset(h int, old, val uint32) bool {
    87  	return atomic.CompareAndSwapUint32(&n.tower[h].nextOffset, old, val)
    88  }
    89  
    90  func (n *node) casPrevOffset(h int, old, val uint32) bool {
    91  	return atomic.CompareAndSwapUint32(&n.tower[h].prevOffset, old, val)
    92  }