FormatZstd.cpp (3925B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 "FormatZstd.h" 8 9 #include "BaseAlgorithms.h" 10 #include "mozilla/dom/TransformStreamDefaultController.h" 11 #include "zstd/zstd.h" 12 13 namespace mozilla::dom::compression { 14 15 NS_IMPL_CYCLE_COLLECTION_INHERITED(ZstdDecompressionStreamAlgorithms, 16 TransformerAlgorithmsBase) 17 NS_IMPL_ADDREF_INHERITED(ZstdDecompressionStreamAlgorithms, 18 TransformerAlgorithmsBase) 19 NS_IMPL_RELEASE_INHERITED(ZstdDecompressionStreamAlgorithms, 20 TransformerAlgorithmsBase) 21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ZstdDecompressionStreamAlgorithms) 22 NS_INTERFACE_MAP_END_INHERITING(TransformerAlgorithmsBase) 23 24 Result<already_AddRefed<ZstdDecompressionStreamAlgorithms>, nsresult> 25 ZstdDecompressionStreamAlgorithms::Create() { 26 RefPtr<ZstdDecompressionStreamAlgorithms> alg = 27 new ZstdDecompressionStreamAlgorithms(); 28 MOZ_TRY(alg->Init()); 29 return alg.forget(); 30 } 31 32 [[nodiscard]] nsresult ZstdDecompressionStreamAlgorithms::Init() { 33 mDStream = ZSTD_createDStream(); 34 if (!mDStream) { 35 return NS_ERROR_OUT_OF_MEMORY; 36 } 37 38 // Refuse any frame requiring larger than (1 << WINDOW_LOG_MAX) window size. 39 // Note: 1 << 23 == 8 * 1024 * 1024 40 static const uint8_t WINDOW_LOG_MAX = 23; 41 ZSTD_DCtx_setParameter(mDStream, ZSTD_d_windowLogMax, WINDOW_LOG_MAX); 42 43 return NS_OK; 44 } 45 46 // Shared by: 47 // https://wicg.github.io/compression/#decompress-and-enqueue-a-chunk 48 // https://wicg.github.io/compression/#decompress-flush-and-enqueue 49 // All data errors throw TypeError by step 2: If this results in an error, 50 // then throw a TypeError. 51 bool ZstdDecompressionStreamAlgorithms::Decompress( 52 JSContext* aCx, Span<const uint8_t> aInput, 53 JS::MutableHandleVector<JSObject*> aOutput, Flush aFlush, 54 ErrorResult& aRv) { 55 ZSTD_inBuffer inBuffer = {/* src */ const_cast<uint8_t*>(aInput.Elements()), 56 /* size */ aInput.Length(), 57 /* pos */ 0}; 58 59 while (inBuffer.pos < inBuffer.size && !mObservedStreamEnd) { 60 UniquePtr<uint8_t[], JS::FreePolicy> buffer( 61 static_cast<uint8_t*>(JS_malloc(aCx, kBufferSize))); 62 if (!buffer) { 63 aRv.ThrowTypeError("Out of memory"); 64 return false; 65 } 66 67 ZSTD_outBuffer outBuffer = {/* dst */ buffer.get(), 68 /* size */ kBufferSize, 69 /* pos */ 0}; 70 71 size_t rv = ZSTD_decompressStream(mDStream, &outBuffer, &inBuffer); 72 if (ZSTD_isError(rv)) { 73 aRv.ThrowTypeError("zstd decompression error: "_ns + 74 nsDependentCString(ZSTD_getErrorName(rv))); 75 return false; 76 } 77 78 if (rv == 0) { 79 mObservedStreamEnd = true; 80 } 81 82 // Step 3: If buffer is empty, return. 83 // (We'll implicitly return when the array is empty.) 84 85 // Step 4: Split buffer into one or more non-empty pieces and convert them 86 // into Uint8Arrays. 87 // (The buffer is 'split' by having a fixed sized buffer above.) 88 89 size_t written = outBuffer.pos; 90 if (written > 0) { 91 JS::Rooted<JSObject*> view(aCx, nsJSUtils::MoveBufferAsUint8Array( 92 aCx, written, std::move(buffer))); 93 if (!view || !aOutput.append(view)) { 94 JS_ClearPendingException(aCx); 95 aRv.ThrowTypeError("Out of memory"); 96 return false; 97 } 98 } 99 } 100 101 return inBuffer.pos == inBuffer.size; 102 } 103 104 ZstdDecompressionStreamAlgorithms::~ZstdDecompressionStreamAlgorithms() { 105 if (mDStream) { 106 ZSTD_freeDStream(mDStream); 107 mDStream = nullptr; 108 } 109 } 110 111 } // namespace mozilla::dom::compression