database

package
v1.11.6-stella Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 25, 2024 License: BSD-3-Clause Imports: 19 Imported by: 0

Documentation

Overview

Package database is a generated GoMock package.

Package database is a generated GoMock package.

Index

Constants

View Source
const (
	// If, when a batch is reset, the cap(batch)/len(batch) > MaxExcessCapacityFactor,
	// the underlying array's capacity will be reduced by a factor of capacityReductionFactor.
	// Higher value for MaxExcessCapacityFactor --> less aggressive array downsizing --> less memory allocations
	// but more unnecessary data in the underlying array that can't be garbage collected.
	// Higher value for CapacityReductionFactor --> more aggressive array downsizing --> more memory allocations
	// but less unnecessary data in the underlying array that can't be garbage collected.
	MaxExcessCapacityFactor = 4
	CapacityReductionFactor = 2
)
View Source
const (
	Uint64Size = 8 // bytes
	BoolSize   = 1 // bytes
	BoolFalse  = 0x00
	BoolTrue   = 0x01
)

Variables

View Source
var (
	// Benchmarks is a list of all database benchmarks
	Benchmarks = map[string]func(b *testing.B, db Database, keys, values [][]byte){
		"Get":            BenchmarkGet,
		"Put":            BenchmarkPut,
		"Delete":         BenchmarkDelete,
		"BatchPut":       BenchmarkBatchPut,
		"BatchDelete":    BenchmarkBatchDelete,
		"BatchWrite":     BenchmarkBatchWrite,
		"ParallelGet":    BenchmarkParallelGet,
		"ParallelPut":    BenchmarkParallelPut,
		"ParallelDelete": BenchmarkParallelDelete,
	}
	// BenchmarkSizes to use with each benchmark
	BenchmarkSizes = [][]int{

		{1024, 32, 32},
		{1024, 256, 256},
		{1024, 2 * units.KiB, 2 * units.KiB},
	}
)
View Source
var (
	ErrClosed   = errors.New("closed")
	ErrNotFound = errors.New("not found")
)

common errors

View Source
var Tests = map[string]func(t *testing.T, db Database){
	"SimpleKeyValue":                   TestSimpleKeyValue,
	"OverwriteKeyValue":                TestOverwriteKeyValue,
	"EmptyKey":                         TestEmptyKey,
	"KeyEmptyValue":                    TestKeyEmptyValue,
	"SimpleKeyValueClosed":             TestSimpleKeyValueClosed,
	"NewBatchClosed":                   TestNewBatchClosed,
	"BatchPut":                         TestBatchPut,
	"BatchDelete":                      TestBatchDelete,
	"BatchReset":                       TestBatchReset,
	"BatchReuse":                       TestBatchReuse,
	"BatchRewrite":                     TestBatchRewrite,
	"BatchReplay":                      TestBatchReplay,
	"BatchReplayPropagateError":        TestBatchReplayPropagateError,
	"BatchInner":                       TestBatchInner,
	"BatchLargeSize":                   TestBatchLargeSize,
	"IteratorSnapshot":                 TestIteratorSnapshot,
	"Iterator":                         TestIterator,
	"IteratorStart":                    TestIteratorStart,
	"IteratorPrefix":                   TestIteratorPrefix,
	"IteratorStartPrefix":              TestIteratorStartPrefix,
	"IteratorMemorySafety":             TestIteratorMemorySafety,
	"IteratorClosed":                   TestIteratorClosed,
	"IteratorError":                    TestIteratorError,
	"IteratorErrorAfterRelease":        TestIteratorErrorAfterRelease,
	"CompactNoPanic":                   TestCompactNoPanic,
	"MemorySafetyDatabase":             TestMemorySafetyDatabase,
	"MemorySafetyBatch":                TestMemorySafetyBatch,
	"AtomicClear":                      TestAtomicClear,
	"Clear":                            TestClear,
	"AtomicClearPrefix":                TestAtomicClearPrefix,
	"ClearPrefix":                      TestClearPrefix,
	"ModifyValueAfterPut":              TestModifyValueAfterPut,
	"ModifyValueAfterBatchPut":         TestModifyValueAfterBatchPut,
	"ModifyValueAfterBatchPutReplay":   TestModifyValueAfterBatchPutReplay,
	"ConcurrentBatches":                TestConcurrentBatches,
	"ManySmallConcurrentKVPairBatches": TestManySmallConcurrentKVPairBatches,
	"PutGetEmpty":                      TestPutGetEmpty,
}

