Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit a74957c

Browse files
committed
Bug 1550108 - Change StartupCache format from zip to custom r=froydnj
I am not aware of anything that depends on StartupCache being a zip file, and since I want to use lz4 compression because inflate is showing up quite a lot in profiles, it's simplest to just use a custom format. This loosely mimicks the ScriptPreloader code, with a few diversions: - Obviously the contents of the cache are compressed. I used lz4 for this as I hit the same file size as deflate at a compression level of 1, which is what the StartupCache was using previously, while decompressing an order of magnitude faster. Seemed like the most conservative change to make. I think it's worth investigating what the impact of slower algs with higher ratios would be, but for right now I settled on this. We'd probably want to look at zstd next. - I use streaming compression for this via lz4frame. This is not strictly necessary, but has the benefit of not requiring as much memory for large buffers, as well as giving us a built-in checksum, rather than relying on the much slower CRC that we were doing with the zip-based approach. - I coded the serialization of the headers inline, since I had to jump back to add the offset and compressed size, which would make the nice Code(...) method for the ScriptPreloader stuff rather more complex. Open to cleaner solutions, but moving it out just felt like extra hoops for the reader to jump through to understand without the benefit of being more concise. Differential Revision: https://phabricator.services.mozilla.com/D34652 --HG-- extra : moz-landing-system : lando
1 parent 1678c5e commit a74957c

4 files changed

Lines changed: 586 additions & 181 deletions

File tree

mfbt/Compression.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#include <string>
1414

1515
#include "lz4/lz4.h"
16+
#include "lz4/lz4frame.h"
1617

18+
using namespace mozilla;
1719
using namespace mozilla::Compression;
1820

1921
/* Our wrappers */
@@ -78,3 +80,113 @@ bool LZ4::decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
7880
*aOutputSize = 0;
7981
return false;
8082
}
83+
84+
LZ4FrameCompressionContext::LZ4FrameCompressionContext(int aCompressionLevel,
85+
size_t aMaxSrcSize,
86+
bool aChecksum,
87+
bool aStableSrc)
88+
: mContext(nullptr),
89+
mCompressionLevel(aCompressionLevel),
90+
mGenerateChecksum(aChecksum),
91+
mStableSrc(aStableSrc),
92+
mMaxSrcSize(aMaxSrcSize),
93+
mWriteBufLen(0) {
94+
LZ4F_contentChecksum_t checksum =
95+
mGenerateChecksum ? LZ4F_contentChecksumEnabled : LZ4F_noContentChecksum;
96+
LZ4F_preferences_t prefs = {
97+
{
98+
LZ4F_max256KB,
99+
LZ4F_blockLinked,
100+
checksum,
101+
},
102+
mCompressionLevel,
103+
};
104+
mWriteBufLen = LZ4F_compressBound(mMaxSrcSize, &prefs);
105+
LZ4F_errorCode_t err = LZ4F_createCompressionContext(&mContext, LZ4F_VERSION);
106+
MOZ_RELEASE_ASSERT(!LZ4F_isError(err));
107+
}
108+
109+
LZ4FrameCompressionContext::~LZ4FrameCompressionContext() {
110+
LZ4F_freeCompressionContext(mContext);
111+
}
112+
113+
Result<Span<const char>, size_t> LZ4FrameCompressionContext::BeginCompressing(
114+
Span<char> aWriteBuffer) {
115+
mWriteBuffer = aWriteBuffer;
116+
LZ4F_contentChecksum_t checksum =
117+
mGenerateChecksum ? LZ4F_contentChecksumEnabled : LZ4F_noContentChecksum;
118+
LZ4F_preferences_t prefs = {
119+
{
120+
LZ4F_max256KB,
121+
LZ4F_blockLinked,
122+
checksum,
123+
},
124+
mCompressionLevel,
125+
};
126+
size_t headerSize = LZ4F_compressBegin(mContext, mWriteBuffer.Elements(),
127+
mWriteBufLen, &prefs);
128+
if (LZ4F_isError(headerSize)) {
129+
return Err(headerSize);
130+
}
131+
132+
return MakeSpan(static_cast<const char*>(mWriteBuffer.Elements()),
133+
headerSize);
134+
}
135+
136+
Result<Span<const char>, size_t>
137+
LZ4FrameCompressionContext::ContinueCompressing(Span<const char> aInput) {
138+
LZ4F_compressOptions_t opts = {};
139+
opts.stableSrc = (uint32_t)mStableSrc;
140+
size_t outputSize =
141+
LZ4F_compressUpdate(mContext, mWriteBuffer.Elements(), mWriteBufLen,
142+
aInput.Elements(), aInput.Length(), &opts);
143+
if (LZ4F_isError(outputSize)) {
144+
return Err(outputSize);
145+
}
146+
147+
return MakeSpan(static_cast<const char*>(mWriteBuffer.Elements()),
148+
outputSize);
149+
}
150+
151+
Result<Span<const char>, size_t> LZ4FrameCompressionContext::EndCompressing() {
152+
size_t outputSize =
153+
LZ4F_compressEnd(mContext, mWriteBuffer.Elements(), mWriteBufLen,
154+
/* options */ nullptr);
155+
if (LZ4F_isError(outputSize)) {
156+
return Err(outputSize);
157+
}
158+
159+
return MakeSpan(static_cast<const char*>(mWriteBuffer.Elements()),
160+
outputSize);
161+
}
162+
163+
LZ4FrameDecompressionContext::LZ4FrameDecompressionContext(bool aStableDest)
164+
: mContext(nullptr), mStableDest(aStableDest) {
165+
LZ4F_errorCode_t err =
166+
LZ4F_createDecompressionContext(&mContext, LZ4F_VERSION);
167+
MOZ_RELEASE_ASSERT(!LZ4F_isError(err));
168+
}
169+
170+
LZ4FrameDecompressionContext::~LZ4FrameDecompressionContext() {
171+
LZ4F_freeDecompressionContext(mContext);
172+
}
173+
174+
Result<LZ4FrameDecompressionResult, size_t>
175+
LZ4FrameDecompressionContext::Decompress(Span<char> aOutput,
176+
Span<const char> aInput) {
177+
LZ4F_decompressOptions_t opts = {};
178+
opts.stableDst = (uint32_t)mStableDest;
179+
size_t outBytes = aOutput.Length();
180+
size_t inBytes = aInput.Length();
181+
size_t result = LZ4F_decompress(mContext, aOutput.Elements(), &outBytes,
182+
aInput.Elements(), &inBytes, &opts);
183+
if (LZ4F_isError(result)) {
184+
return Err(result);
185+
}
186+
187+
LZ4FrameDecompressionResult decompressionResult = {};
188+
decompressionResult.mFinished = !result;
189+
decompressionResult.mSizeRead = inBytes;
190+
decompressionResult.mSizeWritten = outBytes;
191+
return decompressionResult;
192+
}

