You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.9 KiB
136 lines
3.9 KiB
/* |
|
* Copyright 2017 Dgraph Labs, Inc. and Contributors |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
package skl |
|
|
|
import ( |
|
"sync/atomic" |
|
"unsafe" |
|
|
|
"github.com/dgraph-io/badger/y" |
|
) |
|
|
|
const ( |
|
offsetSize = int(unsafe.Sizeof(uint32(0))) |
|
|
|
// Always align nodes on 64-bit boundaries, even on 32-bit architectures, |
|
// so that the node.value field is 64-bit aligned. This is necessary because |
|
// node.getValueOffset uses atomic.LoadUint64, which expects its input |
|
// pointer to be 64-bit aligned. |
|
nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1 |
|
) |
|
|
|
// Arena should be lock-free. |
|
type Arena struct { |
|
n uint32 |
|
buf []byte |
|
} |
|
|
|
// newArena returns a new arena. |
|
func newArena(n int64) *Arena { |
|
// Don't store data at position 0 in order to reserve offset=0 as a kind |
|
// of nil pointer. |
|
out := &Arena{ |
|
n: 1, |
|
buf: make([]byte, n), |
|
} |
|
return out |
|
} |
|
|
|
func (s *Arena) size() int64 { |
|
return int64(atomic.LoadUint32(&s.n)) |
|
} |
|
|
|
func (s *Arena) reset() { |
|
atomic.StoreUint32(&s.n, 0) |
|
} |
|
|
|
// putNode allocates a node in the arena. The node is aligned on a pointer-sized |
|
// boundary. The arena offset of the node is returned. |
|
func (s *Arena) putNode(height int) uint32 { |
|
// Compute the amount of the tower that will never be used, since the height |
|
// is less than maxHeight. |
|
unusedSize := (maxHeight - height) * offsetSize |
|
|
|
// Pad the allocation with enough bytes to ensure pointer alignment. |
|
l := uint32(MaxNodeSize - unusedSize + nodeAlign) |
|
n := atomic.AddUint32(&s.n, l) |
|
y.AssertTruef(int(n) <= len(s.buf), |
|
"Arena too small, toWrite:%d newTotal:%d limit:%d", |
|
l, n, len(s.buf)) |
|
|
|
// Return the aligned offset. |
|
m := (n - l + uint32(nodeAlign)) & ^uint32(nodeAlign) |
|
return m |
|
} |
|
|
|
// Put will *copy* val into arena. To make better use of this, reuse your input |
|
// val buffer. Returns an offset into buf. User is responsible for remembering |
|
// size of val. We could also store this size inside arena but the encoding and |
|
// decoding will incur some overhead. |
|
func (s *Arena) putVal(v y.ValueStruct) uint32 { |
|
l := uint32(v.EncodedSize()) |
|
n := atomic.AddUint32(&s.n, l) |
|
y.AssertTruef(int(n) <= len(s.buf), |
|
"Arena too small, toWrite:%d newTotal:%d limit:%d", |
|
l, n, len(s.buf)) |
|
m := n - l |
|
v.Encode(s.buf[m:]) |
|
return m |
|
} |
|
|
|
func (s *Arena) putKey(key []byte) uint32 { |
|
l := uint32(len(key)) |
|
n := atomic.AddUint32(&s.n, l) |
|
y.AssertTruef(int(n) <= len(s.buf), |
|
"Arena too small, toWrite:%d newTotal:%d limit:%d", |
|
l, n, len(s.buf)) |
|
m := n - l |
|
y.AssertTrue(len(key) == copy(s.buf[m:n], key)) |
|
return m |
|
} |
|
|
|
// getNode returns a pointer to the node located at offset. If the offset is |
|
// zero, then the nil node pointer is returned. |
|
func (s *Arena) getNode(offset uint32) *node { |
|
if offset == 0 { |
|
return nil |
|
} |
|
|
|
return (*node)(unsafe.Pointer(&s.buf[offset])) |
|
} |
|
|
|
// getKey returns byte slice at offset. |
|
func (s *Arena) getKey(offset uint32, size uint16) []byte { |
|
return s.buf[offset : offset+uint32(size)] |
|
} |
|
|
|
// getVal returns byte slice at offset. The given size should be just the value |
|
// size and should NOT include the meta bytes. |
|
func (s *Arena) getVal(offset uint32, size uint16) (ret y.ValueStruct) { |
|
ret.Decode(s.buf[offset : offset+uint32(size)]) |
|
return |
|
} |
|
|
|
// getNodeOffset returns the offset of node in the arena. If the node pointer is |
|
// nil, then the zero offset is returned. |
|
func (s *Arena) getNodeOffset(nd *node) uint32 { |
|
if nd == nil { |
|
return 0 |
|
} |
|
|
|
return uint32(uintptr(unsafe.Pointer(nd)) - uintptr(unsafe.Pointer(&s.buf[0]))) |
|
}
|
|
|