Tests is a list of all database tests

Functions

func AtomicClear

func AtomicClear(readerDB Iteratee, deleterDB KeyValueDeleter) error

func AtomicClearPrefix

func AtomicClearPrefix(readerDB Iteratee, deleterDB KeyValueDeleter, prefix []byte) error

AtomicClearPrefix deletes from [deleterDB] all keys in [readerDB] that have the given [prefix].

func BenchmarkBatchDelete

func BenchmarkBatchDelete(b *testing.B, db Database, keys, _ [][]byte)

BenchmarkBatchDelete measures the time it takes to batch delete.

func BenchmarkBatchPut

func BenchmarkBatchPut(b *testing.B, db Database, keys, values [][]byte)

BenchmarkBatchPut measures the time it takes to batch put.

func BenchmarkBatchWrite

func BenchmarkBatchWrite(b *testing.B, db Database, keys, values [][]byte)

BenchmarkBatchWrite measures the time it takes to batch write.

func BenchmarkDelete

func BenchmarkDelete(b *testing.B, db Database, keys, values [][]byte)

BenchmarkDelete measures the time it takes to delete a (k, v) from a database.

func BenchmarkGet

func BenchmarkGet(b *testing.B, db Database, keys, values [][]byte)

BenchmarkGet measures the time it takes to get an operation from a database.

func BenchmarkParallelDelete

func BenchmarkParallelDelete(b *testing.B, db Database, keys, values [][]byte)

BenchmarkParallelDelete measures the time it takes to delete a (k, v) from the db.

func BenchmarkParallelGet

func BenchmarkParallelGet(b *testing.B, db Database, keys, values [][]byte)

BenchmarkParallelGet measures the time it takes to read in parallel.

func BenchmarkParallelPut

func BenchmarkParallelPut(b *testing.B, db Database, keys, values [][]byte)

BenchmarkParallelPut measures the time it takes to write to the db in parallel.

func BenchmarkPut

func BenchmarkPut(b *testing.B, db Database, keys, values [][]byte)

BenchmarkPut measures the time it takes to write an operation to a database.

func Clear

func Clear(db Database, writeSize int) error

Remove all key-value pairs from [db]. Writes each batch when it reaches [writeSize].

func ClearPrefix

func ClearPrefix(db Database, prefix []byte, writeSize int) error

Removes all keys with the given [prefix] from [db]. Writes each batch when it reaches [writeSize].

func Count

func Count(db Iteratee) (int, error)

func FuzzKeyValue

func FuzzKeyValue(f *testing.F, db Database)

func FuzzNewIteratorWithPrefix

func FuzzNewIteratorWithPrefix(f *testing.F, db Database)

func FuzzNewIteratorWithStartAndPrefix

func FuzzNewIteratorWithStartAndPrefix(f *testing.F, db Database)

func GetBool

func GetBool(db KeyValueReader, key []byte) (bool, error)

func GetID

func GetID(db KeyValueReader, key []byte) (ids.ID, error)

func GetTimestamp

func GetTimestamp(db KeyValueReader, key []byte) (time.Time, error)

func GetUInt32

func GetUInt32(db KeyValueReader, key []byte) (uint32, error)

func GetUInt64

func GetUInt64(db KeyValueReader, key []byte) (uint64, error)

func IsEmpty

func IsEmpty(db Iteratee) (bool, error)

func PackUInt32

func PackUInt32(val uint32) []byte

func PackUInt64

func PackUInt64(val uint64) []byte

func ParseID

func ParseID(b []byte) (ids.ID, error)

func ParseTimestamp

func ParseTimestamp(b []byte) (time.Time, error)

func ParseUInt32

func ParseUInt32(b []byte) (uint32, error)

func ParseUInt64

func ParseUInt64(b []byte) (uint64, error)

func PutBool

func PutBool(db KeyValueWriter, key []byte, b bool) error

func PutID

func PutID(db KeyValueWriter, key []byte, val ids.ID) error

func PutTimestamp

func PutTimestamp(db KeyValueWriter, key []byte, val time.Time) error

