<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blosc Home Page  (Posts about blosc plugins codecs openzl)</title><link>https://blosc.org/</link><description></description><atom:link href="https://blosc.org/categories/blosc-plugins-codecs-openzl.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:blosc@blosc.org"&gt;The Blosc Developers&lt;/a&gt; </copyright><lastBuildDate>Wed, 10 Jun 2026 17:44:33 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title> OpenZL Plugin for Blosc2</title><link>https://blosc.org/posts/openzl-plugin/</link><dc:creator>Luke Shaw</dc:creator><description>&lt;p&gt;Blosc's philosophy of meta-compression is incredibly powerful - one is able to compose pipelines to optimally compress data (for speed or compression ratio), store information about the pipeline alognside the data in metadata, and then rely on a generic decompressor to read this and reverse the pipeline. The OpenZL team share our belief in the validity of this approach and have designed &lt;a class="reference external" href="https://openzl.org/"&gt;a graph-based formalisation with extensive support for all kinds of compression pipelines&lt;/a&gt; for all kinds of data.&lt;/p&gt;
&lt;p&gt;However, Blosc2 is now much more than just a compression library - it offers comprehensive indexing support (including fancy indexing via the python-blosc2 interface) as well as an increasingly rapid compute engine (see &lt;a class="reference external" href="https://ironarray.io/blog/miniexpr-powered-blosc2"&gt;this blog!&lt;/a&gt;). What if we could marry the incredibly comprehensive compression coverage of OpenZL with Blosc2's extended array manipulation functionality?&lt;/p&gt;
&lt;p&gt;Foreseeing precisely this sort of challenge, prior Blosc2 developers implemented a dynamic plugin register functionality (loading the plugin in C-Blosc2, which can be called via Python-Blosc2). This means that with some unintrusive, relatively concise interface code, one can link Blosc2 and OpenZL at runtime (without substantially modifying either) and offer Blosc2 arrays compressed and decompressed with OpenZL.&lt;/p&gt;
&lt;section id="the-openzl-plugin"&gt;
&lt;h2&gt;The OpenZL plugin&lt;/h2&gt;
&lt;p&gt;The source code for the plugin can be found &lt;a class="reference external" href="https://github.com/Blosc/blosc2-openzl"&gt;here&lt;/a&gt;. The minimal skeleton for the plugin layout follows&lt;/p&gt;
&lt;pre class="literal-block"&gt;├── CMakeLists.txt
├── blosc2_openzl
│   └── __init__.py
├── pyproject.toml
├── requirements-build.txt
└── src
    ├── CMakeLists.txt
    ├── blosc2_openzl.c
    └── blosc2_openzl.h&lt;/pre&gt;
&lt;p&gt;The &lt;code class="docutils literal"&gt;blosc2_openzl.c&lt;/code&gt; must implement an encoder and decoder which are exported via an &lt;code class="docutils literal"&gt;info&lt;/code&gt; struct:&lt;/p&gt;
&lt;pre class="literal-block"&gt;#include "blosc2_openzl.h"

BLOSC2_OPENZL_EXPORT codec_info info = {
    .encoder=(char *)"blosc2_openzl_encoder",
    .decoder=(char *)"blosc2_openzl_decoder"
};

int blosc2_openzl_encoder(const uint8_t* src, uint8_t* dest,
                                  int32_t size, uint8_t meta,
                                  blosc2_cparams *cparams, uint8_t id) {
  // code
}


int blosc2_openzl_decoder(const uint8_t *input, int32_t input_len, uint8_t *output,
                            int32_t output_len, uint8_t meta, blosc2_dparams *dparams,
                            const void *chunk) {
  // code
}&lt;/pre&gt;
&lt;p&gt;The header &lt;code class="docutils literal"&gt;blosc2_openzl.h&lt;/code&gt; then makes the &lt;code class="docutils literal"&gt;info&lt;/code&gt; and &lt;code class="docutils literal"&gt;encoder/decoder&lt;/code&gt; functions available to Blosc2:&lt;/p&gt;
&lt;pre class="literal-block"&gt;#include "blosc2.h"
#include "blosc2/codecs-registry.h"
#include "openzl/openzl.h"

