github.com/nutsdb/nutsdb@v1.0.4/list.go (about)

     1  // Copyright 2023 The nutsdb Author. All rights reserved.
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package nutsdb
    16  
    17  import (
    18  	"errors"
    19  	"math"
    20  	"time"
    21  )
    22  
    23  var (
    24  	// ErrListNotFound is returned when the list not found.
    25  	ErrListNotFound = errors.New("the list not found")
    26  
    27  	// ErrCount is returned when count is error.
    28  	ErrCount = errors.New("err count")
    29  
    30  	// ErrEmptyList is returned when the list is empty.
    31  	ErrEmptyList = errors.New("the list is empty")
    32  
    33  	// ErrStartOrEnd is returned when start > end
    34  	ErrStartOrEnd = errors.New("start or end error")
    35  )
    36  
    37  const (
    38  	initialListSeq = math.MaxUint64 / 2
    39  )
    40  
    41  // BTree represents the btree.
    42  
    43  // HeadTailSeq list head and tail seq num
    44  type HeadTailSeq struct {
    45  	Head uint64
    46  	Tail uint64
    47  }
    48  
    49  // List represents the list.
    50  type List struct {
    51  	Items     map[string]*BTree
    52  	TTL       map[string]uint32
    53  	TimeStamp map[string]uint64
    54  	Seq       map[string]*HeadTailSeq
    55  }
    56  
    57  func NewList() *List {
    58  	return &List{
    59  		Items:     make(map[string]*BTree),
    60  		TTL:       make(map[string]uint32),
    61  		TimeStamp: make(map[string]uint64),
    62  		Seq:       make(map[string]*HeadTailSeq),
    63  	}
    64  }
    65  
    66  func (l *List) LPush(key string, r *Record) error {
    67  	return l.push(key, r, true)
    68  }
    69  
    70  func (l *List) RPush(key string, r *Record) error {
    71  	return l.push(key, r, false)
    72  }
    73  
    74  func (l *List) push(key string, r *Record, isLeft bool) error {
    75  	// key is seq + user_key
    76  	userKey, curSeq := decodeListKey([]byte(key))
    77  	userKeyStr := string(userKey)
    78  	if l.IsExpire(userKeyStr) {
    79  		return ErrListNotFound
    80  	}
    81  
    82  	list, ok := l.Items[userKeyStr]
    83  	if !ok {
    84  		l.Items[userKeyStr] = NewBTree()
    85  		list = l.Items[userKeyStr]
    86  	}
    87  
    88  	seq, ok := l.Seq[userKeyStr]
    89  	if !ok {
    90  		l.Seq[userKeyStr] = &HeadTailSeq{Head: initialListSeq, Tail: initialListSeq + 1}
    91  		seq = l.Seq[userKeyStr]
    92  	}
    93  
    94  	list.InsertRecord(ConvertUint64ToBigEndianBytes(curSeq), r)
    95  	if isLeft {
    96  		if seq.Head > curSeq-1 {
    97  			seq.Head = curSeq - 1
    98  		}
    99  	} else {
   100  		if seq.Tail < curSeq+1 {
   101  			seq.Tail = curSeq + 1
   102  		}
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  func (l *List) LPop(key string) (*Record, error) {
   109  	item, err := l.LPeek(key)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	l.Items[key].Delete(item.key)
   115  	l.Seq[key].Head = ConvertBigEndianBytesToUint64(item.key)
   116  	return item.record, nil
   117  }
   118  
   119  // RPop removes and returns the last element of the list stored at key.
   120  func (l *List) RPop(key string) (*Record, error) {
   121  	item, err := l.RPeek(key)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	l.Items[key].Delete(item.key)
   127  	l.Seq[key].Tail = ConvertBigEndianBytesToUint64(item.key)
   128  	return item.record, nil
   129  }
   130  
   131  func (l *List) LPeek(key string) (*Item, error) {
   132  	return l.peek(key, true)
   133  }
   134  
   135  func (l *List) RPeek(key string) (*Item, error) {
   136  	return l.peek(key, false)
   137  }
   138  
   139  func (l *List) peek(key string, isLeft bool) (*Item, error) {
   140  	if l.IsExpire(key) {
   141  		return nil, ErrListNotFound
   142  	}
   143  	list, ok := l.Items[key]
   144  	if !ok {
   145  		return nil, ErrListNotFound
   146  	}
   147  
   148  	if isLeft {
   149  		item, ok := list.Min()
   150  		if ok {
   151  			return item, nil
   152  		}
   153  	} else {
   154  		item, ok := list.Max()
   155  		if ok {
   156  			return item, nil
   157  		}
   158  	}
   159  
   160  	return nil, ErrEmptyList
   161  }
   162  
   163  // LRange returns the specified elements of the list stored at key [start,end]
   164  func (l *List) LRange(key string, start, end int) ([]*Record, error) {
   165  	size, err := l.Size(key)
   166  	if err != nil || size == 0 {
   167  		return nil, err
   168  	}
   169  
   170  	start, end, err = checkBounds(start, end, size)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	var res []*Record
   176  	allRecords := l.Items[key].All()
   177  	for i, item := range allRecords {
   178  		if i >= start && i <= end {
   179  			res = append(res, item)
   180  		}
   181  	}
   182  
   183  	return res, nil
   184  }
   185  
   186  // getRemoveIndexes returns a slice of indices to be removed from the list based on the count
   187  func (l *List) getRemoveIndexes(key string, count int, cmp func(r *Record) (bool, error)) ([][]byte, error) {
   188  	if l.IsExpire(key) {
   189  		return nil, ErrListNotFound
   190  	}
   191  
   192  	list, ok := l.Items[key]
   193  
   194  	if !ok {
   195  		return nil, ErrListNotFound
   196  	}
   197  
   198  	var res [][]byte
   199  	var allItems []*Item
   200  	if 0 == count {
   201  		count = list.Count()
   202  	}
   203  
   204  	allItems = l.Items[key].AllItems()
   205  	if count > 0 {
   206  		for _, item := range allItems {
   207  			if count <= 0 {
   208  				break
   209  			}
   210  			r := item.record
   211  			ok, err := cmp(r)
   212  			if err != nil {
   213  				return nil, err
   214  			}
   215  			if ok {
   216  				res = append(res, item.key)
   217  				count--
   218  			}
   219  		}
   220  	} else {
   221  		for i := len(allItems) - 1; i >= 0; i-- {
   222  			if count >= 0 {
   223  				break
   224  			}
   225  			r := allItems[i].record
   226  			ok, err := cmp(r)
   227  			if err != nil {
   228  				return nil, err
   229  			}
   230  			if ok {
   231  				res = append(res, allItems[i].key)
   232  				count++
   233  			}
   234  		}
   235  	}
   236  
   237  	return res, nil
   238  }
   239  
   240  // LRem removes the first count occurrences of elements equal to value from the list stored at key.
   241  // The count argument influences the operation in the following ways:
   242  // count > 0: Remove elements equal to value moving from head to tail.
   243  // count < 0: Remove elements equal to value moving from tail to head.
   244  // count = 0: Remove all elements equal to value.
   245  func (l *List) LRem(key string, count int, cmp func(r *Record) (bool, error)) error {
   246  	removeIndexes, err := l.getRemoveIndexes(key, count, cmp)
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	list := l.Items[key]
   252  	for _, idx := range removeIndexes {
   253  		list.Delete(idx)
   254  	}
   255  
   256  	return nil
   257  }
   258  
   259  // LTrim trim an existing list so that it will contain only the specified range of elements specified.
   260  func (l *List) LTrim(key string, start, end int) error {
   261  	if l.IsExpire(key) {
   262  		return ErrListNotFound
   263  	}
   264  	if _, ok := l.Items[key]; !ok {
   265  		return ErrListNotFound
   266  	}
   267  
   268  	list := l.Items[key]
   269  	allItems := list.AllItems()
   270  	for i, item := range allItems {
   271  		if i < start || i > end {
   272  			list.Delete(item.key)
   273  		}
   274  	}
   275  
   276  	return nil
   277  }
   278  
   279  // LRemByIndex remove the list element at specified index
   280  func (l *List) LRemByIndex(key string, indexes []int) error {
   281  	if l.IsExpire(key) {
   282  		return ErrListNotFound
   283  	}
   284  
   285  	idxes := l.getValidIndexes(key, indexes)
   286  	if len(idxes) == 0 {
   287  		return nil
   288  	}
   289  
   290  	list := l.Items[key]
   291  	allItems := list.AllItems()
   292  	for i, item := range allItems {
   293  		if _, ok := idxes[i]; ok {
   294  			list.Delete(item.key)
   295  		}
   296  	}
   297  
   298  	return nil
   299  }
   300  
   301  func (l *List) getValidIndexes(key string, indexes []int) map[int]struct{} {
   302  	idxes := make(map[int]struct{})
   303  	listLen, err := l.Size(key)
   304  	if err != nil || 0 == listLen {
   305  		return idxes
   306  	}
   307  
   308  	for _, idx := range indexes {
   309  		if idx < 0 || idx >= listLen {
   310  			continue
   311  		}
   312  		idxes[idx] = struct{}{}
   313  	}
   314  
   315  	return idxes
   316  }
   317  
   318  func (l *List) IsExpire(key string) bool {
   319  	if l == nil {
   320  		return false
   321  	}
   322  
   323  	_, ok := l.TTL[key]
   324  	if !ok {
   325  		return false
   326  	}
   327  
   328  	now := time.Now().Unix()
   329  	timestamp := l.TimeStamp[key]
   330  	if l.TTL[key] > 0 && uint64(l.TTL[key])+timestamp > uint64(now) || l.TTL[key] == uint32(0) {
   331  		return false
   332  	}
   333  
   334  	delete(l.Items, key)
   335  	delete(l.TTL, key)
   336  	delete(l.TimeStamp, key)
   337  	delete(l.Seq, key)
   338  
   339  	return true
   340  }
   341  
   342  func (l *List) Size(key string) (int, error) {
   343  	if l.IsExpire(key) {
   344  		return 0, ErrListNotFound
   345  	}
   346  	if _, ok := l.Items[key]; !ok {
   347  		return 0, ErrListNotFound
   348  	}
   349  
   350  	return l.Items[key].Count(), nil
   351  }
   352  
   353  func (l *List) IsEmpty(key string) (bool, error) {
   354  	size, err := l.Size(key)
   355  	if err != nil || size > 0 {
   356  		return false, err
   357  	}
   358  	return true, nil
   359  }
   360  
   361  func (l *List) GetListTTL(key string) (uint32, error) {
   362  	if l.IsExpire(key) {
   363  		return 0, ErrListNotFound
   364  	}
   365  
   366  	ttl := l.TTL[key]
   367  	timestamp := l.TimeStamp[key]
   368  	if ttl == 0 || timestamp == 0 {
   369  		return 0, nil
   370  	}
   371  
   372  	now := time.Now().Unix()
   373  	remain := timestamp + uint64(ttl) - uint64(now)
   374  
   375  	return uint32(remain), nil
   376  }
   377  
   378  func checkBounds(start, end int, size int) (int, int, error) {
   379  	if start >= 0 && end < 0 {
   380  		end = size + end
   381  	}
   382  
   383  	if start < 0 && end > 0 {
   384  		start = size + start
   385  	}
   386  
   387  	if start < 0 && end < 0 {
   388  		start, end = size+start, size+end
   389  	}
   390  
   391  	if end >= size {
   392  		end = size - 1
   393  	}
   394  
   395  	if start > end {
   396  		return 0, 0, ErrStartOrEnd
   397  	}
   398  
   399  	return start, end, nil
   400  }