func PutUInt32

func PutUInt32(db KeyValueWriter, key []byte, val uint32) error

func PutUInt64

func PutUInt64(db KeyValueWriter, key []byte, val uint64) error

func SetupBenchmark

func SetupBenchmark(b *testing.B, count int, keySize, valueSize int) ([][]byte, [][]byte)

Writes size data into the db in order to setup reads in subsequent tests.

func Size

func Size(db Iteratee) (int, error)

func TestAtomicClear

func TestAtomicClear(t *testing.T, db Database)

func TestAtomicClearPrefix

func TestAtomicClearPrefix(t *testing.T, db Database)

func TestBatchDelete

func TestBatchDelete(t *testing.T, db Database)

TestBatchDelete tests to make sure that batched deletes work as expected.

func TestBatchInner

func TestBatchInner(t *testing.T, db Database)

TestBatchInner tests to make sure that inner can be used to write to the database.

func TestBatchLargeSize

func TestBatchLargeSize(t *testing.T, db Database)

TestBatchLargeSize tests to make sure that the batch can support a large amount of entries.

func TestBatchPut

func TestBatchPut(t *testing.T, db Database)

TestBatchPut tests to make sure that batched writes work as expected.

func TestBatchReplay

func TestBatchReplay(t *testing.T, db Database)

TestBatchReplay tests to make sure that batches will correctly replay their contents.

func TestBatchReplayPropagateError

func TestBatchReplayPropagateError(t *testing.T, db Database)

TestBatchReplayPropagateError tests to make sure that batches will correctly propagate any returned error during Replay.

func TestBatchReset

func TestBatchReset(t *testing.T, db Database)

TestBatchReset tests to make sure that a batch drops un-written operations when it is reset.

func TestBatchReuse

func TestBatchReuse(t *testing.T, db Database)

TestBatchReuse tests to make sure that a batch can be reused once it is reset.

func TestBatchRewrite

func TestBatchRewrite(t *testing.T, db Database)

TestBatchRewrite tests to make sure that write can be called multiple times on a batch and the values will be updated correctly.

func TestClear

func TestClear(t *testing.T, db Database)

func TestClearPrefix

func TestClearPrefix(t *testing.T, db Database)

func TestCompactNoPanic

func TestCompactNoPanic(t *testing.T, db Database)

TestCompactNoPanic tests to make sure compact never panics.

func TestConcurrentBatches

func TestConcurrentBatches(t *testing.T, db Database)

func TestEmptyKey

func TestEmptyKey(t *testing.T, db Database)

func TestIterator

func TestIterator(t *testing.T, db Database)

TestIterator tests to make sure the database iterates over the database contents lexicographically.

func TestIteratorClosed

func TestIteratorClosed(t *testing.T, db Database)

TestIteratorClosed tests to make sure that an iterator that was created with a closed database will report a closed error correctly.

func TestIteratorError

func TestIteratorError(t *testing.T, db Database)

TestIteratorError tests to make sure that an iterator on a database will report itself as being exhausted and return ErrClosed to indicate that the iteration was not successful. Additionally tests that an iterator that has already called Next() can still serve its current value after the underlying DB was closed.

func TestIteratorErrorAfterRelease

func TestIteratorErrorAfterRelease(t *testing.T, db Database)

TestIteratorErrorAfterRelease tests to make sure that an iterator that was released still reports the error correctly.

func TestIteratorMemorySafety

func TestIteratorMemorySafety(t *testing.T, db Database)

TestIteratorMemorySafety tests to make sure that keys can values are able to be modified from the returned iterator.

func TestIteratorPrefix

func TestIteratorPrefix(t *testing.T, db Database)

TestIteratorPrefix tests to make sure the iterator can be configured to skip keys missing the provided prefix.

func TestIteratorSnapshot

func TestIteratorSnapshot(t *testing.T, db Database)

TestIteratorSnapshot tests to make sure the database iterates over a snapshot of the database at the time of the iterator creation.

func TestIteratorStart

func TestIteratorStart(t *testing.T, db Database)

TestIteratorStart tests to make sure the iterator can be configured to start mid way through the database.

func TestIteratorStartPrefix

func TestIteratorStartPrefix(t *testing.T, db Database)

