==========
Interfaces
==========

There  are two  interfaces  to  control the  filter.
One uses  HDF5_'s *generic* interface via  an array of ``unsigned int cd_values`` as is used in `H5Pset_filter() <https://docs.hdfgroup.org/hdf5/develop/group___o_c_p_l.html#ga191c567ee50b2063979cdef156a768c5>`__.
The other uses HDF5_ `property lists <https://docs.hdfgroup.org/hdf5/develop/group___h5_p.html>`__ added to the `dataset creation property list <https://docs.hdfgroup.org/hdf5/develop/group___d_c_p_l.html#title0>`__ used when the dataset to be compressed is being created.
You  can find examples  of writing HDF5_ data using both the `generic <https://github.com/LLNL/H5Z-ZFP/blob/master/test/test_write.c#L263>`_ and `properties <https://github.com/LLNL/H5Z-ZFP/blob/master/test/test_write.c#L290>`__ interfaces in `test_write.c <https://github.com/LLNL/H5Z-ZFP/blob/master/test/test_write.c>`_.

The filter itself supports either interface. The filter also supports all of the standard ZFP_ controls for affecting compression including *rate*, *precision*, *accuracy*, *expert* and *reversible* modes.
For more information and details about these modes of controlling ZFP_ compression, please see the `ZFP README <https://github.com/LLNL/zfp/blob/master/README.md>`_.

Finally, you should *not* attempt to combine the ZFP_ filter with any other *byte order altering* filter such as, for example, HDF5_'s shuffle filter.
Space-performance will be ruined.
This is in contrast to HDF5_'s `deflate <https://docs.hdfgroup.org/hdf5/develop/group___d_c_p_l.html#gaf1f569bfc54552bdb9317d2b63318a0d>`__ filter which often performs *better* when used in conjunction with the `shuffle <https://docs.hdfgroup.org/hdf5/develop/group___d_c_p_l.html#ga31e09cb0bf2da2893eed8a72220e6521>`__ filter.
To understand why, see the description of :ref:`endian issues <endian-issues>`.

.. _generic-interface:

-----------------
Generic Interface
-----------------

The generic interface is the only means of controlling the H5Z-ZFP_ filter when it is used as a `dynamically loaded HDF5 plugin <https://support.hdfgroup.org/HDF5/doc/Advanced/DynamicallyLoadedFilters/HDF5DynamicallyLoadedFilters.pdf>`_.

For the generic interface, the following CPP macros  are defined in the ``H5Zzfp_plugin.h`` header file::

    H5Pset_zfp_rate_cdata(double rate, size_t cd_nelmts, unsigned int *cd_vals);
    H5Pset_zfp_precision_cdata(unsigned int prec, size_t cd_nelmts, unsigned int *cd_vals);
    H5Pset_zfp_accuracy_cdata(double acc, size_t cd_nelmts, unsigned int *cd_vals);
    H5Pset_zfp_expert_cdata(unsigned int minbits, unsigned int maxbits,
                            unsigned int maxprec, int minexp,
                            size_t cd_nelmts, unsigned int *cd_vals);
    H5Pset_zfp_reversible_cdata(size_t cd_nelmts, unsigned int *cd_vals);

These  macros  utilize *type punning* to store the relevant ZFP_ parameters  into  a sufficiently large array (>=6) of ``unsigned int cd_values``.
It is up to the  caller to  then call `H5Pset_filter() <https://docs.hdfgroup.org/hdf5/develop/group___o_c_p_l.html#ga191c567ee50b2063979cdef156a768c5>`__ with  the array  of cd_values constructed by one of these macros.

Here is example code from `test_write.c <https://github.com/LLNL/H5Z-ZFP/blob/master/test/test_write.c>`_...

.. literalinclude:: ../test/test_write.c
   :language: c
   :linenos:
   :start-after: setup zfp filter via generic (cd_values) interface
   :end-before: #else

However, these  macros are only a  convenience.
You do  not **need** the ``H5Zzfp_plugin.h`` header file if you want  to avoid using it.
But, you are then responsible  for setting  up  the ``cd_values``  array  correctly for  the filter.
For reference,  the ``cd_values``  array for  this ZFP_  filter is defined like so...

+-----------+---------------------------------------------------------+
|           |                     cd_values index                     |
+-----------+--------+--------+---------+---------+---------+---------+
| ZFP mode  |     0  |    1   |    2    |    3    |    4    |    5    | 
+-----------+--------+--------+---------+---------+---------+---------+
| rate      |     1  | unused |  rateA  |  rateB  |  unused |  unused |
+-----------+--------+--------+---------+---------+---------+---------+
| precision |     2  | unused |  prec   |  unused |  unused |  unused |
+-----------+--------+--------+---------+---------+---------+---------+
| accuracy  |     3  | unused |  accA   |  accB   |  unused |  unused |
+-----------+--------+--------+---------+---------+---------+---------+
| expert    |     4  | unused |  minbits|  maxbits|  maxprec|  minexp |
+-----------+--------+--------+---------+---------+---------+---------+
| reversible|     5  | unused |  unused |  unused |  unused |  unused |
+-----------+--------+--------+---------+---------+---------+---------+

