1package quad
2
3import (
4	"io"
5)
6
7// Writer is a minimal interface for quad writers. Used for quad serializers and quad stores.
8type Writer interface {
9	WriteQuad(Quad) error
10}
11
12type WriteCloser interface {
13	Writer
14	io.Closer
15}
16
17// BatchWriter is an interface for writing quads in batches.
18//
19// WriteQuads returns a number of quads that where written and an error, if any.
20type BatchWriter interface {
21	WriteQuads(buf []Quad) (int, error)
22}
23
24// Reader is a minimal interface for quad readers. Used for quad deserializers and quad iterators.
25//
26// ReadQuad reads next valid Quad. It returns io.EOF if no quads are left.
27type Reader interface {
28	ReadQuad() (Quad, error)
29}
30
31// Skipper is an interface for quad reader that can skip quads efficiently without decoding them.
32//
33// It returns io.EOF if no quads are left.
34type Skipper interface {
35	SkipQuad() error
36}
37
38type ReadCloser interface {
39	Reader
40	io.Closer
41}
42
43type ReadSkipCloser interface {
44	Reader
45	Skipper
46	io.Closer
47}
48
49// BatchReader is an interface for reading quads in batches.
50//
51// ReadQuads reads at most len(buf) quads into buf. It returns number of quads that were read and an error.
52// It returns an io.EOF if there is no more quads to read.
53type BatchReader interface {
54	ReadQuads(buf []Quad) (int, error)
55}
56
57type Quads struct {
58	s []Quad
59}
60
61func (r *Quads) WriteQuad(q Quad) error {
62	r.s = append(r.s, q)
63	return nil
64}
65
66func (r *Quads) ReadQuad() (Quad, error) {
67	if r == nil || len(r.s) == 0 {
68		return Quad{}, io.EOF
69	}
70	q := r.s[0]
71	r.s = r.s[1:]
72	if len(r.s) == 0 {
73		r.s = nil
74	}
75	return q, nil
76}
77
78// NewReader creates a quad reader from a quad slice.
79func NewReader(quads []Quad) *Quads {
80	return &Quads{s: quads}
81}
82
83// Copy will copy all quads from src to dst. It returns copied quads count and an error, if it failed.
84//
85// Copy will try to cast dst to BatchWriter and will switch to CopyBatch implementation in case of success.
86func Copy(dst Writer, src Reader) (n int, err error) {
87	if bw, ok := dst.(BatchWriter); ok {
88		return CopyBatch(bw, src, 0)
89	}
90	var q Quad
91	for {
92		q, err = src.ReadQuad()
93		if err == io.EOF {
94			err = nil
95			return
96		} else if err != nil {
97			return
98		}
99		if err = dst.WriteQuad(q); err != nil {
100			return
101		}
102		n++
103	}
104}
105
106type batchReader struct {
107	Reader
108}
109
110func (r batchReader) ReadQuads(quads []Quad) (n int, err error) {
111	for ; n < len(quads); n++ {
112		quads[n], err = r.ReadQuad()
113		if err != nil {
114			break
115		}
116	}
117	return
118}
119
120var DefaultBatch = 10000
121
122// CopyBatch will copy all quads from src to dst in a batches of batchSize.
123// It returns copied quads count and an error, if it failed.
124//
125// If batchSize <= 0 default batch size will be used.
126func CopyBatch(dst BatchWriter, src Reader, batchSize int) (cnt int, err error) {
127	if batchSize <= 0 {
128		batchSize = DefaultBatch
129	}
130	buf := make([]Quad, batchSize)
131	bsrc, ok := src.(BatchReader)
132	if !ok {
133		bsrc = batchReader{src}
134	}
135	var n int
136	for err == nil {
137		n, err = bsrc.ReadQuads(buf)
138		if err != nil && err != io.EOF {
139			return
140		}
141		eof := err == io.EOF
142		n, err = dst.WriteQuads(buf[:n])
143		cnt += n
144		if eof {
145			break
146		}
147	}
148	return
149}
150
151// ReadAll reads all quads from r until EOF.
152// It returns a slice with all quads that were read and an error, if any.
153func ReadAll(r Reader) (arr []Quad, err error) {
154	switch rt := r.(type) {
155	case *Quads:
156		arr = make([]Quad, len(rt.s))
157		copy(arr, rt.s)
158		rt.s = nil
159		return
160	}
161	var q Quad
162	for {
163		q, err = r.ReadQuad()
164		if err == io.EOF {
165			return arr, nil
166		} else if err != nil {
167			return nil, err
168		}
169		arr = append(arr, q)
170	}
171}
172