File: pool.go

package info (click to toggle)
golang-github-bodgit-sevenzip 1.6.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,208 kB
  • sloc: makefile: 5
file content (138 lines) | stat: -rw-r--r-- 2,904 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Package pool implements the reader pooling.
package pool

import (
	"container/list"
	"runtime"
	"sort"
	"sync"

	"github.com/bodgit/sevenzip/internal/util"
)

// Pooler is the interface implemented by a pool.
type Pooler interface {
	Get(offset int64) (util.SizeReadSeekCloser, bool)
	Put(offset int64, rc util.SizeReadSeekCloser) (bool, error)
}

// Constructor is the function prototype used to instantiate a pool.
type Constructor func() (Pooler, error)

type noopPool struct{}

// NewNoopPool returns a Pooler that doesn't actually pool anything.
func NewNoopPool() (Pooler, error) {
	return new(noopPool), nil
}

func (noopPool) Get(_ int64) (util.SizeReadSeekCloser, bool) {
	return nil, false
}

func (noopPool) Put(_ int64, rc util.SizeReadSeekCloser) (bool, error) {
	return false, rc.Close() //nolint:wrapcheck
}

type pool struct {
	mutex     sync.Mutex
	size      int
	evictList *list.List
	items     map[int64]*list.Element
}

type entry struct {
	key   int64
	value util.SizeReadSeekCloser
}

// NewPool returns a Pooler that uses a LRU strategy to maintain a fixed pool
// of util.SizeReadSeekCloser's keyed by their stream offset.
func NewPool() (Pooler, error) {
	return &pool{
		size:      runtime.NumCPU(),
		evictList: list.New(),
		items:     make(map[int64]*list.Element),
	}, nil
}

func (p *pool) Get(offset int64) (util.SizeReadSeekCloser, bool) {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if ent, ok := p.items[offset]; ok {
		_ = p.removeElement(ent, false)

		return ent.Value.(*entry).value, true //nolint:forcetypeassert
	}

	// Sort keys in descending order
	keys := p.keys()
	sort.Slice(keys, func(i, j int) bool { return keys[i] > keys[j] })

	for _, k := range keys {
		// First key less than offset is the closest
		if k < offset {
			ent := p.items[k]
			_ = p.removeElement(ent, false)

			return ent.Value.(*entry).value, true //nolint:forcetypeassert
		}
	}

	return nil, false
}

func (p *pool) Put(offset int64, rc util.SizeReadSeekCloser) (bool, error) {
	p.mutex.Lock()
	defer p.mutex.Unlock()

	if _, ok := p.items[offset]; ok {
		return false, nil
	}

	ent := &entry{offset, rc}
	entry := p.evictList.PushFront(ent)
	p.items[offset] = entry

	var err error

	evict := p.evictList.Len() > p.size
	if evict {
		err = p.removeOldest()
	}

	return evict, err
}

func (p *pool) keys() []int64 {
	keys := make([]int64, len(p.items))
	i := 0

	for ent := p.evictList.Back(); ent != nil; ent = ent.Prev() {
		keys[i] = ent.Value.(*entry).key //nolint:forcetypeassert
		i++
	}

	return keys
}

func (p *pool) removeOldest() error {
	if ent := p.evictList.Back(); ent != nil {
		return p.removeElement(ent, true)
	}

	return nil
}

func (p *pool) removeElement(e *list.Element, cb bool) error {
	p.evictList.Remove(e)
	kv := e.Value.(*entry) //nolint:forcetypeassert
	delete(p.items, kv.key)

	if cb {
		return kv.value.Close() //nolint:wrapcheck
	}

	return nil
}