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