-
Notifications
You must be signed in to change notification settings - Fork 3
/
writer.go
132 lines (117 loc) · 3.31 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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package fpc
import (
"encoding/binary"
"errors"
"fmt"
"io"
"math"
)
const (
DefaultCompression = 10
// The reference implementation uses 255 for a max compression, but that
// hardly seems realistic: merely 32 will require 68 gigabytes of working
// memory to compute hashes. Beyond 35 we start hitting panics.
MaxCompression = 32
floatChunkSize = 8
)
// A Writer is an io.WriteCloser which FPC-compresses data it receives
// and writes it to an underlying writer, w. Writes to a Writer are
type Writer struct {
w io.Writer
level int
enc *blockEncoder
wroteHeader bool
closed bool
}
// NewWriter makes a new Writer which writes compressed data to w
// using the default compression level.
func NewWriter(w io.Writer) *Writer {
z, _ := NewWriterLevel(w, DefaultCompression)
return z
}
// NewWriterLevel makes a new Writer which writes compressed data to w
// using a provided compression level. Higher compression levels will
// result in more compressed data, but require exponentially more
// memory. The space required is O(2^level) bytes. NewWriterLevel
// returns an error if an invalid compression level is provided.
func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
if level < 1 || level > MaxCompression {
return nil, fmt.Errorf("fpc: invalid compression level: %d", level)
}
z := &Writer{
w: w,
level: level,
enc: newBlockEncoder(w, uint(level)),
}
return z, nil
}
// Write interprets b as a stream of byte-encoded, 64-bit IEEE 754
// floating point values. The length of b must be a multiple of 8 in
// order to match this expectation.
func (w *Writer) Write(b []byte) (int, error) {
if len(b)%8 != 0 {
return 0, errors.New("fpc.Write: len of data must be a multiple of 8")
}
for i := 0; i < len(b); i += 8 {
if err := w.writeBytes(b[i : i+8]); err != nil {
return i, err
}
}
return len(b), nil
}
// WriteFloat writes a single float64 value to the encoded stream.
func (w *Writer) WriteFloat(f float64) error {
return w.writeFloat64(f)
}
// Flush will make sure all internally-buffered values are written to
// w. FPC's format specifies that data get written in blocks; calling
// Flush will write the current data to a block, even if it results in
// a partial block.
//
// Flush does not flush the underlying io.Writer which w is delegating
// to.
func (w *Writer) Flush() error {
if err := w.ensureHeader(); err != nil {
return err
}
return w.enc.flush()
}
// Close will flush the Writer and make any subsequent writes return
// errors. It does not close the underlying io.Writer which w is
// delegating to.
func (w *Writer) Close() error {
if w.closed == true {
return nil
}
w.closed = true
return w.Flush()
}
func (w *Writer) ensureHeader() error {
if !w.wroteHeader {
w.wroteHeader = true
_, err := w.w.Write([]byte{byte(w.level)})
if err != nil {
return err
}
}
return nil
}
func (w *Writer) writeFloat64(f float64) error {
return w.writeUint64(math.Float64bits(f))
}
func (w *Writer) writeUint64(u uint64) error {
if err := w.ensureHeader(); err != nil {
return err
}
if err := w.enc.encode(u); err != nil {
return err
}
return nil
}
// writeBytes writes a single 8-byte encoded IEEE 754 float
func (w *Writer) writeBytes(b []byte) error {
if err := w.writeUint64(binary.LittleEndian.Uint64(b)); err != nil {
return err
}
return nil
}