Skip to content

Commit a1f8cff

Browse files
committed
internal/idm: drop libnetwork dependency
Rewrite it in terms of github.com/bits-and-blooms/bitset, an uncompressed bitset data structure. It is only used by portallocator, which requires a maximum set length of 65535, so using compressed bitmaps would be a premature optimization. Signed-off-by: Cory Snider <[email protected]>
1 parent 4b261ae commit a1f8cff

21 files changed

+1917
-163
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
code.cloudfoundry.org/clock v1.1.0
77
github.com/Microsoft/go-winio v0.6.1
88
github.com/akutz/memconn v0.1.0
9+
github.com/bits-and-blooms/bitset v1.13.0
910
github.com/cloudflare/cfssl v1.6.4
1011
github.com/container-storage-interface/spec v1.2.0
1112
github.com/distribution/reference v0.5.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
3939
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
4040
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
4141
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
42+
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
43+
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
4244
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
4345
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
4446
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=

internal/idm/idm.go

+45-30
Original file line numberDiff line numberDiff line change
@@ -5,65 +5,80 @@ import (
55
"errors"
66
"fmt"
77

8-
"github.com/docker/docker/libnetwork/bitmap"
8+
"github.com/bits-and-blooms/bitset"
9+
)
10+
11+
var (
12+
// ErrNoBitAvailable is returned when no more bits are available to set
13+
ErrNoBitAvailable = errors.New("no bit available")
14+
// ErrBitAllocated is returned when the specific bit requested is already set
15+
ErrBitAllocated = errors.New("requested bit is already allocated")
916
)
1017

1118
// IDM manages the reservation/release of numerical ids from a contiguous set.
1219
//
1320
// An IDM instance is not safe for concurrent use.
1421
type IDM struct {
15-
start uint64
16-
end uint64
17-
handle *bitmap.Bitmap
22+
start, end uint
23+
set *bitset.BitSet
24+
next uint // index of the bit to start searching for the next serial allocation from (not offset by start)
1825
}
1926

