Browse code

Move truncindex in separate package in pkg/

Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)

LK4D4 authored on 2014/06/25 02:19:15
Showing 6 changed files
... ...
@@ -31,6 +31,7 @@ import (
31 31
 	"github.com/dotcloud/docker/pkg/namesgenerator"
32 32
 	"github.com/dotcloud/docker/pkg/networkfs/resolvconf"
33 33
 	"github.com/dotcloud/docker/pkg/sysinfo"
34
+	"github.com/dotcloud/docker/pkg/truncindex"
34 35
 	"github.com/dotcloud/docker/runconfig"
35 36
 	"github.com/dotcloud/docker/utils"
36 37
 )
... ...
@@ -87,7 +88,7 @@ type Daemon struct {
87 87
 	containers     *contStore
88 88
 	graph          *graph.Graph
89 89
 	repositories   *graph.TagStore
90
-	idIndex        *utils.TruncIndex
90
+	idIndex        *truncindex.TruncIndex
91 91
 	sysInfo        *sysinfo.SysInfo
92 92
 	volumes        *graph.Graph
93 93
 	srv            Server
... ...
@@ -869,7 +870,7 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D
869 869
 		containers:     &contStore{s: make(map[string]*Container)},
870 870
 		graph:          g,
871 871
 		repositories:   repositories,
872
-		idIndex:        utils.NewTruncIndex([]string{}),
872
+		idIndex:        truncindex.NewTruncIndex([]string{}),
873 873
 		sysInfo:        sysInfo,
874 874
 		volumes:        volumes,
875 875
 		config:         config,
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	"github.com/dotcloud/docker/daemon/graphdriver"
17 17
 	"github.com/dotcloud/docker/dockerversion"
18 18
 	"github.com/dotcloud/docker/image"
19
+	"github.com/dotcloud/docker/pkg/truncindex"
19 20
 	"github.com/dotcloud/docker/runconfig"
20 21
 	"github.com/dotcloud/docker/utils"
21 22
 )
... ...
@@ -23,7 +24,7 @@ import (
23 23
 // A Graph is a store for versioned filesystem images and the relationship between them.
24 24
 type Graph struct {
25 25
 	Root    string
26
-	idIndex *utils.TruncIndex
26
+	idIndex *truncindex.TruncIndex
27 27
 	driver  graphdriver.Driver
28 28
 }
29 29
 
... ...
@@ -41,7 +42,7 @@ func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
41 41
 
42 42
 	graph := &Graph{
43 43
 		Root:    abspath,
44
-		idIndex: utils.NewTruncIndex([]string{}),
44
+		idIndex: truncindex.NewTruncIndex([]string{}),
45 45
 		driver:  driver,
46 46
 	}
47 47
 	if err := graph.restore(); err != nil {
... ...
@@ -62,7 +63,7 @@ func (graph *Graph) restore() error {
62 62
 			ids = append(ids, id)
63 63
 		}
64 64
 	}
65
-	graph.idIndex = utils.NewTruncIndex(ids)
65
+	graph.idIndex = truncindex.NewTruncIndex(ids)
66 66
 	utils.Debugf("Restored %d elements", len(dir))
67 67
 	return nil
68 68
 }
