forked from remyoudompheng/go-liblzma
/
writer.go
118 lines (97 loc) · 2.51 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
package xzgo
/*
#cgo LDFLAGS: -llzma
#include <lzma.h>
*/
import "C"
import (
"bytes"
"io"
"unsafe"
)
type Compressor struct {
handle C.lzma_stream
writer io.Writer
buffer []byte
}
var _ io.WriteCloser = &Compressor{}
func NewWriter(w io.Writer, preset Preset) (*Compressor, error) {
enc := new(Compressor)
// The zero lzma_stream is the same thing as LZMA_STREAM_INIT.
enc.writer = w
enc.buffer = make([]byte, DefaultBufsize)
// Initialize encoder
ret := C.lzma_easy_encoder(&enc.handle, C.uint32_t(preset), C.lzma_check(CheckCRC64))
if Errno(ret) != Ok {
return nil, Errno(ret)
}
return enc, nil
}
// Initializes a XZ encoder with additional settings.
func NewWriterCustom(w io.Writer, preset Preset, check Checksum, bufsize int) (*Compressor, error) {
enc := new(Compressor)
// The zero lzma_stream is the same thing as LZMA_STREAM_INIT.
enc.writer = w
enc.buffer = make([]byte, bufsize)
// Initialize encoder
ret := C.lzma_easy_encoder(&enc.handle, C.uint32_t(preset), C.lzma_check(check))
if Errno(ret) != Ok {
return nil, Errno(ret)
}
return enc, nil
}
func (enc *Compressor) Write(in []byte) (n int, er error) {
enc.handle.next_in = (*C.uint8_t)(unsafe.Pointer(&in[0]))
enc.handle.avail_in = C.size_t(len(in))
enc.handle.next_out = (*C.uint8_t)(unsafe.Pointer(&enc.buffer[0]))
enc.handle.avail_out = C.size_t(len(enc.buffer))
ret := C.lzma_code(&enc.handle, C.lzma_action(Run))
switch Errno(ret) {
case Ok:
break
default:
er = Errno(ret)
}
n = len(in) - int(enc.handle.avail_in)
// Write back result.
produced := len(enc.buffer) - int(enc.handle.avail_out)
to_write := bytes.NewBuffer(enc.buffer[:produced])
_, er = io.Copy(enc.writer, to_write)
if er != nil {
// Short write.
return
}
return
}
func (enc *Compressor) Flush() error {
enc.handle.avail_in = 0
for {
enc.handle.next_out = (*C.uint8_t)(unsafe.Pointer(&enc.buffer[0]))
enc.handle.avail_out = C.size_t(len(enc.buffer))
ret := C.lzma_code(&enc.handle, C.lzma_action(Finish))
// Write back result.
produced := len(enc.buffer) - int(enc.handle.avail_out)
to_write := bytes.NewBuffer(enc.buffer[:produced])
_, er := io.Copy(enc.writer, to_write)
if er != nil {
// Short write.
return er
}
if Errno(ret) == StreamEnd {
return nil
}
}
panic("unreachable")
}
// Frees any resources allocated by liblzma. It does not close the
// underlying reader.
func (enc *Compressor) Close() error {
if enc != nil {
er := enc.Flush()
C.lzma_end(&enc.handle)
if er != nil {
return er
}
}
return nil
}