mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-04-20 19:38:39 +00:00
This is spin-off of changes from #459. Transactions are not being used yet, but batches are updated to work with the new API. `database/` package was refactored to split abstract interfaces and implementation via goleveldb. This should make it easier to implement new database types.
132 lines
2.8 KiB
Go
132 lines
2.8 KiB
Go
package deb
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/aptly-dev/aptly/database"
|
|
"github.com/pborman/uuid"
|
|
)
|
|
|
|
// ContentsIndex calculates mapping from files to packages, with sorting and aggregation
|
|
type ContentsIndex struct {
|
|
db database.Storage
|
|
prefix []byte
|
|
}
|
|
|
|
// NewContentsIndex creates empty ContentsIndex
|
|
func NewContentsIndex(db database.Storage) *ContentsIndex {
|
|
return &ContentsIndex{
|
|
db: db,
|
|
prefix: []byte(uuid.New()),
|
|
}
|
|
}
|
|
|
|
// Push adds package to contents index, calculating package contents as required
|
|
func (index *ContentsIndex) Push(qualifiedName []byte, contents []string, dbw database.Writer) error {
|
|
for _, path := range contents {
|
|
// for performance reasons we only write to leveldb during push.
|
|
// merging of qualified names per path will be done in WriteTo
|
|
err := dbw.Put(append(append(append(index.prefix, []byte(path)...), byte(0)), qualifiedName...), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Empty checks whether index contains no packages
|
|
func (index *ContentsIndex) Empty() bool {
|
|
return !index.db.HasPrefix(index.prefix)
|
|
}
|
|
|
|
// WriteTo dumps sorted mapping of files to qualified package names
|
|
func (index *ContentsIndex) WriteTo(w io.Writer) (int64, error) {
|
|
// For performance reasons push method wrote on key per path and package
|
|
// in this method we now need to merge all packages which have the same path
|
|
// and write it to contents index file
|
|
|
|
var n int64
|
|
|
|
nn, err := fmt.Fprintf(w, "%s %s\n", "FILE", "LOCATION")
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
prefixLen := len(index.prefix)
|
|
|
|
var (
|
|
currentPath []byte
|
|
currentPkgs [][]byte
|
|
)
|
|
|
|
err = index.db.ProcessByPrefix(index.prefix, func(key []byte, value []byte) error {
|
|
// cut prefix
|
|
key = key[prefixLen:]
|
|
|
|
i := bytes.Index(key, []byte{0})
|
|
if i == -1 {
|
|
return errors.New("corrupted index entry")
|
|
}
|
|
|
|
path := key[:i]
|
|
pkg := key[i+1:]
|
|
|
|
if !bytes.Equal(path, currentPath) {
|
|
if currentPath != nil {
|
|
nn, err = w.Write(append(currentPath, ' '))
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nn, err = w.Write(bytes.Join(currentPkgs, []byte{','}))
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nn, err = w.Write([]byte{'\n'})
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
currentPath = append([]byte(nil), path...)
|
|
currentPkgs = nil
|
|
}
|
|
|
|
currentPkgs = append(currentPkgs, append([]byte(nil), pkg...))
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
if currentPath != nil {
|
|
nn, err = w.Write(append(currentPath, ' '))
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
nn, err = w.Write(bytes.Join(currentPkgs, []byte{','}))
|
|
n += int64(nn)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
nn, err = w.Write([]byte{'\n'})
|
|
n += int64(nn)
|
|
}
|
|
|
|
return n, err
|
|
}
|