1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/Compression.h"
8 #include "mozilla/CheckedInt.h"
9 
10 // Without including <string>, MSVC 2015 complains about e.g. the impossibility
11 // to convert `const void* const` to `void*` when calling memchr from
12 // corecrt_memory.h.
13 #include <string>
14 
15 #include "lz4/lz4.h"
16 #include "lz4/lz4frame.h"
17 
18 using namespace mozilla;
19 using namespace mozilla::Compression;
20 
21 /* Our wrappers */
22 
compress(const char * aSource,size_t aInputSize,char * aDest)23 size_t LZ4::compress(const char* aSource, size_t aInputSize, char* aDest) {
24   CheckedInt<int> inputSizeChecked = aInputSize;
25   MOZ_ASSERT(inputSizeChecked.isValid());
26   return LZ4_compress_default(aSource, aDest, inputSizeChecked.value(),
27                               LZ4_compressBound(inputSizeChecked.value()));
28 }
29 
compressLimitedOutput(const char * aSource,size_t aInputSize,char * aDest,size_t aMaxOutputSize)30 size_t LZ4::compressLimitedOutput(const char* aSource, size_t aInputSize,
31                                   char* aDest, size_t aMaxOutputSize) {
32   CheckedInt<int> inputSizeChecked = aInputSize;
33   MOZ_ASSERT(inputSizeChecked.isValid());
34   CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
35   MOZ_ASSERT(maxOutputSizeChecked.isValid());
36   return LZ4_compress_default(aSource, aDest, inputSizeChecked.value(),
37                               maxOutputSizeChecked.value());
38 }
39 
decompress(const char * aSource,size_t aInputSize,char * aDest,size_t aMaxOutputSize,size_t * aOutputSize)40 bool LZ4::decompress(const char* aSource, size_t aInputSize, char* aDest,
41                      size_t aMaxOutputSize, size_t* aOutputSize) {
42   CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
43   MOZ_ASSERT(maxOutputSizeChecked.isValid());
44   CheckedInt<int> inputSizeChecked = aInputSize;
45   MOZ_ASSERT(inputSizeChecked.isValid());
46 
47   int ret = LZ4_decompress_safe(aSource, aDest, inputSizeChecked.value(),
48                                 maxOutputSizeChecked.value());
49   if (ret >= 0) {
50     *aOutputSize = ret;
51     return true;
52   }
53 
54   *aOutputSize = 0;
55   return false;
56 }
57 
decompressPartial(const char * aSource,size_t aInputSize,char * aDest,size_t aMaxOutputSize,size_t * aOutputSize)58 bool LZ4::decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
59                             size_t aMaxOutputSize, size_t* aOutputSize) {
60   CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
61   MOZ_ASSERT(maxOutputSizeChecked.isValid());
62   CheckedInt<int> inputSizeChecked = aInputSize;
63   MOZ_ASSERT(inputSizeChecked.isValid());
64 
65   int ret = LZ4_decompress_safe_partial(
66       aSource, aDest, inputSizeChecked.value(), maxOutputSizeChecked.value(),
67       maxOutputSizeChecked.value());
68   if (ret >= 0) {
69     *aOutputSize = ret;
70     return true;
71   }
72 
73   *aOutputSize = 0;
74   return false;
75 }
76 
LZ4FrameCompressionContext(int aCompressionLevel,size_t aMaxSrcSize,bool aChecksum,bool aStableSrc)77 LZ4FrameCompressionContext::LZ4FrameCompressionContext(int aCompressionLevel,
78                                                        size_t aMaxSrcSize,
79                                                        bool aChecksum,
80                                                        bool aStableSrc)
81     : mContext(nullptr),
82       mCompressionLevel(aCompressionLevel),
83       mGenerateChecksum(aChecksum),
84       mStableSrc(aStableSrc),
85       mMaxSrcSize(aMaxSrcSize),
86       mWriteBufLen(0) {
87   LZ4F_contentChecksum_t checksum =
88       mGenerateChecksum ? LZ4F_contentChecksumEnabled : LZ4F_noContentChecksum;
89   LZ4F_preferences_t prefs = {
90       {
91           LZ4F_max256KB,
92           LZ4F_blockLinked,
93           checksum,
94       },
95       mCompressionLevel,
96   };
97   mWriteBufLen = LZ4F_compressBound(mMaxSrcSize, &prefs);
98   LZ4F_errorCode_t err = LZ4F_createCompressionContext(&mContext, LZ4F_VERSION);
99   MOZ_RELEASE_ASSERT(!LZ4F_isError(err));
100 }
101 
~LZ4FrameCompressionContext()102 LZ4FrameCompressionContext::~LZ4FrameCompressionContext() {
103   LZ4F_freeCompressionContext(mContext);
104 }
105 
BeginCompressing(Span<char> aWriteBuffer)106 Result<Span<const char>, size_t> LZ4FrameCompressionContext::BeginCompressing(
107     Span<char> aWriteBuffer) {
108   mWriteBuffer = aWriteBuffer;
109   LZ4F_contentChecksum_t checksum =
110       mGenerateChecksum ? LZ4F_contentChecksumEnabled : LZ4F_noContentChecksum;
111   LZ4F_preferences_t prefs = {
112       {
113           LZ4F_max256KB,
114           LZ4F_blockLinked,
115           checksum,
116       },
117       mCompressionLevel,
118   };
119   size_t headerSize = LZ4F_compressBegin(mContext, mWriteBuffer.Elements(),
120                                          mWriteBufLen, &prefs);
121   if (LZ4F_isError(headerSize)) {
122     return Err(headerSize);
123   }
124 
125   return Span{static_cast<const char*>(mWriteBuffer.Elements()), headerSize};
126 }
127 
128 Result<Span<const char>, size_t>
ContinueCompressing(Span<const char> aInput)129 LZ4FrameCompressionContext::ContinueCompressing(Span<const char> aInput) {
130   LZ4F_compressOptions_t opts = {};
131   opts.stableSrc = (uint32_t)mStableSrc;
132   size_t outputSize =
133       LZ4F_compressUpdate(mContext, mWriteBuffer.Elements(), mWriteBufLen,
134                           aInput.Elements(), aInput.Length(), &opts);
135   if (LZ4F_isError(outputSize)) {
136     return Err(outputSize);
137   }
138 
139   return Span{static_cast<const char*>(mWriteBuffer.Elements()), outputSize};
140 }
141 
EndCompressing()142 Result<Span<const char>, size_t> LZ4FrameCompressionContext::EndCompressing() {
143   size_t outputSize =
144       LZ4F_compressEnd(mContext, mWriteBuffer.Elements(), mWriteBufLen,
145                        /* options */ nullptr);
146   if (LZ4F_isError(outputSize)) {
147     return Err(outputSize);
148   }
149 
150   return Span{static_cast<const char*>(mWriteBuffer.Elements()), outputSize};
151 }
152 
LZ4FrameDecompressionContext(bool aStableDest)153 LZ4FrameDecompressionContext::LZ4FrameDecompressionContext(bool aStableDest)
154     : mContext(nullptr), mStableDest(aStableDest) {
155   LZ4F_errorCode_t err =
156       LZ4F_createDecompressionContext(&mContext, LZ4F_VERSION);
157   MOZ_RELEASE_ASSERT(!LZ4F_isError(err));
158 }
159 
~LZ4FrameDecompressionContext()160 LZ4FrameDecompressionContext::~LZ4FrameDecompressionContext() {
161   LZ4F_freeDecompressionContext(mContext);
162 }
163 
164 Result<LZ4FrameDecompressionResult, size_t>
Decompress(Span<char> aOutput,Span<const char> aInput)165 LZ4FrameDecompressionContext::Decompress(Span<char> aOutput,
166                                          Span<const char> aInput) {
167   LZ4F_decompressOptions_t opts = {};
168   opts.stableDst = (uint32_t)mStableDest;
169   size_t outBytes = aOutput.Length();
170   size_t inBytes = aInput.Length();
171   size_t result = LZ4F_decompress(mContext, aOutput.Elements(), &outBytes,
172                                   aInput.Elements(), &inBytes, &opts);
173   if (LZ4F_isError(result)) {
174     return Err(result);
175   }
176 
177   LZ4FrameDecompressionResult decompressionResult = {};
178   decompressionResult.mFinished = !result;
179   decompressionResult.mSizeRead = inBytes;
180   decompressionResult.mSizeWritten = outBytes;
181   return decompressionResult;
182 }
183