69 69
new file mode 100644
... ...
@@ -0,0 +1,102 @@
0
+package truncindex
1
+
2
+import (
3
+	"fmt"
4
+	"index/suffixarray"
5
+	"strings"
6
+	"sync"
7
+)
8
+
9
+// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
10
+// This is used to retrieve image and container IDs by more convenient shorthand prefixes.
11
+type TruncIndex struct {
12
+	sync.RWMutex
13
+	index *suffixarray.Index
14
+	ids   map[string]bool
15
+	bytes []byte
16
+}
17
+
18
+func NewTruncIndex(ids []string) (idx *TruncIndex) {
19
+	idx = &TruncIndex{
20
+		ids:   make(map[string]bool),
21
+		bytes: []byte{' '},
22
+	}
23
+	for _, id := range ids {
24
+		idx.ids[id] = true
25
+		idx.bytes = append(idx.bytes, []byte(id+" ")...)
26
+	}
27
+	idx.index = suffixarray.New(idx.bytes)
28
+	return
29
+}
30
+
31
+func (idx *TruncIndex) addId(id string) error {
32
+	if strings.Contains(id, " ") {
33
+		return fmt.Errorf("Illegal character: ' '")
34
+	}
35
+	if _, exists := idx.ids[id]; exists {
36
+		return fmt.Errorf("Id already exists: %s", id)
37
+	}
38
+	idx.ids[id] = true
39
+	idx.bytes = append(idx.bytes, []byte(id+" ")...)
40
+	return nil
41
+}
42
+
43
+func (idx *TruncIndex) Add(id string) error {
44
+	idx.Lock()
45
+	defer idx.Unlock()
46
+	if err := idx.addId(id); err != nil {
47
+		return err
48
+	}
49
+	idx.index = suffixarray.New(idx.bytes)
50
+	return nil
51
+}
52
+
53
+func (idx *TruncIndex) AddWithoutSuffixarrayUpdate(id string) error {
54
+	idx.Lock()
55
+	defer idx.Unlock()
56
+	return idx.addId(id)
57
+}
58
+
59
+func (idx *TruncIndex) UpdateSuffixarray() {
60
+	idx.Lock()
61
+	defer idx.Unlock()
62
+	idx.index = suffixarray.New(idx.bytes)
63
+}
64
+
65
+func (idx *TruncIndex) Delete(id string) error {
66
+	idx.Lock()
67
+	defer idx.Unlock()
68
+	if _, exists := idx.ids[id]; !exists {
69
+		return fmt.Errorf("No such id: %s", id)
70
+	}
71
+	before, after, err := idx.lookup(id)
72
+	if err != nil {
73
+		return err
74
+	}
75
+	delete(idx.ids, id)
76
+	idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...)
77
+	idx.index = suffixarray.New(idx.bytes)
78
+	return nil
79
+}
80
+
81
+func (idx *TruncIndex) lookup(s string) (int, int, error) {
82
+	offsets := idx.index.Lookup([]byte(" "+s), -1)
83
+	//log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
84
+	if offsets == nil || len(offsets) == 0 || len(offsets) > 1 {
85
+		return -1, -1, fmt.Errorf("No such id: %s", s)
86
+	}
87
+	offsetBefore := offsets[0] + 1
88
+	offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ")
89
+	return offsetBefore, offsetAfter, nil
90
+}
91
+
92
+func (idx *TruncIndex) Get(s string) (string, error) {
93
+	idx.RLock()
94
+	defer idx.RUnlock()
95
+	before, after, err := idx.lookup(s)
96
+	//log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
97
+	if err != nil {
98
+		return "", err
99
+	}
100
+	return string(idx.bytes[before:after]), err
101
+}
0 102
new file mode 100644
... ...
@@ -0,0 +1,105 @@
0
+package truncindex
1
+
2
+import "testing"
3
+
4
+// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix.
5
+func TestTruncIndex(t *testing.T) {
6
+	ids := []string{}
7
+	index := NewTruncIndex(ids)
8
+	// Get on an empty index
9
+	if _, err := index.Get("foobar"); err == nil {
10
+		t.Fatal("Get on an empty index should return an error")
11
+	}
12
+
13
+	// Spaces should be illegal in an id
14
+	if err := index.Add("I have a space"); err == nil {
15
+		t.Fatalf("Adding an id with ' ' should return an error")
16
+	}
17
+
18
+	id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96"
19
+	// Add an id
20
+	if err := index.Add(id); err != nil {
21
+		t.Fatal(err)
22
+	}
23
+	// Get a non-existing id
24
+	assertIndexGet(t, index, "abracadabra", "", true)
25
+	// Get the exact id
26
+	assertIndexGet(t, index, id, id, false)
27
+	// The first letter should match
28
+	assertIndexGet(t, index, id[:1], id, false)
29
+	// The first half should match
30
+	assertIndexGet(t, index, id[:len(id)/2], id, false)
31
+	// The second half should NOT match
32
+	assertIndexGet(t, index, id[len(id)/2:], "", true)
33
+
34
+	id2 := id[:6] + "blabla"
35
+	// Add an id
36
+	if err := index.Add(id2); err != nil {
37
+		t.Fatal(err)
38
+	}
39
+	// Both exact IDs should work
40
+	assertIndexGet(t, index, id, id, false)
41
+	assertIndexGet(t, index, id2, id2, false)
42
+
43
+	// 6 characters or less should conflict
44
+	assertIndexGet(t, index, id[:6], "", true)
45
+	assertIndexGet(t, index, id[:4], "", true)
46
+	assertIndexGet(t, index, id[:1], "", true)
47
+
48
+	// 7 characters should NOT conflict
49
+	assertIndexGet(t, index, id[:7], id, false)
50
+	assertIndexGet(t, index, id2[:7], id2, false)
51
+
52
+	// Deleting a non-existing id should return an error
53
+	if err := index.Delete("non-existing"); err == nil {
54
+		t.Fatalf("Deleting a non-existing id should return an error")
55
+	}
56
+
57
+	// Deleting id2 should remove conflicts
58
+	if err := index.Delete(id2); err != nil {
59
+		t.Fatal(err)
60
+	}
61
+	// id2 should no longer work
62
+	assertIndexGet(t, index, id2, "", true)
63
+	assertIndexGet(t, index, id2[:7], "", true)
64
+	assertIndexGet(t, index, id2[:11], "", true)
65
+
66
+	// conflicts between id and id2 should be gone
67
+	assertIndexGet(t, index, id[:6], id, false)
68
+	assertIndexGet(t, index, id[:4], id, false)
69
+	assertIndexGet(t, index, id[:1], id, false)
70
+
71
+	// non-conflicting substrings should still not conflict
72
+	assertIndexGet(t, index, id[:7], id, false)
73
+	assertIndexGet(t, index, id[:15], id, false)
74
+	assertIndexGet(t, index, id, id, false)
75
+}
76
+
77
+func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) {
78
+	if result, err := index.Get(input); err != nil && !expectError {
79
+		t.Fatalf("Unexpected error getting '%s': %s", input, err)
80
+	} else if err == nil && expectError {
81
+		t.Fatalf("Getting '%s' should return an error", input)
82
+	} else if result != expectedResult {
83
+		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
84
+	}
85
+}
86
+
87
+func BenchmarkTruncIndexAdd(b *testing.B) {
88
+	ids := []string{"banana", "bananaa", "bananab"}
89
+	b.ResetTimer()
90
+	for i := 0; i < b.N; i++ {
91
+		index := NewTruncIndex([]string{})
92
+		for _, id := range ids {
93
+			index.Add(id)
94
+		}
95
+	}
96
+}
97
+
98
+func BenchmarkTruncIndexNew(b *testing.B) {
99
+	ids := []string{"banana", "bananaa", "bananab"}
100
+	b.ResetTimer()
101
+	for i := 0; i < b.N; i++ {
102
+		NewTruncIndex(ids)
103
+	}
104
+}
... ...
@@ -9,7 +9,6 @@ import (
9 9
 	"encoding/json"
10 10
 	"errors"
11 11
 	"fmt"
12
-	"index/suffixarray"
13 12
 	"io"
14 13
 	"io/ioutil"
15 14
 	"net/http"
... ...
@@ -397,100 +396,6 @@ func GetTotalUsedFds() int {
397 397
 	return -1
398 398
 }
399 399
 
400
-// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
401
-// This is used to retrieve image and container IDs by more convenient shorthand prefixes.
402
-type TruncIndex struct {
403
-	sync.RWMutex
404
-	index *suffixarray.Index
405
-	ids   map[string]bool
406
-	bytes []byte
407
-}
408
-
409
-func NewTruncIndex(ids []string) (idx *TruncIndex) {
410
-	idx = &TruncIndex{
411
-		ids:   make(map[string]bool),
412
-		bytes: []byte{' '},
413
-	}
414
-	for _, id := range ids {
415
-		idx.ids[id] = true
416
-		idx.bytes = append(idx.bytes, []byte(id+" ")...)
417
-	}
418
-	idx.index = suffixarray.New(idx.bytes)
419
-	return
420
-}
421
-
422
-func (idx *TruncIndex) addId(id string) error {
423
-	if strings.Contains(id, " ") {
424
-		return fmt.Errorf("Illegal character: ' '")
425
-	}
426
-	if _, exists := idx.ids[id]; exists {
427
-		return fmt.Errorf("Id already exists: %s", id)
428
-	}
429
-	idx.ids[id] = true
430
-	idx.bytes = append(idx.bytes, []byte(id+" ")...)
431
-	return nil
432
-}
433
-
434
-func (idx *TruncIndex) Add(id string) error {
435
-	idx.Lock()
436
-	defer idx.Unlock()
437
-	if err := idx.addId(id); err != nil {
438
-		return err
439
-	}
440
-	idx.index = suffixarray.New(idx.bytes)
441
-	return nil
442
-}
443
-
444
-func (idx *TruncIndex) AddWithoutSuffixarrayUpdate(id string) error {
445
-	idx.Lock()
446
-	defer idx.Unlock()
447
-	return idx.addId(id)
448
-}
449
-
450
-func (idx *TruncIndex) UpdateSuffixarray() {
451
-	idx.Lock()
452
-	defer idx.Unlock()
453
-	idx.index = suffixarray.New(idx.bytes)
454
-}
455
-
456
-func (idx *TruncIndex) Delete(id string) error {
457
-	idx.Lock()
458
-	defer idx.Unlock()
459
-	if _, exists := idx.ids[id]; !exists {
460
-		return fmt.Errorf("No such id: %s", id)
461
-	}
462
-	before, after, err := idx.lookup(id)
463
-	if err != nil {
464
-		return err
465
-	}
466
-	delete(idx.ids, id)
467
-	idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...)
468
-	idx.index = suffixarray.New(idx.bytes)
469
-	return nil
470
-}
471
-
472
-func (idx *TruncIndex) lookup(s string) (int, int, error) {
473
-	offsets := idx.index.Lookup([]byte(" "+s), -1)
474
-	//log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
475
-	if offsets == nil || len(offsets) == 0 || len(offsets) > 1 {
476
-		return -1, -1, fmt.Errorf("No such id: %s", s)
477
-	}
478
-	offsetBefore := offsets[0] + 1
479
-	offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ")
480
-	return offsetBefore, offsetAfter, nil
481
-}
482
-
483
-func (idx *TruncIndex) Get(s string) (string, error) {
484
-	idx.RLock()
485
-	defer idx.RUnlock()
486
-	before, after, err := idx.lookup(s)
487
-	//log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
488
-	if err != nil {
489
-		return "", err
490
-	}
491
-	return string(idx.bytes[before:after]), err
492
-}
493
-
494 400
 // TruncateID returns a shorthand version of a string identifier for convenience.
