github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/structure/list.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package structure 15 16 import ( 17 "encoding/binary" 18 19 "github.com/insionng/yougam/libraries/juju/errors" 20 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/terror" 22 ) 23 24 type listMeta struct { 25 LIndex int64 26 RIndex int64 27 } 28 29 func (meta listMeta) Value() []byte { 30 buf := make([]byte, 16) 31 binary.BigEndian.PutUint64(buf[0:8], uint64(meta.LIndex)) 32 binary.BigEndian.PutUint64(buf[8:16], uint64(meta.RIndex)) 33 return buf 34 } 35 36 func (meta listMeta) IsEmpty() bool { 37 return meta.LIndex >= meta.RIndex 38 } 39 40 // LPush prepends one or multiple values to a list. 41 func (t *TxStructure) LPush(key []byte, values ...[]byte) error { 42 return t.listPush(key, true, values...) 43 } 44 45 // RPush appends one or multiple values to a list. 46 func (t *TxStructure) RPush(key []byte, values ...[]byte) error { 47 return t.listPush(key, false, values...) 48 } 49 50 func (t *TxStructure) listPush(key []byte, left bool, values ...[]byte) error { 51 if len(values) == 0 { 52 return nil 53 } 54 55 metaKey := t.encodeListMetaKey(key) 56 meta, err := t.loadListMeta(metaKey) 57 if err != nil { 58 return errors.Trace(err) 59 } 60 61 index := int64(0) 62 for _, v := range values { 63 if left { 64 meta.LIndex-- 65 index = meta.LIndex 66 } else { 67 index = meta.RIndex 68 meta.RIndex++ 69 } 70 71 dataKey := t.encodeListDataKey(key, index) 72 if err = t.txn.Set(dataKey, v); err != nil { 73 return errors.Trace(err) 74 } 75 } 76 77 return t.txn.Set(metaKey, meta.Value()) 78 } 79 80 // LPop removes and gets the first element in a list. 81 func (t *TxStructure) LPop(key []byte) ([]byte, error) { 82 return t.listPop(key, true) 83 } 84 85 // RPop removes and gets the last element in a list. 86 func (t *TxStructure) RPop(key []byte) ([]byte, error) { 87 return t.listPop(key, false) 88 } 89 90 func (t *TxStructure) listPop(key []byte, left bool) ([]byte, error) { 91 metaKey := t.encodeListMetaKey(key) 92 meta, err := t.loadListMeta(metaKey) 93 if err != nil || meta.IsEmpty() { 94 return nil, errors.Trace(err) 95 } 96 97 index := int64(0) 98 if left { 99 index = meta.LIndex 100 meta.LIndex++ 101 } else { 102 meta.RIndex-- 103 index = meta.RIndex 104 } 105 106 dataKey := t.encodeListDataKey(key, index) 107 108 var data []byte 109 data, err = t.txn.Get(dataKey) 110 if err != nil { 111 return nil, errors.Trace(err) 112 } 113 114 if err = t.txn.Delete(dataKey); err != nil { 115 return nil, errors.Trace(err) 116 } 117 118 if !meta.IsEmpty() { 119 err = t.txn.Set(metaKey, meta.Value()) 120 } else { 121 err = t.txn.Delete(metaKey) 122 } 123 124 return data, errors.Trace(err) 125 } 126 127 // LLen gets the length of a list. 128 func (t *TxStructure) LLen(key []byte) (int64, error) { 129 metaKey := t.encodeListMetaKey(key) 130 meta, err := t.loadListMeta(metaKey) 131 return meta.RIndex - meta.LIndex, errors.Trace(err) 132 } 133 134 // LIndex gets an element from a list by its index. 135 func (t *TxStructure) LIndex(key []byte, index int64) ([]byte, error) { 136 metaKey := t.encodeListMetaKey(key) 137 meta, err := t.loadListMeta(metaKey) 138 if err != nil || meta.IsEmpty() { 139 return nil, errors.Trace(err) 140 } 141 142 index = adjustIndex(index, meta.LIndex, meta.RIndex) 143 144 if index >= meta.LIndex && index < meta.RIndex { 145 return t.txn.Get(t.encodeListDataKey(key, index)) 146 } 147 return nil, nil 148 } 149 150 // LSet updates an element in the list by its index. 151 func (t *TxStructure) LSet(key []byte, index int64, value []byte) error { 152 metaKey := t.encodeListMetaKey(key) 153 meta, err := t.loadListMeta(metaKey) 154 if err != nil || meta.IsEmpty() { 155 return errors.Trace(err) 156 } 157 158 index = adjustIndex(index, meta.LIndex, meta.RIndex) 159 160 if index >= meta.LIndex && index < meta.RIndex { 161 return t.txn.Set(t.encodeListDataKey(key, index), value) 162 } 163 return errInvalidListIndex.Gen("invalid list index %d", index) 164 } 165 166 // LClear removes the list of the key. 167 func (t *TxStructure) LClear(key []byte) error { 168 metaKey := t.encodeListMetaKey(key) 169 meta, err := t.loadListMeta(metaKey) 170 if err != nil || meta.IsEmpty() { 171 return errors.Trace(err) 172 } 173 174 for index := meta.LIndex; index < meta.RIndex; index++ { 175 dataKey := t.encodeListDataKey(key, index) 176 if err = t.txn.Delete(dataKey); err != nil { 177 return errors.Trace(err) 178 } 179 } 180 181 return t.txn.Delete(metaKey) 182 } 183 184 func (t *TxStructure) loadListMeta(metaKey []byte) (listMeta, error) { 185 v, err := t.txn.Get(metaKey) 186 if terror.ErrorEqual(err, kv.ErrNotExist) { 187 err = nil 188 } else if err != nil { 189 return listMeta{}, errors.Trace(err) 190 } 191 192 meta := listMeta{0, 0} 193 if v == nil { 194 return meta, nil 195 } 196 197 if len(v) != 16 { 198 return meta, errInvalidListMetaData 199 } 200 201 meta.LIndex = int64(binary.BigEndian.Uint64(v[0:8])) 202 meta.RIndex = int64(binary.BigEndian.Uint64(v[8:16])) 203 return meta, nil 204 } 205 206 func adjustIndex(index int64, min, max int64) int64 { 207 if index >= 0 { 208 return index + min 209 } 210 211 return index + max 212 }