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  }