/
fastzlib.go
135 lines (115 loc) · 4.19 KB
/
fastzlib.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
// Copyright 2013, Datadog Inc. All rights reserved.
package czlib
import (
"errors"
"unsafe"
)
/*
#cgo pkg-config: zlib
#include "fastzlib.h"
#include <stdlib.h>
*/
import "C"
// An UnsafeByte is a []byte whose backing array has been allocated in C and
// thus is not subject to the Go garbage collector. The Unsafe versions of
// Compress and Decompress return this in order to prevent copying the unsafe
// memory into collected memory.
type UnsafeByte []byte
// NewUnsafeByte creates a []byte from the unsafe pointer without a copy,
// using the method outlined in this mailing list post:
//
// https://groups.google.com/forum/#!topic/golang-nuts/KyXR0fDp0HA
//
// but amended to use the three-index slices from go1.2 to set the capacity
// of b correctly:
//
// https://tip.golang.org/doc/go1.2#three_index
//
// This means this code only works in go1.2+.
//
// This shouldn't copy the underlying array; it's just casting it
// Afterwards, we use reflect to fix the Cap & len of the slice.
func NewUnsafeByte(p *C.char, length int) UnsafeByte {
var b UnsafeByte
b = UnsafeByte((*[1<<31 - 1]byte)(unsafe.Pointer(p))[:length:length])
return b
}
// Free the underlying byte array; doing this twice would be bad.
func (b UnsafeByte) Free() {
C.free(unsafe.Pointer(&b[0]))
}
func Compress(input []byte) ([]byte, error) {
return Compress2(input, 0)
}
// Compress returns the input compressed using zlib, or an error if encountered.
func Compress2(input []byte, wbits int) ([]byte, error) {
var cInput *C.char
if len(input) != 0 {
cInput = (*C.char)(unsafe.Pointer(&input[0]))
}
ret := C.c_compress2(cInput, C.uint(len(input)), C.int(wbits))
// if there was an error compressing, return it and free the original message
if ret.err != nil {
msg := C.GoString((*C.char)(ret.err))
C.free(unsafe.Pointer(ret.err))
return []byte{}, errors.New(msg)
}
// NOTE: this creates a copy of the return *char as a Go []byte.
// FIXME: uint -> int conversion here is dangerous
b := C.GoBytes(unsafe.Pointer(ret.str), C.int(ret.len))
C.free(unsafe.Pointer(ret.str))
return b, nil
}
// Decompress returns the input decompressed using zlib, or an error if encountered.
func Decompress(input []byte) ([]byte, error) {
var cInput *C.char
if len(input) != 0 {
cInput = (*C.char)(unsafe.Pointer(&input[0]))
}
// send the input byte without copying iy
ret := C.c_decompress(cInput, C.uint(len(input)))
// if there was an error decompressing, return it and free the original message
if ret.err != nil {
msg := C.GoString((*C.char)(ret.err))
C.free(unsafe.Pointer(ret.err))
return []byte{}, errors.New(msg)
}
// NOTE: this creates a copy of the return *char as a Go []byte.
// FIXME: uint -> int conversion here is dangerous
b := C.GoBytes(unsafe.Pointer(ret.str), C.int(ret.len))
C.free(unsafe.Pointer(ret.str))
return b, nil
}
// UnsafeDecompress unzips input into an UnsafeByte without copying the result
// malloced in C. The UnsafeByte returned can be used as a normal []byte but
// must be manually free'd w/ UnsafeByte.Free()
func UnsafeDecompress(input []byte) (UnsafeByte, error) {
cInput := (*C.char)(unsafe.Pointer(&input[0]))
ret := C.c_decompress(cInput, C.uint(len(input)))
// if there was an error decompressing, return it and free the original message
if ret.err != nil {
msg := C.GoString((*C.char)(ret.err))
C.free(unsafe.Pointer(ret.err))
return UnsafeByte{}, errors.New(msg)
}
b := NewUnsafeByte((*C.char)(ret.str), int(ret.len))
return b, nil
}
func UnsafeCompress(input []byte) (UnsafeByte, error) {
return UnsafeCompress2(input, 0)
}
// UnsafeCompress zips input into an UnsafeByte without copying the result
// malloced in C. The UnsafeByte returned can be used as a normal []byte but must
// be manually free'd w/ UnsafeByte.Free()
func UnsafeCompress2(input []byte, wbits int) (UnsafeByte, error) {
cInput := (*C.char)(unsafe.Pointer(&input[0]))
ret := C.c_compress2(cInput, C.uint(len(input)), C.int(wbits))
// if there was an error decompressing, return it and free the original message
if ret.err != nil {
msg := C.GoString((*C.char)(ret.err))
C.free(unsafe.Pointer(ret.err))
return UnsafeByte{}, errors.New(msg)
}
b := NewUnsafeByte((*C.char)(ret.str), int(ret.len))
return b, nil
}