ledisdb/ledis/t_list.go

322 lines
6.0 KiB
Go

package ledis
import (
"encoding/binary"
"errors"
"github.com/siddontang/go-leveldb/leveldb"
)
const (
listHeadSeq int32 = 1
listTailSeq int32 = 2
listMinSeq int32 = 1000
listMaxSeq int32 = 1<<31 - 1000
listInitialSeq int32 = listMinSeq + (listMaxSeq-listMinSeq)/2
)
var errLMetaKey = errors.New("invalid lmeta key")
var errListKey = errors.New("invalid list key")
var errListSeq = errors.New("invalid list sequence, overflow")
func encode_lmeta_key(key []byte) []byte {
buf := make([]byte, len(key)+1)
buf[0] = LMETA_TYPE
copy(buf[1:], key)
return buf
}
func decode_lmeta_key(ek []byte) ([]byte, error) {
if len(ek) == 0 || ek[0] != LMETA_TYPE {
return nil, errLMetaKey
}
return ek[1:], nil
}
func encode_list_key(key []byte, seq int32) []byte {
buf := make([]byte, len(key)+9)
pos := 0
buf[pos] = LIST_TYPE
pos++
binary.BigEndian.PutUint32(buf[pos:], uint32(len(key)))
pos += 4
copy(buf[pos:], key)
pos += len(key)
binary.BigEndian.PutUint32(buf[pos:], uint32(seq))
return buf
}
func decode_list_key(ek []byte) (key []byte, seq int32, err error) {
if len(ek) < 9 || ek[0] != LIST_TYPE {
err = errListKey
return
}
keyLen := int(binary.BigEndian.Uint32(ek[1:]))
if keyLen+9 != len(ek) {
err = errListKey
return
}
key = ek[5 : 5+keyLen]
seq = int32(binary.BigEndian.Uint32(ek[5+keyLen:]))
return
}
func (db *DB) lpush(key []byte, args [][]byte, whereSeq int32) (int64, error) {
t := db.listTx
t.Lock()
defer t.Unlock()
metaKey := encode_lmeta_key(key)
headSeq, tailSeq, size, err := db.lGetMeta(metaKey)
if err != nil {
return 0, err
}
var delta int32 = 1
var seq int32 = 0
if whereSeq == listHeadSeq {
delta = -1
seq = headSeq
} else {
seq = tailSeq
}
if size == 0 {
headSeq = listInitialSeq
tailSeq = listInitialSeq
seq = headSeq
} else {
seq += delta
}
for i := 0; i < len(args); i++ {
t.Put(encode_list_key(key, seq+int32(i)*delta), args[i])
//to do add binlog
}
seq += int32(len(args)-1) * delta
if seq <= listMinSeq || seq >= listMaxSeq {
return 0, errListSeq
}
size += int32(len(args))
if whereSeq == listHeadSeq {
headSeq = seq
} else {
tailSeq = seq
}
db.lSetMeta(metaKey, headSeq, tailSeq, size)
err = t.Commit()
return int64(size), err
}
func (db *DB) lpop(key []byte, whereSeq int32) ([]byte, error) {
t := db.listTx
t.Lock()
defer t.Unlock()
metaKey := encode_lmeta_key(key)
headSeq, tailSeq, size, err := db.lGetMeta(metaKey)
if err != nil {
return nil, err
}
var seq int32 = 0
var delta int32 = 1
if whereSeq == listHeadSeq {
seq = headSeq
} else {
delta = -1
seq = tailSeq
}
itemKey := encode_list_key(key, seq)
var value []byte
value, err = db.db.Get(itemKey)
if err != nil {
return nil, err
}
t.Delete(itemKey)
seq += delta
size--
if size <= 0 {
t.Delete(metaKey)
} else {
if whereSeq == listHeadSeq {
headSeq = seq
} else {
tailSeq = seq
}
db.lSetMeta(metaKey, headSeq, tailSeq, size)
}
//todo add binlog
err = t.Commit()
return value, err
}
func (db *DB) lGetSeq(key []byte, whereSeq int32) (int64, error) {
ek := encode_list_key(key, whereSeq)
return Int64(db.db.Get(ek))
}
func (db *DB) lGetMeta(ek []byte) (headSeq int32, tailSeq int32, size int32, err error) {
var v []byte
v, err = db.db.Get(ek)
if err != nil {
return
} else if v == nil {
size = 0
return
} else {
headSeq = int32(binary.LittleEndian.Uint32(v[0:4]))
tailSeq = int32(binary.LittleEndian.Uint32(v[4:8]))
size = int32(binary.LittleEndian.Uint32(v[8:]))
}
return
}
func (db *DB) lSetMeta(ek []byte, headSeq int32, tailSeq int32, size int32) {
t := db.listTx
buf := make([]byte, 12)
binary.LittleEndian.PutUint32(buf[0:4], uint32(headSeq))
binary.LittleEndian.PutUint32(buf[4:8], uint32(tailSeq))
binary.LittleEndian.PutUint32(buf[8:], uint32(size))
t.Put(ek, buf)
}
func (db *DB) LIndex(key []byte, index int32) ([]byte, error) {
var seq int32
headSeq, tailSeq, _, err := db.lGetMeta(encode_lmeta_key(key))
if err != nil {
return nil, err
}
if index >= 0 {
seq = headSeq + index
} else {
seq = tailSeq + index + 1
}
return db.db.Get(encode_list_key(key, seq))
}
func (db *DB) LLen(key []byte) (int64, error) {
ek := encode_lmeta_key(key)
_, _, size, err := db.lGetMeta(ek)
return int64(size), err
}
func (db *DB) LPop(key []byte) ([]byte, error) {
return db.lpop(key, listHeadSeq)
}
func (db *DB) LPush(key []byte, args [][]byte) (int64, error) {
return db.lpush(key, args, listHeadSeq)
}
func (db *DB) LRange(key []byte, start int32, stop int32) ([]interface{}, error) {
v := make([]interface{}, 0, 16)
var startSeq int32
var stopSeq int32
if start > stop {
return []interface{}{}, nil
}
headSeq, tailSeq, _, err := db.lGetMeta(encode_lmeta_key(key))
if err != nil {
return nil, err
}
if start >= 0 && stop >= 0 {
startSeq = headSeq + start
stopSeq = headSeq + stop
} else if start < 0 && stop < 0 {
startSeq = tailSeq + start + 1
stopSeq = tailSeq + stop + 1
} else {
//start < 0 && stop > 0
startSeq = tailSeq + start + 1
stopSeq = headSeq + stop
}
if startSeq < listMinSeq {
startSeq = listMinSeq
} else if stopSeq > listMaxSeq {
stopSeq = listMaxSeq
}
it := db.db.Iterator(encode_list_key(key, startSeq),
encode_list_key(key, stopSeq), leveldb.RangeClose, 0, -1)
for ; it.Valid(); it.Next() {
v = append(v, it.Value())
}
it.Close()
return v, nil
}
func (db *DB) RPop(key []byte) ([]byte, error) {
return db.lpop(key, listTailSeq)
}
func (db *DB) RPush(key []byte, args [][]byte) (int64, error) {
return db.lpush(key, args, listTailSeq)
}
func (db *DB) LClear(key []byte) (int64, error) {
mk := encode_lmeta_key(key)
t := db.listTx
t.Lock()
defer t.Unlock()
metaKey := encode_lmeta_key(key)
headSeq, tailSeq, _, err := db.lGetMeta(metaKey)
if err != nil {
return 0, err
}
var num int64 = 0
it := db.db.Iterator(encode_list_key(key, headSeq),
encode_list_key(key, tailSeq), leveldb.RangeClose, 0, -1)
for ; it.Valid(); it.Next() {
t.Delete(it.Key())
num++
}
it.Close()
t.Delete(mk)
err = t.Commit()
return num, err
}