arche / internal/syncpkg/pack.go

commit 154431fd
  1package syncpkg
  2
  3import (
  4	"encoding/binary"
  5	"fmt"
  6	"io"
  7)
  8
  9const (
 10	packEntryEnd    byte = 0
 11	packEntryObject byte = 1
 12)
 13
 14type PackEntry struct {
 15	ID   [32]byte
 16	Kind string
 17	Data []byte
 18}
 19
 20func WritePack(w io.Writer, entries []PackEntry) error {
 21	for _, e := range entries {
 22		if err := writePackEntry(w, e); err != nil {
 23			return err
 24		}
 25	}
 26	return writePackEnd(w)
 27}
 28
 29func writePackEntry(w io.Writer, e PackEntry) error {
 30	if _, err := w.Write([]byte{packEntryObject}); err != nil {
 31		return err
 32	}
 33	if _, err := w.Write(e.ID[:]); err != nil {
 34		return err
 35	}
 36	kindBytes := []byte(e.Kind)
 37	if len(kindBytes) > 255 {
 38		return fmt.Errorf("kind string too long: %d", len(kindBytes))
 39	}
 40	if _, err := w.Write([]byte{byte(len(kindBytes))}); err != nil {
 41		return err
 42	}
 43	if _, err := w.Write(kindBytes); err != nil {
 44		return err
 45	}
 46	var lenBuf [4]byte
 47	binary.BigEndian.PutUint32(lenBuf[:], uint32(len(e.Data)))
 48	if _, err := w.Write(lenBuf[:]); err != nil {
 49		return err
 50	}
 51	_, err := w.Write(e.Data)
 52	return err
 53}
 54
 55func writePackEnd(w io.Writer) error {
 56	_, err := w.Write([]byte{packEntryEnd})
 57	return err
 58}
 59
 60func ReadPack(r io.Reader) ([]PackEntry, error) {
 61	var entries []PackEntry
 62	for {
 63		var typeBuf [1]byte
 64		if _, err := io.ReadFull(r, typeBuf[:]); err != nil {
 65			if err == io.EOF {
 66				return nil, fmt.Errorf("pack stream ended without end-of-pack marker")
 67			}
 68			return nil, fmt.Errorf("read pack entry type: %w", err)
 69		}
 70		switch typeBuf[0] {
 71		case packEntryEnd:
 72			return entries, nil
 73		case packEntryObject:
 74			e, err := readPackEntry(r)
 75			if err != nil {
 76				return nil, err
 77			}
 78			entries = append(entries, e)
 79		default:
 80			return nil, fmt.Errorf("unknown pack entry type %d", typeBuf[0])
 81		}
 82	}
 83}
 84
 85func readPackEntry(r io.Reader) (PackEntry, error) {
 86	var e PackEntry
 87	if _, err := io.ReadFull(r, e.ID[:]); err != nil {
 88		return e, fmt.Errorf("read object ID: %w", err)
 89	}
 90
 91	var kindLenBuf [1]byte
 92	if _, err := io.ReadFull(r, kindLenBuf[:]); err != nil {
 93		return e, fmt.Errorf("read kind length: %w", err)
 94	}
 95	kindBytes := make([]byte, kindLenBuf[0])
 96	if len(kindBytes) > 0 {
 97		if _, err := io.ReadFull(r, kindBytes); err != nil {
 98			return e, fmt.Errorf("read kind: %w", err)
 99		}
100	}
101	e.Kind = string(kindBytes)
102
103	var lenBuf [4]byte
104	if _, err := io.ReadFull(r, lenBuf[:]); err != nil {
105		return e, fmt.Errorf("read data length: %w", err)
106	}
107	dataLen := binary.BigEndian.Uint32(lenBuf[:])
108	if dataLen > 0 {
109		e.Data = make([]byte, dataLen)
110		if _, err := io.ReadFull(r, e.Data); err != nil {
111			return e, fmt.Errorf("read data: %w", err)
112		}
113	}
114	return e, nil
115}