mirror of
https://github.com/duanhf2012/origin.git
synced 2026-02-03 22:45:13 +08:00
新增排行榜服务
This commit is contained in:
3000
rpc/rank.pb.go
Normal file
3000
rpc/rank.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
96
sysservice/rankservice/RankData.go
Normal file
96
sysservice/rankservice/RankData.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package rankservice
|
||||
|
||||
import (
|
||||
"github.com/duanhf2012/origin/rpc"
|
||||
"github.com/duanhf2012/origin/util/algorithms/skip"
|
||||
"github.com/duanhf2012/origin/util/sync"
|
||||
)
|
||||
|
||||
var emptyRankData RankData
|
||||
|
||||
var RankDataPool = sync.NewPoolEx(make(chan sync.IPoolData, 10240), func() sync.IPoolData {
|
||||
var newRankData RankData
|
||||
return &newRankData
|
||||
})
|
||||
|
||||
type RankData struct {
|
||||
*rpc.RankData
|
||||
|
||||
ref bool
|
||||
compareFunc func(other skip.Comparator) int
|
||||
}
|
||||
|
||||
func NewRankData(isDec bool, data *rpc.RankData) *RankData {
|
||||
ret := RankDataPool.Get().(*RankData)
|
||||
ret.compareFunc = ret.ascCompare
|
||||
if isDec {
|
||||
ret.compareFunc = ret.desCompare
|
||||
}
|
||||
ret.RankData = data
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func ReleaseRankData(rankData *RankData) {
|
||||
RankDataPool.Put(rankData)
|
||||
}
|
||||
|
||||
func (p *RankData) Reset() {
|
||||
*p = emptyRankData
|
||||
}
|
||||
|
||||
func (p *RankData) IsRef() bool {
|
||||
return p.ref
|
||||
}
|
||||
|
||||
func (p *RankData) Ref() {
|
||||
p.ref = true
|
||||
}
|
||||
|
||||
func (p *RankData) UnRef() {
|
||||
p.ref = false
|
||||
}
|
||||
|
||||
func (p *RankData) Compare(other skip.Comparator) int {
|
||||
return p.compareFunc(other)
|
||||
}
|
||||
|
||||
func (p *RankData) GetKey() uint64 {
|
||||
return p.Key
|
||||
}
|
||||
|
||||
func (p *RankData) ascCompare(other skip.Comparator) int {
|
||||
otherRankData := other.(*RankData)
|
||||
|
||||
if otherRankData.Key == p.Key {
|
||||
return 0
|
||||
}
|
||||
|
||||
retFlg := compareMoreThan(p.SortData, otherRankData.SortData)
|
||||
if retFlg == 0 {
|
||||
if p.Key > otherRankData.Key {
|
||||
retFlg = 1
|
||||
} else {
|
||||
retFlg = -1
|
||||
}
|
||||
}
|
||||
return retFlg
|
||||
}
|
||||
|
||||
func (p *RankData) desCompare(other skip.Comparator) int {
|
||||
otherRankData := other.(*RankData)
|
||||
|
||||
if otherRankData.Key == p.Key {
|
||||
return 0
|
||||
}
|
||||
|
||||
retFlg := compareMoreThan(otherRankData.SortData, p.SortData)
|
||||
if retFlg == 0 {
|
||||
if p.Key > otherRankData.Key {
|
||||
retFlg = -1
|
||||
} else {
|
||||
retFlg = 1
|
||||
}
|
||||
}
|
||||
return retFlg
|
||||
}
|
||||
52
sysservice/rankservice/RankFunc.go
Normal file
52
sysservice/rankservice/RankFunc.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package rankservice
|
||||
|
||||
func transformLevel(level int32) interface{} {
|
||||
switch level {
|
||||
case 8:
|
||||
return uint8(0)
|
||||
case 16:
|
||||
return uint16(0)
|
||||
case 32:
|
||||
return uint32(0)
|
||||
case 64:
|
||||
return uint64(0)
|
||||
default:
|
||||
return uint32(0)
|
||||
}
|
||||
}
|
||||
|
||||
func compareIsEqual(firstSortData, secondSortData []int64) bool {
|
||||
firstLen := len(firstSortData)
|
||||
if firstLen != len(secondSortData) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := firstLen - 1; i >= 0; i-- {
|
||||
if firstSortData[i] != secondSortData[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func compareMoreThan(firstSortData, secondSortData []int64) int {
|
||||
firstLen := len(firstSortData)
|
||||
secondLen := len(secondSortData)
|
||||
minLen := firstLen
|
||||
if firstLen > secondLen {
|
||||
minLen = secondLen
|
||||
}
|
||||
|
||||
for i := 0; i < minLen; i++ {
|
||||
if firstSortData[i] > secondSortData[i] {
|
||||
return 1
|
||||
}
|
||||
|
||||
if firstSortData[i] < secondSortData[i] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
52
sysservice/rankservice/RankInterface.go
Normal file
52
sysservice/rankservice/RankInterface.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package rankservice
|
||||
|
||||
import "github.com/duanhf2012/origin/service"
|
||||
|
||||
type RankDataChangeType int8
|
||||
|
||||
const (
|
||||
RankDataNone RankDataChangeType = 0
|
||||
RankDataAdd RankDataChangeType = 1 //数据插入
|
||||
RankDataUpdate RankDataChangeType = 2 //数据更新
|
||||
RankDataDelete RankDataChangeType = 3 //数据删除
|
||||
)
|
||||
|
||||
type IRankSkip interface {
|
||||
GetRankID() uint64
|
||||
GetRankLen() uint64
|
||||
}
|
||||
|
||||
// RankDataChangeCallBack 排行数据变化时调用
|
||||
//type RankDataChangeCallBack interface {
|
||||
// CB(iRankService service.IService, rankSkip IRankSkip, changeType RankDataChangeType, changed []*RankData)
|
||||
//}
|
||||
|
||||
type IRankModule interface {
|
||||
service.IModule
|
||||
|
||||
OnStart(mapRankSkip map[uint64]*RankSkip) error //服务开启时回调
|
||||
OnEnterRank(rankSkip IRankSkip, enterData []*RankData) //进入排行
|
||||
OnLeaveRank(rankSkip IRankSkip, leaveData []*RankData) //离开排行
|
||||
OnChangeRankData(rankSkip IRankSkip, changeData []*RankData) //当排行数据变化时
|
||||
OnStop(mapRankSkip map[uint64]*RankSkip) //服务结束时回调
|
||||
}
|
||||
|
||||
type DefaultRankModule struct {
|
||||
service.Module
|
||||
}
|
||||
|
||||
func (dr *DefaultRankModule) OnStart(mapRankSkip map[uint64]*RankSkip) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dr *DefaultRankModule) OnEnterRank(rankSkip IRankSkip, enterData []*RankData) {
|
||||
}
|
||||
|
||||
func (dr *DefaultRankModule) OnLeaveRank(rankSkip IRankSkip, leaveData []*RankData) {
|
||||
}
|
||||
|
||||
func (dr *DefaultRankModule) OnChangeRankData(rankSkip IRankSkip, changeData []*RankData) {
|
||||
}
|
||||
|
||||
func (dr *DefaultRankModule) OnStop(mapRankSkip map[uint64]*RankSkip) {
|
||||
}
|
||||
195
sysservice/rankservice/RankService.go
Normal file
195
sysservice/rankservice/RankService.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package rankservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duanhf2012/origin/log"
|
||||
"github.com/duanhf2012/origin/node"
|
||||
"github.com/duanhf2012/origin/rpc"
|
||||
"github.com/duanhf2012/origin/service"
|
||||
)
|
||||
|
||||
func init() {
|
||||
node.Setup(&RankService{})
|
||||
}
|
||||
|
||||
const PreMapRankSkipLen = 10
|
||||
const ManualAddRankSkip = "RPC_ManualAddRankSkip"
|
||||
const UpsetRank = "RPC_UpsetRank"
|
||||
const DeleteRankDataByKey = "RPC_DeleteRankDataByKey"
|
||||
const FindRankDataByKey = "RPC_FindRankDataByKey"
|
||||
const FindRankDataByPos = "RPC_FindRankDataByPos"
|
||||
const FindRankDataListStartTo = "RPC_FindRankDataListStartTo"
|
||||
|
||||
type RankService struct {
|
||||
service.Service
|
||||
|
||||
mapRankSkip map[uint64]*RankSkip
|
||||
rankModule IRankModule
|
||||
}
|
||||
|
||||
func (rs *RankService) OnInit() error {
|
||||
rs.mapRankSkip = make(map[uint64]*RankSkip, PreMapRankSkipLen)
|
||||
err := rs.dealCfg()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.rankModule != nil {
|
||||
_, err = rs.AddModule(rs.rankModule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
rs.AddModule(&DefaultRankModule{})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RankService) OnStart() {
|
||||
rs.rankModule.OnStart(rs.mapRankSkip)
|
||||
}
|
||||
|
||||
func (rs *RankService) OnRelease() {
|
||||
rs.rankModule.OnStop(rs.mapRankSkip)
|
||||
}
|
||||
|
||||
// 安装排行模块
|
||||
func (rs *RankService) SetupRankModule(rankModule IRankModule) {
|
||||
rs.rankModule = rankModule
|
||||
}
|
||||
|
||||
// RPC_ManualAddRankSkip 提供手动添加排行榜
|
||||
func (rs *RankService) RPC_ManualAddRankSkip(addInfo *rpc.AddRankList, addResult *rpc.RankResult) error {
|
||||
addList := make([]uint64, 0, PreMapRankSkipLen)
|
||||
for _, addRankListData := range addInfo.AddList {
|
||||
if addRankListData.RankId == 0 {
|
||||
rs.deleteRankList(addList)
|
||||
return fmt.Errorf("RPC_AddRankSkip must has rank id")
|
||||
}
|
||||
|
||||
newSkip := NewRankSkip(addRankListData.IsDec, transformLevel(addRankListData.SkipListLevel), addRankListData.MaxRank)
|
||||
rs.mapRankSkip[addRankListData.RankId] = newSkip
|
||||
addList = append(addList, addRankListData.RankId)
|
||||
}
|
||||
|
||||
addResult.AddCount = 1
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RPC_UpsetRank 更新排行榜
|
||||
func (rs *RankService) RPC_UpsetRank(upsetInfo *rpc.UpsetRankData, upsetResult *rpc.RankResult) error {
|
||||
rankSkip, ok := rs.mapRankSkip[upsetInfo.RankId]
|
||||
if ok == false || rankSkip == nil {
|
||||
return fmt.Errorf("RPC_UpsetRank[", upsetInfo.RankId, "] no this rank id")
|
||||
}
|
||||
|
||||
addCount, updateCount := rankSkip.UpsetRank(upsetInfo.RankDataList)
|
||||
upsetResult.AddCount = addCount
|
||||
upsetResult.ModifyCount = updateCount
|
||||
return nil
|
||||
}
|
||||
|
||||
// RPC_DeleteRankDataByKey 按排行的key进行删除
|
||||
func (rs *RankService) RPC_DeleteRankDataByKey(delInfo *rpc.DeleteByKey, delResult *rpc.RankResult) error {
|
||||
rankSkip, ok := rs.mapRankSkip[delInfo.RankId]
|
||||
if ok == false || rankSkip == nil {
|
||||
return fmt.Errorf("RPC_DeleteRankDataByKey[", delInfo.RankId, "] no this rank type")
|
||||
}
|
||||
|
||||
removeCount := rankSkip.DeleteRankData(delInfo.KeyList)
|
||||
if removeCount == 0 {
|
||||
log.SError("remove count is zero")
|
||||
}
|
||||
|
||||
delResult.RemoveCount = removeCount
|
||||
return nil
|
||||
}
|
||||
|
||||
// RPC_FindRankDataByKey 按key查找
|
||||
func (rs *RankService) RPC_FindRankDataByKey(findInfo *rpc.FindRankDataByKey, findResult *rpc.RankPosData) error {
|
||||
rankObj, ok := rs.mapRankSkip[findInfo.RankId]
|
||||
if ok == false || rankObj == nil {
|
||||
return fmt.Errorf("RPC_FindRankDataByKey[", findInfo.RankId, "] no this rank type")
|
||||
}
|
||||
|
||||
findRankData, rankPos := rankObj.GetRankNodeData(findInfo.Key)
|
||||
if findRankData != nil {
|
||||
findResult.Data = findRankData.Data
|
||||
findResult.Key = findRankData.Key
|
||||
findResult.SortData = findRankData.SortData
|
||||
findResult.RankPos = rankPos
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RPC_FindRankDataByPos 按pos查找
|
||||
func (rs *RankService) RPC_FindRankDataByPos(findInfo *rpc.FindRankDataByPos, findResult *rpc.RankPosData) error {
|
||||
rankObj, ok := rs.mapRankSkip[findInfo.RankId]
|
||||
if ok == false || rankObj == nil {
|
||||
return fmt.Errorf("RPC_FindRankDataByKey[", findInfo.RankId, "] no this rank type")
|
||||
}
|
||||
|
||||
findRankData, rankPos := rankObj.GetRankNodeDataByPos(findInfo.Pos)
|
||||
if findRankData != nil {
|
||||
findResult.Data = findRankData.Data
|
||||
findResult.Key = findRankData.Key
|
||||
findResult.SortData = findRankData.SortData
|
||||
findResult.RankPos = rankPos
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RPC_FindRankDataListStartTo 按pos查找,start开始count个排行数据
|
||||
func (rs *RankService) RPC_FindRankDataListStartTo(findInfo *rpc.FindRankDataListStartTo, findResult *rpc.RankDataList) error {
|
||||
rankObj, ok := rs.mapRankSkip[findInfo.RankId]
|
||||
if ok == false || rankObj == nil {
|
||||
return fmt.Errorf("RPC_FindRankDataListStartTo[", findInfo.RankId, "] no this rank type")
|
||||
}
|
||||
|
||||
findResult.RankDataCount = rankObj.GetRankLen()
|
||||
return rankObj.GetRankDataFromToLimit(findInfo.StartPos, findInfo.Count, findResult)
|
||||
}
|
||||
|
||||
func (rs *RankService) deleteRankList(delIdList []uint64) {
|
||||
if rs.mapRankSkip == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, id := range delIdList {
|
||||
delete(rs.mapRankSkip, id)
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RankService) dealCfg() error {
|
||||
mapDBServiceCfg, ok := rs.GetServiceCfg().(map[string]interface{})
|
||||
if ok == false {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfgList, okList := mapDBServiceCfg["SortCfg"].([]interface{})
|
||||
if okList == false {
|
||||
return fmt.Errorf("RankService SortCfg must be list")
|
||||
}
|
||||
|
||||
for _, cfg := range cfgList {
|
||||
mapCfg, okCfg := cfg.(map[string]interface{})
|
||||
if okCfg == false {
|
||||
return fmt.Errorf("RankService SortCfg data must be map or struct")
|
||||
}
|
||||
|
||||
rankId, okId := mapCfg["RankID"].(float64)
|
||||
if okId == false {
|
||||
return fmt.Errorf("RankService SortCfg data must has RankID[number]")
|
||||
}
|
||||
|
||||
level, _ := mapCfg["SkipListLevel"].(float64)
|
||||
isDec, _ := mapCfg["IsDec"].(bool)
|
||||
maxRank, _ := mapCfg["MaxRank"].(float64)
|
||||
|
||||
newSkip := NewRankSkip(isDec, transformLevel(int32(level)), uint64(maxRank))
|
||||
rs.mapRankSkip[uint64(rankId)] = newSkip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
262
sysservice/rankservice/RankSkip.go
Normal file
262
sysservice/rankservice/RankSkip.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package rankservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duanhf2012/origin/rpc"
|
||||
"github.com/duanhf2012/origin/util/algorithms/skip"
|
||||
)
|
||||
|
||||
type RankSkip struct {
|
||||
rankId uint64 //排行榜ID
|
||||
isDes bool //是否为降序 true:降序 false:升序
|
||||
skipList *skip.SkipList //跳表
|
||||
mapRankData map[uint64]*RankData //排行数据map
|
||||
maxLen uint64 //排行数据长度
|
||||
rankModule IRankModule
|
||||
}
|
||||
|
||||
// NewRankSkip 创建排行榜
|
||||
func NewRankSkip(isDes bool, level interface{}, maxLen uint64) *RankSkip {
|
||||
ret := &RankSkip{}
|
||||
|
||||
ret.isDes = isDes
|
||||
ret.skipList = skip.New(level)
|
||||
ret.mapRankData = make(map[uint64]*RankData, 10240)
|
||||
ret.maxLen = maxLen
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (rs *RankSkip) SetupRankModule(rankModule IRankModule) {
|
||||
rs.rankModule = rankModule
|
||||
}
|
||||
|
||||
// GetRankID 获取排行榜Id
|
||||
func (rs *RankSkip) GetRankID() uint64 {
|
||||
return rs.rankId
|
||||
}
|
||||
|
||||
// GetRankLen 获取排行榜长度
|
||||
func (rs *RankSkip) GetRankLen() uint64 {
|
||||
return rs.skipList.Len()
|
||||
}
|
||||
|
||||
func (rs *RankSkip) UpsetRank(upsetRankData []*rpc.RankData) (addCount int32, modifyCount int32) {
|
||||
addList := make([]*RankData, 0, 1)
|
||||
updateList := make([]*RankData, 0, 1)
|
||||
for _, upsetData := range upsetRankData {
|
||||
changeData, changeType := rs.upsetRank(upsetData)
|
||||
if changeData == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch changeType {
|
||||
case RankDataAdd:
|
||||
addList = append(addList, changeData)
|
||||
case RankDataUpdate:
|
||||
updateList = append(updateList, changeData)
|
||||
}
|
||||
}
|
||||
|
||||
if len(addList) > 0 {
|
||||
rs.rankModule.OnEnterRank(rs, addList)
|
||||
}
|
||||
|
||||
if len(updateList) > 0 {
|
||||
rs.rankModule.OnChangeRankData(rs, updateList)
|
||||
}
|
||||
|
||||
addCount = int32(len(addList))
|
||||
modifyCount = int32(len(updateList))
|
||||
return
|
||||
}
|
||||
|
||||
// UpsetRank 更新玩家排行数据,返回变化后的数据及变化类型
|
||||
func (rs *RankSkip) upsetRank(upsetData *rpc.RankData) (*RankData, RankDataChangeType) {
|
||||
rankNode, ok := rs.mapRankData[upsetData.Key]
|
||||
if ok == true {
|
||||
//找到的情况对比排名数据是否有变化,无变化进行data更新,有变化则进行删除更新
|
||||
if compareIsEqual(rankNode.SortData, upsetData.SortData) {
|
||||
rankNode.Data = upsetData.GetData()
|
||||
return nil, RankDataNone
|
||||
}
|
||||
|
||||
if upsetData.Data == nil {
|
||||
upsetData.Data = rankNode.Data
|
||||
}
|
||||
rs.skipList.Delete(rankNode)
|
||||
ReleaseRankData(rankNode)
|
||||
|
||||
newRankData := NewRankData(rs.isDes, upsetData)
|
||||
rs.skipList.Insert(newRankData)
|
||||
rs.mapRankData[upsetData.Key] = newRankData
|
||||
return newRankData, RankDataUpdate
|
||||
}
|
||||
|
||||
if rs.checkCanInsert(upsetData) {
|
||||
newRankData := NewRankData(rs.isDes, upsetData)
|
||||
rs.skipList.Insert(newRankData)
|
||||
rs.mapRankData[upsetData.Key] = newRankData
|
||||
return newRankData, RankDataAdd
|
||||
}
|
||||
|
||||
return nil, RankDataNone
|
||||
}
|
||||
|
||||
// DeleteRankData 删除排行数据
|
||||
func (rs *RankSkip) DeleteRankData(delKeys []uint64) int32 {
|
||||
removeRankData := make([]*RankData, 0, 1)
|
||||
//预统计处理,进行回调
|
||||
for _, key := range delKeys {
|
||||
rankData, ok := rs.mapRankData[key]
|
||||
if ok == false {
|
||||
continue
|
||||
}
|
||||
|
||||
removeRankData = append(removeRankData, rankData)
|
||||
}
|
||||
rs.rankModule.OnLeaveRank(rs, removeRankData)
|
||||
|
||||
//从排行榜中删除
|
||||
for _, rankData := range removeRankData {
|
||||
rs.skipList.Delete(rankData)
|
||||
ReleaseRankData(rankData)
|
||||
delete(rs.mapRankData, rankData.Key)
|
||||
}
|
||||
|
||||
return int32(len(removeRankData))
|
||||
}
|
||||
|
||||
// GetRankNodeData 获取,返回排名节点与名次
|
||||
func (rs *RankSkip) GetRankNodeData(findKey uint64) (*RankData, uint64) {
|
||||
rankNode, ok := rs.mapRankData[findKey]
|
||||
if ok == false {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
_, index := rs.skipList.GetWithPosition(rankNode)
|
||||
return rankNode, index
|
||||
}
|
||||
|
||||
// GetRankNodeDataByPos 获取,返回排名节点与名次
|
||||
func (rs *RankSkip) GetRankNodeDataByPos(pos uint64) (*RankData, uint64) {
|
||||
rankNode := rs.skipList.ByPosition(pos)
|
||||
if rankNode == nil {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
return rankNode.(*RankData), pos
|
||||
}
|
||||
|
||||
// GetRankKeyPrevToLimit 获取key前count名的数据
|
||||
func (rs *RankSkip) GetRankKeyPrevToLimit(findKey, count uint64, result *rpc.RankDataList) error {
|
||||
if rs.GetRankLen() <= 0 {
|
||||
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||
}
|
||||
|
||||
findData, ok := rs.mapRankData[findKey]
|
||||
if ok == false {
|
||||
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||
}
|
||||
|
||||
_, rankPos := rs.skipList.GetWithPosition(findData)
|
||||
iter := rs.skipList.Iter(findData)
|
||||
iterCount := uint64(0)
|
||||
for iter.Prev() && iterCount < count {
|
||||
rankData := iter.Value().(*RankData)
|
||||
result.RankPosDataList = append(result.RankPosDataList, &rpc.RankPosData{
|
||||
Key: rankData.Key,
|
||||
RankPos: rankPos - iterCount,
|
||||
SortData: rankData.SortData,
|
||||
Data: rankData.Data,
|
||||
})
|
||||
iterCount++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRankKeyPrevToLimit 获取key前count名的数据
|
||||
func (rs *RankSkip) GetRankKeyNextToLimit(findKey, count uint64, result *rpc.RankDataList) error {
|
||||
if rs.GetRankLen() <= 0 {
|
||||
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||
}
|
||||
|
||||
findData, ok := rs.mapRankData[findKey]
|
||||
if ok == false {
|
||||
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||
}
|
||||
|
||||
_, rankPos := rs.skipList.GetWithPosition(findData)
|
||||
iter := rs.skipList.Iter(findData)
|
||||
iterCount := uint64(0)
|
||||
for iter.Next() && iterCount < count {
|
||||
rankData := iter.Value().(*RankData)
|
||||
result.RankPosDataList = append(result.RankPosDataList, &rpc.RankPosData{
|
||||
Key: rankData.Key,
|
||||
RankPos: rankPos + iterCount,
|
||||
SortData: rankData.SortData,
|
||||
Data: rankData.Data,
|
||||
})
|
||||
iterCount++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRankList 获取排行榜数据,startPos开始的count个数据
|
||||
func (rs *RankSkip) GetRankDataFromToLimit(startPos, count uint64, result *rpc.RankDataList) error {
|
||||
if rs.GetRankLen() <= 0 {
|
||||
return fmt.Errorf("rank[", rs.rankId, "] no data")
|
||||
}
|
||||
|
||||
if result.RankDataCount < startPos {
|
||||
startPos = result.RankDataCount - 1
|
||||
}
|
||||
|
||||
iter := rs.skipList.IterAtPosition(startPos)
|
||||
iterCount := uint64(0)
|
||||
for iter.Next() && iterCount < count {
|
||||
rankData := iter.Value().(*RankData)
|
||||
result.RankPosDataList = append(result.RankPosDataList, &rpc.RankPosData{
|
||||
Key: rankData.Key,
|
||||
RankPos: iterCount + startPos,
|
||||
SortData: rankData.SortData,
|
||||
Data: rankData.Data,
|
||||
})
|
||||
iterCount++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkCanInsert 检查是否能插入
|
||||
func (rs *RankSkip) checkCanInsert(upsetData *rpc.RankData) bool {
|
||||
//maxLen为0,不限制长度
|
||||
if rs.maxLen == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
//没有放满,则进行插入
|
||||
rankLen := rs.skipList.Len()
|
||||
if rs.maxLen > rankLen {
|
||||
return true
|
||||
}
|
||||
|
||||
//已经放满了,进行数据比较
|
||||
lastPosData := rs.skipList.ByPosition(rankLen - 1)
|
||||
lastRankData := lastPosData.(*RankData)
|
||||
moreThanFlag := compareMoreThan(upsetData.SortData, lastRankData.SortData)
|
||||
//降序排列,比最后一位小,不能插入 升序排列,比最后一位大,不能插入
|
||||
if (rs.isDes == true && moreThanFlag < 0) || (rs.isDes == false && moreThanFlag > 0) || moreThanFlag == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
//移除最后一位
|
||||
//回调模块,该RandData从排行中删除
|
||||
rs.rankModule.OnLeaveRank(rs, []*RankData{lastRankData})
|
||||
rs.skipList.Delete(lastPosData)
|
||||
delete(rs.mapRankData, lastRankData.Key)
|
||||
ReleaseRankData(lastRankData)
|
||||
return true
|
||||
}
|
||||
76
sysservice/rankservice/rank.proto
Normal file
76
sysservice/rankservice/rank.proto
Normal file
@@ -0,0 +1,76 @@
|
||||
syntax = "proto3";
|
||||
package rpc;
|
||||
option go_package = ".;rpc";
|
||||
|
||||
// RankData 排行数据
|
||||
message RankData {
|
||||
uint64 Key = 1; //数据主建
|
||||
repeated int64 SortData = 2; //参与排行的数据
|
||||
bytes Data = 3; //不参与排行的数据
|
||||
int64 expireMs = 4; //剩余有效时间毫秒,如果为0永不过期
|
||||
}
|
||||
|
||||
// RankPosData 排行数据——查询返回
|
||||
message RankPosData {
|
||||
uint64 Key = 1; //数据主建
|
||||
uint64 RankPos = 2; //名次
|
||||
repeated int64 SortData = 3; //参与排行的数据
|
||||
bytes Data = 4; //不参与排行的数据
|
||||
}
|
||||
|
||||
// RankList 排行榜数据
|
||||
message RankList {
|
||||
uint64 RankId = 1; //排行榜类型
|
||||
int32 SkipListLevel = 2; //排行榜level-生成的跳表的level, 8/16/32/64等
|
||||
bool IsDec = 3; //不参与排行的数据
|
||||
uint64 MaxRank = 4; //最大排名
|
||||
}
|
||||
|
||||
// UpsetRankData 更新排行榜数据
|
||||
message UpsetRankData {
|
||||
uint64 RankId = 1; //排行榜的ID
|
||||
repeated RankData RankDataList = 2; //排行数据
|
||||
}
|
||||
|
||||
// DeleteByKey 更新排行榜数据
|
||||
message DeleteByKey {
|
||||
uint64 RankId = 1; //排行榜的分类ID
|
||||
repeated uint64 KeyList = 2; //排行数据
|
||||
}
|
||||
|
||||
// AddRankList 新增排行榜
|
||||
message AddRankList {
|
||||
repeated RankList AddList = 1; //添加的排行榜列表
|
||||
}
|
||||
|
||||
// FindRankDataByKey 查找排行信息
|
||||
message FindRankDataByKey {
|
||||
uint64 RankId = 1; //排行榜的ID
|
||||
uint64 Key = 2; //排行的key
|
||||
}
|
||||
|
||||
// FindRankDataByPos 查找排行信息
|
||||
message FindRankDataByPos {
|
||||
uint64 RankId = 1; //排行榜的ID
|
||||
uint64 Pos = 2; //排行名次
|
||||
}
|
||||
|
||||
// FindRankDataListStartTo 查找排行信息,StartPos开始Count个
|
||||
message FindRankDataListStartTo {
|
||||
uint64 RankId = 1; //排行榜的ID
|
||||
uint64 StartPos = 2; //排行的位置 0开始
|
||||
uint64 Count = 3; //查询格式
|
||||
}
|
||||
|
||||
// RankDataList
|
||||
message RankDataList {
|
||||
uint64 RankDataCount = 1; //排行长度
|
||||
repeated RankPosData RankPosDataList = 2; //排行数据
|
||||
}
|
||||
|
||||
// RankResult
|
||||
message RankResult {
|
||||
int32 AddCount = 1; //增加记录
|
||||
int32 RemoveCount = 2; //删除数量
|
||||
int32 ModifyCount = 3; //修改数量
|
||||
}
|
||||
47
util/algorithms/skip/interface.go
Normal file
47
util/algorithms/skip/interface.go
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2014 Workiva, LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package skip
|
||||
|
||||
// Comparator is a generic interface that represents items that can
|
||||
// be compared.
|
||||
type Comparator interface {
|
||||
// Compare compares this interface with another. Returns a positive
|
||||
// number if this interface is greater, 0 if equal, negative number
|
||||
// if less.
|
||||
Compare(Comparator) int
|
||||
}
|
||||
|
||||
// Comparators is a typed list of type Comparator.
|
||||
type Comparators []Comparator
|
||||
|
||||
// Iterator defines an interface that allows a consumer to iterate
|
||||
// all results of a query. All values will be visited in-order.
|
||||
type Iterator interface {
|
||||
// Next returns a bool indicating if there is future value
|
||||
// in the iterator and moves the iterator to that value.
|
||||
Next() bool
|
||||
// Prev returns a bool indicating if there is Previous value
|
||||
// in the iterator and moves the iterator to that value.
|
||||
Prev() bool
|
||||
// Value returns a Comparator representing the iterator's current
|
||||
// position. If there is no value, this returns nil.
|
||||
Value() Comparator
|
||||
// exhaust is a helper method that will iterate this iterator
|
||||
// to completion and return a list of resulting Entries
|
||||
// in order.
|
||||
exhaust() Comparators
|
||||
}
|
||||
86
util/algorithms/skip/iterator.go
Normal file
86
util/algorithms/skip/iterator.go
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright 2014 Workiva, LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package skip
|
||||
|
||||
const iteratorExhausted = -2
|
||||
|
||||
// iterator represents an object that can be iterated. It will
|
||||
// return false on Next and nil on Value if there are no further
|
||||
// values to be iterated.
|
||||
type iterator struct {
|
||||
first bool
|
||||
n *node
|
||||
}
|
||||
|
||||
// Next returns a bool indicating if there are any further values
|
||||
// in this iterator.
|
||||
func (iter *iterator) Next() bool {
|
||||
if iter.first {
|
||||
iter.first = false
|
||||
return iter.n != nil
|
||||
}
|
||||
|
||||
if iter.n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
iter.n = iter.n.forward[0]
|
||||
return iter.n != nil
|
||||
}
|
||||
|
||||
// Prev returns a bool indicating if there are any Previous values
|
||||
// in this iterator.
|
||||
func (iter *iterator) Prev() bool {
|
||||
if iter.first {
|
||||
iter.first = false
|
||||
return iter.n != nil
|
||||
}
|
||||
|
||||
if iter.n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
iter.n = iter.n.preNode
|
||||
return iter.n != nil && iter.n.entry != nil
|
||||
}
|
||||
|
||||
// Value returns a Comparator representing the iterator's present
|
||||
// position in the query. Returns nil if no values remain to iterate.
|
||||
func (iter *iterator) Value() Comparator {
|
||||
if iter.n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return iter.n.entry
|
||||
}
|
||||
|
||||
// exhaust is a helper method to exhaust this iterator and return
|
||||
// all remaining entries.
|
||||
func (iter *iterator) exhaust() Comparators {
|
||||
entries := make(Comparators, 0, 10)
|
||||
for i := iter; i.Next(); {
|
||||
entries = append(entries, i.Value())
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// nilIterator returns an iterator that will always return false
|
||||
// for Next and nil for Value.
|
||||
func nilIterator() *iterator {
|
||||
return &iterator{}
|
||||
}
|
||||
50
util/algorithms/skip/node.go
Normal file
50
util/algorithms/skip/node.go
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2014 Workiva, LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package skip
|
||||
|
||||
type widths []uint64
|
||||
|
||||
type nodes []*node
|
||||
|
||||
type node struct {
|
||||
// forward denotes the forward pointing pointers in this
|
||||
// node.
|
||||
forward nodes
|
||||
//zero level pre node
|
||||
preNode *node
|
||||
// widths keeps track of the distance between this pointer
|
||||
// and the forward pointers so we can access skip list
|
||||
// values by position in logarithmic time.
|
||||
widths widths
|
||||
// entry is the associated value with this node.
|
||||
entry Comparator
|
||||
}
|
||||
|
||||
func (n *node) Compare(e Comparator) int {
|
||||
return n.entry.Compare(e)
|
||||
}
|
||||
|
||||
// newNode will allocate and return a new node with the entry
|
||||
// provided. maxLevels will determine the length of the forward
|
||||
// pointer list associated with this node.
|
||||
func newNode(cmp Comparator, maxLevels uint8) *node {
|
||||
return &node{
|
||||
entry: cmp,
|
||||
forward: make(nodes, maxLevels),
|
||||
widths: make(widths, maxLevels),
|
||||
}
|
||||
}
|
||||
494
util/algorithms/skip/skip.go
Normal file
494
util/algorithms/skip/skip.go
Normal file
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
Copyright 2014 Workiva, LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package skip defines a skiplist datastructure. That is, a data structure
|
||||
that probabilistically determines relationships between keys. By doing
|
||||
so, it becomes easier to program than a binary search tree but maintains
|
||||
similar speeds.
|
||||
|
||||
Performance characteristics:
|
||||
Insert: O(log n)
|
||||
Search: O(log n)
|
||||
Delete: O(log n)
|
||||
Space: O(n)
|
||||
|
||||
Recently added is the capability to address, insert, and replace an
|
||||
entry by position. This capability is acheived by saving the width
|
||||
of the "gap" between two nodes. Searching for an item by position is
|
||||
very similar to searching by value in that the same basic algorithm is
|
||||
used but we are searching for width instead of value. Because this avoids
|
||||
the overhead associated with Golang interfaces, operations by position
|
||||
are about twice as fast as operations by value. Time complexities listed
|
||||
below.
|
||||
|
||||
SearchByPosition: O(log n)
|
||||
InsertByPosition: O(log n)
|
||||
|
||||
More information here: http://cglab.ca/~morin/teaching/5408/refs/p90b.pdf
|
||||
|
||||
Benchmarks:
|
||||
BenchmarkInsert-8 2000000 930 ns/op
|
||||
BenchmarkGet-8 2000000 989 ns/op
|
||||
BenchmarkDelete-8 3000000 600 ns/op
|
||||
BenchmarkPrepend-8 1000000 1468 ns/op
|
||||
BenchmarkByPosition-8 10000000 202 ns/op
|
||||
BenchmarkInsertAtPosition-8 3000000 485 ns/op
|
||||
|
||||
CPU profiling has shown that the most expensive thing we do here
|
||||
is call Compare. A potential optimization for gets only is to
|
||||
do a binary search in the forward/width lists instead of visiting
|
||||
every value. We could also use generics if Golang had them and
|
||||
let the consumer specify primitive types, which would speed up
|
||||
these operation dramatically.
|
||||
*/
|
||||
package skip
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const p = .5 // the p level defines the probability that a node
|
||||
// with a value at level i also has a value at i+1. This number
|
||||
// is also important in determining max level. Max level will
|
||||
// be defined as L(N) where L = log base (1/p) of n where n
|
||||
// is the number of items in the list and N is the number of possible
|
||||
// items in the universe. If p = .5 then maxlevel = 32 is appropriate
|
||||
// for uint32.
|
||||
|
||||
// lockedSource is an implementation of rand.Source that is safe for
|
||||
// concurrent use by multiple goroutines. The code is modeled after
|
||||
// https://golang.org/src/math/rand/rand.go.
|
||||
type lockedSource struct {
|
||||
mu sync.Mutex
|
||||
src rand.Source
|
||||
}
|
||||
|
||||
// Int63 implements the rand.Source interface.
|
||||
func (ls *lockedSource) Int63() (n int64) {
|
||||
ls.mu.Lock()
|
||||
n = ls.src.Int63()
|
||||
ls.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Seed implements the rand.Source interface.
|
||||
func (ls *lockedSource) Seed(seed int64) {
|
||||
ls.mu.Lock()
|
||||
ls.src.Seed(seed)
|
||||
ls.mu.Unlock()
|
||||
}
|
||||
|
||||
// generator will be the common generator to create random numbers. It
|
||||
// is seeded with unix nanosecond when this line is executed at runtime,
|
||||
// and only executed once ensuring all random numbers come from the same
|
||||
// randomly seeded generator.
|
||||
var generator = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
|
||||
|
||||
func generateLevel(maxLevel uint8) uint8 {
|
||||
var level uint8
|
||||
for level = uint8(1); level < maxLevel-1; level++ {
|
||||
if generator.Float64() >= p {
|
||||
|
||||
return level
|
||||
}
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
func insertNode(sl *SkipList, n *node, cmp Comparator, pos uint64, cache nodes, posCache widths, allowDuplicate bool) Comparator {
|
||||
if !allowDuplicate && n != nil && n.Compare(cmp) == 0 { // a simple update in this case
|
||||
oldEntry := n.entry
|
||||
n.entry = cmp
|
||||
return oldEntry
|
||||
}
|
||||
atomic.AddUint64(&sl.num, 1)
|
||||
|
||||
nodeLevel := generateLevel(sl.maxLevel)
|
||||
if nodeLevel > sl.level {
|
||||
for i := sl.level; i < nodeLevel; i++ {
|
||||
cache[i] = sl.head
|
||||
}
|
||||
sl.level = nodeLevel
|
||||
}
|
||||
|
||||
nn := newNode(cmp, nodeLevel)
|
||||
for i := uint8(0); i < nodeLevel; i++ {
|
||||
if i == 0 {
|
||||
nn.preNode = cache[i]
|
||||
if cache[i].forward[i] != nil {
|
||||
cache[i].forward[i].preNode = nn
|
||||
}
|
||||
}
|
||||
|
||||
nn.forward[i] = cache[i].forward[i]
|
||||
cache[i].forward[i] = nn
|
||||
|
||||
formerWidth := cache[i].widths[i]
|
||||
if formerWidth == 0 {
|
||||
nn.widths[i] = 0
|
||||
} else {
|
||||
nn.widths[i] = posCache[i] + formerWidth + 1 - pos
|
||||
}
|
||||
|
||||
if cache[i].forward[i] != nil {
|
||||
cache[i].widths[i] = pos - posCache[i]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for i := nodeLevel; i < sl.level; i++ {
|
||||
if cache[i].forward[i] == nil {
|
||||
continue
|
||||
}
|
||||
cache[i].widths[i]++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func splitAt(sl *SkipList, index uint64) (*SkipList, *SkipList) {
|
||||
right := &SkipList{}
|
||||
right.maxLevel = sl.maxLevel
|
||||
right.level = sl.level
|
||||
right.cache = make(nodes, sl.maxLevel)
|
||||
right.posCache = make(widths, sl.maxLevel)
|
||||
right.head = newNode(nil, sl.maxLevel)
|
||||
sl.searchByPosition(index, sl.cache, sl.posCache) // populate the cache that needs updating
|
||||
|
||||
for i := uint8(0); i <= sl.level; i++ {
|
||||
right.head.forward[i] = sl.cache[i].forward[i]
|
||||
if sl.cache[i].forward[i] != nil {
|
||||
right.head.widths[i] = sl.cache[i].widths[i] - (index - sl.posCache[i])
|
||||
}
|
||||
sl.cache[i].widths[i] = 0
|
||||
sl.cache[i].forward[i] = nil
|
||||
}
|
||||
|
||||
right.num = sl.Len() - index // right is not in user's hands yet
|
||||
atomic.AddUint64(&sl.num, -right.num)
|
||||
|
||||
sl.resetMaxLevel()
|
||||
right.resetMaxLevel()
|
||||
|
||||
return sl, right
|
||||
}
|
||||
|
||||
// Skip list is a datastructure that probabalistically determines
|
||||
// relationships between nodes. This results in a structure
|
||||
// that performs similarly to a BST but is much easier to build
|
||||
// from a programmatic perspective (no rotations).
|
||||
type SkipList struct {
|
||||
maxLevel, level uint8
|
||||
head *node
|
||||
num uint64
|
||||
// a list of nodes that can be reused, should reduce
|
||||
// the number of allocations in the insert/delete case.
|
||||
cache nodes
|
||||
posCache widths
|
||||
}
|
||||
|
||||
// init will initialize this skiplist. The parameter is expected
|
||||
// to be of some uint type which will set this skiplist's maximum
|
||||
// level.
|
||||
func (sl *SkipList) init(ifc interface{}) {
|
||||
switch ifc.(type) {
|
||||
case uint8:
|
||||
sl.maxLevel = 8
|
||||
case uint16:
|
||||
sl.maxLevel = 16
|
||||
case uint32:
|
||||
sl.maxLevel = 32
|
||||
case uint64, uint:
|
||||
sl.maxLevel = 64
|
||||
}
|
||||
sl.cache = make(nodes, sl.maxLevel)
|
||||
sl.posCache = make(widths, sl.maxLevel)
|
||||
sl.head = newNode(nil, sl.maxLevel)
|
||||
}
|
||||
|
||||
func (sl *SkipList) search(cmp Comparator, update nodes, widths widths) (*node, uint64) {
|
||||
if sl.Len() == 0 { // nothing in the list
|
||||
return nil, 1
|
||||
}
|
||||
|
||||
var pos uint64 = 0
|
||||
var offset uint8
|
||||
var alreadyChecked *node
|
||||
n := sl.head
|
||||
for i := uint8(0); i <= sl.level; i++ {
|
||||
offset = sl.level - i
|
||||
for n.forward[offset] != nil && n.forward[offset] != alreadyChecked && n.forward[offset].Compare(cmp) < 0 {
|
||||
pos += n.widths[offset]
|
||||
n = n.forward[offset]
|
||||
}
|
||||
|
||||
alreadyChecked = n
|
||||
if update != nil {
|
||||
update[offset] = n
|
||||
widths[offset] = pos
|
||||
}
|
||||
}
|
||||
|
||||
return n.forward[0], pos + 1
|
||||
}
|
||||
|
||||
func (sl *SkipList) resetMaxLevel() {
|
||||
if sl.level < 1 {
|
||||
sl.level = 1
|
||||
return
|
||||
}
|
||||
for sl.head.forward[sl.level-1] == nil && sl.level > 1 {
|
||||
sl.level--
|
||||
}
|
||||
}
|
||||
|
||||
func (sl *SkipList) searchByPosition(position uint64, update nodes, widths widths) (*node, uint64) {
|
||||
if sl.Len() == 0 { // nothing in the list
|
||||
return nil, 1
|
||||
}
|
||||
|
||||
if position > sl.Len() {
|
||||
return nil, 1
|
||||
}
|
||||
|
||||
var pos uint64 = 0
|
||||
var offset uint8
|
||||
n := sl.head
|
||||
for i := uint8(0); i <= sl.level; i++ {
|
||||
offset = sl.level - i
|
||||
for n.forward[offset] != nil && pos+n.widths[offset] <= position {
|
||||
pos += n.widths[offset]
|
||||
n = n.forward[offset]
|
||||
}
|
||||
|
||||
if update != nil {
|
||||
update[offset] = n
|
||||
widths[offset] = pos
|
||||
}
|
||||
}
|
||||
|
||||
return n, pos + 1
|
||||
}
|
||||
|
||||
// Get will retrieve values associated with the keys provided. If an
|
||||
// associated value could not be found, a nil is returned in its place.
|
||||
// This is an O(log n) operation.
|
||||
func (sl *SkipList) Get(comparators ...Comparator) Comparators {
|
||||
result := make(Comparators, 0, len(comparators))
|
||||
|
||||
var n *node
|
||||
for _, cmp := range comparators {
|
||||
n, _ = sl.search(cmp, nil, nil)
|
||||
if n != nil && n.Compare(cmp) == 0 {
|
||||
result = append(result, n.entry)
|
||||
} else {
|
||||
result = append(result, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetWithPosition will retrieve the value with the provided key and
|
||||
// return the position of that value within the list. Returns nil, 0
|
||||
// if an associated value could not be found.
|
||||
func (sl *SkipList) GetWithPosition(cmp Comparator) (Comparator, uint64) {
|
||||
n, pos := sl.search(cmp, nil, nil)
|
||||
if n == nil {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
return n.entry, pos - 1
|
||||
}
|
||||
|
||||
// ByPosition returns the Comparator at the given position.
|
||||
func (sl *SkipList) ByPosition(position uint64) Comparator {
|
||||
n, _ := sl.searchByPosition(position+1, nil, nil)
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return n.entry
|
||||
}
|
||||
|
||||
func (sl *SkipList) insert(cmp Comparator) Comparator {
|
||||
n, pos := sl.search(cmp, sl.cache, sl.posCache)
|
||||
return insertNode(sl, n, cmp, pos, sl.cache, sl.posCache, false)
|
||||
}
|
||||
|
||||
// Insert will insert the provided comparators into the list. Returned
|
||||
// is a list of comparators that were overwritten. This is expected to
|
||||
// be an O(log n) operation.
|
||||
func (sl *SkipList) Insert(comparators ...Comparator) Comparators {
|
||||
overwritten := make(Comparators, 0, len(comparators))
|
||||
for _, cmp := range comparators {
|
||||
overwritten = append(overwritten, sl.insert(cmp))
|
||||
}
|
||||
|
||||
return overwritten
|
||||
}
|
||||
|
||||
func (sl *SkipList) insertAtPosition(position uint64, cmp Comparator) {
|
||||
if position > sl.Len() {
|
||||
position = sl.Len()
|
||||
}
|
||||
n, pos := sl.searchByPosition(position, sl.cache, sl.posCache)
|
||||
insertNode(sl, n, cmp, pos, sl.cache, sl.posCache, true)
|
||||
}
|
||||
|
||||
// InsertAtPosition will insert the provided Comparator at the provided position.
|
||||
// If position is greater than the length of the skiplist, the Comparator
|
||||
// is appended. This method bypasses order checks and checks for
|
||||
// duplicates so use with caution.
|
||||
func (sl *SkipList) InsertAtPosition(position uint64, cmp Comparator) {
|
||||
sl.insertAtPosition(position, cmp)
|
||||
}
|
||||
|
||||
func (sl *SkipList) replaceAtPosition(position uint64, cmp Comparator) {
|
||||
n, _ := sl.searchByPosition(position+1, nil, nil)
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
n.entry = cmp
|
||||
}
|
||||
|
||||
// Replace at position will replace the Comparator at the provided position
|
||||
// with the provided Comparator. If the provided position does not exist,
|
||||
// this operation is a no-op.
|
||||
func (sl *SkipList) ReplaceAtPosition(position uint64, cmp Comparator) {
|
||||
sl.replaceAtPosition(position, cmp)
|
||||
}
|
||||
|
||||
func (sl *SkipList) delete(cmp Comparator) Comparator {
|
||||
n, _ := sl.search(cmp, sl.cache, sl.posCache)
|
||||
|
||||
if n == nil || n.Compare(cmp) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
atomic.AddUint64(&sl.num, ^uint64(0)) // decrement
|
||||
|
||||
for i := uint8(0); i <= sl.level; i++ {
|
||||
if sl.cache[i].forward[i] != n {
|
||||
if sl.cache[i].forward[i] != nil {
|
||||
sl.cache[i].widths[i]--
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
if n.forward[i] != nil {
|
||||
n.forward[i].preNode = sl.cache[i]
|
||||
}
|
||||
n.preNode = nil
|
||||
}
|
||||
|
||||
sl.cache[i].widths[i] += n.widths[i] - 1
|
||||
sl.cache[i].forward[i] = n.forward[i]
|
||||
}
|
||||
|
||||
for sl.level > 1 && sl.head.forward[sl.level-1] == nil {
|
||||
sl.head.widths[sl.level] = 0
|
||||
sl.level--
|
||||
}
|
||||
|
||||
return n.entry
|
||||
}
|
||||
|
||||
// Delete will remove the provided keys from the skiplist and return
|
||||
// a list of in-order Comparators that were deleted. This is a no-op if
|
||||
// an associated key could not be found. This is an O(log n) operation.
|
||||
func (sl *SkipList) Delete(comparators ...Comparator) Comparators {
|
||||
deleted := make(Comparators, 0, len(comparators))
|
||||
|
||||
for _, cmp := range comparators {
|
||||
deleted = append(deleted, sl.delete(cmp))
|
||||
}
|
||||
|
||||
return deleted
|
||||
}
|
||||
|
||||
// Len returns the number of items in this skiplist.
|
||||
func (sl *SkipList) Len() uint64 {
|
||||
return atomic.LoadUint64(&sl.num)
|
||||
}
|
||||
|
||||
func (sl *SkipList) iterAtPosition(pos uint64) *iterator {
|
||||
n, _ := sl.searchByPosition(pos, nil, nil)
|
||||
if n == nil || n.entry == nil {
|
||||
return nilIterator()
|
||||
}
|
||||
|
||||
return &iterator{
|
||||
first: true,
|
||||
n: n,
|
||||
}
|
||||
}
|
||||
|
||||
// IterAtPosition is the sister method to Iter only the user defines
|
||||
// a position in the skiplist to begin iteration instead of a value.
|
||||
func (sl *SkipList) IterAtPosition(pos uint64) Iterator {
|
||||
return sl.iterAtPosition(pos + 1)
|
||||
}
|
||||
|
||||
func (sl *SkipList) iter(cmp Comparator) *iterator {
|
||||
n, _ := sl.search(cmp, nil, nil)
|
||||
if n == nil {
|
||||
return nilIterator()
|
||||
}
|
||||
|
||||
return &iterator{
|
||||
first: true,
|
||||
n: n,
|
||||
}
|
||||
}
|
||||
|
||||
// Iter will return an iterator that can be used to iterate
|
||||
// over all the values with a key equal to or greater than
|
||||
// the key provided.
|
||||
func (sl *SkipList) Iter(cmp Comparator) Iterator {
|
||||
return sl.iter(cmp)
|
||||
}
|
||||
|
||||
// SplitAt will split the current skiplist into two lists. The first
|
||||
// skiplist returned is the "left" list and the second is the "right."
|
||||
// The index defines the last item in the left list. If index is greater
|
||||
// then the length of this list, only the left skiplist is returned
|
||||
// and the right will be nil. This is a mutable operation and modifies
|
||||
// the content of this list.
|
||||
func (sl *SkipList) SplitAt(index uint64) (*SkipList, *SkipList) {
|
||||
index++ // 0-index offset
|
||||
if index >= sl.Len() {
|
||||
return sl, nil
|
||||
}
|
||||
return splitAt(sl, index)
|
||||
}
|
||||
|
||||
// New will allocate, initialize, and return a new skiplist.
|
||||
// The provided parameter should be of type uint and will determine
|
||||
// the maximum possible level that will be created to ensure
|
||||
// a random and quick distribution of levels. Parameter must
|
||||
// be a uint type.
|
||||
func New(ifc interface{}) *SkipList {
|
||||
sl := &SkipList{}
|
||||
sl.init(ifc)
|
||||
return sl
|
||||
}
|
||||
Reference in New Issue
Block a user