A/B are high/low 32-bit words of a double.

Note that  the cd_values  used in the generic interface to  ``H5Pset_filter()`` are **not the same** cd_values ultimately stored  to the HDF5_ dataset header for a compressed dataset.
The  values are transformed in the set_local method to use ZFP_'s internal routines for 'meta' and 'mode' data.
So, don't make the mistake of examining  the values you find in a file and think you can use those same  values, for example, in an invocation of h5repack.

.. _properties-interface:

--------------------
Properties Interface
--------------------

For the properties interface, the following functions are defined in the ``H5Zzfp_props.h`` header file::

    herr_t H5Pset_zfp_rate(hid_t dcpl_id, double rate);
    herr_t H5Pset_zfp_precision(hid_t dcpl_id, unsigned int prec);
    herr_t H5Pset_zfp_accuracy(hid_t dcpl_id, double acc);
    herr_t H5Pset_zfp_expert(hid_t dcpl_id,
        unsigned int minbits, unsigned int maxbits,
        unsigned int maxprec, int minexp);
    herr_t H5Pset_zfp_reversible(hid_t dcpl_id);

These  functions take a dataset creation property list, ``hid_t dcp_lid`` and create  temporary HDF5_ property list entries  to control the  ZFP_ filter.
Calling any of these functions removes the effects of any previous call to any one of these functions.
In addition, calling any one of these functions also has the effect of adding the filter to the pipeline.

Here is example code from `test_write.c <https://github.com/LLNL/H5Z-ZFP/blob/master/test/test_write.c>`_...

.. literalinclude:: ../test/test_write.c
   :language: c
   :linenos:
   :start-after: When filter is used as a library, we need to init it
   :end-before: #endif 

The properties interface  is more type-safe than the generic interface.
However, there  is no way for the implementation of the properties interface to reside within the filter plugin itself.
The properties interface requires that the caller link with  with the filter as a *library*, ``libh5zzfp.a``.
The generic interface does not require this.

Note that either interface can be used whether the filter is used as a plugin or as a library.
The difference is whether the application calls ``H5Z_zfp_initialize()`` or not.

-----------------
Fortran Interface
-----------------

Fortran equivalents for both the properties and generic interfaces, described above, has been added by Scot Breitenfeld of the HDF5_ group.
The code that implements the Fortran interfaces is in the file ``H5Zzfp_props_f.F90``.
An example of its use is in ``test/test_rw_fortran.F90``.

.. _plugin-vs-library:

----------------------------
Plugin vs. Library Operation
----------------------------

The filter is designed to be compiled for use as both a standalone HDF5_ `dynamically loaded HDF5 plugin <https://support.hdfgroup.org/HDF5/doc/Advanced/DynamicallyLoadedFilters/HDF5DynamicallyLoadedFilters.pdf>`_ and as an explicitly linked *library*.
When it is used as a plugin, it is a best practice to link the ZFP_ library into the plugin dynamic/shared object as a *static* library.
Why? In so doing, we ensure that all ZFP_ public namespace symbols remain *confined* to the plugin so as not to interfere with any application that may be directly explicitly linking to the ZFP_ library for other reasons.

All HDF5_ applications are *required* to *find* the plugin dynamic library (named ``lib*.{so,dylib}``) in a directory specified by the environment variable, ``HDF5_PLUGIN_PATH``.
Currently, the HDF5 library offers no mechanism for applications themselves to have pre-programmed paths in which to search for a plugin.
Applications are then always vulnerable to an incorrectly specified or unspecified ``HDF5_PLUGIN_PATH`` environment variable.

However, the plugin can also be used explicitly as a *library*.
In this case, **do** **not** specify the ``HDF5_PLUGIN_PATH`` environment variable and instead have the application link to ``libH5Zzfp.a`` in the ``lib`` dir of the installation.
Instead two initialization and finalization routines are defined::

    int H5Z_zfp_initialize(void);
    int H5Z_zfp_finalize(void);

These functions are defined in the ``H5Zzfp_lib.h`` header file.
Any applications that wish to use the filter as a *library* are required to call the initialization routine, ``H5Z_zfp_initialize()`` before the filter can be referenced.
In addition, to free up resources used by the filter, applications may call ``H5Z_zfp_finalize()`` when they are done using the filter.