BLOSC2_OPENZL_EXPORT int blosc2_openzl_encoder(...);

BLOSC2_OPENZL_EXPORT int blosc2_openzl_decoder(...);

// Declare the info struct as extern
extern BLOSC2_OPENZL_EXPORT codec_info info;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="pep-427-and-wheel-structure"&gt;
&lt;h2&gt;PEP 427 and wheel structure&lt;/h2&gt;
&lt;p&gt;In order for the plugin to dynamically link to Blosc2, it has to be able to find the Blosc2 library at runtime. This has historically been quite finicky since different platforms and package managers may store Python packages (and the associated &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;.so/.dylib/.dll&lt;/span&gt;&lt;/code&gt; library objects differently). Consequently, PEP 427 recommends distributing the Python wheels for packages which depend on compiled objects such as Python-Blosc2 in the following way&lt;/p&gt;
&lt;pre class="literal-block"&gt;blosc2
  ├── __init__.py
  ├── lib
  │   ├── libblosc2.so
  │   ├── cmake
  │   └── pkgconfig
  └── include
      └── blosc2.h&lt;/pre&gt;
&lt;p&gt;Finding the necessary &lt;code class="docutils literal"&gt;libblosc2.so&lt;/code&gt; object from the top-level &lt;code class="docutils literal"&gt;CMakeLists.txt&lt;/code&gt; file for the plugin is then as easy as:&lt;/p&gt;
&lt;pre class="literal-block"&gt;# Find blosc2 package location using Python
execute_process(
    COMMAND "${Python_EXECUTABLE}" -c "import blosc2, pathlib; print(pathlib.Path(blosc2.__file__).parent)"
    OUTPUT_VARIABLE BLOSC2_PACKAGE_DIR
)
set(BLOSC2_INCLUDE_DIR "${BLOSC2_PACKAGE_DIR}/include")
set(BLOSC2_LIB_DIR "${BLOSC2_PACKAGE_DIR}/lib")&lt;/pre&gt;
&lt;p&gt;After building the plugin backend in &lt;code class="docutils literal"&gt;src/CMakelists.txt&lt;/code&gt; one simply links the plugin to the backend (in this case &lt;code class="docutils literal"&gt;openzl&lt;/code&gt;) and installs like so:&lt;/p&gt;
&lt;pre class="literal-block"&gt;add_library(blosc2_openzl SHARED blosc2_openzl.c)
target_include_directories(blosc2_openzl PUBLIC ${BLOSC2_INCLUDE_DIR})
target_link_libraries(blosc2_openzl ${OPENZL_TARGET})
# Install
install(TARGETS blosc2_openzl
    RUNTIME DESTINATION blosc2_openzl
    LIBRARY DESTINATION blosc2_openzl
)&lt;/pre&gt;
&lt;p&gt;Note that it is not necessary to link &lt;code class="docutils literal"&gt;blosc2_openzl&lt;/code&gt; and &lt;code class="docutils literal"&gt;blosc2&lt;/code&gt; in &lt;code class="docutils literal"&gt;target_link_libraries&lt;/code&gt; as the former depends only on macros and structs defined in header files - and not functions. This makes the &lt;code class="docutils literal"&gt;libblosc2_openzl.so&lt;/code&gt; object especially light and robust, as blosc2 is not registered as an explicit dependency. In fact on Linux, even if the &lt;code class="docutils literal"&gt;blosc2_openzl.c&lt;/code&gt; were to include blosc2 functions, it is still not necessary to perform such linking!&lt;/p&gt;
&lt;p&gt;Following PEP 427 allows one to add an additional safeguard to check if the plugin fails to find blosc2 by adding the RUNTIME_PATH property to the installed object&lt;/p&gt;
&lt;pre class="literal-block"&gt;set_target_properties(blosc2_openzl PROPERTIES
    INSTALL_RPATH "$ORIGIN/../blosc2/lib"
)&lt;/pre&gt;
&lt;p&gt;It also allows one to easily find the plugin &lt;code class="docutils literal"&gt;.so&lt;/code&gt; object when calling via python - in the &lt;code class="docutils literal"&gt;blosc2_openzl/__init__.py&lt;/code&gt; file one can find the library path as easily as &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;os.path.abspath(Path(__file__).parent&lt;/span&gt; / libname)&lt;/code&gt; where &lt;code class="docutils literal"&gt;libname&lt;/code&gt; is the desired &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;.so/.dylib/.dll&lt;/span&gt;&lt;/code&gt; object (depending on platform). All these benefits have led us to update the wheel structure for &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;python-blosc2&lt;/span&gt;&lt;/code&gt; in the latest 4.0 release.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="using-openzl-from-python"&gt;
&lt;h2&gt;Using OpenZL from Python&lt;/h2&gt;
&lt;p&gt;Installing is then as simple as:&lt;/p&gt;
&lt;pre class="literal-block"&gt;pip install blosc2_openzl&lt;/pre&gt;
&lt;p&gt;One can also download the project and use the &lt;code class="docutils literal"&gt;cmake&lt;/code&gt; and &lt;code class="docutils literal"&gt;cmake &lt;span class="pre"&gt;--build&lt;/span&gt;&lt;/code&gt; commands to compile C-level tests or examples. But let's get compressing with &lt;code class="docutils literal"&gt;python&lt;/code&gt; straight away:&lt;/p&gt;
&lt;pre class="literal-block"&gt;import blosc2
import numpy as np
import blosc2_openzl
from blosc2_openzl import OpenZLProfile as OZLP
prof = OZLP.OZLPROF_SH_BD_LZ4
# Define the compression parameters for Blosc2
cparams = {'codec': blosc2.Codec.OPENZL, 'codec_meta': prof.value}

