aboutsummaryrefslogblamecommitdiffstats
path: root/formats/idxfile/decoder.go
blob: 884d32b5baa650a87cc1e6e3eded81bfb4f01099 (plain) (tree)
1
2
3
4
5
6
7
8
9



               


                
                                       
                                               






























































                                                                                    
                                      








                                            




                                                  
                                  
                                                         




                                  
                                                   



















                                                                   
                                                                             









                                                   
                                              




















                                                                  
package idxfile

import (
	"bytes"
	"errors"
	"io"

	"gopkg.in/src-d/go-git.v4/core"
	"gopkg.in/src-d/go-git.v4/utils/binary"
)

var (
	// ErrUnsupportedVersion is returned by Decode when the idx file version
	// is not supported.
	ErrUnsupportedVersion = errors.New("Unsuported version")
	// ErrMalformedIdxFile is returned by Decode when the idx file is corrupted.
	ErrMalformedIdxFile = errors.New("Malformed IDX file")
)

// A Decoder reads and decodes idx files from an input stream.
type Decoder struct {
	io.Reader
}

// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r}
}

// Decode reads the whole idx object from its input and stores it in the
// value pointed to by idx.
func (d *Decoder) Decode(idx *Idxfile) error {
	if err := validateHeader(d); err != nil {
		return err
	}

	flow := []func(*Idxfile, io.Reader) error{
		readVersion,
		readFanout,
		readObjectNames,
		readCRC32,
		readOffsets,
		readChecksums,
	}

	for _, f := range flow {
		if err := f(idx, d); err != nil {
			return err
		}
	}

	if !idx.isValid() {
		return ErrMalformedIdxFile
	}

	return nil
}

func validateHeader(r io.Reader) error {
	var h = make([]byte, 4)
	if _, err := r.Read(h); err != nil {
		return err
	}

	if !bytes.Equal(h, idxHeader) {
		return ErrMalformedIdxFile
	}

	return nil
}

func readVersion(idx *Idxfile, r io.Reader) error {
	v, err := binary.ReadUint32(r)
	if err != nil {
		return err
	}

	if v > VersionSupported {
		return ErrUnsupportedVersion
	}

	idx.Version = v
	return nil
}

func readFanout(idx *Idxfile, r io.Reader) error {
	var err error
	for i := 0; i < 255; i++ {
		idx.Fanout[i], err = binary.ReadUint32(r)
		if err != nil {
			return err
		}
	}

	idx.ObjectCount, err = binary.ReadUint32(r)
	return err
}

func readObjectNames(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)
	for i := 0; i < c; i++ {
		var ref core.Hash
		if _, err := r.Read(ref[:]); err != nil {
			return err
		}

		idx.Entries = append(idx.Entries, Entry{Hash: ref})
	}

	return nil
}

func readCRC32(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)
	for i := 0; i < c; i++ {
		if err := binary.Read(r, &idx.Entries[i].CRC32); err != nil {
			return err
		}
	}

	return nil
}

func readOffsets(idx *Idxfile, r io.Reader) error {
	c := int(idx.ObjectCount)
	for i := 0; i < c; i++ {
		o, err := binary.ReadUint32(r)
		if err != nil {
			return err
		}

		idx.Entries[i].Offset = uint64(o)
	}

	return nil
}

func readChecksums(idx *Idxfile, r io.Reader) error {
	if _, err := r.Read(idx.PackfileChecksum[:]); err != nil {
		return err
	}

	if _, err := r.Read(idx.IdxChecksum[:]); err != nil {
		return err
	}

	return nil
}