mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 05:18:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			215 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2019 Lunny Xiao. All rights reserved.
 | 
						|
// Use of this source code is governed by a MIT-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package levelqueue
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/binary"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/syndtr/goleveldb/leveldb"
 | 
						|
)
 | 
						|
 | 
						|
// Queue defines a queue struct
 | 
						|
type Queue struct {
 | 
						|
	db       *leveldb.DB
 | 
						|
	highLock sync.Mutex
 | 
						|
	lowLock  sync.Mutex
 | 
						|
	low      int64
 | 
						|
	high     int64
 | 
						|
}
 | 
						|
 | 
						|
// Open opens a queue object or create it if not exist
 | 
						|
func Open(dataDir string) (*Queue, error) {
 | 
						|
	db, err := leveldb.OpenFile(dataDir, nil)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	var queue = &Queue{
 | 
						|
		db: db,
 | 
						|
	}
 | 
						|
	queue.low, err = queue.readID(lowKey)
 | 
						|
	if err == leveldb.ErrNotFound {
 | 
						|
		queue.low = 1
 | 
						|
		err = db.Put(lowKey, id2bytes(1), nil)
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	queue.high, err = queue.readID(highKey)
 | 
						|
	if err == leveldb.ErrNotFound {
 | 
						|
		err = db.Put(highKey, id2bytes(0), nil)
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return queue, nil
 | 
						|
}
 | 
						|
 | 
						|
func (queue *Queue) readID(key []byte) (int64, error) {
 | 
						|
	bs, err := queue.db.Get(key, nil)
 | 
						|
	if err != nil {
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
	return bytes2id(bs)
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	lowKey  = []byte("low")
 | 
						|
	highKey = []byte("high")
 | 
						|
)
 | 
						|
 | 
						|
func (queue *Queue) highincrement() (int64, error) {
 | 
						|
	id := queue.high + 1
 | 
						|
	queue.high = id
 | 
						|
	err := queue.db.Put(highKey, id2bytes(queue.high), nil)
 | 
						|
	if err != nil {
 | 
						|
		queue.high = queue.high - 1
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
	return id, nil
 | 
						|
}
 | 
						|
 | 
						|
func (queue *Queue) highdecrement() (int64, error) {
 | 
						|
	queue.high = queue.high - 1
 | 
						|
	err := queue.db.Put(highKey, id2bytes(queue.high), nil)
 | 
						|
	if err != nil {
 | 
						|
		queue.high = queue.high + 1
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
	return queue.high, nil
 | 
						|
}
 | 
						|
 | 
						|
func (queue *Queue) lowincrement() (int64, error) {
 | 
						|
	queue.low = queue.low + 1
 | 
						|
	err := queue.db.Put(lowKey, id2bytes(queue.low), nil)
 | 
						|
	if err != nil {
 | 
						|
		queue.low = queue.low - 1
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
	return queue.low, nil
 | 
						|
}
 | 
						|
 | 
						|
func (queue *Queue) lowdecrement() (int64, error) {
 | 
						|
	queue.low = queue.low - 1
 | 
						|
	err := queue.db.Put(lowKey, id2bytes(queue.low), nil)
 | 
						|
	if err != nil {
 | 
						|
		queue.low = queue.low + 1
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
	return queue.low, nil
 | 
						|
}
 | 
						|
 | 
						|
// Len returns the length of the queue
 | 
						|
func (queue *Queue) Len() int64 {
 | 
						|
	queue.lowLock.Lock()
 | 
						|
	queue.highLock.Lock()
 | 
						|
	l := queue.high - queue.low + 1
 | 
						|
	queue.highLock.Unlock()
 | 
						|
	queue.lowLock.Unlock()
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
func id2bytes(id int64) []byte {
 | 
						|
	var buf = make([]byte, 8)
 | 
						|
	binary.PutVarint(buf, id)
 | 
						|
	return buf
 | 
						|
}
 | 
						|
 | 
						|
func bytes2id(b []byte) (int64, error) {
 | 
						|
	return binary.ReadVarint(bytes.NewReader(b))
 | 
						|
}
 | 
						|
 | 
						|
// RPush pushes a data from right of queue
 | 
						|
func (queue *Queue) RPush(data []byte) error {
 | 
						|
	queue.highLock.Lock()
 | 
						|
	id, err := queue.highincrement()
 | 
						|
	if err != nil {
 | 
						|
		queue.highLock.Unlock()
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	err = queue.db.Put(id2bytes(id), data, nil)
 | 
						|
	queue.highLock.Unlock()
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// LPush pushes a data from left of queue
 | 
						|
func (queue *Queue) LPush(data []byte) error {
 | 
						|
	queue.highLock.Lock()
 | 
						|
	id, err := queue.lowdecrement()
 | 
						|
	if err != nil {
 | 
						|
		queue.highLock.Unlock()
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	err = queue.db.Put(id2bytes(id), data, nil)
 | 
						|
	queue.highLock.Unlock()
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
// RPop pop a data from right of queue
 | 
						|
func (queue *Queue) RPop() ([]byte, error) {
 | 
						|
	queue.highLock.Lock()
 | 
						|
	currentID := queue.high
 | 
						|
 | 
						|
	res, err := queue.db.Get(id2bytes(currentID), nil)
 | 
						|
	if err != nil {
 | 
						|
		queue.highLock.Unlock()
 | 
						|
		if err == leveldb.ErrNotFound {
 | 
						|
			return nil, ErrNotFound
 | 
						|
		}
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = queue.highdecrement()
 | 
						|
	if err != nil {
 | 
						|
		queue.highLock.Unlock()
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	err = queue.db.Delete(id2bytes(currentID), nil)
 | 
						|
	queue.highLock.Unlock()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return res, nil
 | 
						|
}
 | 
						|
 | 
						|
// LPop pop a data from left of queue
 | 
						|
func (queue *Queue) LPop() ([]byte, error) {
 | 
						|
	queue.lowLock.Lock()
 | 
						|
	currentID := queue.low
 | 
						|
 | 
						|
	res, err := queue.db.Get(id2bytes(currentID), nil)
 | 
						|
	if err != nil {
 | 
						|
		queue.lowLock.Unlock()
 | 
						|
		if err == leveldb.ErrNotFound {
 | 
						|
			return nil, ErrNotFound
 | 
						|
		}
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = queue.lowincrement()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	err = queue.db.Delete(id2bytes(currentID), nil)
 | 
						|
	queue.lowLock.Unlock()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return res, nil
 | 
						|
}
 | 
						|
 | 
						|
// Close closes the queue
 | 
						|
func (queue *Queue) Close() error {
 | 
						|
	err := queue.db.Close()
 | 
						|
	queue.db = nil
 | 
						|
	return err
 | 
						|
}
 |