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}