# Create (uncompressed) array
np_array = np.arange(1000).reshape((10,100))

# Compression with the OpenZL codec
bl_array = blosc2.asarray(np_array, cparams=cparams)
print(bl_array.cratio) # print compression ratio
&amp;gt;&amp;gt; 25.078369905956112&lt;/pre&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;The &lt;code class="docutils literal"&gt;OpenZLProfile&lt;/code&gt; enum contains the available profile pipelines that have been implemented in the plugin, which use the &lt;code class="docutils literal"&gt;codec_meta&lt;/code&gt; field (an 8-bit integer) to specify the desired transformation via codecs, filters and other nodes for the compression graph. Starting from the Least-Significant-Bit (LSB), setting the bits tells OpenZL how to build the graph:&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;CODEC | SHUFFLE | DELTA | SPLIT | CRC | x | x | x |&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;CODEC - If set, use LZ4. Else ZSTD.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SHUFFLE - If set, use shuffle (outputs a stream for every byte of input data typesize)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DELTA - If set, apply a bytedelta (to all streams if necessary)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SPLIT - If set, do not recombine the byte streams&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CRC - If set, store a checksum during compression and check it during decompression&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;The remaining bits may be used in the future.&lt;/p&gt;
&lt;p&gt;In the future it would be great to further expand the OpenZL functionalities that we can offer via the plugin, such as bespoke transformers trained via machine learning techniques - see the OpenZL page for a flavour of what can be done with the (still evolving) library.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;C-Blosc2's ability to support dynamically loaded plugins allows the library to grow in features without increasing the size and complexity of the library itself. For more information about user-defined plugins, refer to this &lt;a class="reference external" href="https://www.blosc.org/posts/registering-plugins/"&gt;blog entry&lt;/a&gt;. We have put this to work to offer linkage with the rather complex OpenZL library with a relatively rapid turnaround from design to prototype to full release in around a month. This is thanks to prior hard work by open source contributors from Blosc but naturally also OpenZL - many thanks to all!&lt;/p&gt;
&lt;p&gt;If you find our work useful and valuable, we would be grateful if you could support us by &lt;a class="reference external" href="https://www.blosc.org/pages/donate/"&gt;making a donation&lt;/a&gt;. Your contribution will help us continue to develop and improve Blosc packages, making them more accessible and useful for everyone.  Our team is committed to creating high-quality and efficient software, and your support will help us to achieve this goal.&lt;/p&gt;
&lt;/section&gt;</description><category>blosc plugins codecs openzl</category><guid>https://blosc.org/posts/openzl-plugin/</guid><pubDate>Fri, 30 Jan 2026 10:32:20 GMT</pubDate></item></channel></rss>