aboutsummaryrefslogblamecommitdiffstats
path: root/plumbing/format/idxfile/decoder.go
blob: 4243f7652854161c1799ac06ad8a17340cb7602a (plain) (tree)
1
2
3
4
5
6
7
8
9
10


               
               
               


                

                                               









                                                                                    
                                                            
                     
                     

 
                                                                 
                                       
                                           

 
                                                                               




























                                                  
                                                    










                                                   
                                      








                                            




                                                  
                                  
                                                         




                                  
                                                   





                                                       
                                     
                                                                 


                                  
                                                                    







                                                 
                                                                             









                                                   
                                              










                                                     
                                                                          


                          
                                                                     




                          
package idxfile

import (
	"bufio"
	"bytes"
	"errors"
	"io"

	"gopkg.in/src-d/go-git.v4/plumbing"
	"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")
)

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

// NewDecoder builds a new idx stream decoder, that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{bufio.NewReader(r)}
}

// Decode reads from the stream and decode the content into the Idxfile struct.
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 := io.ReadFull(r, 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 plumbing.Hash
		if _, err := io.ReadFull(r, 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 := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil {
		return err
	}

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

	return nil
}