Version 0 of Zstandard

Updated 2017-05-20 07:22:49 by dbohdan

Zstandard or zstd is a compression algorithm and C library. It compresses data at least twice as fast as zlib with comparable compression ratios.

Code

The following code shows how to use libzstd from Tcl through Critcl.

# A demo showing how to use Zstandard from Tcl.
# Copyright (c) 2017 dbohdan.
# License: MIT.
# If you installed libzstd.so.1 to /usr/local/lib on *nix, you may need to
# run this file with `LD_LIBRARY_PATH=/usr/local/lib tclsh zstd.tcl`.
package require critcl 3

namespace eval ::zstd {}
critcl::ccode {
    #include <zstd.h>
}
critcl::clibraries -lzstd

critcl::ccommand zstd::compress {cdata interp objc objv} {
    int level = 3;
    int max_level = ZSTD_maxCLevel();
    int rc = -1;
    void *source_buf;
    void *dest_buf;
    int source_len;
    size_t dest_size;
    size_t compressed_size;

    if (objc != 2 && objc != 3) {
        Tcl_WrongNumArgs(interp, objc, objv, "data ?level?");
        return TCL_ERROR;
    }
    if (objc == 3) {
        rc = Tcl_GetIntFromObj(interp, objv[2], &level);
        if (rc != TCL_OK || ((level < 1) || (level > max_level))) {
            Tcl_SetObjResult(interp,
                             Tcl_ObjPrintf("level must be integer between "
                                           "1 and %d", max_level));
            return TCL_ERROR;
        }
    }

    source_buf = (void *)Tcl_GetString(objv[1]);
    source_len = Tcl_GetCharLength(objv[1]);
    dest_size = ZSTD_compressBound(source_len);
    dest_buf = malloc(dest_size);
    if (dest_buf == NULL) {
        Tcl_SetObjResult(interp,
                         Tcl_NewStringObj("can't allocate memory to compress "
                                          "data", -1));
        return TCL_ERROR;
    }
    compressed_size = ZSTD_compress(dest_buf, dest_size, source_buf,
                                    source_len, level);
    if (ZSTD_isError(compressed_size)) {
            Tcl_SetObjResult(interp,
                             Tcl_ObjPrintf("zstd encoding error: %s",
                                           ZSTD_getErrorName(compressed_size)));
            return TCL_ERROR;
    }

    Tcl_SetObjResult(interp, Tcl_NewStringObj(dest_buf, compressed_size));
    free(dest_buf);

    return TCL_OK;
}

critcl::ccommand zstd::decompress {cdata interp objc objv} {
#define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1)
#define ZSTD_CONTENTSIZE_ERROR   (0ULL - 2)

    void *source_buf;
    void *dest_buf;
    int source_len;
    size_t dest_size;
    unsigned long long decompressed_size;

    if (objc != 2) {
        Tcl_WrongNumArgs(interp, objc, objv, "data");
        return TCL_ERROR;
    }

    source_buf = (void *)Tcl_GetString(objv[1]);
    source_len = Tcl_GetCharLength(objv[1]);
    dest_size = ZSTD_findDecompressedSize(source_buf, source_len);

    if (dest_size == ZSTD_CONTENTSIZE_ERROR) {
        Tcl_SetObjResult(interp,
                         Tcl_NewStringObj("data wasn't compressed by zstd",
                                          -1));
        return TCL_ERROR;
    } else if (dest_size == ZSTD_CONTENTSIZE_UNKNOWN) {
        Tcl_SetObjResult(interp,
                         Tcl_NewStringObj("original data size unknown; "
                                          "can't decompress due to no support "
                                          "for streaming decompression", -1));
        return TCL_ERROR;
    }

    dest_buf = malloc(dest_size);
    if (dest_buf == NULL) {
        Tcl_SetObjResult(interp,
                         Tcl_NewStringObj("can't allocate memory to decompress "
                                          "data", -1));
        return TCL_ERROR;
    }

    decompressed_size = ZSTD_decompress(dest_buf, dest_size, source_buf,
                                        source_len);

    if (decompressed_size != dest_size) {
        Tcl_SetObjResult(interp,
                         Tcl_ObjPrintf("zstd decoding error: %s",
                                       ZSTD_getErrorName(decompressed_size)));
        return TCL_ERROR;
    }

    Tcl_SetObjResult(interp, Tcl_NewStringObj(dest_buf, decompressed_size));
    free(dest_buf);

    return TCL_OK;
}

puts [zstd::decompress [zstd::compress hello!]]