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





                         
             

            
                                       























































































                                                                                    
                                    



















                                                                   
                                                                                               








































                                                                    
package idxfile

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"

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

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 := readInt32(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 = readInt32(r)
		if err != nil {
			return err
		}
	}

	idx.ObjectCount, err = readInt32(r)
	fmt.Println(idx.ObjectCount)
	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, binary.BigEndian, &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 := readInt32(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
}

func readInt32(r io.Reader) (uint32, error) {
	var v uint32
	if err := binary.Read(r, binary.BigEndian, &v); err != nil {
		return 0, err
	}

	return v, nil
}