Hi, my name is Walid, a backend developer who’s currently learning Go and sharing my journey by writing about it along the way.
Resource :
- The Go Programming Language book by Alan A. A. Donovan & Brian W. Kernighan
- Matt Holiday go course
In this tutorial, we will demonstrate how to integrate Go with the libbzip2 compression library using cgo. We will keep the C code in a separate file and call it from Go to compress a file.
Prerequisites
Before we begin, ensure you have the following installed:
-
libbzip2 Development Library:
- On Debian-based systems:
sudo apt install libbz2-dev
Project Structure
To keep things organized, we will separate our Go and C code into different files. Create the following directory structure:
project-directory/ ├── main.go ├── bz2compress.c └── bz2compress.h Writing the C Code
1. Create bz2compress.h
This header file declares the functions we will implement in bz2compress.c.
#ifndef BZ2COMPRESS_H #define BZ2COMPRESS_H #include <bzlib.h> int bz2compress(bz_stream *strm, int action, char *in, unsigned int *inlen, char *out, unsigned int *outlen); bz_stream* bz2alloc(void); void bz2free(bz_stream* strm); #endif // BZ2COMPRESS_H 2. Create bz2compress.c
This file implements the functions declared in the header.
#include "bz2compress.h" #include <stdlib.h> int bz2compress(bz_stream *strm, int action, char *in, unsigned int *inlen, char *out, unsigned int *outlen) { strm->next_in = in; strm->avail_in = *inlen; strm->next_out = out; strm->avail_out = *outlen; int ret = BZ2_bzCompress(strm, action); *inlen -= strm->avail_in; *outlen -= strm->avail_out; strm->next_in = strm->next_out = NULL; return ret; } bz_stream* bz2alloc(void) { return (bz_stream*)calloc(1, sizeof(bz_stream)); } void bz2free(bz_stream* strm) { free(strm); } Writing the Go Code
Now, let's write the Go code to use these C functions.
3. Create main.go
package main /* #cgo LDFLAGS: -lbz2 #include "bz2compress.h" */ import "C" import ( "fmt" "io" "os" "unsafe" ) func main() { inputFile := "input.txt" outputFile := "output.bz2" inFile, err := os.Open(inputFile) if err != nil { fmt.Fprintf(os.Stderr, "Error opening input file: %v\n", err) os.Exit(1) } defer inFile.Close() outFile, err := os.Create(outputFile) if err != nil { fmt.Fprintf(os.Stderr, "Error creating output file: %v\n", err) os.Exit(1) } defer outFile.Close() const bufferSize = 64 * 1024 inBuffer := make([]byte, bufferSize) outBuffer := make([]byte, bufferSize) strm := C.bz2alloc() defer C.bz2free(strm) if ret := C.BZ2_bzCompressInit(strm, 9, 0, 0); ret != C.BZ_OK { fmt.Fprintf(os.Stderr, "BZ2_bzCompressInit failed: %d\n", ret) os.Exit(1) } defer C.BZ2_bzCompressEnd(strm) for { inLen, err := inFile.Read(inBuffer) if err != nil && err != io.EOF { fmt.Fprintf(os.Stderr, "Error reading input file: %v\n", err) os.Exit(1) } if inLen == 0 { break } inLenC := C.uint(inLen) outLenC := C.uint(bufferSize) ret := C.bz2compress(strm, C.BZ_RUN, (*C.char)(unsafe.Pointer(&inBuffer[0])), &inLenC, (*C.char)(unsafe.Pointer(&outBuffer[0])), &outLenC) if ret != C.BZ_RUN_OK { fmt.Fprintf(os.Stderr, "BZ2_bzCompress failed: %d\n", ret) os.Exit(1) } if _, err := outFile.Write(outBuffer[:outLenC]); err != nil { fmt.Fprintf(os.Stderr, "Error writing to output file: %v\n", err) os.Exit(1) } } fmt.Println("Compression completed successfully.") } Building and Running the Application
- Initialize the Go Module
go mod init project-directory - Build the Project
go build - Run the Application
./project-directory Ensure input.txt exists before running the program. The compressed output will be saved as output.bz2.
Conclusion
By following this tutorial, you have successfully integrated C code with Go using cgo. Keeping the C code separate improves maintainability and allows better modularity. This approach can be extended to use other C libraries in Go applications efficiently.
If you have any questions or suggestions, feel free to leave a comment below!
Top comments (0)