Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
ZipLZ4.cxx
Go to the documentation of this file.
1// Original Author: Brian Bockelman
2
3/*************************************************************************
4 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#include "ZipLZ4.h"
12
13#include "ROOT/RConfig.hxx"
14
15#include <cinttypes>
16#include <cstdint>
17#include <cstdio>
18#include <cstring>
19#include <lz4.h>
20#include <lz4hc.h>
21#include <xxhash.h>
22
23// Header consists of:
24// - 2 byte identifier "L4"
25// - 1 byte LZ4 version string.
26// - 3 bytes of uncompressed size
27// - 3 bytes of compressed size
28// - 8 byte checksum using xxhash 64.
29static const int kChecksumOffset = 2 + 1 + 3 + 3;
30static const int kChecksumSize = sizeof(XXH64_canonical_t);
32
33void R__zipLZ4(int cxlevel, int *srcsize, const char *src, int *tgtsize, char *tgt, int *irep)
34{
35 int LZ4_version = LZ4_versionNumber();
36 uint64_t out_size; /* compressed size */
37 uint64_t in_size = (unsigned)(*srcsize);
38
39 *irep = 0;
40
41 if (R__unlikely(*tgtsize <= 0)) {
42 return;
43 }
44
45 // Refuse to compress more than 16MB at a time -- we are only allowed 3 bytes for size info.
46 if (R__unlikely(*srcsize > 0xffffff || *srcsize < 0)) {
47 return;
48 }
49
50 int returnStatus;
51 if (cxlevel > 9) {
52 cxlevel = 9;
53 }
54 if (cxlevel >= 4) {
55 returnStatus = LZ4_compress_HC(src, &tgt[kHeaderSize], *srcsize, *tgtsize - kHeaderSize, cxlevel);
56 } else {
57 returnStatus = LZ4_compress_default(src, &tgt[kHeaderSize], *srcsize, *tgtsize - kHeaderSize);
58 }
59
60 if (R__unlikely(returnStatus == 0)) { /* LZ4 compression failed */
61 return;
62 }
63 XXH64_hash_t checksumResult = XXH64(tgt + kHeaderSize, returnStatus, 0);
64
65 tgt[0] = 'L';
66 tgt[1] = '4';
67 tgt[2] = (LZ4_version / (100 * 100));
68
69 out_size = returnStatus + kChecksumSize; /* compressed size, including the checksum. */
70
71 // NOTE: these next 6 bytes are required from the ROOT compressed buffer format;
72 // upper layers will assume they are laid out in a specific manner.
73 tgt[3] = (char)(out_size & 0xff);
74 tgt[4] = (char)((out_size >> 8) & 0xff);
75 tgt[5] = (char)((out_size >> 16) & 0xff);
76
77 tgt[6] = (char)(in_size & 0xff); /* decompressed size */
78 tgt[7] = (char)((in_size >> 8) & 0xff);
79 tgt[8] = (char)((in_size >> 16) & 0xff);
80
81 // Write out checksum.
82 XXH64_canonicalFromHash(reinterpret_cast<XXH64_canonical_t *>(tgt + kChecksumOffset), checksumResult);
83
84 *irep = (int)returnStatus + kHeaderSize;
85}
86
87void R__unzipLZ4(int *srcsize, const unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
88{
89 // NOTE: We don't check that srcsize / tgtsize is reasonable or within the ROOT-imposed limits.
90 // This is assumed to be handled by the upper layers.
91
92 int LZ4_version = LZ4_versionNumber() / (100 * 100);
93 *irep = 0;
94 if (R__unlikely(src[0] != 'L' || src[1] != '4')) {
95 fprintf(stderr, "R__unzipLZ4: algorithm run against buffer with incorrect header (got %d%d; expected %d%d).\n",
96 src[0], src[1], 'L', '4');
97 return;
98 }
99 if (R__unlikely(src[2] != LZ4_version)) {
100 fprintf(stderr,
101 "R__unzipLZ4: This version of LZ4 is incompatible with the on-disk version (got %d; expected %d).\n",
102 src[2], LZ4_version);
103 return;
104 }
105
106 int inputBufferSize = *srcsize - kHeaderSize;
107
108 // TODO: The checksum followed by the decompression means we iterate through the buffer twice.
109 // We should perform some performance tests to see whether we can interleave the two -- i.e., at
110 // what size of chunks does interleaving (avoiding two fetches from RAM) improve enough for the
111 // extra function call costs? NOTE that ROOT limits the buffer size to 16MB.
112 XXH64_hash_t checksumResult = XXH64(src + kHeaderSize, inputBufferSize, 0);
113 XXH64_hash_t checksumFromFile =
114 XXH64_hashFromCanonical(reinterpret_cast<const XXH64_canonical_t *>(src + kChecksumOffset));
115
116 if (R__unlikely(checksumFromFile != checksumResult)) {
117 fprintf(
118 stderr,
119 "R__unzipLZ4: Buffer corruption error! Calculated checksum %llu; checksum calculated in the file was %llu.\n",
120 (unsigned long long) checksumResult, (unsigned long long) checksumFromFile);
121 return;
122 }
123 int returnStatus = LZ4_decompress_safe((char *)(&src[kHeaderSize]), (char *)(tgt), inputBufferSize, *tgtsize);
124 if (R__unlikely(returnStatus < 0)) {
125 fprintf(stderr, "R__unzipLZ4: error in decompression around byte %d out of maximum %d.\n", -returnStatus,
126 *tgtsize);
127 return;
128 }
129
130 *irep = returnStatus;
131}
#define R__unlikely(expr)
Definition RConfig.hxx:586
void R__unzipLZ4(int *srcsize, const unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
Definition ZipLZ4.cxx:87
static const int kChecksumOffset
Definition ZipLZ4.cxx:29
static const int kChecksumSize
Definition ZipLZ4.cxx:30
static const int kHeaderSize
Definition ZipLZ4.cxx:31
void R__zipLZ4(int cxlevel, int *srcsize, const char *src, int *tgtsize, char *tgt, int *irep)
Definition ZipLZ4.cxx:33