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  }