495 401
 // A collision with other shorthands is very unlikely, but possible.
496 402
 // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller
... ...
@@ -135,108 +135,6 @@ func TestRaceWriteBroadcaster(t *testing.T) {
135 135
 	<-c
136 136
 }
137 137
 
138
-// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix.
139
-func TestTruncIndex(t *testing.T) {
140
-	ids := []string{}
141
-	index := NewTruncIndex(ids)
142
-	// Get on an empty index
143
-	if _, err := index.Get("foobar"); err == nil {
144
-		t.Fatal("Get on an empty index should return an error")
145
-	}
146
-
147
-	// Spaces should be illegal in an id
148
-	if err := index.Add("I have a space"); err == nil {
149
-		t.Fatalf("Adding an id with ' ' should return an error")
150
-	}
151
-
152
-	id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96"
153
-	// Add an id
154
-	if err := index.Add(id); err != nil {
155
-		t.Fatal(err)
156
-	}
157
-	// Get a non-existing id
158
-	assertIndexGet(t, index, "abracadabra", "", true)
159
-	// Get the exact id
160
-	assertIndexGet(t, index, id, id, false)
161
-	// The first letter should match
162
-	assertIndexGet(t, index, id[:1], id, false)
163
-	// The first half should match
164
-	assertIndexGet(t, index, id[:len(id)/2], id, false)
165
-	// The second half should NOT match
166
-	assertIndexGet(t, index, id[len(id)/2:], "", true)
167
-
168
-	id2 := id[:6] + "blabla"
169
-	// Add an id
170
-	if err := index.Add(id2); err != nil {
171
-		t.Fatal(err)
172
-	}
173
-	// Both exact IDs should work
174
-	assertIndexGet(t, index, id, id, false)
175
-	assertIndexGet(t, index, id2, id2, false)
176
-
177
-	// 6 characters or less should conflict
178
-	assertIndexGet(t, index, id[:6], "", true)
179
-	assertIndexGet(t, index, id[:4], "", true)
180
-	assertIndexGet(t, index, id[:1], "", true)
181
-
182
-	// 7 characters should NOT conflict
183
-	assertIndexGet(t, index, id[:7], id, false)
184
-	assertIndexGet(t, index, id2[:7], id2, false)
185
-
186
-	// Deleting a non-existing id should return an error
187
-	if err := index.Delete("non-existing"); err == nil {
188
-		t.Fatalf("Deleting a non-existing id should return an error")
189
-	}
190
-
191
-	// Deleting id2 should remove conflicts
192
-	if err := index.Delete(id2); err != nil {
193
-		t.Fatal(err)
194
-	}
195
-	// id2 should no longer work
196
-	assertIndexGet(t, index, id2, "", true)
197
-	assertIndexGet(t, index, id2[:7], "", true)
198
-	assertIndexGet(t, index, id2[:11], "", true)
199
-
200
-	// conflicts between id and id2 should be gone
201
-	assertIndexGet(t, index, id[:6], id, false)
202
-	assertIndexGet(t, index, id[:4], id, false)
203
-	assertIndexGet(t, index, id[:1], id, false)
204
-
205
-	// non-conflicting substrings should still not conflict
206
-	assertIndexGet(t, index, id[:7], id, false)
207
-	assertIndexGet(t, index, id[:15], id, false)
208
-	assertIndexGet(t, index, id, id, false)
209
-}
210
-
211
-func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) {
212
-	if result, err := index.Get(input); err != nil && !expectError {
213
-		t.Fatalf("Unexpected error getting '%s': %s", input, err)
214
-	} else if err == nil && expectError {
215
-		t.Fatalf("Getting '%s' should return an error", input)
216
-	} else if result != expectedResult {
217
-		t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
218
-	}
219
-}
220
-
221
-func BenchmarkTruncIndexAdd(b *testing.B) {
222
-	ids := []string{"banana", "bananaa", "bananab"}
223
-	b.ResetTimer()
224
-	for i := 0; i < b.N; i++ {
225
-		index := NewTruncIndex([]string{})
226
-		for _, id := range ids {
227
-			index.Add(id)
228
-		}
229
-	}
230
-}
231
-
232
-func BenchmarkTruncIndexNew(b *testing.B) {
233
-	ids := []string{"banana", "bananaa", "bananab"}
234
-	b.ResetTimer()
235
-	for i := 0; i < b.N; i++ {
236
-		NewTruncIndex(ids)
237
-	}
238
-}
239
-
240 138
 func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
241 139
 	if r := CompareKernelVersion(a, b); r != result {
242 140
 		t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)