--- /dev/null
+package match
+
+import (
+ "sort"
+ "sync"
+
+ "github.com/bytom/vapor/application/mov/common"
+ "github.com/bytom/vapor/application/mov/database"
+)
+
+// OrderBook is used to handle the mov orders in memory like stack
+type OrderBook struct {
+ movStore database.MovStore
+ // key of tradePair -> []order
+ dbOrders *sync.Map
+ // key of tradePair -> iterator
+ orderIterators *sync.Map
+
+ // key of tradePair -> []order
+ arrivalAddOrders *sync.Map
+ // key of order -> order
+ arrivalDelOrders *sync.Map
+}
+
+func arrangeArrivalAddOrders(orders []*common.Order) *sync.Map {
+ orderMap := make(map[string][]*common.Order)
+ for _, order := range orders {
+ orderMap[order.TradePair().Key()] = append(orderMap[order.TradePair().Key()], order)
+ }
+
+ arrivalOrderMap := &sync.Map{}
+ for key, orders := range orderMap {
+ sort.Sort(sort.Reverse(common.OrderSlice(orders)))
+ arrivalOrderMap.Store(key, orders)
+ }
+ return arrivalOrderMap
+}
+
+func arrangeArrivalDelOrders(orders []*common.Order) *sync.Map {
+ arrivalDelOrderMap := &sync.Map{}
+ for _, order := range orders {
+ arrivalDelOrderMap.Store(order.Key(), order)
+ }
+ return arrivalDelOrderMap
+}
+
+// NewOrderBook create a new OrderBook object
+func NewOrderBook(movStore database.MovStore, arrivalAddOrders, arrivalDelOrders []*common.Order) *OrderBook {
+ return &OrderBook{
+ movStore: movStore,
+ dbOrders: &sync.Map{},
+ orderIterators: &sync.Map{},
+
+ arrivalAddOrders: arrangeArrivalAddOrders(arrivalAddOrders),
+ arrivalDelOrders: arrangeArrivalDelOrders(arrivalDelOrders),
+ }
+}
+
+// AddOrder add the in memory temp order to order table, because temp order is what left for the
+// partial trade order, so the price should be lowest.
+func (o *OrderBook) AddOrder(order *common.Order) {
+ tradePairKey := order.TradePair().Key()
+ orders := o.getArrivalAddOrders(tradePairKey)
+ // use binary search to find the insert position
+ i := sort.Search(len(orders), func(i int) bool { return order.Cmp(orders[i]) > 0 })
+ orders = append(orders, order)
+ copy(orders[i+1:], orders[i:])
+ orders[i] = order
+
+ o.arrivalAddOrders.Store(tradePairKey, orders)
+}
+
+// DelOrder mark the order has been deleted in order book
+func (o *OrderBook) DelOrder(order *common.Order) {
+ o.arrivalDelOrders.Store(order.Key(), order)
+}
+
+// PeekOrder return the next lowest order of given trade pair
+func (o *OrderBook) PeekOrder(tradePair *common.TradePair) *common.Order {
+ if len(o.getDBOrders(tradePair.Key())) == 0 {
+ o.extendDBOrders(tradePair)
+ }
+
+ var nextOrder *common.Order
+ orders := o.getDBOrders(tradePair.Key())
+ if len(orders) != 0 {
+ nextOrder = orders[len(orders)-1]
+ }
+
+ if nextOrder != nil && o.getArrivalDelOrders(nextOrder.Key()) != nil {
+ o.dbOrders.Store(tradePair.Key(), orders[0:len(orders)-1])
+ return o.PeekOrder(tradePair)
+ }
+
+ arrivalOrder := o.peekArrivalOrder(tradePair)
+ if nextOrder == nil || (arrivalOrder != nil && arrivalOrder.Cmp(nextOrder) < 0) {
+ nextOrder = arrivalOrder
+ }
+ return nextOrder
+}
+
+// PeekOrders return the next lowest orders by given array of trade pairs
+func (o *OrderBook) PeekOrders(tradePairs []*common.TradePair) []*common.Order {
+ var orders []*common.Order
+ for _, tradePair := range tradePairs {
+ order := o.PeekOrder(tradePair)
+ if order == nil {
+ return nil
+ }
+
+ orders = append(orders, order)
+ }
+ return orders
+}
+
+// PopOrder delete the next lowest order of given trade pair
+func (o *OrderBook) PopOrder(tradePair *common.TradePair) {
+ order := o.PeekOrder(tradePair)
+ if order == nil {
+ return
+ }
+
+ orders := o.getDBOrders(tradePair.Key())
+ if len(orders) != 0 && orders[len(orders)-1].Key() == order.Key() {
+ o.dbOrders.Store(tradePair.Key(), orders[0:len(orders)-1])
+ return
+ }
+
+ arrivalOrders := o.getArrivalAddOrders(tradePair.Key())
+ if len(arrivalOrders) != 0 && arrivalOrders[len(arrivalOrders)-1].Key() == order.Key() {
+ o.arrivalAddOrders.Store(tradePair.Key(), arrivalOrders[0:len(arrivalOrders)-1])
+ }
+}
+
+// PopOrders delete the next lowest orders by given trade pairs
+func (o *OrderBook) PopOrders(tradePairs []*common.TradePair) []*common.Order {
+ var orders []*common.Order
+ for _, tradePair := range tradePairs {
+ o.PopOrder(tradePair)
+ }
+ return orders
+}
+
+func (o *OrderBook) getDBOrders(tradePairKey string) []*common.Order {
+ if orders, ok := o.dbOrders.Load(tradePairKey); ok {
+ return orders.([]*common.Order)
+ }
+ return []*common.Order{}
+}
+
+func (o *OrderBook) getArrivalAddOrders(tradePairKey string) []*common.Order {
+ if orders, ok := o.arrivalAddOrders.Load(tradePairKey); ok {
+ return orders.([]*common.Order)
+ }
+ return []*common.Order{}
+}
+
+func (o *OrderBook) getArrivalDelOrders(orderKey string) *common.Order {
+ if order, ok := o.arrivalDelOrders.Load(orderKey); ok {
+ return order.(*common.Order)
+ }
+ return nil
+}
+
+func (o *OrderBook) extendDBOrders(tradePair *common.TradePair) {
+ iterator, ok := o.orderIterators.Load(tradePair.Key())
+ if !ok {
+ iterator = database.NewOrderIterator(o.movStore, tradePair)
+ o.orderIterators.Store(tradePair.Key(), iterator)
+ }
+
+ nextOrders := iterator.(*database.OrderIterator).NextBatch()
+ orders := o.getDBOrders(tradePair.Key())
+ for i := len(nextOrders) - 1; i >= 0; i-- {
+ orders = append(orders, nextOrders[i])
+ }
+ o.dbOrders.Store(tradePair.Key(), orders)
+}
+
+func (o *OrderBook) peekArrivalOrder(tradePair *common.TradePair) *common.Order {
+ orders := o.getArrivalAddOrders(tradePair.Key())
+ delPos := len(orders)
+ for i := delPos - 1; i >= 0; i-- {
+ if o.getArrivalDelOrders(orders[i].Key()) != nil {
+ delPos = i
+ } else {
+ break
+ }
+ }
+
+ if delPos < len(orders) {
+ orders = orders[:delPos]
+ o.arrivalAddOrders.Store(tradePair.Key(), orders)
+ }
+
+ if size := len(orders); size > 0 {
+ return orders[size-1]
+ }
+ return nil
+}