TestIteratorStartPrefix tests to make sure that the iterator can start mid way through the database while skipping a prefix.

func TestKeyEmptyValue

func TestKeyEmptyValue(t *testing.T, db Database)

func TestManySmallConcurrentKVPairBatches

func TestManySmallConcurrentKVPairBatches(t *testing.T, db Database)

func TestMemorySafetyBatch

func TestMemorySafetyBatch(t *testing.T, db Database)

TestMemorySafetyBatch ensures it is safe to modify a key after passing it to Batch.Put.

func TestMemorySafetyDatabase

func TestMemorySafetyDatabase(t *testing.T, db Database)

TestMemorySafetyDatabase ensures it is safe to modify a key after passing it to Database.Put and Database.Get.

func TestModifyValueAfterBatchPut

func TestModifyValueAfterBatchPut(t *testing.T, db Database)

func TestModifyValueAfterBatchPutReplay

func TestModifyValueAfterBatchPutReplay(t *testing.T, db Database)

func TestModifyValueAfterPut

func TestModifyValueAfterPut(t *testing.T, db Database)

func TestNewBatchClosed

func TestNewBatchClosed(t *testing.T, db Database)

TestNewBatchClosed tests to make sure that calling NewBatch on a closed database returns a batch that errors correctly.

func TestOverwriteKeyValue

func TestOverwriteKeyValue(t *testing.T, db Database)

func TestPutGetEmpty

func TestPutGetEmpty(t *testing.T, db Database)

func TestSimpleKeyValue

func TestSimpleKeyValue(t *testing.T, db Database)

TestSimpleKeyValue tests to make sure that simple Put + Get + Delete + Has calls return the expected values.

func TestSimpleKeyValueClosed

func TestSimpleKeyValueClosed(t *testing.T, db Database)

TestSimpleKeyValueClosed tests to make sure that Put + Get + Delete + Has calls return the correct error when the database has been closed.

Types

type Batch

type Batch interface {
	KeyValueWriterDeleter

	// Size retrieves the amount of data queued up for writing, this includes
	// the keys, values, and deleted keys.
	Size() int

	// Write flushes any accumulated data to disk.
	Write() error

	// Reset resets the batch for reuse.
	Reset()

	// Replay replays the batch contents in the same order they were written
	// to the batch.
	Replay(w KeyValueWriterDeleter) error

	// Inner returns a Batch writing to the inner database, if one exists. If
	// this batch is already writing to the base DB, then itself should be
	// returned.
	Inner() Batch
}

Batch is a write-only database that commits changes to its host database when Write is called. A batch cannot be used concurrently.

type BatchOp

type BatchOp struct {
	Key    []byte
	Value  []byte
	Delete bool
}

type BatchOps

type BatchOps struct {
	Ops []BatchOp
	// contains filtered or unexported fields
}

func (*BatchOps) Delete

func (b *BatchOps) Delete(key []byte) error

func (*BatchOps) Put

func (b *BatchOps) Put(key, value []byte) error

func (*BatchOps) Replay

func (b *BatchOps) Replay(w KeyValueWriterDeleter) error

func (*BatchOps) Reset

func (b *BatchOps) Reset()

func (*BatchOps) Size

func (b *BatchOps) Size() int

type Batcher

type Batcher interface {
	// NewBatch creates a write-only database that buffers changes to its host db
	// until a final write is called.
	NewBatch() Batch
}

Batcher wraps the NewBatch method of a backing data store.

type Compacter

type Compacter interface {
	// Compact the underlying DB for the given key range.
	// Specifically, deleted and overwritten versions are discarded,
	// and the data is rearranged to reduce the cost of operations
	// needed to access the data. This operation should typically only
	// be invoked by users who understand the underlying implementation.
	//
	// A nil start is treated as a key before all keys in the DB.
	// And a nil limit is treated as a key after all keys in the DB.
	// Therefore if both are nil then it will compact entire DB.
	//
	// Note: [start] and [limit] are safe to modify and read after calling Compact.
	Compact(start []byte, limit []byte) error
}

Compacter wraps the Compact method of a backing data store.

type Database

Database contains all the methods required to allow handling different key-value data stores backing the database.

type Iteratee

