1 //
2 // Copyright 2017 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24
25 #include "pxr/pxr.h"
26
27 #include "pxr/base/tf/diagnostic.h"
28 #include "pxr/base/tf/fastCompression.h"
29
30 // XXX: Need to isolate symbols here?
31 #include "pxrLZ4/lz4.h"
32
33 PXR_NAMESPACE_OPEN_SCOPE
34
35 using namespace pxr_lz4;
36
37 size_t
GetMaxInputSize()38 TfFastCompression::GetMaxInputSize()
39 {
40 return 127 * static_cast<size_t>(LZ4_MAX_INPUT_SIZE);
41 }
42
43 size_t
GetCompressedBufferSize(size_t inputSize)44 TfFastCompression::GetCompressedBufferSize(size_t inputSize)
45 {
46 if (inputSize > GetMaxInputSize())
47 return 0;
48
49 // If it fits in one chunk then it's just the compress bound plus 1.
50 if (inputSize <= LZ4_MAX_INPUT_SIZE) {
51 return LZ4_compressBound(inputSize) + 1;
52 }
53 size_t nWholeChunks = inputSize / LZ4_MAX_INPUT_SIZE;
54 size_t partChunkSz = inputSize % LZ4_MAX_INPUT_SIZE;
55 size_t sz = 1 + nWholeChunks *
56 (LZ4_compressBound(LZ4_MAX_INPUT_SIZE) + sizeof(int32_t));
57 if (partChunkSz)
58 sz += LZ4_compressBound(partChunkSz) + sizeof(int32_t);
59 return sz;
60 }
61
62 size_t
CompressToBuffer(char const * input,char * compressed,size_t inputSize)63 TfFastCompression::CompressToBuffer(
64 char const *input, char *compressed, size_t inputSize)
65 {
66 if (inputSize > GetMaxInputSize()) {
67 TF_CODING_ERROR("Attempted to compress a buffer of %zu bytes, "
68 "more than the maximum supported %zu",
69 inputSize, GetMaxInputSize());
70 return 0;
71 }
72
73 // If it fits in one chunk, just do it.
74 char const * const origCompressed = compressed;
75 if (inputSize <= LZ4_MAX_INPUT_SIZE) {
76 compressed[0] = 0; // < zero byte means one chunk.
77 compressed += 1 + LZ4_compress_default(
78 input, compressed + 1, inputSize,
79 GetCompressedBufferSize(inputSize));
80 } else {
81 size_t nWholeChunks = inputSize / LZ4_MAX_INPUT_SIZE;
82 size_t partChunkSz = inputSize % LZ4_MAX_INPUT_SIZE;
83 *compressed++ = nWholeChunks + (partChunkSz ? 1 : 0);
84 auto writeChunk = [](char const *&input, char *&output, size_t size) {
85 char *o = output;
86 output += sizeof(int32_t);
87 int32_t n = LZ4_compress_default(
88 input, output, size, LZ4_compressBound(size));
89 memcpy(o, &n, sizeof(n));
90 output += n;
91 input += size;
92 };
93 for (size_t chunk = 0; chunk != nWholeChunks; ++chunk) {
94 writeChunk(input, compressed, LZ4_MAX_INPUT_SIZE);
95 }
96 if (partChunkSz) {
97 writeChunk(input, compressed, partChunkSz);
98 }
99 }
100
101 return compressed - origCompressed;
102 }
103
104 size_t
DecompressFromBuffer(char const * compressed,char * output,size_t compressedSize,size_t maxOutputSize)105 TfFastCompression::DecompressFromBuffer(
106 char const *compressed, char *output,
107 size_t compressedSize, size_t maxOutputSize)
108 {
109 // Check first byte for # chunks.
110 int nChunks = *compressed++;
111 if (nChunks == 0) {
112 // Just one.
113 int nDecompressed = LZ4_decompress_safe(
114 compressed, output, compressedSize-1, maxOutputSize);
115 if (nDecompressed < 0) {
116 TF_RUNTIME_ERROR("Failed to decompress data, possibly corrupt? "
117 "LZ4 error code: %d", nDecompressed);
118 return 0;
119 }
120 return nDecompressed;
121 } else {
122 // Do each chunk.
123 size_t totalDecompressed = 0;
124 for (int i = 0; i != nChunks; ++i) {
125 int32_t chunkSize = 0;
126 memcpy(&chunkSize, compressed, sizeof(chunkSize));
127 compressed += sizeof(chunkSize);
128 int nDecompressed = LZ4_decompress_safe(
129 compressed, output, chunkSize,
130 std::min<size_t>(LZ4_MAX_INPUT_SIZE, maxOutputSize));
131 if (nDecompressed < 0) {
132 TF_RUNTIME_ERROR("Failed to decompress data, possibly corrupt? "
133 "LZ4 error code: %d", nDecompressed);
134 return 0;
135 }
136 compressed += chunkSize;
137 output += nDecompressed;
138 maxOutputSize -= nDecompressed;
139 totalDecompressed += nDecompressed;
140 }
141 return totalDecompressed;
142 }
143 // unreachable.
144 }
145
146 PXR_NAMESPACE_CLOSE_SCOPE
147