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.
275 lines
5.4 KiB
275 lines
5.4 KiB
// Copyright 2017 The DB Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package db // import "modernc.org/db"
|
|
|
|
const (
|
|
oDListPrev = 8 * iota // int64 0 8
|
|
oDListNext // int64 8 8
|
|
oDListData // [dataSize]byte 16 dataSize
|
|
)
|
|
|
|
// DList is a node of a doubly linked list.
|
|
type DList struct {
|
|
*DB
|
|
Off int64 // Location in the database. R/O
|
|
}
|
|
|
|
// NewDList returns a newly allocated DList or an error, if any. The datasize
|
|
// parameter is the fixed size of data associated with the list node. To
|
|
// get/set the node data, use the ReadAt/WriteAt methods of db, using
|
|
// DList.DataOff() as the offset. Reading or writing more than datasize data at
|
|
// DataOff() is undefined behavior and may irreparably corrupt the database.
|
|
//
|
|
// The result of NewDList is not a part of any list.
|
|
func (db *DB) NewDList(dataSize int64) (DList, error) {
|
|
off, err := db.Alloc(oDListData + dataSize)
|
|
if err != nil {
|
|
return DList{}, err
|
|
}
|
|
|
|
r, err := db.OpenDList(off)
|
|
if err != nil {
|
|
return DList{}, err
|
|
}
|
|
|
|
if err := r.setPrev(0); err != nil {
|
|
return DList{}, err
|
|
}
|
|
|
|
return r, r.setNext(0)
|
|
}
|
|
|
|
// OpenDList returns an existing DList found at offset off or an error, if any.
|
|
// The off argument must have been acquired from NewDList.
|
|
func (db *DB) OpenDList(off int64) (DList, error) { return DList{db, off}, nil }
|
|
|
|
func (l DList) setNext(off int64) error { return l.w8(l.Off+oDListNext, off) }
|
|
func (l DList) setPrev(off int64) error { return l.w8(l.Off+oDListPrev, off) }
|
|
|
|
// DataOff returns the offset in db at which data of l are located.
|
|
func (l DList) DataOff() int64 { return l.Off + oDListData }
|
|
|
|
// Next returns the offset of the next node of l.
|
|
func (l DList) Next() (int64, error) { return l.r8(l.Off + oDListNext) }
|
|
|
|
// Prev returns the offset of the prev node of l.
|
|
func (l DList) Prev() (int64, error) { return l.r8(l.Off + oDListPrev) }
|
|
|
|
// InsertAfter inserts l after the DList node at off. Node l must not be
|
|
// already a part of any list.
|
|
func (l DList) InsertAfter(off int64) error {
|
|
n, err := l.OpenDList(off)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
afterNext, err := n.Next()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = n.setNext(l.Off); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = l.setPrev(off); err != nil {
|
|
return err
|
|
}
|
|
|
|
if afterNext != 0 {
|
|
n, err := l.OpenDList(afterNext)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := n.setPrev(l.Off); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return l.setNext(afterNext)
|
|
}
|
|
|
|
// InsertBefore inserts l before the DList node at off. Node l must not be
|
|
// already a part of any list.
|
|
func (l DList) InsertBefore(off int64) error {
|
|
n, err := l.OpenDList(off)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
prev, err := n.Prev()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if n.setPrev(l.Off); err != nil {
|
|
return err
|
|
}
|
|
|
|
if prev != 0 {
|
|
p, err := l.OpenDList(prev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := p.setNext(l.Off); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if l.setPrev(prev); err != nil {
|
|
return err
|
|
}
|
|
|
|
return l.setNext(n.Off)
|
|
}
|
|
|
|
// Remove removes l from a list.
|
|
//
|
|
// The free function may be nil, otherwise it's called with the result of
|
|
// l.DataOff() before removing l.
|
|
func (l DList) Remove(free func(off int64) error) error {
|
|
if free != nil {
|
|
if err := free(l.DataOff()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
prev, err := l.Prev()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
next, err := l.Next()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var p, n DList
|
|
if prev != 0 {
|
|
if p, err = l.OpenDList(prev); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if next != 0 {
|
|
if n, err = l.OpenDList(next); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case prev == 0:
|
|
switch {
|
|
case next == 0:
|
|
// nop
|
|
default:
|
|
if err = n.setPrev(0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
default:
|
|
switch {
|
|
case next == 0:
|
|
if err = p.setNext(0); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
if err := p.setNext(next); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := n.setPrev(prev); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return l.Free(l.Off)
|
|
}
|
|
|
|
// RemoveToLast removes all nodes from a list starting at l to the end of the
|
|
// list.
|
|
//
|
|
// The free function may be nil, otherwise it's called with the result of
|
|
// l.DataOff() before removing l.
|
|
func (l DList) RemoveToLast(free func(off int64) error) error {
|
|
prev, err := l.Prev()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if prev != 0 {
|
|
p, err := l.OpenDList(prev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := p.setNext(0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for l.Off != 0 {
|
|
if free != nil {
|
|
if err := free(l.DataOff()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
next, err := l.Next()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := l.Free(l.Off); err != nil {
|
|
return err
|
|
}
|
|
|
|
l.Off = next
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveToFirst removes all nodes from a list starting at first list node, up
|
|
// to and including l.
|
|
//
|
|
// The free function may be nil, otherwise it's called with the result of
|
|
// l.DataOff() before removing l.
|
|
func (l DList) RemoveToFirst(free func(off int64) error) error {
|
|
next, err := l.Next()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if next != 0 {
|
|
n, err := l.OpenDList(next)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := n.setPrev(0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for l.Off != 0 {
|
|
if free != nil {
|
|
if err := free(l.DataOff()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
prev, err := l.Prev()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := l.Free(l.Off); err != nil {
|
|
return err
|
|
}
|
|
|
|
l.Off = prev
|
|
}
|
|
return nil
|
|
}
|