1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package index
18
19 import (
20 "context"
21 "errors"
22 "fmt"
23 "strconv"
24 "strings"
25
26 "perkeep.org/pkg/blob"
27 "perkeep.org/pkg/sorted"
28 )
29
30 func (ix *Index) EnumerateBlobs(ctx context.Context, dest chan<- blob.SizedRef, after string, limit int) (err error) {
31 defer close(dest)
32 it := ix.s.Find("have:"+after, "have~")
33 defer func() {
34 closeErr := it.Close()
35 if err == nil {
36 err = closeErr
37 }
38 }()
39
40 afterKey := "have:" + after
41 n := int(0)
42 for n < limit && it.Next() {
43 k := it.Key()
44 if k <= afterKey {
45 continue
46 }
47 if !strings.HasPrefix(k, "have:") {
48 break
49 }
50 n++
51 br, ok := blob.Parse(k[len("have:"):])
52 if !ok {
53 continue
54 }
55 size, err := parseHaveVal(it.Value())
56 if err == nil {
57 select {
58 case dest <- blob.SizedRef{Ref: br, Size: uint32(size)}:
59 case <-ctx.Done():
60 return ctx.Err()
61 }
62 }
63 }
64 return nil
65 }
66
67 func (ix *Index) StatBlobs(ctx context.Context, blobs []blob.Ref, fn func(blob.SizedRef) error) error {
68 for _, br := range blobs {
69 key := "have:" + br.String()
70 v, err := ix.s.Get(key)
71 if errors.Is(err, sorted.ErrNotFound) {
72 continue
73 }
74 if err != nil {
75 return fmt.Errorf("error looking up key %q: %w", key, err)
76 }
77 size, err := parseHaveVal(v)
78 if err != nil {
79 return fmt.Errorf("invalid size for key %q = %q", key, v)
80 }
81 if err := fn(blob.SizedRef{Ref: br, Size: uint32(size)}); err != nil {
82 return err
83 }
84 }
85 return nil
86 }
87
88
89
90
91
92 func parseHaveVal(val string) (size uint64, err error) {
93 pipei := strings.Index(val, "|")
94 if pipei >= 0 {
95
96 val = val[:pipei]
97 }
98 return strconv.ParseUint(val, 10, 32)
99 }