ebcc-sys 0.3.0+ebcc.0.1.4

Low-level Rust bindings to the EBCC compressor
Documentation
#include "hdf5_stub.h"
#include <stdlib.h>
#include <assert.h>
#include "ebcc_codec.h"
#include "log.h"

#define H5Z_FILTER_EBCC 308

static size_t H5Z_filter_ebcc(unsigned int flags, size_t cd_nelmts, const unsigned int cd_values[], size_t nbytes, size_t *buf_size, void **buf);

/*static htri_t can_apply_ebcc(hid_t dcpl_id, hid_t type_id, hid_t space_id);*/


const H5Z_class2_t H5Z_EBCC[1] = {{
    H5Z_CLASS_T_VERS,               /* H5Z_class_t version */
    (H5Z_filter_t) H5Z_FILTER_EBCC,  /* Filter id number             */
    1,                              /* encoder_present flag (set to true) */
    1,                              /* decoder_present flag (set to true) */
    "HDF5 EBCC filter L&L",         /* Filter name for debugging    */
    NULL,                           /* The "can apply" callback     */
    NULL,                           /* The "set local" callback     */
    (H5Z_func_t) H5Z_filter_ebcc,    /* The actual filter function   */
}};



EBCC_API H5PL_type_t H5PLget_plugin_type(void) { return H5PL_TYPE_FILTER; }
EBCC_API const void *H5PLget_plugin_info(void) { return H5Z_EBCC; }

float uint_ptr_to_float(const unsigned int *ptr) {
    return *((float *) ptr);
}

float uint_ptr_to_double(const unsigned int *ptr) {
    return *((double *) ptr);
}

EBCC_API void populate_config(codec_config_t *config, size_t cd_nelmts, const unsigned int cd_values[], size_t buf_size) {
    if (cd_nelmts < 4) {
        log_fatal("EBCC filter requires at least 4 configuration values, got %lu", cd_nelmts);
        exit(1);
    }

    for (size_t i = 0; i < NDIMS; ++i) {
        config->chunk_dims[i] = 0;
    }

    size_t tile_height = cd_values[0];
    size_t tile_width = cd_values[1];

    if (tile_height < EBCC_MIN_INTERNAL_IMAGE_DIM || tile_width < EBCC_MIN_INTERNAL_IMAGE_DIM ||
            tile_height > EBCC_MAX_INTERNAL_IMAGE_DIM ||
            tile_width > EBCC_MAX_INTERNAL_IMAGE_DIM) {
        log_fatal("Tile size %lu x %lu is invalid, each dimension must be between %d and %d",
                tile_height, tile_width, EBCC_MIN_INTERNAL_IMAGE_DIM, EBCC_MAX_INTERNAL_IMAGE_DIM);
        exit(1);
    }

    size_t tile_size = tile_height * tile_width;
    config->dims[0] = buf_size / sizeof(float);
    if (config->dims[0] < tile_size) {
        log_fatal("Buffer size %lu is smaller than the tile size %lu x %lu = %lu", config->dims[0], tile_height, tile_width, tile_size);
        exit(1);
    }
    if (config->dims[0] % tile_size != 0) {
        log_fatal("Buffer size %lu is not divisible by the tile size %lu x %lu = %lu", config->dims[0], tile_height, tile_width, tile_size);
        exit(1);
    }
    for (size_t i = 0; i < 2; ++i) {
        size_t cur = cd_values[i];
        config->dims[0] /= cur;
        config->dims[i + 1] = cur;
    }
    if (config->dims[1] != 0 && config->dims[0] > EBCC_MAX_INTERNAL_IMAGE_DIM / config->dims[1]) {
        log_fatal("Flattened EBCC image height %lu x %lu exceeds the limit of %d",
                config->dims[0], config->dims[1],
                EBCC_MAX_INTERNAL_IMAGE_DIM);
        exit(1);
    }

    config->base_cr = uint_ptr_to_float(&cd_values[2]);
    config->residual_compression_type = (residual_t) cd_values[3];
    switch (config->residual_compression_type) {
        case NONE:
            assert(cd_nelmts == 4);
            break;
        case MAX_ERROR:
        case RELATIVE_ERROR:
            assert(cd_nelmts == 5);
            config->error = uint_ptr_to_float(&cd_values[4]);
            break;
    }
}

/* can_apply func*/
/* need hdf5_dl.c from hdf5plugins*/
/*
static htri_t can_apply_ebcc(hid_t dcpl_id, hid_t type_id, hid_t space_id) {
    H5D_layout_t layout;
    H5T_class_t class;
    htri_t is_float;
    htri_t not_1d;
    htri_t is_chunked;

    layout = H5Pget_layout(dcpl_id);
    is_chunked = (layout == H5D_CHUNKED);
    class = H5Tget_class(type_id);
    is_float = (class == H5T_FLOAT);
    not_1d = (H5Sget_simple_extent_ndims(space_id) >= 2);

    return is_float && not_1d && is_chunked;
}

*/

/**
 * cd_values:
 *  rows
 *  columns
 *  residual mode: none=0, sparsification_ratio=1, max_error=2, relative_error=3, quantile=4
 *  max error (as float) OR quantile (as double)
*/
// cd_values should be: frame rows, frame columns, compression_ratio, quantile (thousandth), levels, quantile (optional)
static size_t H5Z_filter_ebcc(unsigned int flags, size_t cd_nelmts, const unsigned int cd_values[], size_t nbytes, size_t *buf_size, void **buf) {
    if (flags & H5Z_FLAG_REVERSE) {
        // decompress data

        float *out_buffer = NULL;
        *buf_size = ebcc_decode(*buf, nbytes, &out_buffer);

        free_buffer(*buf);
        *buf = out_buffer;

        return *buf_size;
    } else {
        codec_config_t config;

        populate_config(&config, cd_nelmts, cd_values, *buf_size);

        uint8_t *out_buffer = NULL;
        *buf_size = ebcc_encode(*buf, &config, &out_buffer);

        free_buffer(*buf);
        *buf = out_buffer;

        return *buf_size;
    }
}