package idxfile
import (
"bytes"
"math"
"sort"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/utils/binary"
)
type object struct {
hash plumbing.Hash
offset int64
crc uint32
}
type objects []object
// Writer implements a packfile Observer interface and is used to generate
// indexes.
type Writer struct {
count uint32
checksum plumbing.Hash
objects objects
}
// Create index returns a filled MemoryIndex with the information filled by
// the observer callbacks.
func (w *Writer) CreateIndex() (*MemoryIndex, error) {
idx := new(MemoryIndex)
sort.Sort(w.objects)
// unmap all fans by default
for i := range idx.FanoutMapping {
idx.FanoutMapping[i] = noMapping
}
buf := new(bytes.Buffer)
last := -1
bucket := -1
for i, o := range w.objects {
fan := o.hash[0]
// fill the gaps between fans
for j := last + 1; j < int(fan); j++ {
idx.Fanout[j] = uint32(i)
}
// update the number of objects for this position
idx.Fanout[fan] = uint32(i + 1)
// we move from one bucket to another, update counters and allocate
// memory
if last != int(fan) {
bucket++
idx.FanoutMapping[fan] = bucket
last = int(fan)
idx.Names = append(idx.Names, make([]byte, 0))
idx.Offset32 = append(idx.Offset32, make([]byte, 0))
idx.Crc32 = append(idx.Crc32, make([]byte, 0))
}
idx.Names[bucket] = append(idx.Names[bucket], o.hash[:]...)
// TODO: implement 64 bit offsets
if o.offset > math.MaxInt32 {
panic("64 bit offsets not implemented")
}
buf.Truncate(0)
binary.WriteUint32(buf, uint32(o.offset))
idx.Offset32[bucket] = append(idx.Offset32[bucket], buf.Bytes()...)
buf.Truncate(0)
binary.WriteUint32(buf, uint32(o.crc))
idx.Crc32[bucket] = append(idx.Crc32[bucket], buf.Bytes()...)
}
for j := last + 1; j < 256; j++ {
idx.Fanout[j] = uint32(len(w.objects))
}
idx.PackfileChecksum = w.checksum
// TODO: fill IdxChecksum
return idx, nil
}
// Add appends new object data.
func (w *Writer) Add(h plumbing.Hash, pos int64, crc uint32) {
w.objects = append(w.objects, object{h, pos, crc})
}
// OnHeader implements packfile.Observer interface.
func (w *Writer) OnHeader(count uint32) error {
w.count = count
w.objects = make(objects, 0, count)
return nil
}
// OnInflatedObjectHeader implements packfile.Observer interface.
func (w *Writer) OnInflatedObjectHeader(t plumbing.ObjectType, objSize int64, pos int64) error {
return nil
}
// OnInflatedObjectContent implements packfile.Observer interface.
func (w *Writer) OnInflatedObjectContent(h plumbing.Hash, pos int64, crc uint32) error {
w.Add(h, pos, crc)
return nil
}
// OnFooter implements packfile.Observer interface.
func (w *Writer) OnFooter(h plumbing.Hash) error {
w.checksum = h
return nil
}
func (o objects) Len() int {
return len(o)
}
func (o objects) Less(i int, j int) bool {
cmp := bytes.Compare(o[i].hash[:], o[j].hash[:])
return cmp < 0
}
func (o objects) Swap(i int, j int) {
o[i], o[j] = o[j], o[i]
}