/
writer.go
141 lines (124 loc) · 3.15 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
133
134
135
136
137
138
139
140
141
// Copyright 2011-2019 Rémy Oudompheng. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xz
/*
#cgo LDFLAGS: -llzma
#include <lzma.h>
#include <stdlib.h>
int go_lzma_code(
lzma_stream* handle,
void* next_in,
void* next_out,
lzma_action action
);
*/
import "C"
import (
"bytes"
"io"
"unsafe"
)
type Compressor struct {
handle *C.lzma_stream
writer io.Writer
buffer []byte
}
var _ io.WriteCloser = &Compressor{}
func allocLzmaStream(t *C.lzma_stream) *C.lzma_stream {
return (*C.lzma_stream)(C.calloc(1, (C.size_t)(unsafe.Sizeof(*t))))
}
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)
enc.handle = allocLzmaStream(enc.handle)
// 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)
enc.handle = allocLzmaStream(enc.handle)
// 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) {
for n < len(in) {
enc.handle.avail_in = C.size_t(len(in) - n)
enc.handle.avail_out = C.size_t(len(enc.buffer))
ret := C.go_lzma_code(
enc.handle,
unsafe.Pointer(&in[n]),
unsafe.Pointer(&enc.buffer[0]),
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)
_, er = enc.writer.Write(enc.buffer[:produced])
if er != nil {
// Short write.
return
}
}
return
}
func (enc *Compressor) Flush() error {
enc.handle.avail_in = 0
for {
enc.handle.avail_out = C.size_t(len(enc.buffer))
// If Flush is invoked after Write produced an error, avail_in and next_in will point to
// the bytes previously provided to Write, which may no longer be valid.
enc.handle.avail_in = 0
ret := C.go_lzma_code(
enc.handle,
nil,
unsafe.Pointer(&enc.buffer[0]),
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
}
}
}
// 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)
C.free(unsafe.Pointer(enc.handle))
enc.handle = nil
if er != nil {
return er
}
}
return nil
}