mfbt/Compression.h

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111

1212
#include "mozilla/Assertions.h"
1313
#include "mozilla/Types.h"
14+
#include "mozilla/Result.h"
15+
#include "mozilla/Span.h"
16+
#include "mozilla/UniquePtr.h"
17+
18+
struct LZ4F_cctx_s; // compression context
19+
struct LZ4F_dctx_s; // decompression context
1420

1521
namespace mozilla {
1622
namespace Compression {
@@ -137,6 +143,91 @@ class LZ4 {
137143
}
138144
};
139145

146+
/**
147+
* Context for LZ4 Frame-based streaming compression. Use this if you
148+
* want to incrementally compress something or if you want to compress
149+
* something such that another application can read it.
150+
*/
151+
class LZ4FrameCompressionContext final {
152+
public:
153+
MFBT_API LZ4FrameCompressionContext(int aCompressionLevel, size_t aMaxSrcSize,
154+
bool aChecksum, bool aStableSrc = false);
155+
156+
MFBT_API ~LZ4FrameCompressionContext();
157+
158+
size_t GetRequiredWriteBufferLength() { return mWriteBufLen; }
159+
160+
/**
161+
* Begin streaming frame-based compression.
162+
*
163+
* @return a Result with a Span containing the frame header, or an lz4 error
164+
* code (size_t).
165+
*/
166+
MFBT_API Result<Span<const char>, size_t> BeginCompressing(
167+
Span<char> aWriteBuffer);
168+
169+
/**
170+
* Continue streaming frame-based compression with the provided input.
171+
*
172+
* @param aInput input buffer to be compressed.
173+
* @return a Result with a Span containing compressed output, or an lz4 error
174+
* code (size_t).
175+
*/
176+
MFBT_API Result<Span<const char>, size_t> ContinueCompressing(
177+
Span<const char> aInput);
178+
179+
/**
180+
* Finalize streaming frame-based compression with the provided input.
181+
*
182+
* @return a Result with a Span containing compressed output and the frame
183+
* footer, or an lz4 error code (size_t).
184+
*/
185+
MFBT_API Result<Span<const char>, size_t> EndCompressing();
186+
187+
private:
188+
LZ4F_cctx_s* mContext;
189+
int mCompressionLevel;
190+
bool mGenerateChecksum;
191+
bool mStableSrc;
192+
size_t mMaxSrcSize;
193+
size_t mWriteBufLen;
194+
Span<char> mWriteBuffer;
195+
};
196+
197+
struct LZ4FrameDecompressionResult {
198+
size_t mSizeRead;
199+
size_t mSizeWritten;
200+
bool mFinished;
201+
};
202+
203+
/**
204+
* Context for LZ4 Frame-based streaming decompression. Use this if you
205+
* want to decompress something compressed by LZ4FrameCompressionContext
206+
* or by another application.
207+
*/
208+
class LZ4FrameDecompressionContext final {
209+
public:
210+
explicit MFBT_API LZ4FrameDecompressionContext(bool aStableDest = false);
211+
MFBT_API ~LZ4FrameDecompressionContext();
212+
213+
/**
214+
* Decompress a buffer/part of a buffer compressed with
215+
* LZ4FrameCompressionContext or another application.
216+
*
217+
* @param aOutput output buffer to be write results into.
218+
* @param aInput input buffer to be decompressed.
219+
* @return a Result with information on bytes read/written and whether we
220+
* completely decompressed the input into the output, or an lz4 error code
221+
* (size_t).
222+
*/
223+
MFBT_API Result<LZ4FrameDecompressionResult, size_t> Decompress(
224+
Span<char> aOutput, Span<const char> aInput);
225+
226+
private:
227+
LZ4F_dctx_s* mContext;
228+
bool mStableDest;
229+
};
230+
140231
} /* namespace Compression */
141232
} /* namespace mozilla */
142233

0 commit comments

Comments
 (0)