github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/petri/acyclic/structure/list.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 "context" 18 "encoding/binary" 19 20 "github.com/whtcorpsinc/errors" 21 "github.com/whtcorpsinc/milevadb/ekv" 22 ) 23 24 type listMeta struct { 25 LIndex int64 26 RIndex int64 27 } 28 29 func (spacetime listMeta) Value() []byte { 30 buf := make([]byte, 16) 31 binary.BigEndian.PutUint64(buf[0:8], uint64(spacetime.LIndex)) 32 binary.BigEndian.PutUint64(buf[8:16], uint64(spacetime.RIndex)) 33 return buf 34 } 35 36 func (spacetime listMeta) IsEmpty() bool { 37 return spacetime.LIndex >= spacetime.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 t.readWriter == nil { 52 return ErrWriteOnSnapshot 53 } 54 if len(values) == 0 { 55 return nil 56 } 57 58 spacetimeKey := t.encodeListMetaKey(key) 59 spacetime, err := t.loadListMeta(spacetimeKey) 60 if err != nil { 61 return errors.Trace(err) 62 } 63 64 var index int64 65 for _, v := range values { 66 if left { 67 spacetime.LIndex-- 68 index = spacetime.LIndex 69 } else { 70 index = spacetime.RIndex 71 spacetime.RIndex++ 72 } 73 74 dataKey := t.encodeListDataKey(key, index) 75 if err = t.readWriter.Set(dataKey, v); err != nil { 76 return errors.Trace(err) 77 } 78 } 79 80 return t.readWriter.Set(spacetimeKey, spacetime.Value()) 81 } 82 83 // LPop removes and gets the first element in a list. 84 func (t *TxStructure) LPop(key []byte) ([]byte, error) { 85 return t.listPop(key, true) 86 } 87 88 // RPop removes and gets the last element in a list. 89 func (t *TxStructure) RPop(key []byte) ([]byte, error) { 90 return t.listPop(key, false) 91 } 92 93 func (t *TxStructure) listPop(key []byte, left bool) ([]byte, error) { 94 if t.readWriter == nil { 95 return nil, ErrWriteOnSnapshot 96 } 97 spacetimeKey := t.encodeListMetaKey(key) 98 spacetime, err := t.loadListMeta(spacetimeKey) 99 if err != nil || spacetime.IsEmpty() { 100 return nil, errors.Trace(err) 101 } 102 103 var index int64 104 if left { 105 index = spacetime.LIndex 106 spacetime.LIndex++ 107 } else { 108 spacetime.RIndex-- 109 index = spacetime.RIndex 110 } 111 112 dataKey := t.encodeListDataKey(key, index) 113 114 var data []byte 115 data, err = t.reader.Get(context.TODO(), dataKey) 116 if err != nil { 117 return nil, errors.Trace(err) 118 } 119 120 if err = t.readWriter.Delete(dataKey); err != nil { 121 return nil, errors.Trace(err) 122 } 123 124 if !spacetime.IsEmpty() { 125 err = t.readWriter.Set(spacetimeKey, spacetime.Value()) 126 } else { 127 err = t.readWriter.Delete(spacetimeKey) 128 } 129 130 return data, errors.Trace(err) 131 } 132 133 // LLen gets the length of a list. 134 func (t *TxStructure) LLen(key []byte) (int64, error) { 135 spacetimeKey := t.encodeListMetaKey(key) 136 spacetime, err := t.loadListMeta(spacetimeKey) 137 return spacetime.RIndex - spacetime.LIndex, errors.Trace(err) 138 } 139 140 // LGetAll gets all elements of this list in order from right to left. 141 func (t *TxStructure) LGetAll(key []byte) ([][]byte, error) { 142 spacetimeKey := t.encodeListMetaKey(key) 143 spacetime, err := t.loadListMeta(spacetimeKey) 144 if err != nil || spacetime.IsEmpty() { 145 return nil, errors.Trace(err) 146 } 147 148 length := int(spacetime.RIndex - spacetime.LIndex) 149 elements := make([][]byte, 0, length) 150 for index := spacetime.RIndex - 1; index >= spacetime.LIndex; index-- { 151 e, err := t.reader.Get(context.TODO(), t.encodeListDataKey(key, index)) 152 if err != nil { 153 return nil, errors.Trace(err) 154 } 155 elements = append(elements, e) 156 } 157 return elements, nil 158 } 159 160 // LIndex gets an element from a list by its index. 161 func (t *TxStructure) LIndex(key []byte, index int64) ([]byte, error) { 162 spacetimeKey := t.encodeListMetaKey(key) 163 spacetime, err := t.loadListMeta(spacetimeKey) 164 if err != nil || spacetime.IsEmpty() { 165 return nil, errors.Trace(err) 166 } 167 168 index = adjustIndex(index, spacetime.LIndex, spacetime.RIndex) 169 170 if index >= spacetime.LIndex && index < spacetime.RIndex { 171 return t.reader.Get(context.TODO(), t.encodeListDataKey(key, index)) 172 } 173 return nil, nil 174 } 175 176 // LSet uFIDelates an element in the list by its index. 177 func (t *TxStructure) LSet(key []byte, index int64, value []byte) error { 178 if t.readWriter == nil { 179 return ErrWriteOnSnapshot 180 } 181 spacetimeKey := t.encodeListMetaKey(key) 182 spacetime, err := t.loadListMeta(spacetimeKey) 183 if err != nil || spacetime.IsEmpty() { 184 return errors.Trace(err) 185 } 186 187 index = adjustIndex(index, spacetime.LIndex, spacetime.RIndex) 188 189 if index >= spacetime.LIndex && index < spacetime.RIndex { 190 return t.readWriter.Set(t.encodeListDataKey(key, index), value) 191 } 192 return ErrInvalidListIndex.GenWithStack("invalid list index %d", index) 193 } 194 195 // LClear removes the list of the key. 196 func (t *TxStructure) LClear(key []byte) error { 197 if t.readWriter == nil { 198 return ErrWriteOnSnapshot 199 } 200 spacetimeKey := t.encodeListMetaKey(key) 201 spacetime, err := t.loadListMeta(spacetimeKey) 202 if err != nil || spacetime.IsEmpty() { 203 return errors.Trace(err) 204 } 205 206 for index := spacetime.LIndex; index < spacetime.RIndex; index++ { 207 dataKey := t.encodeListDataKey(key, index) 208 if err = t.readWriter.Delete(dataKey); err != nil { 209 return errors.Trace(err) 210 } 211 } 212 213 return t.readWriter.Delete(spacetimeKey) 214 } 215 216 func (t *TxStructure) loadListMeta(spacetimeKey []byte) (listMeta, error) { 217 v, err := t.reader.Get(context.TODO(), spacetimeKey) 218 if ekv.ErrNotExist.Equal(err) { 219 err = nil 220 } 221 if err != nil { 222 return listMeta{}, errors.Trace(err) 223 } 224 225 spacetime := listMeta{0, 0} 226 if v == nil { 227 return spacetime, nil 228 } 229 230 if len(v) != 16 { 231 return spacetime, ErrInvalidListMetaData 232 } 233 234 spacetime.LIndex = int64(binary.BigEndian.Uint64(v[0:8])) 235 spacetime.RIndex = int64(binary.BigEndian.Uint64(v[8:16])) 236 return spacetime, nil 237 } 238 239 func adjustIndex(index int64, min, max int64) int64 { 240 if index >= 0 { 241 return index + min 242 } 243 244 return index + max 245 }