Initial commit
This commit is contained in:
201
vendor/github.com/cockroachdb/pebble/internal/arenaskl/LICENSE
generated
vendored
Normal file
201
vendor/github.com/cockroachdb/pebble/internal/arenaskl/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
93
vendor/github.com/cockroachdb/pebble/internal/arenaskl/README.md
generated
vendored
Normal file
93
vendor/github.com/cockroachdb/pebble/internal/arenaskl/README.md
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
# arenaskl
|
||||
|
||||
Fast, lock-free, arena-based Skiplist implementation in Go that supports iteration
|
||||
in both directions.
|
||||
|
||||
## Advantages
|
||||
|
||||
Arenaskl offers several advantages over other skiplist implementations:
|
||||
|
||||
* High performance that linearly scales with the number of cores. This is
|
||||
achieved by allocating from a fixed-size arena and by avoiding locks.
|
||||
* Iterators that can be allocated on the stack and easily cloned by value.
|
||||
* Simple-to-use and low overhead model for detecting and handling race conditions
|
||||
with other threads.
|
||||
* Support for iterating in reverse (i.e. previous links).
|
||||
|
||||
## Limitations
|
||||
|
||||
The advantages come at a cost that prevents arenaskl from being a general-purpose
|
||||
skiplist implementation:
|
||||
|
||||
* The size of the arena sets a hard upper bound on the combined size of skiplist
|
||||
nodes, keys, and values. This limit includes even the size of deleted nodes,
|
||||
keys, and values.
|
||||
* Deletion is not supported. Instead, higher-level code is expected to
|
||||
add deletion tombstones and needs to process those tombstones
|
||||
appropriately.
|
||||
|
||||
## Pedigree
|
||||
|
||||
This code is based on Andy Kimball's arenaskl code:
|
||||
|
||||
https://github.com/andy-kimball/arenaskl
|
||||
|
||||
The arenaskl code is based on the skiplist found in Badger, a Go-based
|
||||
KV store:
|
||||
|
||||
https://github.com/dgraph-io/badger/tree/master/skl
|
||||
|
||||
The skiplist in Badger is itself based on a C++ skiplist built for
|
||||
Facebook's RocksDB:
|
||||
|
||||
https://github.com/facebook/rocksdb/tree/master/memtable
|
||||
|
||||
## Benchmarks
|
||||
|
||||
The benchmarks consist of a mix of reads and writes executed in parallel. The
|
||||
fraction of reads is indicated in the run name: "frac_X" indicates a run where
|
||||
X percent of the operations are reads.
|
||||
|
||||
The results are much better than `skiplist` and `slist`.
|
||||
|
||||
```
|
||||
name time/op
|
||||
ReadWrite/frac_0-8 470ns ±11%
|
||||
ReadWrite/frac_10-8 462ns ± 3%
|
||||
ReadWrite/frac_20-8 436ns ± 2%
|
||||
ReadWrite/frac_30-8 410ns ± 2%
|
||||
ReadWrite/frac_40-8 385ns ± 2%
|
||||
ReadWrite/frac_50-8 360ns ± 4%
|
||||
ReadWrite/frac_60-8 386ns ± 1%
|
||||
ReadWrite/frac_70-8 352ns ± 2%
|
||||
ReadWrite/frac_80-8 306ns ± 3%
|
||||
ReadWrite/frac_90-8 253ns ± 4%
|
||||
ReadWrite/frac_100-8 28.1ns ± 2%
|
||||
```
|
||||
|
||||
Note that the above numbers are for concurrent operations using 8x
|
||||
parallelism. The same benchmarks without concurrency (use these
|
||||
numbers when comparing vs batchskl):
|
||||
|
||||
```
|
||||
name time/op
|
||||
ReadWrite/frac_0 1.53µs ± 1%
|
||||
ReadWrite/frac_10 1.46µs ± 2%
|
||||
ReadWrite/frac_20 1.39µs ± 3%
|
||||
ReadWrite/frac_30 1.28µs ± 3%
|
||||
ReadWrite/frac_40 1.21µs ± 2%
|
||||
ReadWrite/frac_50 1.11µs ± 3%
|
||||
ReadWrite/frac_60 1.23µs ±17%
|
||||
ReadWrite/frac_70 1.16µs ± 4%
|
||||
ReadWrite/frac_80 959ns ± 3%
|
||||
ReadWrite/frac_90 738ns ± 5%
|
||||
ReadWrite/frac_100 81.9ns ± 2%
|
||||
```
|
||||
|
||||
Forward and backward iteration are also fast:
|
||||
|
||||
```
|
||||
name time/op
|
||||
IterNext 3.97ns ± 5%
|
||||
IterPrev 3.88ns ± 3%
|
||||
```
|
||||
109
vendor/github.com/cockroachdb/pebble/internal/arenaskl/arena.go
generated
vendored
Normal file
109
vendor/github.com/cockroachdb/pebble/internal/arenaskl/arena.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2017 Dgraph Labs, Inc. and Contributors
|
||||
* Modifications copyright (C) 2017 Andy Kimball 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 arenaskl
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
)
|
||||
|
||||
// Arena is lock-free.
|
||||
type Arena struct {
|
||||
n uint64
|
||||
buf []byte
|
||||
}
|
||||
|
||||
const (
|
||||
align4 = 3
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrArenaFull indicates that the arena is full and cannot perform any more
|
||||
// allocations.
|
||||
ErrArenaFull = errors.New("allocation failed because arena is full")
|
||||
)
|
||||
|
||||
// NewArena allocates a new arena using the specified buffer as the backing
|
||||
// store.
|
||||
func NewArena(buf []byte) *Arena {
|
||||
// Don't store data at position 0 in order to reserve offset=0 as a kind
|
||||
// of nil pointer.
|
||||
return &Arena{
|
||||
n: 1,
|
||||
buf: buf,
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the number of bytes allocated by the arena.
|
||||
func (a *Arena) Size() uint32 {
|
||||
s := atomic.LoadUint64(&a.n)
|
||||
if s > math.MaxUint32 {
|
||||
// Saturate at MaxUint32.
|
||||
return math.MaxUint32
|
||||
}
|
||||
return uint32(s)
|
||||
}
|
||||
|
||||
// Capacity returns the capacity of the arena.
|
||||
func (a *Arena) Capacity() uint32 {
|
||||
return uint32(len(a.buf))
|
||||
}
|
||||
|
||||
func (a *Arena) alloc(size, align, overflow uint32) (uint32, uint32, error) {
|
||||
// Verify that the arena isn't already full.
|
||||
origSize := atomic.LoadUint64(&a.n)
|
||||
if int(origSize) > len(a.buf) {
|
||||
return 0, 0, ErrArenaFull
|
||||
}
|
||||
|
||||
// Pad the allocation with enough bytes to ensure the requested alignment.
|
||||
padded := uint32(size) + align
|
||||
|
||||
newSize := atomic.AddUint64(&a.n, uint64(padded))
|
||||
if int(newSize)+int(overflow) > len(a.buf) {
|
||||
return 0, 0, ErrArenaFull
|
||||
}
|
||||
|
||||
// Return the aligned offset.
|
||||
offset := (uint32(newSize) - padded + align) & ^align
|
||||
return offset, padded, nil
|
||||
}
|
||||
|
||||
func (a *Arena) getBytes(offset uint32, size uint32) []byte {
|
||||
if offset == 0 {
|
||||
return nil
|
||||
}
|
||||
return a.buf[offset : offset+size : offset+size]
|
||||
}
|
||||
|
||||
func (a *Arena) getPointer(offset uint32) unsafe.Pointer {
|
||||
if offset == 0 {
|
||||
return nil
|
||||
}
|
||||
return unsafe.Pointer(&a.buf[offset])
|
||||
}
|
||||
|
||||
func (a *Arena) getPointerOffset(ptr unsafe.Pointer) uint32 {
|
||||
if ptr == nil {
|
||||
return 0
|
||||
}
|
||||
return uint32(uintptr(ptr) - uintptr(unsafe.Pointer(&a.buf[0])))
|
||||
}
|
||||
80
vendor/github.com/cockroachdb/pebble/internal/arenaskl/flush_iterator.go
generated
vendored
Normal file
80
vendor/github.com/cockroachdb/pebble/internal/arenaskl/flush_iterator.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2017 Dgraph Labs, Inc. and Contributors
|
||||
* Modifications copyright (C) 2017 Andy Kimball 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 arenaskl
|
||||
|
||||
import "github.com/cockroachdb/pebble/internal/base"
|
||||
|
||||
// flushIterator is an iterator over the skiplist object. Use Skiplist.NewFlushIter
|
||||
// to construct an iterator. The current state of the iterator can be cloned by
|
||||
// simply value copying the struct.
|
||||
type flushIterator struct {
|
||||
Iterator
|
||||
bytesIterated *uint64
|
||||
}
|
||||
|
||||
// flushIterator implements the base.InternalIterator interface.
|
||||
var _ base.InternalIterator = (*flushIterator)(nil)
|
||||
|
||||
func (it *flushIterator) String() string {
|
||||
return "memtable"
|
||||
}
|
||||
|
||||
func (it *flushIterator) SeekGE(key []byte, flags base.SeekGEFlags) (*base.InternalKey, []byte) {
|
||||
panic("pebble: SeekGE unimplemented")
|
||||
}
|
||||
|
||||
func (it *flushIterator) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (it *flushIterator) SeekLT(key []byte, flags base.SeekLTFlags) (*base.InternalKey, []byte) {
|
||||
panic("pebble: SeekLT unimplemented")
|
||||
}
|
||||
|
||||
// First seeks position at the first entry in list. Returns the key and value
|
||||
// if the iterator is pointing at a valid entry, and (nil, nil) otherwise. Note
|
||||
// that First only checks the upper bound. It is up to the caller to ensure
|
||||
// that key is greater than or equal to the lower bound.
|
||||
func (it *flushIterator) First() (*base.InternalKey, []byte) {
|
||||
key, val := it.Iterator.First()
|
||||
if key == nil {
|
||||
return nil, nil
|
||||
}
|
||||
*it.bytesIterated += uint64(it.nd.allocSize)
|
||||
return key, val
|
||||
}
|
||||
|
||||
// Next advances to the next position. Returns the key and value if the
|
||||
// iterator is pointing at a valid entry, and (nil, nil) otherwise.
|
||||
// Note: flushIterator.Next mirrors the implementation of Iterator.Next
|
||||
// due to performance. Keep the two in sync.
|
||||
func (it *flushIterator) Next() (*base.InternalKey, []byte) {
|
||||
it.nd = it.list.getNext(it.nd, 0)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
}
|
||||
it.decodeKey()
|
||||
*it.bytesIterated += uint64(it.nd.allocSize)
|
||||
return &it.key, it.value()
|
||||
}
|
||||
|
||||
func (it *flushIterator) Prev() (*base.InternalKey, []byte) {
|
||||
panic("pebble: Prev unimplemented")
|
||||
}
|
||||
274
vendor/github.com/cockroachdb/pebble/internal/arenaskl/iterator.go
generated
vendored
Normal file
274
vendor/github.com/cockroachdb/pebble/internal/arenaskl/iterator.go
generated
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright 2017 Dgraph Labs, Inc. and Contributors
|
||||
* Modifications copyright (C) 2017 Andy Kimball 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 arenaskl
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
)
|
||||
|
||||
type splice struct {
|
||||
prev *node
|
||||
next *node
|
||||
}
|
||||
|
||||
func (s *splice) init(prev, next *node) {
|
||||
s.prev = prev
|
||||
s.next = next
|
||||
}
|
||||
|
||||
// Iterator is an iterator over the skiplist object. Use Skiplist.NewIter
|
||||
// to construct an iterator. The current state of the iterator can be cloned by
|
||||
// simply value copying the struct. All iterator methods are thread-safe.
|
||||
type Iterator struct {
|
||||
list *Skiplist
|
||||
nd *node
|
||||
key base.InternalKey
|
||||
lower []byte
|
||||
upper []byte
|
||||
}
|
||||
|
||||
// Iterator implements the base.InternalIterator interface.
|
||||
var _ base.InternalIterator = (*Iterator)(nil)
|
||||
|
||||
var iterPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Iterator{}
|
||||
},
|
||||
}
|
||||
|
||||
// Close resets the iterator.
|
||||
func (it *Iterator) Close() error {
|
||||
it.list = nil
|
||||
it.nd = nil
|
||||
it.lower = nil
|
||||
it.upper = nil
|
||||
iterPool.Put(it)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *Iterator) String() string {
|
||||
return "memtable"
|
||||
}
|
||||
|
||||
// Error returns any accumulated error.
|
||||
func (it *Iterator) Error() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SeekGE moves the iterator to the first entry whose key is greater than or
|
||||
// equal to the given key. Returns the key and value if the iterator is
|
||||
// pointing at a valid entry, and (nil, nil) otherwise. Note that SeekGE only
|
||||
// checks the upper bound. It is up to the caller to ensure that key is greater
|
||||
// than or equal to the lower bound.
|
||||
func (it *Iterator) SeekGE(key []byte, flags base.SeekGEFlags) (*base.InternalKey, []byte) {
|
||||
if flags.TrySeekUsingNext() {
|
||||
if it.nd == it.list.tail {
|
||||
// Iterator is done.
|
||||
return nil, nil
|
||||
}
|
||||
less := it.list.cmp(it.key.UserKey, key) < 0
|
||||
// Arbitrary constant. By measuring the seek cost as a function of the
|
||||
// number of elements in the skip list, and fitting to a model, we
|
||||
// could adjust the number of nexts based on the current size of the
|
||||
// skip list.
|
||||
const numNexts = 5
|
||||
for i := 0; less && i < numNexts; i++ {
|
||||
k, _ := it.Next()
|
||||
if k == nil {
|
||||
// Iterator is done.
|
||||
return nil, nil
|
||||
}
|
||||
less = it.list.cmp(it.key.UserKey, key) < 0
|
||||
}
|
||||
if !less {
|
||||
return &it.key, it.value()
|
||||
}
|
||||
}
|
||||
_, it.nd, _ = it.seekForBaseSplice(key)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.upper != nil && it.list.cmp(it.upper, it.key.UserKey) <= 0 {
|
||||
it.nd = it.list.tail
|
||||
return nil, nil
|
||||
}
|
||||
return &it.key, it.value()
|
||||
}
|
||||
|
||||
// SeekPrefixGE moves the iterator to the first entry whose key is greater than
|
||||
// or equal to the given key. This method is equivalent to SeekGE and is
|
||||
// provided so that an arenaskl.Iterator implements the
|
||||
// internal/base.InternalIterator interface.
|
||||
func (it *Iterator) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
return it.SeekGE(key, flags)
|
||||
}
|
||||
|
||||
// SeekLT moves the iterator to the last entry whose key is less than the given
|
||||
// key. Returns the key and value if the iterator is pointing at a valid entry,
|
||||
// and (nil, nil) otherwise. Note that SeekLT only checks the lower bound. It
|
||||
// is up to the caller to ensure that key is less than the upper bound.
|
||||
func (it *Iterator) SeekLT(key []byte, flags base.SeekLTFlags) (*base.InternalKey, []byte) {
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// the upper-bound.
|
||||
it.nd, _, _ = it.seekForBaseSplice(key)
|
||||
if it.nd == it.list.head {
|
||||
return nil, nil
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.lower != nil && it.list.cmp(it.lower, it.key.UserKey) > 0 {
|
||||
it.nd = it.list.head
|
||||
return nil, nil
|
||||
}
|
||||
return &it.key, it.value()
|
||||
}
|
||||
|
||||
// First seeks position at the first entry in list. Returns the key and value
|
||||
// if the iterator is pointing at a valid entry, and (nil, nil) otherwise. Note
|
||||
// that First only checks the upper bound. It is up to the caller to ensure
|
||||
// that key is greater than or equal to the lower bound (e.g. via a call to SeekGE(lower)).
|
||||
func (it *Iterator) First() (*base.InternalKey, []byte) {
|
||||
it.nd = it.list.getNext(it.list.head, 0)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.upper != nil && it.list.cmp(it.upper, it.key.UserKey) <= 0 {
|
||||
it.nd = it.list.tail
|
||||
return nil, nil
|
||||
}
|
||||
return &it.key, it.value()
|
||||
}
|
||||
|
||||
// Last seeks position at the last entry in list. Returns the key and value if
|
||||
// the iterator is pointing at a valid entry, and (nil, nil) otherwise. Note
|
||||
// that Last only checks the lower bound. It is up to the caller to ensure that
|
||||
// key is less than the upper bound (e.g. via a call to SeekLT(upper)).
|
||||
func (it *Iterator) Last() (*base.InternalKey, []byte) {
|
||||
it.nd = it.list.getPrev(it.list.tail, 0)
|
||||
if it.nd == it.list.head {
|
||||
return nil, nil
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.lower != nil && it.list.cmp(it.lower, it.key.UserKey) > 0 {
|
||||
it.nd = it.list.head
|
||||
return nil, nil
|
||||
}
|
||||
return &it.key, it.value()
|
||||
}
|
||||
|
||||
// Next advances to the next position. Returns the key and value if the
|
||||
// iterator is pointing at a valid entry, and (nil, nil) otherwise.
|
||||
// Note: flushIterator.Next mirrors the implementation of Iterator.Next
|
||||
// due to performance. Keep the two in sync.
|
||||
func (it *Iterator) Next() (*base.InternalKey, []byte) {
|
||||
it.nd = it.list.getNext(it.nd, 0)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.upper != nil && it.list.cmp(it.upper, it.key.UserKey) <= 0 {
|
||||
it.nd = it.list.tail
|
||||
return nil, nil
|
||||
}
|
||||
return &it.key, it.value()
|
||||
}
|
||||
|
||||
// Prev moves to the previous position. Returns the key and value if the
|
||||
// iterator is pointing at a valid entry, and (nil, nil) otherwise.
|
||||
func (it *Iterator) Prev() (*base.InternalKey, []byte) {
|
||||
it.nd = it.list.getPrev(it.nd, 0)
|
||||
if it.nd == it.list.head {
|
||||
return nil, nil
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.lower != nil && it.list.cmp(it.lower, it.key.UserKey) > 0 {
|
||||
it.nd = it.list.head
|
||||
return nil, nil
|
||||
}
|
||||
return &it.key, it.value()
|
||||
}
|
||||
|
||||
// value returns the value at the current position.
|
||||
func (it *Iterator) value() []byte {
|
||||
return it.nd.getValue(it.list.arena)
|
||||
}
|
||||
|
||||
// Head true iff the iterator is positioned at the sentinel head node.
|
||||
func (it *Iterator) Head() bool {
|
||||
return it.nd == it.list.head
|
||||
}
|
||||
|
||||
// Tail true iff the iterator is positioned at the sentinel tail node.
|
||||
func (it *Iterator) Tail() bool {
|
||||
return it.nd == it.list.tail
|
||||
}
|
||||
|
||||
// SetBounds sets the lower and upper bounds for the iterator. Note that the
|
||||
// result of Next and Prev will be undefined until the iterator has been
|
||||
// repositioned with SeekGE, SeekPrefixGE, SeekLT, First, or Last.
|
||||
func (it *Iterator) SetBounds(lower, upper []byte) {
|
||||
it.lower = lower
|
||||
it.upper = upper
|
||||
}
|
||||
|
||||
func (it *Iterator) decodeKey() {
|
||||
b := it.list.arena.getBytes(it.nd.keyOffset, it.nd.keySize)
|
||||
// This is a manual inline of base.DecodeInternalKey, because the Go compiler
|
||||
// seems to refuse to automatically inline it currently.
|
||||
l := len(b) - 8
|
||||
if l >= 0 {
|
||||
it.key.Trailer = binary.LittleEndian.Uint64(b[l:])
|
||||
it.key.UserKey = b[:l:l]
|
||||
} else {
|
||||
it.key.Trailer = uint64(base.InternalKeyKindInvalid)
|
||||
it.key.UserKey = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Iterator) seekForBaseSplice(key []byte) (prev, next *node, found bool) {
|
||||
ikey := base.MakeSearchKey(key)
|
||||
level := int(it.list.Height() - 1)
|
||||
|
||||
prev = it.list.head
|
||||
for {
|
||||
prev, next, found = it.list.findSpliceForLevel(ikey, level, prev)
|
||||
|
||||
if found {
|
||||
if level != 0 {
|
||||
// next is pointing at the target node, but we need to find previous on
|
||||
// the bottom level.
|
||||
prev = it.list.getPrev(next, 0)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if level == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
level--
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
131
vendor/github.com/cockroachdb/pebble/internal/arenaskl/node.go
generated
vendored
Normal file
131
vendor/github.com/cockroachdb/pebble/internal/arenaskl/node.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2017 Dgraph Labs, Inc. and Contributors
|
||||
* Modifications copyright (C) 2017 Andy Kimball 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 arenaskl
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
)
|
||||
|
||||
// MaxNodeSize returns the maximum space needed for a node with the specified
|
||||
// key and value sizes. This could overflow a uint32, which is why a uint64
|
||||
// is used here. If a key/value overflows a uint32, it should not be added to
|
||||
// the skiplist.
|
||||
func MaxNodeSize(keySize, valueSize uint32) uint64 {
|
||||
return uint64(maxNodeSize) + uint64(keySize) + uint64(valueSize) + align4
|
||||
}
|
||||
|
||||
type links struct {
|
||||
nextOffset uint32
|
||||
prevOffset uint32
|
||||
}
|
||||
|
||||
func (l *links) init(prevOffset, nextOffset uint32) {
|
||||
l.nextOffset = nextOffset
|
||||
l.prevOffset = prevOffset
|
||||
}
|
||||
|
||||
type node struct {
|
||||
// Immutable fields, so no need to lock to access key.
|
||||
keyOffset uint32
|
||||
keySize uint32
|
||||
valueSize uint32
|
||||
allocSize uint32
|
||||
|
||||
// Most nodes do not need to use the full height of the tower, since the
|
||||
// probability of each successive level decreases exponentially. Because
|
||||
// these elements are never accessed, they do not need to be allocated.
|
||||
// Therefore, when a node is allocated in the arena, its memory footprint
|
||||
// is deliberately truncated to not include unneeded tower elements.
|
||||
//
|
||||
// All accesses to elements should use CAS operations, with no need to lock.
|
||||
tower [maxHeight]links
|
||||
}
|
||||
|
||||
func newNode(
|
||||
arena *Arena, height uint32, key base.InternalKey, value []byte,
|
||||
) (nd *node, err error) {
|
||||
if height < 1 || height > maxHeight {
|
||||
panic("height cannot be less than one or greater than the max height")
|
||||
}
|
||||
keySize := key.Size()
|
||||
if int64(keySize) > math.MaxUint32 {
|
||||
panic("key is too large")
|
||||
}
|
||||
valueSize := len(value)
|
||||
if int64(len(value)) > math.MaxUint32 {
|
||||
panic("value is too large")
|
||||
}
|
||||
if int64(len(value))+int64(keySize)+int64(maxNodeSize) > math.MaxUint32 {
|
||||
panic("combined key and value size is too large")
|
||||
}
|
||||
|
||||
nd, err = newRawNode(arena, height, uint32(keySize), uint32(valueSize))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
key.Encode(nd.getKeyBytes(arena))
|
||||
copy(nd.getValue(arena), value)
|
||||
return
|
||||
}
|
||||
|
||||
func newRawNode(arena *Arena, height uint32, keySize, valueSize uint32) (nd *node, err error) {
|
||||
// Compute the amount of the tower that will never be used, since the height
|
||||
// is less than maxHeight.
|
||||
unusedSize := uint32((maxHeight - int(height)) * linksSize)
|
||||
nodeSize := uint32(maxNodeSize) - unusedSize
|
||||
|
||||
nodeOffset, allocSize, err := arena.alloc(nodeSize+keySize+valueSize, align4, unusedSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nd = (*node)(arena.getPointer(nodeOffset))
|
||||
nd.keyOffset = nodeOffset + nodeSize
|
||||
nd.keySize = keySize
|
||||
nd.valueSize = valueSize
|
||||
nd.allocSize = allocSize
|
||||
return
|
||||
}
|
||||
|
||||
func (n *node) getKeyBytes(arena *Arena) []byte {
|
||||
return arena.getBytes(n.keyOffset, n.keySize)
|
||||
}
|
||||
|
||||
func (n *node) getValue(arena *Arena) []byte {
|
||||
return arena.getBytes(n.keyOffset+n.keySize, uint32(n.valueSize))
|
||||
}
|
||||
|
||||
func (n *node) nextOffset(h int) uint32 {
|
||||
return atomic.LoadUint32(&n.tower[h].nextOffset)
|
||||
}
|
||||
|
||||
func (n *node) prevOffset(h int) uint32 {
|
||||
return atomic.LoadUint32(&n.tower[h].prevOffset)
|
||||
}
|
||||
|
||||
func (n *node) casNextOffset(h int, old, val uint32) bool {
|
||||
return atomic.CompareAndSwapUint32(&n.tower[h].nextOffset, old, val)
|
||||
}
|
||||
|
||||
func (n *node) casPrevOffset(h int, old, val uint32) bool {
|
||||
return atomic.CompareAndSwapUint32(&n.tower[h].prevOffset, old, val)
|
||||
}
|
||||
479
vendor/github.com/cockroachdb/pebble/internal/arenaskl/skl.go
generated
vendored
Normal file
479
vendor/github.com/cockroachdb/pebble/internal/arenaskl/skl.go
generated
vendored
Normal file
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright 2017 Dgraph Labs, Inc. and Contributors
|
||||
* Modifications copyright (C) 2017 Andy Kimball 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
Adapted from RocksDB inline skiplist.
|
||||
|
||||
Key differences:
|
||||
- No optimization for sequential inserts (no "prev").
|
||||
- No custom comparator.
|
||||
- Support overwrites. This requires care when we see the same key when inserting.
|
||||
For RocksDB or LevelDB, overwrites are implemented as a newer sequence number in the key, so
|
||||
there is no need for values. We don't intend to support versioning. In-place updates of values
|
||||
would be more efficient.
|
||||
- We discard all non-concurrent code.
|
||||
- We do not support Splices. This simplifies the code a lot.
|
||||
- No AllocateNode or other pointer arithmetic.
|
||||
- We combine the findLessThan, findGreaterOrEqual, etc into one function.
|
||||
*/
|
||||
|
||||
/*
|
||||
Further adapted from Badger: https://github.com/dgraph-io/badger.
|
||||
|
||||
Key differences:
|
||||
- Support for previous pointers - doubly linked lists. Note that it's up to higher
|
||||
level code to deal with the intermediate state that occurs during insertion,
|
||||
where node A is linked to node B, but node B is not yet linked back to node A.
|
||||
- Iterator includes mutator functions.
|
||||
*/
|
||||
|
||||
package arenaskl // import "github.com/cockroachdb/pebble/internal/arenaskl"
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/fastrand"
|
||||
)
|
||||
|
||||
const (
|
||||
maxHeight = 20
|
||||
maxNodeSize = int(unsafe.Sizeof(node{}))
|
||||
linksSize = int(unsafe.Sizeof(links{}))
|
||||
pValue = 1 / math.E
|
||||
)
|
||||
|
||||
// ErrRecordExists indicates that an entry with the specified key already
|
||||
// exists in the skiplist. Duplicate entries are not directly supported and
|
||||
// instead must be handled by the user by appending a unique version suffix to
|
||||
// keys.
|
||||
var ErrRecordExists = errors.New("record with this key already exists")
|
||||
|
||||
// Skiplist is a fast, cocnurrent skiplist implementation that supports forward
|
||||
// and backward iteration. See batchskl.Skiplist for a non-concurrent
|
||||
// skiplist. Keys and values are immutable once added to the skiplist and
|
||||
// deletion is not supported. Instead, higher-level code is expected to add new
|
||||
// entries that shadow existing entries and perform deletion via tombstones. It
|
||||
// is up to the user to process these shadow entries and tombstones
|
||||
// appropriately during retrieval.
|
||||
type Skiplist struct {
|
||||
arena *Arena
|
||||
cmp base.Compare
|
||||
head *node
|
||||
tail *node
|
||||
height uint32 // Current height. 1 <= height <= maxHeight. CAS.
|
||||
|
||||
// If set to true by tests, then extra delays are added to make it easier to
|
||||
// detect unusual race conditions.
|
||||
testing bool
|
||||
}
|
||||
|
||||
// Inserter TODO(peter)
|
||||
type Inserter struct {
|
||||
spl [maxHeight]splice
|
||||
height uint32
|
||||
}
|
||||
|
||||
// Add TODO(peter)
|
||||
func (ins *Inserter) Add(list *Skiplist, key base.InternalKey, value []byte) error {
|
||||
return list.addInternal(key, value, ins)
|
||||
}
|
||||
|
||||
var (
|
||||
probabilities [maxHeight]uint32
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Precompute the skiplist probabilities so that only a single random number
|
||||
// needs to be generated and so that the optimal pvalue can be used (inverse
|
||||
// of Euler's number).
|
||||
p := float64(1.0)
|
||||
for i := 0; i < maxHeight; i++ {
|
||||
probabilities[i] = uint32(float64(math.MaxUint32) * p)
|
||||
p *= pValue
|
||||
}
|
||||
}
|
||||
|
||||
// NewSkiplist constructs and initializes a new, empty skiplist. All nodes, keys,
|
||||
// and values in the skiplist will be allocated from the given arena.
|
||||
func NewSkiplist(arena *Arena, cmp base.Compare) *Skiplist {
|
||||
skl := &Skiplist{}
|
||||
skl.Reset(arena, cmp)
|
||||
return skl
|
||||
}
|
||||
|
||||
// Reset the skiplist to empty and re-initialize.
|
||||
func (s *Skiplist) Reset(arena *Arena, cmp base.Compare) {
|
||||
// Allocate head and tail nodes.
|
||||
head, err := newRawNode(arena, maxHeight, 0, 0)
|
||||
if err != nil {
|
||||
panic("arenaSize is not large enough to hold the head node")
|
||||
}
|
||||
head.keyOffset = 0
|
||||
|
||||
tail, err := newRawNode(arena, maxHeight, 0, 0)
|
||||
if err != nil {
|
||||
panic("arenaSize is not large enough to hold the tail node")
|
||||
}
|
||||
tail.keyOffset = 0
|
||||
|
||||
// Link all head/tail levels together.
|
||||
headOffset := arena.getPointerOffset(unsafe.Pointer(head))
|
||||
tailOffset := arena.getPointerOffset(unsafe.Pointer(tail))
|
||||
for i := 0; i < maxHeight; i++ {
|
||||
head.tower[i].nextOffset = tailOffset
|
||||
tail.tower[i].prevOffset = headOffset
|
||||
}
|
||||
|
||||
*s = Skiplist{
|
||||
arena: arena,
|
||||
cmp: cmp,
|
||||
head: head,
|
||||
tail: tail,
|
||||
height: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Height returns the height of the highest tower within any of the nodes that
|
||||
// have ever been allocated as part of this skiplist.
|
||||
func (s *Skiplist) Height() uint32 { return atomic.LoadUint32(&s.height) }
|
||||
|
||||
// Arena returns the arena backing this skiplist.
|
||||
func (s *Skiplist) Arena() *Arena { return s.arena }
|
||||
|
||||
// Size returns the number of bytes that have allocated from the arena.
|
||||
func (s *Skiplist) Size() uint32 { return s.arena.Size() }
|
||||
|
||||
// Add adds a new key if it does not yet exist. If the key already exists, then
|
||||
// Add returns ErrRecordExists. If there isn't enough room in the arena, then
|
||||
// Add returns ErrArenaFull.
|
||||
func (s *Skiplist) Add(key base.InternalKey, value []byte) error {
|
||||
var ins Inserter
|
||||
return s.addInternal(key, value, &ins)
|
||||
}
|
||||
|
||||
func (s *Skiplist) addInternal(key base.InternalKey, value []byte, ins *Inserter) error {
|
||||
if s.findSplice(key, ins) {
|
||||
// Found a matching node, but handle case where it's been deleted.
|
||||
return ErrRecordExists
|
||||
}
|
||||
|
||||
if s.testing {
|
||||
// Add delay to make it easier to test race between this thread
|
||||
// and another thread that sees the intermediate state between
|
||||
// finding the splice and using it.
|
||||
runtime.Gosched()
|
||||
}
|
||||
|
||||
nd, height, err := s.newNode(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ndOffset := s.arena.getPointerOffset(unsafe.Pointer(nd))
|
||||
|
||||
// We always insert from the base level and up. After you add a node in base
|
||||
// level, we cannot create a node in the level above because it would have
|
||||
// discovered the node in the base level.
|
||||
var found bool
|
||||
var invalidateSplice bool
|
||||
for i := 0; i < int(height); i++ {
|
||||
prev := ins.spl[i].prev
|
||||
next := ins.spl[i].next
|
||||
|
||||
if prev == nil {
|
||||
// New node increased the height of the skiplist, so assume that the
|
||||
// new level has not yet been populated.
|
||||
if next != nil {
|
||||
panic("next is expected to be nil, since prev is nil")
|
||||
}
|
||||
|
||||
prev = s.head
|
||||
next = s.tail
|
||||
}
|
||||
|
||||
// +----------------+ +------------+ +----------------+
|
||||
// | prev | | nd | | next |
|
||||
// | prevNextOffset |---->| | | |
|
||||
// | |<----| prevOffset | | |
|
||||
// | | | nextOffset |---->| |
|
||||
// | | | |<----| nextPrevOffset |
|
||||
// +----------------+ +------------+ +----------------+
|
||||
//
|
||||
// 1. Initialize prevOffset and nextOffset to point to prev and next.
|
||||
// 2. CAS prevNextOffset to repoint from next to nd.
|
||||
// 3. CAS nextPrevOffset to repoint from prev to nd.
|
||||
for {
|
||||
prevOffset := s.arena.getPointerOffset(unsafe.Pointer(prev))
|
||||
nextOffset := s.arena.getPointerOffset(unsafe.Pointer(next))
|
||||
nd.tower[i].init(prevOffset, nextOffset)
|
||||
|
||||
// Check whether next has an updated link to prev. If it does not,
|
||||
// that can mean one of two things:
|
||||
// 1. The thread that added the next node hasn't yet had a chance
|
||||
// to add the prev link (but will shortly).
|
||||
// 2. Another thread has added a new node between prev and next.
|
||||
nextPrevOffset := next.prevOffset(i)
|
||||
if nextPrevOffset != prevOffset {
|
||||
// Determine whether #1 or #2 is true by checking whether prev
|
||||
// is still pointing to next. As long as the atomic operations
|
||||
// have at least acquire/release semantics (no need for
|
||||
// sequential consistency), this works, as it is equivalent to
|
||||
// the "publication safety" pattern.
|
||||
prevNextOffset := prev.nextOffset(i)
|
||||
if prevNextOffset == nextOffset {
|
||||
// Ok, case #1 is true, so help the other thread along by
|
||||
// updating the next node's prev link.
|
||||
next.casPrevOffset(i, nextPrevOffset, prevOffset)
|
||||
}
|
||||
}
|
||||
|
||||
if prev.casNextOffset(i, nextOffset, ndOffset) {
|
||||
// Managed to insert nd between prev and next, so update the next
|
||||
// node's prev link and go to the next level.
|
||||
if s.testing {
|
||||
// Add delay to make it easier to test race between this thread
|
||||
// and another thread that sees the intermediate state between
|
||||
// setting next and setting prev.
|
||||
runtime.Gosched()
|
||||
}
|
||||
|
||||
next.casPrevOffset(i, prevOffset, ndOffset)
|
||||
break
|
||||
}
|
||||
|
||||
// CAS failed. We need to recompute prev and next. It is unlikely to
|
||||
// be helpful to try to use a different level as we redo the search,
|
||||
// because it is unlikely that lots of nodes are inserted between prev
|
||||
// and next.
|
||||
prev, next, found = s.findSpliceForLevel(key, i, prev)
|
||||
if found {
|
||||
if i != 0 {
|
||||
panic("how can another thread have inserted a node at a non-base level?")
|
||||
}
|
||||
|
||||
return ErrRecordExists
|
||||
}
|
||||
invalidateSplice = true
|
||||
}
|
||||
}
|
||||
|
||||
// If we had to recompute the splice for a level, invalidate the entire
|
||||
// cached splice.
|
||||
if invalidateSplice {
|
||||
ins.height = 0
|
||||
} else {
|
||||
// The splice was valid. We inserted a node between spl[i].prev and
|
||||
// spl[i].next. Optimistically update spl[i].prev for use in a subsequent
|
||||
// call to add.
|
||||
for i := uint32(0); i < height; i++ {
|
||||
ins.spl[i].prev = nd
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewIter returns a new Iterator object. The lower and upper bound parameters
|
||||
// control the range of keys the iterator will return. Specifying for nil for
|
||||
// lower or upper bound disables the check for that boundary. Note that lower
|
||||
// bound is not checked on {SeekGE,First} and upper bound is not check on
|
||||
// {SeekLT,Last}. The user is expected to perform that check. Note that it is
|
||||
// safe for an iterator to be copied by value.
|
||||
func (s *Skiplist) NewIter(lower, upper []byte) *Iterator {
|
||||
it := iterPool.Get().(*Iterator)
|
||||
*it = Iterator{list: s, nd: s.head, lower: lower, upper: upper}
|
||||
return it
|
||||
}
|
||||
|
||||
// NewFlushIter returns a new flushIterator, which is similar to an Iterator
|
||||
// but also sets the current number of the bytes that have been iterated
|
||||
// through.
|
||||
func (s *Skiplist) NewFlushIter(bytesFlushed *uint64) base.InternalIterator {
|
||||
return &flushIterator{
|
||||
Iterator: Iterator{list: s, nd: s.head},
|
||||
bytesIterated: bytesFlushed,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Skiplist) newNode(
|
||||
key base.InternalKey, value []byte,
|
||||
) (nd *node, height uint32, err error) {
|
||||
height = s.randomHeight()
|
||||
nd, err = newNode(s.arena, height, key, value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Try to increase s.height via CAS.
|
||||
listHeight := s.Height()
|
||||
for height > listHeight {
|
||||
if atomic.CompareAndSwapUint32(&s.height, listHeight, height) {
|
||||
// Successfully increased skiplist.height.
|
||||
break
|
||||
}
|
||||
|
||||
listHeight = s.Height()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Skiplist) randomHeight() uint32 {
|
||||
rnd := fastrand.Uint32()
|
||||
|
||||
h := uint32(1)
|
||||
for h < maxHeight && rnd <= probabilities[h] {
|
||||
h++
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (s *Skiplist) findSplice(key base.InternalKey, ins *Inserter) (found bool) {
|
||||
listHeight := s.Height()
|
||||
var level int
|
||||
|
||||
prev := s.head
|
||||
if ins.height < listHeight {
|
||||
// Our cached height is less than the list height, which means there were
|
||||
// inserts that increased the height of the list. Recompute the splice from
|
||||
// scratch.
|
||||
ins.height = listHeight
|
||||
level = int(ins.height)
|
||||
} else {
|
||||
// Our cached height is equal to the list height.
|
||||
for ; level < int(listHeight); level++ {
|
||||
spl := &ins.spl[level]
|
||||
if s.getNext(spl.prev, level) != spl.next {
|
||||
// One or more nodes have been inserted between the splice at this
|
||||
// level.
|
||||
continue
|
||||
}
|
||||
if spl.prev != s.head && !s.keyIsAfterNode(spl.prev, key) {
|
||||
// Key lies before splice.
|
||||
level = int(listHeight)
|
||||
break
|
||||
}
|
||||
if spl.next != s.tail && s.keyIsAfterNode(spl.next, key) {
|
||||
// Key lies after splice.
|
||||
level = int(listHeight)
|
||||
break
|
||||
}
|
||||
// The splice brackets the key!
|
||||
prev = spl.prev
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for level = level - 1; level >= 0; level-- {
|
||||
var next *node
|
||||
prev, next, found = s.findSpliceForLevel(key, level, prev)
|
||||
if next == nil {
|
||||
next = s.tail
|
||||
}
|
||||
ins.spl[level].init(prev, next)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Skiplist) findSpliceForLevel(
|
||||
key base.InternalKey, level int, start *node,
|
||||
) (prev, next *node, found bool) {
|
||||
prev = start
|
||||
|
||||
for {
|
||||
// Assume prev.key < key.
|
||||
next = s.getNext(prev, level)
|
||||
if next == s.tail {
|
||||
// Tail node, so done.
|
||||
break
|
||||
}
|
||||
|
||||
offset, size := next.keyOffset, next.keySize
|
||||
nextKey := s.arena.buf[offset : offset+size]
|
||||
n := int32(size) - 8
|
||||
cmp := s.cmp(key.UserKey, nextKey[:n])
|
||||
if cmp < 0 {
|
||||
// We are done for this level, since prev.key < key < next.key.
|
||||
break
|
||||
}
|
||||
if cmp == 0 {
|
||||
// User-key equality.
|
||||
var nextTrailer uint64
|
||||
if n >= 0 {
|
||||
nextTrailer = binary.LittleEndian.Uint64(nextKey[n:])
|
||||
} else {
|
||||
nextTrailer = uint64(base.InternalKeyKindInvalid)
|
||||
}
|
||||
if key.Trailer == nextTrailer {
|
||||
// Internal key equality.
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if key.Trailer > nextTrailer {
|
||||
// We are done for this level, since prev.key < key < next.key.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Keep moving right on this level.
|
||||
prev = next
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Skiplist) keyIsAfterNode(nd *node, key base.InternalKey) bool {
|
||||
ndKey := s.arena.buf[nd.keyOffset : nd.keyOffset+nd.keySize]
|
||||
n := int32(nd.keySize) - 8
|
||||
cmp := s.cmp(ndKey[:n], key.UserKey)
|
||||
if cmp < 0 {
|
||||
return true
|
||||
}
|
||||
if cmp > 0 {
|
||||
return false
|
||||
}
|
||||
// User-key equality.
|
||||
var ndTrailer uint64
|
||||
if n >= 0 {
|
||||
ndTrailer = binary.LittleEndian.Uint64(ndKey[n:])
|
||||
} else {
|
||||
ndTrailer = uint64(base.InternalKeyKindInvalid)
|
||||
}
|
||||
if key.Trailer == ndTrailer {
|
||||
// Internal key equality.
|
||||
return false
|
||||
}
|
||||
return key.Trailer < ndTrailer
|
||||
}
|
||||
|
||||
func (s *Skiplist) getNext(nd *node, h int) *node {
|
||||
offset := atomic.LoadUint32(&nd.tower[h].nextOffset)
|
||||
return (*node)(s.arena.getPointer(offset))
|
||||
}
|
||||
|
||||
func (s *Skiplist) getPrev(nd *node, h int) *node {
|
||||
offset := atomic.LoadUint32(&nd.tower[h].prevOffset)
|
||||
return (*node)(s.arena.getPointer(offset))
|
||||
}
|
||||
Reference in New Issue
Block a user