type Iteratee interface {
	// NewIterator creates an iterator over the entire keyspace contained within
	// the key-value database.
	NewIterator() Iterator

	// NewIteratorWithStart creates an iterator over a subset of database
	// content starting at a particular initial key.
	NewIteratorWithStart(start []byte) Iterator

	// NewIteratorWithPrefix creates an iterator over a subset of database
	// content with a particular key prefix.
	NewIteratorWithPrefix(prefix []byte) Iterator

	// NewIteratorWithStartAndPrefix creates an iterator over a subset of
	// database content with a particular key prefix starting at a specified
	// key.
	NewIteratorWithStartAndPrefix(start, prefix []byte) Iterator
}

Iteratee wraps the NewIterator methods of a backing data store.

type Iterator

type Iterator interface {
	// Next moves the iterator to the next key/value pair. It returns whether
	// the iterator successfully moved to a new key/value pair.
	// The iterator may return false if the underlying database has been closed
	// before the iteration has completed, in which case future calls to Error()
	// must return [ErrClosed].
	Next() bool

	// Error returns any accumulated error. Exhausting all the key/value pairs
	// is not considered to be an error.
	// Error should be called after all key/value pairs have been exhausted ie.
	// after Next() has returned false.
	Error() error

	// Key returns the key of the current key/value pair, or nil if done.
	// If the database is closed, must still report the current contents.
	// Behavior is undefined after Release is called.
	Key() []byte

	// Value returns the value of the current key/value pair, or nil if done.
	// If the database is closed, must still report the current contents.
	// Behavior is undefined after Release is called.
	Value() []byte

	// Release releases associated resources. Release should always succeed and
	// can be called multiple times without causing error.
	Release()
}

Iterator iterates over a database's key/value pairs.

When it encounters an error any seek will return false and will yield no key/ value pairs. The error can be queried by calling the Error method. Calling Release is still necessary.

An iterator must be released after use, but it is not necessary to read an iterator until exhaustion. An iterator is not safe for concurrent use, but it is safe to use multiple iterators concurrently.

type IteratorError

type IteratorError struct {
	Err error
}

IteratorError does nothing and returns the provided error

func (*IteratorError) Error

func (i *IteratorError) Error() error

func (*IteratorError) Key

func (*IteratorError) Key() []byte

func (*IteratorError) Next

func (*IteratorError) Next() bool

func (*IteratorError) Release

func (*IteratorError) Release()

func (*IteratorError) Value

func (*IteratorError) Value() []byte

type KeyValueDeleter

type KeyValueDeleter interface {
	// Delete removes the key from the key-value data store.
	//
	// Note: [key] is safe to modify and read after calling Delete.
	Delete(key []byte) error
}

KeyValueDeleter wraps the Delete method of a backing data store.

type KeyValueReader

type KeyValueReader interface {
	// Has retrieves if a key is present in the key-value data store.
	//
	// Note: [key] is safe to modify and read after calling Has.
	Has(key []byte) (bool, error)

	// Get retrieves the given key if it's present in the key-value data store.
	// Returns ErrNotFound if the key is not present in the key-value data store.
	//
	// Note: [key] is safe to modify and read after calling Get.
	// The returned byte slice is safe to read, but cannot be modified.
	Get(key []byte) ([]byte, error)
}

KeyValueReader wraps the Has and Get method of a backing data store.

type KeyValueReaderWriter

type KeyValueReaderWriter interface {
	KeyValueReader
	KeyValueWriter
}

KeyValueReaderWriter allows read/write acccess to a backing data store.

type KeyValueReaderWriterDeleter

type KeyValueReaderWriterDeleter interface {
	KeyValueReader
	KeyValueWriter
	KeyValueDeleter
}

KeyValueReaderWriterDeleter allows read/write/delete access to a backing data store.

type KeyValueWriter

type KeyValueWriter interface {
	// Put inserts the given value into the key-value data store.
	//
	// Note: [key] and [value] are safe to modify and read after calling Put.
	//
	// If [value] is nil or an empty slice, then when it's retrieved
	// it may be nil or an empty slice.
	//
	// Similarly, a nil [key] is treated the same as an empty slice.
	Put(key []byte, value []byte) error
}

KeyValueWriter wraps the Put method of a backing data store.

type KeyValueWriterDeleter

type KeyValueWriterDeleter interface {
	KeyValueWriter
	KeyValueDeleter
}

