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)