2027
// New returns an instance of id manager for a [start,end] set of numerical ids.
21-
func New(start, end uint64) (*IDM, error) {
28+
func New(start, end uint) (*IDM, error) {
2229
if end <= start {
2330
return nil, fmt.Errorf("invalid set range: [%d, %d]", start, end)
2431
}
2532

26-
return &IDM{start: start, end: end, handle: bitmap.New(1 + end - start)}, nil
33+
return &IDM{start: start, end: end, set: bitset.New(1 + end - start)}, nil
2734
}
2835

2936
// GetID returns the first available id in the set.
30-
func (i *IDM) GetID(serial bool) (uint64, error) {
31-
if i.handle == nil {
37+
func (i *IDM) GetID(serial bool) (uint, error) {
38+
if i.set == nil {
3239
return 0, errors.New("ID set is not initialized")
3340
}
34-
ordinal, err := i.handle.SetAny(serial)
35-
return i.start + ordinal, err
41+
var (
42+
ordinal uint
43+
ok bool
44+
)
45+
if serial && i.next != 0 {
46+
ordinal, ok = i.set.NextClear(i.next)
47+
if ok {
48+
goto found
49+
}
50+
}
51+
ordinal, ok = i.set.NextClear(0)
52+
if !ok {
53+
return 0, ErrNoBitAvailable
54+
}
55+
56+
found:
57+
i.set.Set(ordinal)
58+
i.next = ordinal + 1
59+
if i.next > i.end-i.start {
60+
i.next = 0
61+
}
62+
return i.start + ordinal, nil
3663
}
3764

3865
// GetSpecificID tries to reserve the specified id.
39-
func (i *IDM) GetSpecificID(id uint64) error {
40-
if i.handle == nil {
66+
func (i *IDM) GetSpecificID(id uint) error {
67+
if i.set == nil {
4168
return errors.New("ID set is not initialized")
4269
}
4370

4471
if id < i.start || id > i.end {
4572
return errors.New("requested id does not belong to the set")
4673
}
47-
48-
return i.handle.Set(id - i.start)
49-
}
50-
51-
// GetIDInRange returns the first available id in the set within a [start,end] range.
52-
func (i *IDM) GetIDInRange(start, end uint64, serial bool) (uint64, error) {
53-
if i.handle == nil {
54-
return 0, errors.New("ID set is not initialized")
55-
}
56-
57-
if start < i.start || end > i.end {
58-
return 0, errors.New("requested range does not belong to the set")
74+
if i.set.Test(id - i.start) {
75+
return ErrBitAllocated
5976
}
60-
61-
ordinal, err := i.handle.SetAnyInRange(start-i.start, end-i.start, serial)
62-
63-
return i.start + ordinal, err
77+
i.set.Set(id - i.start)
78+
return nil
6479
}
6580

6681
// Release releases the specified id.
67-
func (i *IDM) Release(id uint64) {
68-
i.handle.Unset(id - i.start)
82+
func (i *IDM) Release(id uint) {
83+
i.set.Clear(id - i.start)
6984
}

internal/idm/idm_test.go

+2-128
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ func TestNew(t *testing.T) {
99
if err != nil {
1010
t.Errorf("idm.New(0, 10) error = %v", err)
1111
}
12-
if i.handle == nil {
12+
if i.set == nil {
1313
t.Error("set is not initialized")
1414
}
1515
if i.start != 0 {
@@ -60,7 +60,7 @@ func TestAllocate(t *testing.T) {
6060
t.Errorf("i.GetID(false) error = %v", err)
6161
}
6262
if o != 52 {
63-
t.Errorf("i.GetID(false) = %v, want 51", o)
63+
t.Errorf("i.GetID(false) = %v, want 52", o)
6464
}
6565

6666
o, err = i.GetID(false)
@@ -97,132 +97,6 @@ func TestUninitialized(t *testing.T) {
9797
}
9898
}
9999

100-
func TestAllocateInRange(t *testing.T) {
101-
i, err := New(5, 10)
102-
if err != nil {
103-
t.Fatal(err)
104-
}
105-
106-
o, err := i.GetIDInRange(6, 6, false)
107-
if err != nil {
108-
t.Errorf("i.GetIDInRange(6, 6, false) error = %v", err)
109-
}
110-
if o != 6 {
111-
t.Errorf("i.GetIDInRange(6, 6, false) = %d, want 6", o)
112-
}
113-
114-
if err = i.GetSpecificID(6); err == nil {
115-
t.Errorf("i.GetSpecificID(6): allocating already-allocated id should fail")
116-
}
117-
118-
o, err = i.GetID(false)
119-
if err != nil {
120-
t.Errorf("i.GetID(false) error = %v", err)
121-
}
122-
if o != 5 {
123-
t.Errorf("i.GetID(false) = %v, want 5", o)
124-
}
125-
126-
i.Release(6)
127-
128-
o, err = i.GetID(false)
129-
if err != nil {
130-
t.Errorf("i.GetID(false) error = %v", err)
131-
}
132-
if o != 6 {
133-
t.Errorf("i.GetID(false) = %v, want 6", o)
134-
}
135-
136-
for n := uint64(7); n <= 10; n++ {
137-
o, err := i.GetIDInRange(7, 10, false)
138-
if err != nil {
139-
t.Errorf("i.GetIDInRange(7, 10, false) error = %v", err)
140-
}
141-
if o != n {
142-
t.Errorf("i.GetIDInRange(7, 10, false) = %d, want %d", o, n)
143-
}
144-
}
145-
146-
if err = i.GetSpecificID(7); err == nil {
147-
t.Errorf("i.GetSpecificID(7): allocating already-allocated id should fail")
148-
}
149-
150-
if err = i.GetSpecificID(10); err == nil {
151-
t.Errorf("i.GetSpecificID(10): allocating already-allocated id should fail")
152-
}
153-
154-
i.Release(10)
155-
156-
o, err = i.GetIDInRange(5, 10, false)
157-
if err != nil {
158-
t.Errorf("i.GetIDInRange(5, 10, false) error = %v", err)
159-
}
160-
if o != 10 {
161-
t.Errorf("i.GetIDInRange(5, 10, false) = %d, want 10", o)
162-
}
163-
164-
i.Release(5)
165-
166-
o, err = i.GetIDInRange(5, 10, false)
167-
if err != nil {
168-
t.Errorf("i.GetIDInRange(5, 10, false) error = %v", err)
169-
}
170-
if o != 5 {
171-
t.Errorf("i.GetIDInRange(5, 10, false) = %d, want 5", o)
172-
}
173-
174-
for n := uint64(5); n <= 10; n++ {
175-
i.Release(n)
176-
}
177-
178-
for n := uint64(5); n <= 10; n++ {
179-
o, err := i.GetIDInRange(5, 10, false)
180-
if err != nil {
181-
t.Errorf("i.GetIDInRange(5, 10, false) error = %v", err)
182-
}
183-
if o != n {
184-
t.Errorf("i.GetIDInRange(5, 10, false) = %d, want %d", o, n)
185-
}
186-
}
187-
188-
for n := uint64(5); n <= 10; n++ {
189-
if err = i.GetSpecificID(n); err == nil {
190-
t.Errorf("i.GetSpecificID(%d): allocating already-allocated id should fail", n)
191-
}
192-
}
193-
194-
// New larger set
195-
const ul = (1 << 24) - 1
196-
i, err = New(0, ul)
197-
if err != nil {
198-
t.Fatalf("New(0, %d) error = %v", ul, err)
199-
}
200-
201-
o, err = i.GetIDInRange(4096, ul, false)
202-
if err != nil {
203-
t.Errorf("i.GetIDInRange(4096, %d, false) error = %v", ul, err)
204-
}
205-
if o != 4096 {
206-
t.Errorf("i.GetIDInRange(4096, %d, false) = %d, want 4096", ul, o)
207-
}
208-
209-
o, err = i.GetIDInRange(4096, ul, false)
210-
if err != nil {
211-
t.Errorf("i.GetIDInRange(4096, %d, false) error = %v", ul, err)
212-
}
213-
if o != 4097 {
214-
t.Errorf("i.GetIDInRange(4096, %d, false) = %d, want 4097", ul, o)
215-
}
216-
217-
o, err = i.GetIDInRange(4096, ul, false)
218-
if err != nil {
219-
t.Errorf("i.GetIDInRange(4096, %d, false) error = %v", ul, err)
220-
}
221-
if o != 4098 {
222-
t.Errorf("i.GetIDInRange(4096, %d, false) = %d, want 4098", ul, o)
223-
}
224-
}
225-
226100
func TestAllocateSerial(t *testing.T) {
227101
i, err := New(50, 55)
228102
if err != nil {

manager/allocator/portallocator.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -372,18 +372,18 @@ func (ps *portSpace) allocate(p *api.PortConfig) (err error) {
372372
// If it falls in the dynamic port range check out
373373
// from dynamic port space first.
374374
if p.PublishedPort >= dynamicPortStart && p.PublishedPort <= dynamicPortEnd {
375-
if err = ps.dynamicPortSpace.GetSpecificID(uint64(p.PublishedPort)); err != nil {
375+
if err = ps.dynamicPortSpace.GetSpecificID(uint(p.PublishedPort)); err != nil {
376376
return err
377377
}
378378

379379
defer func() {
380380
if err != nil {
381-
ps.dynamicPortSpace.Release(uint64(p.PublishedPort))
381+
ps.dynamicPortSpace.Release(uint(p.PublishedPort))
382382
}
383383
}()
384384
}
385385

386-
return ps.masterPortSpace.GetSpecificID(uint64(p.PublishedPort))
386+
return ps.masterPortSpace.GetSpecificID(uint(p.PublishedPort))
387387
}
388388

389389
// Check out an arbitrary port from dynamic port space.
@@ -408,8 +408,8 @@ func (ps *portSpace) allocate(p *api.PortConfig) (err error) {
408408

409409
func (ps *portSpace) free(p *api.PortConfig) {
410410
if p.PublishedPort >= dynamicPortStart && p.PublishedPort <= dynamicPortEnd {
411-
ps.dynamicPortSpace.Release(uint64(p.PublishedPort))
411+
ps.dynamicPortSpace.Release(uint(p.PublishedPort))
412412
}
413413

414-
ps.masterPortSpace.Release(uint64(p.PublishedPort))
414+
ps.masterPortSpace.Release(uint(p.PublishedPort))
415415
}

vendor/github.com/bits-and-blooms/bitset/.gitignore

+26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/bits-and-blooms/bitset/.travis.yml

+37
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)