KeyValueWriterDeleter allows write/delete acccess to a backing data store.

type MockBatch

type MockBatch struct {
	// contains filtered or unexported fields
}

MockBatch is a mock of Batch interface.

func NewMockBatch

func NewMockBatch(ctrl *gomock.Controller) *MockBatch

NewMockBatch creates a new mock instance.

func (*MockBatch) Delete

func (m *MockBatch) Delete(arg0 []byte) error

Delete mocks base method.

func (*MockBatch) EXPECT

func (m *MockBatch) EXPECT() *MockBatchMockRecorder

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockBatch) Inner

func (m *MockBatch) Inner() Batch

Inner mocks base method.

func (*MockBatch) Put

func (m *MockBatch) Put(arg0, arg1 []byte) error

Put mocks base method.

func (*MockBatch) Replay

func (m *MockBatch) Replay(arg0 KeyValueWriterDeleter) error

Replay mocks base method.

func (*MockBatch) Reset

func (m *MockBatch) Reset()

Reset mocks base method.

func (*MockBatch) Size

func (m *MockBatch) Size() int

Size mocks base method.

func (*MockBatch) Write

func (m *MockBatch) Write() error

Write mocks base method.

type MockBatchMockRecorder

type MockBatchMockRecorder struct {
	// contains filtered or unexported fields
}

MockBatchMockRecorder is the mock recorder for MockBatch.

func (*MockBatchMockRecorder) Delete

func (mr *MockBatchMockRecorder) Delete(arg0 any) *gomock.Call

Delete indicates an expected call of Delete.

func (*MockBatchMockRecorder) Inner

func (mr *MockBatchMockRecorder) Inner() *gomock.Call

Inner indicates an expected call of Inner.

func (*MockBatchMockRecorder) Put

func (mr *MockBatchMockRecorder) Put(arg0, arg1 any) *gomock.Call

Put indicates an expected call of Put.

func (*MockBatchMockRecorder) Replay

func (mr *MockBatchMockRecorder) Replay(arg0 any) *gomock.Call

Replay indicates an expected call of Replay.

func (*MockBatchMockRecorder) Reset

func (mr *MockBatchMockRecorder) Reset() *gomock.Call

Reset indicates an expected call of Reset.

func (*MockBatchMockRecorder) Size

func (mr *MockBatchMockRecorder) Size() *gomock.Call

Size indicates an expected call of Size.

func (*MockBatchMockRecorder) Write

func (mr *MockBatchMockRecorder) Write() *gomock.Call

Write indicates an expected call of Write.

type MockIterator

type MockIterator struct {
	// contains filtered or unexported fields
}

MockIterator is a mock of Iterator interface.

func NewMockIterator

func NewMockIterator(ctrl *gomock.Controller) *MockIterator

NewMockIterator creates a new mock instance.

func (*MockIterator) EXPECT

EXPECT returns an object that allows the caller to indicate expected use.

func (*MockIterator) Error

func (m *MockIterator) Error() error

Error mocks base method.

func (*MockIterator) Key

func (m *MockIterator) Key() []byte

Key mocks base method.

func (*MockIterator) Next

func (m *MockIterator) Next() bool

Next mocks base method.

func (*MockIterator) Release

func (m *MockIterator) Release()

Release mocks base method.

func (*MockIterator) Value

func (m *MockIterator) Value() []byte

Value mocks base method.

type MockIteratorMockRecorder

type MockIteratorMockRecorder struct {
	// contains filtered or unexported fields
}

MockIteratorMockRecorder is the mock recorder for MockIterator.

func (*MockIteratorMockRecorder) Error

func (mr *MockIteratorMockRecorder) Error() *gomock.Call

Error indicates an expected call of Error.

func (*MockIteratorMockRecorder) Key

Key indicates an expected call of Key.

func (*MockIteratorMockRecorder) Next

func (mr *MockIteratorMockRecorder) Next() *gomock.Call

Next indicates an expected call of Next.

func (*MockIteratorMockRecorder) Release

func (mr *MockIteratorMockRecorder) Release() *gomock.Call

Release indicates an expected call of Release.

func (*MockIteratorMockRecorder) Value

func (mr *MockIteratorMockRecorder) Value() *gomock.Call

Value indicates an expected call of Value.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL