Skip to content

Commit 634e94d

Browse files
authored
Merge pull request #50 from areaDetector/add_lz4hdf5
Add lz4hdf5
2 parents 23ede75 + 27f0abe commit 634e94d

6 files changed

Lines changed: 226 additions & 0 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ CONFIG_SITE.local.* files in in the configure/ directory of the appropriate rele
4949
[top-level areaDetector](https://github.com/areaDetector/areaDetector) repository
5050
or in ADSupport/configure/CONFIG_SITE.*.
5151

52+
This module also builds a library for the HDF5 version of the LZ4 codec which compresses
53+
in block units. The library is called lz4hdf5.
54+
This codec adds a 12-byte header that contains the size of the uncompressed data and the block size.
55+
Each block contains a 4-byte header that contains the compressed length of that block.
56+
[Documentation](https://github.com/dectris/HDF5Plugin/blob/master/HDF5_LZ4.pdf)
57+
The code for this library is derived from the
58+
[H5Zlz4.c file](https://github.com/nexusformat/HDF5-External-Filter-Plugins/blob/master/LZ4/src/H5Zlz4.c)
59+
in the HDF5 library.
60+
5261
This module also builds shareable libraries for HDF5 compression filter plugins.
5362
These plugins can be used with any HDF5 application built with HDF5 1.8.11 or later.
5463
The plugin libraries that are built are HDF5_blosc_plugin.so, HDF5_bshuf_plugin.so,

RELEASE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ the EXAMPLE_RELEASE_PATHS.local, EXAMPLE_RELEASE_LIBS.local, and EXAMPLE_RELEASE
1212
files respectively, in the configure/ directory of the appropriate release of the
1313
[top-level areaDetector](https://github.com/areaDetector/areaDetector) repository.
1414

15+
## __R1-11 (April XXX, 2026)__
16+
* Added support for the HDF5 version of the LZ4 codec which compress in blocks.
17+
The code is modified from the HFZlz4.c file in the HDF external filter plugins package.
18+
This is the codec used by Dectris for LZ4 compression on the Stream2 interface.
19+
* Minor changes to support building with mingw32.
20+
* Made X11 support in GraphicsMagick optional to support cross-compiling.
21+
* Fixed incompatible pointer type in GraphicsMagick libjasper that caused error on RHEL 10.
22+
1523
## __R1-10 (May 26, 2021)__
1624
* Changed the support for reading MJPEG streams in GraphicsMagickSrc and xml2Src.
1725
In R1-5 nanohttp.c and nanohppt.h in xml2Src were changed to support MJPEG streams.

supportApp/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,8 @@ GraphicsMagickSrc_DEPEND_DIRS += tiffSrc zlibSrc xml2Src jpegSrc
3939

4040
DIRS += cbfSrc
4141

42+
DIRS += lz4hdf5Src
43+
lz4hdf5Src_DEPEND_DIRS += bitshuffleSrc
44+
4245
include $(TOP)/configure/RULES_DIRS
4346

supportApp/lz4hdf5Src/Makefile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
TOP=../..
2+
include $(TOP)/configure/CONFIG
3+
#----------------------------------------
4+
# ADD MACRO DEFINITIONS AFTER THIS LINE
5+
#=============================
6+
7+
INC += lz4hdf5.h
8+
9+
LIBRARY_IOC += lz4hdf5
10+
lz4hdf5_SRCS += lz4hdf5.c
11+
lz4hdf5_LIBS += bitshuffle
12+
lz4hdf5_SYS_LIBS_WIN32 += ws2_32
13+
14+
include $(TOP)/configure/RULES
15+
#----------------------------------------
16+
# ADD RULES AFTER THIS LINE

supportApp/lz4hdf5Src/lz4hdf5.c

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* This file implements a version of the lz4 codec where the data is compressed in blocks.
3+
* This codec adds a 12-byte header that contains the size of the uncompressed data and the block size.
4+
* Each block contains a 4-byte header that contains the compressed length of that block.
5+
* This codec is used by the HDF5 library and by Dectris for the Stream2 interface on their detectors.
6+
* The documentation is here: https://github.com/dectris/HDF5Plugin/blob/master/HDF5_LZ4.pdf
7+
* This code for this library is derived from the H5Zlz4.c file in the HDF5 External Filter Plugins library.
8+
* https://github.com/nexusformat/HDF5-External-Filter-Plugins/blob/master/LZ4/src/H5Zlz4.c
9+
*/
10+
11+
#include <sys/types.h>
12+
#include <stdint.h>
13+
#include <stdlib.h>
14+
#include <string.h>
15+
#include <stdio.h>
16+
#if defined(_WIN32)
17+
#include <winsock2.h>
18+
#else
19+
#include <arpa/inet.h>
20+
#endif
21+
#include <lz4.h>
22+
#include <epicsExport.h>
23+
#include <lz4hdf5.h>
24+
25+
#define htonll(x) ( ( (uint64_t)(htonl( (uint32_t)((x << 32) >> 32)))<< 32) | htonl( ((uint32_t)(x >> 32)) ))
26+
#define ntohll(x) htonll(x)
27+
28+
#define htobe16t(x) htons(x)
29+
#define htobe32t(x) htonl(x)
30+
#define htobe64t(x) htonll(x)
31+
#define be16toht(x) ntohs(x)
32+
#define be32toht(x) ntohl(x)
33+
#define be64toht(x) ntohll(x)
34+
35+
36+
#define DEFAULT_BLOCK_SIZE 1<<30; /* 1GB. LZ4 needs blocks < 1.9GB. */
37+
38+
epicsShareFunc size_t decompress_lz4hdf5(const char *inbuf, char *outbuf, size_t maxOutputSize, size_t *blockSizeOut)
39+
{
40+
size_t ret_value;
41+
42+
uint32_t *i32Buf;
43+
uint32_t blockSize;
44+
char *roBuf; /* pointer to current write position */
45+
uint64_t decompSize;
46+
const char* rpos = inbuf; /* pointer to current read position */
47+
const uint64_t * const i64Buf = (uint64_t *) rpos;
48+
const uint64_t origSize = (uint64_t)(be64toht(*i64Buf));/* is saved in be format */
49+
if (origSize > maxOutputSize) {
50+
printf("maxOutputSize=%d too small, needs to be at least %d\n", (int)maxOutputSize, (int)origSize);
51+
return 0;
52+
}
53+
rpos += 8; /* advance the pointer */
54+
55+
i32Buf = (uint32_t*)rpos;
56+
blockSize = (uint32_t)(be32toht(*i32Buf));
57+
rpos += 4;
58+
if(blockSize>origSize)
59+
blockSize = (uint32_t)origSize;
60+
*blockSizeOut = blockSize;
61+
roBuf = (char*)outbuf; /* pointer to current write position */
62+
decompSize = 0;
63+
/// start with the first block ///
64+
while(decompSize < origSize)
65+
{
66+
uint32_t compressedBlockSize; /// is saved in be format
67+
68+
if(origSize-decompSize < blockSize) /* the last block can be smaller than blockSize. */
69+
blockSize = (uint32_t)(origSize-decompSize);
70+
i32Buf = (uint32_t*)rpos;
71+
compressedBlockSize = be32toht(*i32Buf); /// is saved in be format
72+
rpos += 4;
73+
if(compressedBlockSize == blockSize) /* there was no compression */
74+
{
75+
memcpy(roBuf, rpos, blockSize);
76+
}
77+
else /* do the decompression */
78+
{
79+
#if LZ4_VERSION_NUMBER > 10300
80+
int compressedBytes = LZ4_decompress_fast(rpos, roBuf, blockSize);
81+
#else
82+
int compressedBytes = LZ4_uncompress(rpos, roBuf, blockSize);
83+
#endif
84+
if(compressedBytes != compressedBlockSize)
85+
{
86+
printf("decompressed size not the same: %d, != %d\n", compressedBytes, compressedBlockSize);
87+
return 0;
88+
}
89+
}
90+
91+
rpos += compressedBlockSize; /* advance the read pointer to the next block */
92+
roBuf += blockSize; /* advance the write pointer */
93+
decompSize += blockSize;
94+
}
95+
ret_value = (size_t)origSize; // should always work, as orig_size cannot be > 2GB (sizeof(size_t) < 4GB)
96+
return ret_value;
97+
}
98+
99+
epicsShareFunc size_t compress_lz4hdf5(const char *inbuf, char *outbuf, size_t nbytes, size_t maxOutputSize, size_t blockSize)
100+
{
101+
size_t nBlocks;
102+
size_t outSize; /* size of the output buffer. Header size (12 bytes) is included */
103+
size_t block;
104+
uint64_t *i64Buf;
105+
uint32_t *i32Buf;
106+
size_t maxDestSize;
107+
const char *rpos; /* pointer to current read position */
108+
char *roBuf; /* pointer to current write position */
109+
110+
if (nbytes > INT32_MAX)
111+
{
112+
/* can only compress chunks up to 2GB */
113+
return 0;
114+
}
115+
116+
if (blockSize == 0) blockSize = DEFAULT_BLOCK_SIZE;
117+
if (blockSize > nbytes)
118+
{
119+
blockSize = nbytes;
120+
}
121+
nBlocks = (nbytes-1)/blockSize +1;
122+
maxDestSize = LZ4_compressBound((int)nbytes) + 4 + 8 + nBlocks*4;
123+
if (maxDestSize > maxOutputSize) {
124+
printf("maxOutputSize=%d too small, needs to be at least %d\n", (int)maxOutputSize, (int)maxDestSize);
125+
return 0;
126+
}
127+
128+
rpos = inbuf; /* pointer to current read position */
129+
roBuf = outbuf; /* pointer to current write position */
130+
/* header */
131+
i64Buf = (uint64_t *) (roBuf);
132+
i64Buf[0] = htobe64t((uint64_t)nbytes); /* Store decompressed size in be format */
133+
roBuf += 8;
134+
135+
i32Buf = (uint32_t *) (roBuf);
136+
i32Buf[0] = htobe32t((uint32_t)blockSize); /* Store the block size in be format */
137+
roBuf += 4;
138+
139+
outSize = 12; /* size of the output buffer. Header size (12 bytes) is included */
140+
141+
for(block = 0; block < nBlocks; ++block)
142+
{
143+
uint32_t compBlockSize; /// reserve space for compBlockSize
144+
size_t origWritten = block*blockSize;
145+
if(nbytes - origWritten < blockSize) /* the last block may be < blockSize */
146+
blockSize = nbytes - origWritten;
147+
148+
#if LZ4_VERSION_NUMBER > 10300
149+
compBlockSize = LZ4_compress_default(rpos, roBuf+4, (int)blockSize, (int)(maxDestSize-outSize)); /// reserve space for compBlockSize
150+
#else
151+
compBlockSize = LZ4_compress(rpos, roBuf+4, blockSize); /// reserve space for compBlockSize
152+
#endif
153+
if(!compBlockSize)
154+
return 0;
155+
if(compBlockSize >= blockSize) /* compression did not save any space, do a memcpy instead */
156+
{
157+
compBlockSize = (uint32_t)blockSize;
158+
memcpy(roBuf+4, rpos, blockSize);
159+
}
160+
161+
i32Buf = (uint32_t *) (roBuf);
162+
i32Buf[0] = htobe32t((uint32_t)compBlockSize); /* write blocksize */
163+
roBuf += 4;
164+
165+
rpos += blockSize; /* advance read pointer */
166+
roBuf += compBlockSize; /* advance write pointer */
167+
outSize += compBlockSize + 4;
168+
}
169+
170+
return outSize;
171+
172+
}
173+

supportApp/lz4hdf5Src/lz4hdf5.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef LZ4HDF5_H
2+
#define LZ4HDF5_H
3+
4+
#include <shareLib.h>
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif /* __cplusplus */
9+
10+
epicsShareFunc size_t decompress_lz4hdf5(const char *inbuf, char *outbuf, size_t maxOutputSize, size_t *blockSize);
11+
epicsShareFunc size_t compress_lz4hdf5(const char *inbuf, char *outbuf, size_t nbytes, size_t maxOutputSize, size_t blockSize);
12+
13+
#ifdef __cplusplus
14+
}
15+
#endif /* __cplusplus */
16+
17+
#endif /* LZ4HDF5_H */

0 commit comments

Comments
 (0)