github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/compress/libdeflate/libdeflate_cgo.go (about) 1 // Copyright 2018 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache-2.0 3 // license that can be found in the LICENSE file. 4 5 //go:build cgo || !arm64 6 // +build cgo !arm64 7 8 package libdeflate 9 10 /* 11 #include "libdeflate.h" 12 */ 13 import "C" 14 15 import ( 16 "fmt" 17 "unsafe" 18 ) 19 20 // Decompressor is a minimal interface to libdeflate's decompressor object. It 21 // allocates a C memory area, so users are responsible for calling 22 // Decompressor.Cleanup() when done to avoid memory leaks. Multiple 23 // Decompressors can be active at once. 24 // 25 // The Reader interface is not directly implemented here; instead, it is 26 // assumed that the user is decompressing a multi-block BGZF-like format, and 27 // they're fine with manual calls to Decompress(). 28 type Decompressor struct { 29 cobj *C.struct_libdeflate_decompressor 30 } 31 32 // Init allocates workspace needed by Decompress(). 33 func (dd *Decompressor) Init() error { 34 if dd.cobj == nil { 35 dd.cobj = C.libdeflate_alloc_decompressor() 36 if dd.cobj == nil { 37 return fmt.Errorf("libdeflate: failed to allocate decompressor") 38 } 39 } 40 return nil 41 } 42 43 // Decompress performs raw DEFLATE decompression on a byte slice. outData[] 44 // must be large enough to fit the decompressed data. Byte count of the 45 // decompressed data is returned on success (it may be smaller than 46 // len(outData)). 47 func (dd *Decompressor) Decompress(outData, inData []byte) (int, error) { 48 // Tolerate zero-length blocks on input, even though we don't on output. 49 // (Can be relevant when a BGZF file was formed by raw byte concatenation of 50 // smaller BGZF files.) 51 // Note that we can't use the usual *reflect.SliceHeader replacement for 52 // unsafe.Pointer(&inData[0]): that produces a "cgo argument has Go pointer 53 // to Go pointer" compile error. 54 if len(inData) == 0 { 55 return 0, nil 56 } 57 var outLen C.size_t 58 errcode := C.libdeflate_deflate_decompress( 59 dd.cobj, unsafe.Pointer(&inData[0]), C.size_t(len(inData)), 60 unsafe.Pointer(&outData[0]), C.size_t(len(outData)), &outLen) 61 if errcode != C.LIBDEFLATE_SUCCESS { 62 return 0, fmt.Errorf("libdeflate: libdeflate_deflate_decompress() error code %d", errcode) 63 } 64 return int(outLen), nil 65 } 66 67 // GzipDecompress performs gzip decompression on a byte slice. outData[] must 68 // be large enough to fit the decompressed data. Byte count of the 69 // decompressed data is returned on success (it may be smaller than 70 // len(outData)). 71 func (dd *Decompressor) GzipDecompress(outData, inData []byte) (int, error) { 72 if len(inData) == 0 { 73 return 0, nil 74 } 75 var outLen C.size_t 76 errcode := C.libdeflate_gzip_decompress( 77 dd.cobj, unsafe.Pointer(&inData[0]), C.size_t(len(inData)), 78 unsafe.Pointer(&outData[0]), C.size_t(len(outData)), &outLen) 79 if errcode != C.LIBDEFLATE_SUCCESS { 80 return 0, fmt.Errorf("libdeflate: libdeflate_gzip_decompress() error code %d", errcode) 81 } 82 return int(outLen), nil 83 } 84 85 // Cleanup frees workspace memory. 86 func (dd *Decompressor) Cleanup() { 87 if dd.cobj != nil { 88 C.libdeflate_free_decompressor(dd.cobj) 89 dd.cobj = nil 90 } 91 } 92 93 // Compressor is a minimal interface to libdeflate's compressor object. 94 type Compressor struct { 95 cobj *C.struct_libdeflate_compressor 96 } 97 98 // Init allocates workspace needed by Compress(). 99 func (cc *Compressor) Init(compressionLevel int) error { 100 if cc.cobj == nil { 101 if compressionLevel == -1 { 102 compressionLevel = 5 103 } 104 cc.cobj = C.libdeflate_alloc_compressor(C.int(compressionLevel)) 105 if cc.cobj == nil { 106 return fmt.Errorf("libdeflate: failed to allocate compressor") 107 } 108 } 109 return nil 110 } 111 112 // Compress performs raw DEFLATE compression on a byte slice. outData[] must 113 // be large enough to fit the compressed data. Byte count of the compressed 114 // data is returned on success. 115 // Zero is currently returned on failure. A side effect is that inData cannot 116 // be length zero; this function will panic or crash if it is. 117 func (cc *Compressor) Compress(outData, inData []byte) int { 118 // We *want* to crash on length-zero (that implies an error in the calling 119 // code, we don't want to be writing zero-length BGZF blocks without knowing 120 // about it), so we intentionally exclude the len(inData) == 0 check. 121 outLen := int(C.libdeflate_deflate_compress( 122 cc.cobj, unsafe.Pointer(&inData[0]), C.size_t(len(inData)), 123 unsafe.Pointer(&outData[0]), C.size_t(len(outData)))) 124 return outLen 125 } 126 127 // Cleanup frees workspace memory. 128 func (cc *Compressor) Cleanup() { 129 if cc.cobj != nil { 130 C.libdeflate_free_compressor(cc.cobj) 131 cc.cobj = nil 132 } 133 }