1package deb 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 9 "github.com/aptly-dev/aptly/database" 10 "github.com/pborman/uuid" 11) 12 13// ContentsIndex calculates mapping from files to packages, with sorting and aggregation 14type ContentsIndex struct { 15 db database.Storage 16 prefix []byte 17} 18 19// NewContentsIndex creates empty ContentsIndex 20func NewContentsIndex(db database.Storage) *ContentsIndex { 21 return &ContentsIndex{ 22 db: db, 23 prefix: []byte(uuid.New()), 24 } 25} 26 27// Push adds package to contents index, calculating package contents as required 28func (index *ContentsIndex) Push(qualifiedName []byte, contents []string) error { 29 for _, path := range contents { 30 // for performance reasons we only write to leveldb during push. 31 // merging of qualified names per path will be done in WriteTo 32 err := index.db.Put(append(append(append(index.prefix, []byte(path)...), byte(0)), qualifiedName...), nil) 33 if err != nil { 34 return err 35 } 36 } 37 38 return nil 39} 40 41// Empty checks whether index contains no packages 42func (index *ContentsIndex) Empty() bool { 43 return !index.db.HasPrefix(index.prefix) 44} 45 46// WriteTo dumps sorted mapping of files to qualified package names 47func (index *ContentsIndex) WriteTo(w io.Writer) (int64, error) { 48 // For performance reasons push method wrote on key per path and package 49 // in this method we now need to merge all packages which have the same path 50 // and write it to contents index file 51 52 var n int64 53 54 nn, err := fmt.Fprintf(w, "%s %s\n", "FILE", "LOCATION") 55 n += int64(nn) 56 if err != nil { 57 return n, err 58 } 59 60 prefixLen := len(index.prefix) 61 62 var ( 63 currentPath []byte 64 currentPkgs [][]byte 65 ) 66 67 err = index.db.ProcessByPrefix(index.prefix, func(key []byte, value []byte) error { 68 // cut prefix 69 key = key[prefixLen:] 70 71 i := bytes.Index(key, []byte{0}) 72 if i == -1 { 73 return errors.New("corrupted index entry") 74 } 75 76 path := key[:i] 77 pkg := key[i+1:] 78 79 if !bytes.Equal(path, currentPath) { 80 if currentPath != nil { 81 nn, err = w.Write(append(currentPath, ' ')) 82 n += int64(nn) 83 if err != nil { 84 return err 85 } 86 87 nn, err = w.Write(bytes.Join(currentPkgs, []byte{','})) 88 n += int64(nn) 89 if err != nil { 90 return err 91 } 92 93 nn, err = w.Write([]byte{'\n'}) 94 n += int64(nn) 95 if err != nil { 96 return err 97 } 98 } 99 100 currentPath = append([]byte(nil), path...) 101 currentPkgs = nil 102 } 103 104 currentPkgs = append(currentPkgs, append([]byte(nil), pkg...)) 105 106 return nil 107 }) 108 109 if err != nil { 110 return n, err 111 } 112 113 if currentPath != nil { 114 nn, err = w.Write(append(currentPath, ' ')) 115 n += int64(nn) 116 if err != nil { 117 return n, err 118 } 119 120 nn, err = w.Write(bytes.Join(currentPkgs, []byte{','})) 121 n += int64(nn) 122 if err != nil { 123 return n, err 124 } 125 126 nn, err = w.Write([]byte{'\n'}) 127 n += int64(nn) 128 } 129 130 return n, err 131} 132