mirror of
https://github.com/rclone/rclone.git
synced 2024-12-05 06:40:52 +01:00
179 lines
3.7 KiB
Go
179 lines
3.7 KiB
Go
|
package bbolt
|
||
|
|
||
|
import "sort"
|
||
|
|
||
|
// hashmapFreeCount returns count of free pages(hashmap version)
|
||
|
func (f *freelist) hashmapFreeCount() int {
|
||
|
// use the forwardmap to get the total count
|
||
|
count := 0
|
||
|
for _, size := range f.forwardMap {
|
||
|
count += int(size)
|
||
|
}
|
||
|
return count
|
||
|
}
|
||
|
|
||
|
// hashmapAllocate serves the same purpose as arrayAllocate, but use hashmap as backend
|
||
|
func (f *freelist) hashmapAllocate(txid txid, n int) pgid {
|
||
|
if n == 0 {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// if we have a exact size match just return short path
|
||
|
if bm, ok := f.freemaps[uint64(n)]; ok {
|
||
|
for pid := range bm {
|
||
|
// remove the span
|
||
|
f.delSpan(pid, uint64(n))
|
||
|
|
||
|
f.allocs[pid] = txid
|
||
|
|
||
|
for i := pgid(0); i < pgid(n); i++ {
|
||
|
delete(f.cache, pid+pgid(i))
|
||
|
}
|
||
|
return pid
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// lookup the map to find larger span
|
||
|
for size, bm := range f.freemaps {
|
||
|
if size < uint64(n) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
for pid := range bm {
|
||
|
// remove the initial
|
||
|
f.delSpan(pid, uint64(size))
|
||
|
|
||
|
f.allocs[pid] = txid
|
||
|
|
||
|
remain := size - uint64(n)
|
||
|
|
||
|
// add remain span
|
||
|
f.addSpan(pid+pgid(n), remain)
|
||
|
|
||
|
for i := pgid(0); i < pgid(n); i++ {
|
||
|
delete(f.cache, pid+pgid(i))
|
||
|
}
|
||
|
return pid
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// hashmapReadIDs reads pgids as input an initial the freelist(hashmap version)
|
||
|
func (f *freelist) hashmapReadIDs(pgids []pgid) {
|
||
|
f.init(pgids)
|
||
|
|
||
|
// Rebuild the page cache.
|
||
|
f.reindex()
|
||
|
}
|
||
|
|
||
|
// hashmapGetFreePageIDs returns the sorted free page ids
|
||
|
func (f *freelist) hashmapGetFreePageIDs() []pgid {
|
||
|
count := f.free_count()
|
||
|
if count == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
m := make([]pgid, 0, count)
|
||
|
for start, size := range f.forwardMap {
|
||
|
for i := 0; i < int(size); i++ {
|
||
|
m = append(m, start+pgid(i))
|
||
|
}
|
||
|
}
|
||
|
sort.Sort(pgids(m))
|
||
|
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
// hashmapMergeSpans try to merge list of pages(represented by pgids) with existing spans
|
||
|
func (f *freelist) hashmapMergeSpans(ids pgids) {
|
||
|
for _, id := range ids {
|
||
|
// try to see if we can merge and update
|
||
|
f.mergeWithExistingSpan(id)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mergeWithExistingSpan merges pid to the existing free spans, try to merge it backward and forward
|
||
|
func (f *freelist) mergeWithExistingSpan(pid pgid) {
|
||
|
prev := pid - 1
|
||
|
next := pid + 1
|
||
|
|
||
|
preSize, mergeWithPrev := f.backwardMap[prev]
|
||
|
nextSize, mergeWithNext := f.forwardMap[next]
|
||
|
newStart := pid
|
||
|
newSize := uint64(1)
|
||
|
|
||
|
if mergeWithPrev {
|
||
|
//merge with previous span
|
||
|
start := prev + 1 - pgid(preSize)
|
||
|
f.delSpan(start, preSize)
|
||
|
|
||
|
newStart -= pgid(preSize)
|
||
|
newSize += preSize
|
||
|
}
|
||
|
|
||
|
if mergeWithNext {
|
||
|
// merge with next span
|
||
|
f.delSpan(next, nextSize)
|
||
|
newSize += nextSize
|
||
|
}
|
||
|
|
||
|
f.addSpan(newStart, newSize)
|
||
|
}
|
||
|
|
||
|
func (f *freelist) addSpan(start pgid, size uint64) {
|
||
|
f.backwardMap[start-1+pgid(size)] = size
|
||
|
f.forwardMap[start] = size
|
||
|
if _, ok := f.freemaps[size]; !ok {
|
||
|
f.freemaps[size] = make(map[pgid]struct{})
|
||
|
}
|
||
|
|
||
|
f.freemaps[size][start] = struct{}{}
|
||
|
}
|
||
|
|
||
|
func (f *freelist) delSpan(start pgid, size uint64) {
|
||
|
delete(f.forwardMap, start)
|
||
|
delete(f.backwardMap, start+pgid(size-1))
|
||
|
delete(f.freemaps[size], start)
|
||
|
if len(f.freemaps[size]) == 0 {
|
||
|
delete(f.freemaps, size)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// initial from pgids using when use hashmap version
|
||
|
// pgids must be sorted
|
||
|
func (f *freelist) init(pgids []pgid) {
|
||
|
if len(pgids) == 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
size := uint64(1)
|
||
|
start := pgids[0]
|
||
|
|
||
|
if !sort.SliceIsSorted([]pgid(pgids), func(i, j int) bool { return pgids[i] < pgids[j] }) {
|
||
|
panic("pgids not sorted")
|
||
|
}
|
||
|
|
||
|
f.freemaps = make(map[uint64]pidSet)
|
||
|
f.forwardMap = make(map[pgid]uint64)
|
||
|
f.backwardMap = make(map[pgid]uint64)
|
||
|
|
||
|
for i := 1; i < len(pgids); i++ {
|
||
|
// continuous page
|
||
|
if pgids[i] == pgids[i-1]+1 {
|
||
|
size++
|
||
|
} else {
|
||
|
f.addSpan(start, size)
|
||
|
|
||
|
size = 1
|
||
|
start = pgids[i]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// init the tail
|
||
|
if size != 0 && start != 0 {
|
||
|
f.addSpan(start, size)
|
||
|
}
|
||
|
}
|