imposm3/cache/delta.go

202 lines
4.5 KiB
Go
Raw Normal View History

package cache
import (
2013-04-15 23:54:48 +04:00
"code.google.com/p/goprotobuf/proto"
2013-04-14 16:50:38 +04:00
"container/list"
2013-04-15 23:54:48 +04:00
bin "encoding/binary"
"goposm/binary"
"goposm/element"
2013-04-14 16:50:38 +04:00
"sync"
)
2013-04-15 23:54:48 +04:00
func packNodes(nodes map[int64]element.Node) *DeltaCoords {
var lastLon, lastLat int64
var lon, lat int64
2013-04-15 23:54:48 +04:00
var lastId int64
ids := make([]int64, len(nodes))
lons := make([]int64, len(nodes))
lats := make([]int64, len(nodes))
2013-04-15 23:54:48 +04:00
i := 0
for id, nd := range nodes {
lon = int64(binary.CoordToInt(nd.Long))
lat = int64(binary.CoordToInt(nd.Lat))
ids[i] = id - lastId
lons[i] = lon - lastLon
lats[i] = lat - lastLat
lastId = id
lastLon = lon
lastLat = lat
2013-04-15 23:54:48 +04:00
i++
}
return &DeltaCoords{Ids: ids, Lats: lats, Lons: lons}
}
2013-04-14 16:50:38 +04:00
2013-04-15 23:54:48 +04:00
func unpackNodes(deltaCoords *DeltaCoords) map[int64]element.Node {
nodes := make(map[int64]element.Node, len(deltaCoords.Ids))
2013-04-14 16:50:38 +04:00
var lastLon, lastLat int64
var lon, lat int64
var lastId, id int64
2013-04-15 23:54:48 +04:00
for i := 0; i < len(deltaCoords.Ids); i++ {
2013-04-14 16:50:38 +04:00
id = lastId + deltaCoords.Ids[i]
lon = lastLon + deltaCoords.Lats[i]
lat = lastLat + deltaCoords.Lons[i]
2013-04-15 23:54:48 +04:00
nodes[id] = element.Node{
OSMElem: element.OSMElem{Id: int64(id)},
Long: binary.IntToCoord(uint32(lon)),
Lat: binary.IntToCoord(uint32(lat)),
}
2013-04-14 16:50:38 +04:00
lastId = id
lastLon = lon
lastLat = lat
}
return nodes
}
2013-04-15 23:54:48 +04:00
type CoordsBunch struct {
sync.Mutex
id int64
coords map[int64]element.Node
elem *list.Element
needsWrite bool
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
type DeltaCoordsCache struct {
Cache
lruList *list.List
table map[int64]*CoordsBunch
capacity int64
mu sync.Mutex
2013-04-14 16:50:38 +04:00
}
2013-04-16 23:14:19 +04:00
func NewDeltaCoordsCache(path string) (*DeltaCoordsCache, error) {
2013-04-15 23:54:48 +04:00
coordsCache := DeltaCoordsCache{}
2013-04-16 23:14:19 +04:00
err := coordsCache.open(path)
if err != nil {
return nil, err
}
2013-04-15 23:54:48 +04:00
coordsCache.lruList = list.New()
coordsCache.table = make(map[int64]*CoordsBunch)
coordsCache.capacity = 100
2013-04-16 23:14:19 +04:00
return &coordsCache, nil
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
func (self *DeltaCoordsCache) Close() {
for getBunchId, bunch := range self.table {
if bunch.needsWrite {
self.putCoordsPacked(getBunchId, bunch.coords)
}
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
self.Cache.Close()
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
func (self *DeltaCoordsCache) GetCoord(id int64) (element.Node, bool) {
getBunchId := getBunchId(id)
bunch := self.getBunch(getBunchId)
defer bunch.Unlock()
node, ok := bunch.coords[id]
if !ok {
return element.Node{}, false
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
return node, true
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
func (self *DeltaCoordsCache) PutCoords(nodes []element.Node) {
var start, currentgetBunchId int64
currentgetBunchId = getBunchId(nodes[0].Id)
start = 0
for i, node := range nodes {
getBunchId := getBunchId(node.Id)
if getBunchId != currentgetBunchId {
bunch := self.getBunch(currentgetBunchId)
for _, nd := range nodes[start : i-1] {
bunch.coords[nd.Id] = nd
}
currentgetBunchId = getBunchId
start = int64(i)
bunch.needsWrite = true
bunch.Unlock()
}
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
bunch := self.getBunch(currentgetBunchId)
for _, nd := range nodes[start:] {
bunch.coords[nd.Id] = nd
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
bunch.needsWrite = true
bunch.Unlock()
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
func (p *DeltaCoordsCache) putCoordsPacked(getBunchId int64, nodes map[int64]element.Node) {
if len(nodes) == 0 {
return
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
keyBuf := make([]byte, 8)
bin.PutVarint(keyBuf, getBunchId)
2013-04-14 16:50:38 +04:00
2013-04-15 23:54:48 +04:00
deltaCoords := packNodes(nodes)
data, err := proto.Marshal(deltaCoords)
if err != nil {
panic(err)
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
p.db.Put(p.wo, keyBuf, data)
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
func (p *DeltaCoordsCache) getCoordsPacked(getBunchId int64) map[int64]element.Node {
keyBuf := make([]byte, 8)
bin.PutVarint(keyBuf, getBunchId)
2013-04-14 16:50:38 +04:00
2013-04-15 23:54:48 +04:00
data, err := p.db.Get(p.ro, keyBuf)
if err != nil {
panic(err)
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
deltaCoords := &DeltaCoords{}
err = proto.Unmarshal(data, deltaCoords)
if err != nil {
panic(err)
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
nodes := unpackNodes(deltaCoords)
return nodes
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
func getBunchId(nodeId int64) int64 {
return nodeId / (1024 * 32)
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
func (self *DeltaCoordsCache) getBunch(getBunchId int64) *CoordsBunch {
self.mu.Lock()
defer self.mu.Unlock()
bunch, ok := self.table[getBunchId]
2013-04-14 16:50:38 +04:00
if !ok {
2013-04-15 23:54:48 +04:00
elem := self.lruList.PushFront(getBunchId)
nodes := self.getCoordsPacked(getBunchId)
2013-04-14 16:50:38 +04:00
if nodes == nil {
2013-04-15 23:54:48 +04:00
bunch = &CoordsBunch{elem: elem}
2013-04-14 16:50:38 +04:00
} else {
2013-04-15 23:54:48 +04:00
bunch = &CoordsBunch{id: getBunchId, coords: nodes, elem: elem}
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
self.table[getBunchId] = bunch
} else {
self.lruList.MoveToFront(bunch.elem)
2013-04-14 16:50:38 +04:00
}
2013-04-15 23:54:48 +04:00
bunch.Lock()
self.CheckCapacity()
2013-04-14 16:50:38 +04:00
return bunch
}
2013-04-15 23:54:48 +04:00
func (self *DeltaCoordsCache) CheckCapacity() {
for int64(len(self.table)) > self.capacity {
elem := self.lruList.Back()
getBunchId := self.lruList.Remove(elem).(int64)
bunch := self.table[getBunchId]
if bunch.needsWrite {
self.putCoordsPacked(getBunchId, bunch.coords)
}
delete(self.table, getBunchId)
}
}