forked from firedancer-io/radiance
/
writer.go
91 lines (77 loc) · 2.12 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package car
import (
"io"
"github.com/filecoin-project/go-leb128"
"github.com/ipfs/go-cid"
"github.com/ipld/go-car"
)
type OutStream interface {
WriteBlock(Block) error
}
// Writer produces CARv1 files with size tracking.
//
// The implementation is kinda memory-efficient.
// Needs up to IPLD block size plus peanuts of memory.
//
// # Rationale
//
// github.com/ipld/go-car/v2 is not helpful because it wants to traverse a complete IPLD link system.
// However we create an IPLD link system (Merkle-DAG) on the fly in a single pass as we read the chain.
// CARv1 is simple enough that we can roll a custom block writer, so no big deal.
type Writer struct {
out countingWriter
}
// NewWriter creates a new CARv1 Writer and writes the header.
func NewWriter(out io.Writer) (*Writer, error) {
w := &Writer{out: newCountingWriter(out)}
// Deliberately using the go-car v0 library here.
// go-car v2 doesn't seem to expose the CARv1 header format.
hdr := car.CarHeader{
Roots: []cid.Cid{IdentityCID}, // placeholder
Version: 1,
}
if err := car.WriteHeader(&hdr, w.out); err != nil {
return nil, err
}
return w, nil
}
// Write raw bytes to CAR.
func (w *Writer) Write(data []byte) (n int, err error) {
return w.out.Write(data)
}
// WriteBlock writes out a length-CID-value tuple.
func (w *Writer) WriteBlock(b Block) (err error) {
if _, err = w.out.Write(leb128.FromUInt64(uint64(b.Length))); err != nil {
return err
}
if _, err = w.out.Write(b.Cid.Bytes()); err != nil {
return err
}
_, err = w.out.Write(b.Data)
return
}
// Written returns the number of bytes written so far.
func (w *Writer) Written() int64 {
return w.out.written()
}
// countingWriter wraps io.Writer, but counts number of written bytes.
// Not thread safe.
type countingWriter struct {
io.Writer
n *int64
}
func newCountingWriter(w io.Writer) countingWriter {
return countingWriter{
Writer: w,
n: new(int64),
}
}
func (c countingWriter) Write(data []byte) (n int, err error) {
n, err = c.Writer.Write(data)
*c.n += int64(n)
return
}
// written returns number of bytes written so far.
func (c countingWriter) written() int64 {
return *c.n
}