Merged PR 2300105: switch to use inbox zlib on android.
updating android to use the ndk version of libz.so instead of the static lib built in the xplatappx repo. If you want to use the xplatappx one instead, you can specify the -xzlib build option to makeaosp.sh Refactored the inflatestream code to keep the common portions of stream state management and pulled out the compression implementation specifics into a compressionObject PAL implementation. This reduces the binary size by ~30K on android. Related work items: #18607665
This commit is contained in:
Родитель
eb5b7dd841
Коммит
fad5fc8b2a
|
@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.4.0 FATAL_ERROR)
|
|||
|
||||
ADD_CUSTOM_TARGET(LIBS)
|
||||
|
||||
IF((NOT ((MACOS) OR (IOS))) OR USE_ZLIB)
|
||||
IF((NOT ((MACOS) OR (IOS) OR (AOSP))) OR USE_MSIX_SDK_ZLIB)
|
||||
# For mac and ios we use inbox libcompression apis.
|
||||
# ZLIB
|
||||
# set(AMD64 OFF CACHE BOOL "Disable building i686 assembly implementation" FORCE)
|
||||
|
|
10
makeaosp.sh
10
makeaosp.sh
|
@ -7,16 +7,18 @@ build=MinSizeRel
|
|||
version=19
|
||||
sdk=
|
||||
sdkver=24
|
||||
dataCompressionLib=NDK_libz
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: makeaosp [-ndk ndk_path] [-arch arch] [-ndkver ndk_version] [-sdk sdk_path] [-sdkver sdk_version] [-b buildType]"
|
||||
echo "usage: makeaosp [-ndk ndk_path] [-arch arch] [-ndkver ndk_version] [-sdk sdk_path] [-sdkver sdk_version] [-b buildType] [-xzlib]"
|
||||
echo $'\t' "-ndk Path to Android NDK. Default $ANDROID_NDK_ROOT or $ANDROID_NDK"
|
||||
echo $'\t' "-ndkver Android NDK version. Default/minimum 19."
|
||||
echo $'\t' "-sdk Path to Android SDK. Default $ANDROID_HOME."
|
||||
echo $'\t' "-sdkver Android SDK version. Default/minimum 24."
|
||||
echo $'\t' "-arch Architecture ABI. Default x86"
|
||||
echo $'\t' "-b Build type. Default MinSizeRel"
|
||||
echo $'\t' "-xzlib Use MSIX SDK Zlib instead of inbox libz.so"
|
||||
}
|
||||
|
||||
printsetup()
|
||||
|
@ -27,6 +29,7 @@ printsetup()
|
|||
echo "SDK Version:" $sdkver
|
||||
echo "Architecture:" $arch
|
||||
echo "Build Type:" $build
|
||||
echo "Zlib:" $dataCompressionLib
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
|
@ -46,6 +49,9 @@ while [ "$1" != "" ]; do
|
|||
-h ) usage
|
||||
exit
|
||||
;;
|
||||
-xzlib ) dataCompressionLib=MSIX_SDK_zlib
|
||||
zlib="-DUSE_MSIX_SDK_ZLIB=on"
|
||||
;;
|
||||
-sdk ) shift
|
||||
sdk=$1
|
||||
;;
|
||||
|
@ -90,5 +96,5 @@ cmake -DCMAKE_SYSTEM_NAME=Android \
|
|||
-DCMAKE_ANDROID_ARCH_ABI="$arch" \
|
||||
-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang \
|
||||
-DCMAKE_ANDROID_STL_TYPE=c++_shared \
|
||||
-DCMAKE_BUILD_TYPE="$build" -DAOSP=on ..
|
||||
-DCMAKE_BUILD_TYPE="$build" $zlib -DAOSP=on ..
|
||||
make
|
||||
|
|
|
@ -6,10 +6,10 @@ dataCompressionLib=libcompression
|
|||
|
||||
usage()
|
||||
{
|
||||
echo "usage: makemac [-b buildType] [-arch] [-zlib]"
|
||||
echo "usage: makemac [-b buildType] [-arch] [-xzlib]"
|
||||
echo $'\t' "-b Build type. Default MinSizeRel"
|
||||
echo $'\t' "-arch OSX Architecture. Default x86_64 (simulator)"
|
||||
echo $'\t' "-zlib Use Zlib instead of inbox libCompression api. Default on iOS is libCompression."
|
||||
echo $'\t' "-xzlib Use MSIX SDK Zlib instead of inbox libCompression api. Default on iOS is libCompression."
|
||||
}
|
||||
|
||||
printsetup()
|
||||
|
@ -27,9 +27,8 @@ while [ "$1" != "" ]; do
|
|||
-arch ) shift
|
||||
arch=$1
|
||||
;;
|
||||
-zlib ) shift
|
||||
dataCompressionLib=zlib
|
||||
zlib="-DUSE_ZLIB=on"
|
||||
-xzlib )dataCompressionLib=MSIX_SDK_zlib
|
||||
zlib="-DUSE_MSIX_SDK_ZLIB=on"
|
||||
;;
|
||||
-h ) usage
|
||||
exit
|
||||
|
|
|
@ -5,9 +5,9 @@ dataCompressionLib=libcompression
|
|||
|
||||
usage()
|
||||
{
|
||||
echo "usage: makemac [-b buildType] [-zlib]"
|
||||
echo "usage: makemac [-b buildType] [-xzlib]"
|
||||
echo $'\t' "-b Build type. Default MinSizeRel"
|
||||
echo $'\t' "-zlib Use Zlib instead of inbox libCompression api. Default on MacOS is libCompression."
|
||||
echo $'\t' "-xzlib Use MSIX SDK Zlib instead of inbox libCompression api. Default on MacOS is libCompression."
|
||||
}
|
||||
|
||||
printsetup()
|
||||
|
@ -21,9 +21,8 @@ while [ "$1" != "" ]; do
|
|||
-b ) shift
|
||||
build=$1
|
||||
;;
|
||||
-zlib ) shift
|
||||
dataCompressionLib=zlib
|
||||
zlib="-DUSE_ZLIB=on"
|
||||
-xzlib )dataCompressionLib=MSIX_SDK_zlib
|
||||
zlib="-DUSE_MSIX_SDK_ZLIB=on"
|
||||
;;
|
||||
-h ) usage
|
||||
exit
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace MSIX {
|
||||
enum class CompressionOperation
|
||||
{
|
||||
Inflate,
|
||||
Deflate
|
||||
};
|
||||
|
||||
enum class CompressionStatus
|
||||
{
|
||||
Ok,
|
||||
Error,
|
||||
End,
|
||||
NeedDictionary
|
||||
};
|
||||
|
||||
class ICompressionObject
|
||||
{
|
||||
public:
|
||||
virtual CompressionStatus Initialize(CompressionOperation operation) = 0;
|
||||
virtual CompressionStatus Inflate() = 0;
|
||||
virtual CompressionStatus Cleanup() = 0;
|
||||
virtual std::size_t GetAvailableSourceSize() = 0;
|
||||
virtual std::size_t GetAvailableDestinationSize() = 0;
|
||||
virtual void SetInput(std::uint8_t* buffer, std::size_t size) = 0;
|
||||
virtual void SetOutput(std::uint8_t* buffer, std::size_t size) = 0;
|
||||
virtual ~ICompressionObject() = default;
|
||||
};
|
||||
|
||||
std::unique_ptr<ICompressionObject> CreateCompressionObject();
|
||||
}
|
|
@ -3,10 +3,10 @@
|
|||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#pragma once
|
||||
#include "Compression.h"
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "ComHelper.hpp"
|
||||
#include "ICompressionObject.hpp"
|
||||
|
||||
#undef max
|
||||
#undef min
|
||||
|
@ -70,8 +70,8 @@ namespace MSIX {
|
|||
ULONGLONG m_fileCurrentWindowPositionEnd = 0;
|
||||
ULONGLONG m_fileCurrentPosition = 0;
|
||||
|
||||
compression_stream m_compressionStream;
|
||||
compression_status m_compressionStatus;
|
||||
std::unique_ptr<ICompressionObject> m_compressionObject;
|
||||
CompressionStatus m_compressionStatus = CompressionStatus::Ok;
|
||||
|
||||
std::unique_ptr<std::vector<std::uint8_t>> m_compressedBuffer;
|
||||
std::unique_ptr<std::vector<std::uint8_t>> m_inflateWindow;
|
|
@ -159,12 +159,10 @@ set(LIB_PUBLIC_HEADERS
|
|||
../inc/MSIXWindows.hpp
|
||||
)
|
||||
|
||||
if (((IOS) OR (MACOS)) AND (NOT USE_ZLIB))
|
||||
set(InflateStreamCpp PAL/DataCompression/Apple/InflateStream.cpp)
|
||||
set(InflateStreamHpp PAL/DataCompression/Apple/InflateStream.hpp)
|
||||
if (((IOS) OR (MACOS)) AND (NOT USE_MSIX_SDK_ZLIB))
|
||||
set(CompressionObjectCpp PAL/DataCompression/Apple/CompressionObject.cpp)
|
||||
else()
|
||||
set(InflateStreamCpp PAL/DataCompression/Zlib/InflateStream.cpp)
|
||||
set(InflateStreamHpp PAL/DataCompression/Zlib/InflateStream.hpp)
|
||||
set(CompressionObjectCpp PAL/DataCompression/Zlib/CompressionObject.cpp)
|
||||
endif()
|
||||
|
||||
set(LIB_PRIVATE_HEADERS
|
||||
|
@ -182,7 +180,8 @@ set(LIB_PRIVATE_HEADERS
|
|||
../inc/Enumerators.hpp
|
||||
../inc/Exceptions.hpp
|
||||
../inc/FileStream.hpp
|
||||
${InflateStreamHpp}
|
||||
../inc/ICompressionObject.hpp
|
||||
../inc/InflateStream.hpp
|
||||
../inc/Log.hpp
|
||||
../inc/MSIXFactory.hpp
|
||||
../inc/MSIXResource.hpp
|
||||
|
@ -208,7 +207,7 @@ set(LIB_SOURCES
|
|||
AppxPackaging_i.cpp
|
||||
AppxSignature.cpp
|
||||
Exceptions.cpp
|
||||
${InflateStreamCpp}
|
||||
InflateStream.cpp
|
||||
Log.cpp
|
||||
UnicodeConversion.cpp
|
||||
msix.cpp
|
||||
|
@ -219,6 +218,7 @@ set(LIB_SOURCES
|
|||
${Signature}
|
||||
${XmlParser}
|
||||
${Applicability}
|
||||
${CompressionObjectCpp}
|
||||
)
|
||||
|
||||
# Copy out public headers
|
||||
|
@ -264,10 +264,14 @@ set_target_properties(${LIBRARY_NAME} PROPERTIES
|
|||
PUBLIC_HEADER "${LIB_HEADERS}" # specify the public headers
|
||||
)
|
||||
|
||||
if (((IOS) OR (MACOS)) AND (NOT USE_ZLIB))
|
||||
if (((IOS) OR (MACOS)) AND (NOT USE_MSIX_SDK_ZLIB))
|
||||
# for macos and ios use the inbox libcompression zlib apis instead of zlib, unless zlib is explicitly requested.
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE libcompression.dylib)
|
||||
include_directories(${include_directories} ${CMAKE_PROJECT_ROOT}/src/msix/PAL/DataCompression/Apple)
|
||||
elseif ((AOSP) AND (NOT USE_MSIX_SDK_ZLIB))
|
||||
# for AOSP, use the libz.so from the android ndk.
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE -lz)
|
||||
else()
|
||||
include_directories(
|
||||
${include_directories}
|
||||
|
|
|
@ -32,25 +32,23 @@ namespace MSIX {
|
|||
InflateHandler([](InflateStream* self, void*, ULONG)
|
||||
{
|
||||
ThrowHrIfFailed(self->m_stream->Seek({0}, StreamBase::START, nullptr));
|
||||
self->m_compressionStream = { 0 };
|
||||
self->m_fileCurrentPosition = 0;
|
||||
self->m_fileCurrentWindowPositionEnd = 0;
|
||||
|
||||
self->m_compressionStatus = compression_stream_init(&(self->m_compressionStream), COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB);
|
||||
ThrowErrorIfNot(Error::InflateInitialize, (self->m_compressionStatus == COMPRESSION_STATUS_OK), "compression_stream_init failed");
|
||||
self->m_compressionStatus = self->m_compressionObject->Initialize(CompressionOperation::Inflate);
|
||||
ThrowErrorIfNot(Error::InflateInitialize, (self->m_compressionStatus == CompressionStatus::Ok), "compression_stream_init failed");
|
||||
return std::make_pair(true, InflateStream::State::READY_TO_READ);
|
||||
}), // State::UNINITIALIZED
|
||||
|
||||
// State::READY_TO_READ
|
||||
InflateHandler([](InflateStream* self, void*, ULONG)
|
||||
{
|
||||
ThrowErrorIfNot(Error::InflateRead,(self->m_compressionStream.src_size == 0), "uninflated bytes overwritten");
|
||||
ThrowErrorIfNot(Error::InflateRead,(self->m_compressionObject->GetAvailableSourceSize() == 0), "uninflated bytes overwritten");
|
||||
ULONG available = 0;
|
||||
self->m_compressedBuffer = std::make_unique<std::vector<std::uint8_t>>(BufferSize);
|
||||
ThrowHrIfFailed(self->m_stream->Read(self->m_compressedBuffer->data(), self->m_compressedBuffer->size(), &available));
|
||||
ThrowHrIfFailed(self->m_stream->Read(self->m_compressedBuffer->data(), static_cast<ULONG>(self->m_compressedBuffer->size()), &available));
|
||||
ThrowErrorIf(Error::FileRead, (available == 0), "Getting nothing back is unexpected here.");
|
||||
self->m_compressionStream.src_size = static_cast<size_t>(available);
|
||||
self->m_compressionStream.src_ptr = self->m_compressedBuffer->data();
|
||||
self->m_compressionObject->SetInput(self->m_compressedBuffer->data(), static_cast<size_t>(available));
|
||||
return std::make_pair(true, InflateStream::State::READY_TO_INFLATE);
|
||||
}), // State::READY_TO_READ
|
||||
|
||||
|
@ -59,19 +57,18 @@ namespace MSIX {
|
|||
{
|
||||
self->m_inflateWindow = std::make_unique<std::vector<std::uint8_t>>(BufferSize);
|
||||
self->m_inflateWindowPosition = 0;
|
||||
self->m_compressionStream.dst_size = self->m_inflateWindow->size();
|
||||
self->m_compressionStream.dst_ptr = self->m_inflateWindow->data();
|
||||
self->m_compressionStatus = compression_stream_process(&(self->m_compressionStream), 0);
|
||||
self->m_compressionObject->SetOutput(self->m_inflateWindow->data(), self->m_inflateWindow->size());
|
||||
self->m_compressionStatus = self->m_compressionObject->Inflate();
|
||||
switch (self->m_compressionStatus)
|
||||
{
|
||||
case COMPRESSION_STATUS_ERROR:
|
||||
case CompressionStatus::Error:
|
||||
self->Cleanup();
|
||||
ThrowErrorIfNot(Error::InflateCorruptData, false, "inflate failed unexpectedly.");
|
||||
break;
|
||||
case COMPRESSION_STATUS_OK:
|
||||
case COMPRESSION_STATUS_END:
|
||||
case CompressionStatus::Ok:
|
||||
case CompressionStatus::End:
|
||||
default:
|
||||
self->m_fileCurrentWindowPositionEnd += (BufferSize - self->m_compressionStream.dst_size);
|
||||
self->m_fileCurrentWindowPositionEnd += (BufferSize - self->m_compressionObject->GetAvailableDestinationSize());
|
||||
return std::make_pair(true, InflateStream::State::READY_TO_COPY);
|
||||
}
|
||||
}), // State::READY_TO_INFLATE
|
||||
|
@ -82,7 +79,7 @@ namespace MSIX {
|
|||
// Check if we're actually at the end of stream.
|
||||
if (self->m_fileCurrentPosition >= self->m_uncompressedSize)
|
||||
{
|
||||
ThrowErrorIfNot(Error::InflateCorruptData, ((self->m_compressionStatus == COMPRESSION_STATUS_END) && (self->m_compressionStream.src_size == 0)), "unexpected extra data");
|
||||
ThrowErrorIfNot(Error::InflateCorruptData, ((self->m_compressionStatus == CompressionStatus::End) && (self->m_compressionObject->GetAvailableSourceSize() == 0)), "unexpected extra data");
|
||||
return std::make_pair(true, InflateStream::State::CLEANUP);
|
||||
}
|
||||
|
||||
|
@ -90,7 +87,7 @@ namespace MSIX {
|
|||
if (self->m_fileCurrentWindowPositionEnd < self->m_seekPosition)
|
||||
{
|
||||
self->m_fileCurrentPosition = self->m_fileCurrentWindowPositionEnd;
|
||||
return std::make_pair(true, (self->m_compressionStream.dst_size == 0) ? InflateStream::State::READY_TO_INFLATE : InflateStream::State::READY_TO_READ);
|
||||
return std::make_pair(true, (self->m_compressionObject->GetAvailableDestinationSize() == 0) ? InflateStream::State::READY_TO_INFLATE : InflateStream::State::READY_TO_READ);
|
||||
}
|
||||
|
||||
// now that we're within the window between current file position and seek position
|
||||
|
@ -100,10 +97,10 @@ namespace MSIX {
|
|||
|
||||
// Calculate the difference between the beginning of the window and the seek position.
|
||||
// if there's nothing left in the window to copy, then we need to fetch another window.
|
||||
ULONG bytesRemainingInWindow = (BufferSize - self->m_compressionStream.dst_size) - self->m_inflateWindowPosition;
|
||||
ULONG bytesRemainingInWindow = static_cast<ULONG>((BufferSize - self->m_compressionObject->GetAvailableDestinationSize()) - self->m_inflateWindowPosition);
|
||||
if (bytesRemainingInWindow == 0)
|
||||
{
|
||||
return std::make_pair(true, (self->m_compressionStream.dst_size == 0) ? InflateStream::State::READY_TO_INFLATE : InflateStream::State::READY_TO_READ);
|
||||
return std::make_pair(true, (self->m_compressionObject->GetAvailableDestinationSize() == 0) ? InflateStream::State::READY_TO_INFLATE : InflateStream::State::READY_TO_READ);
|
||||
}
|
||||
|
||||
ULONG bytesToCopy = std::min(countBytes, bytesRemainingInWindow);
|
||||
|
@ -140,7 +137,7 @@ namespace MSIX {
|
|||
m_state(State::UNINITIALIZED),
|
||||
m_uncompressedSize(uncompressedSize)
|
||||
{
|
||||
m_compressionStream = {0};
|
||||
m_compressionObject = CreateCompressionObject();
|
||||
}
|
||||
|
||||
InflateStream::~InflateStream()
|
||||
|
@ -209,7 +206,7 @@ namespace MSIX {
|
|||
{
|
||||
if (m_state != State::UNINITIALIZED)
|
||||
{
|
||||
compression_stream_destroy(&m_compressionStream);
|
||||
m_compressionObject->Cleanup();
|
||||
m_state = State::UNINITIALIZED;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#include "ICompressionObject.hpp"
|
||||
#include "Compression.h"
|
||||
#include "Exceptions.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
class CompressionObject final : public ICompressionObject
|
||||
{
|
||||
public:
|
||||
CompressionObject() = default;
|
||||
|
||||
// ICompressionObject interface
|
||||
CompressionStatus Initialize(CompressionOperation operation) noexcept
|
||||
{
|
||||
m_compressionStream = {0};
|
||||
|
||||
switch (operation)
|
||||
{
|
||||
case CompressionOperation::Inflate:
|
||||
return GetStatus(compression_stream_init(&m_compressionStream, COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB));
|
||||
break;
|
||||
default:
|
||||
NOTIMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
CompressionStatus Inflate() noexcept
|
||||
{
|
||||
return GetStatus(compression_stream_process(&m_compressionStream, 0));
|
||||
}
|
||||
|
||||
CompressionStatus Cleanup() noexcept
|
||||
{
|
||||
return GetStatus(compression_stream_destroy(&m_compressionStream));
|
||||
}
|
||||
|
||||
size_t GetAvailableSourceSize() noexcept
|
||||
{
|
||||
return m_compressionStream.src_size;
|
||||
}
|
||||
|
||||
size_t GetAvailableDestinationSize() noexcept
|
||||
{
|
||||
return m_compressionStream.dst_size;
|
||||
}
|
||||
|
||||
void SetInput(uint8_t* buffer, size_t size) noexcept
|
||||
{
|
||||
m_compressionStream.src_ptr = buffer;
|
||||
m_compressionStream.src_size = size;
|
||||
}
|
||||
|
||||
void SetOutput(uint8_t* buffer, size_t size) noexcept
|
||||
{
|
||||
m_compressionStream.dst_ptr = buffer;
|
||||
m_compressionStream.dst_size = size;
|
||||
}
|
||||
|
||||
private:
|
||||
compression_stream m_compressionStream = {0};
|
||||
|
||||
CompressionStatus GetStatus(compression_status status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case COMPRESSION_STATUS_OK:
|
||||
return CompressionStatus::Ok;
|
||||
case COMPRESSION_STATUS_ERROR:
|
||||
return CompressionStatus::Error;
|
||||
case COMPRESSION_STATUS_END:
|
||||
return CompressionStatus::End;
|
||||
default:
|
||||
NOTSUPPORTED;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ICompressionObject> CreateCompressionObject()
|
||||
{
|
||||
return std::make_unique<CompressionObject>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#include "ICompressionObject.hpp"
|
||||
#include "Exceptions.hpp"
|
||||
#ifdef WIN32
|
||||
#include "zlib.h"
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
class CompressionObject final : public ICompressionObject
|
||||
{
|
||||
public:
|
||||
CompressionObject() = default;
|
||||
|
||||
// ICompressionObject interface
|
||||
CompressionStatus Initialize(CompressionOperation operation) noexcept
|
||||
{
|
||||
m_zstrm = { 0 };
|
||||
|
||||
switch (operation)
|
||||
{
|
||||
case CompressionOperation::Inflate:
|
||||
return GetStatus(inflateInit2(&m_zstrm, -MAX_WBITS));
|
||||
break;
|
||||
default:
|
||||
NOTIMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
CompressionStatus Inflate() noexcept
|
||||
{
|
||||
return GetStatus(inflate(&m_zstrm, Z_NO_FLUSH));
|
||||
}
|
||||
|
||||
CompressionStatus Cleanup() noexcept
|
||||
{
|
||||
return GetStatus(inflateEnd(&m_zstrm));
|
||||
}
|
||||
|
||||
size_t GetAvailableSourceSize() noexcept
|
||||
{
|
||||
return m_zstrm.avail_in;
|
||||
}
|
||||
|
||||
size_t GetAvailableDestinationSize() noexcept
|
||||
{
|
||||
return m_zstrm.avail_out;
|
||||
}
|
||||
|
||||
void SetInput(uint8_t* buffer, size_t size) noexcept
|
||||
{
|
||||
m_zstrm.next_in = buffer;
|
||||
m_zstrm.avail_in = static_cast<uint32_t>(size);
|
||||
}
|
||||
|
||||
void SetOutput(uint8_t* buffer, size_t size) noexcept
|
||||
{
|
||||
m_zstrm.next_out = buffer;
|
||||
m_zstrm.avail_out = static_cast<uint32_t>(size);
|
||||
}
|
||||
|
||||
private:
|
||||
z_stream m_zstrm;
|
||||
|
||||
CompressionStatus GetStatus(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case Z_OK:
|
||||
return CompressionStatus::Ok;
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
case Z_BUF_ERROR:
|
||||
case Z_STREAM_ERROR:
|
||||
case Z_ERRNO:
|
||||
return CompressionStatus::Error;
|
||||
case Z_STREAM_END:
|
||||
return CompressionStatus::End;
|
||||
case Z_NEED_DICT:
|
||||
return CompressionStatus::NeedDictionary;
|
||||
default:
|
||||
NOTSUPPORTED;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ICompressionObject> CreateCompressionObject()
|
||||
{
|
||||
return std::make_unique<CompressionObject>();
|
||||
}
|
||||
}
|
|
@ -1,215 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#define NOMINMAX /* windows.h, or more correctly windef.h, defines min as a macro... */
|
||||
#include "Exceptions.hpp"
|
||||
#include "ZipFileStream.hpp"
|
||||
#include "InflateStream.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
// Buffer size used for compressed buffer and inflate window.
|
||||
// See zlib's updatewindow comment.
|
||||
static const size_t BufferSize = 32*1024;
|
||||
|
||||
struct InflateHandler
|
||||
{
|
||||
typedef std::pair<bool, InflateStream::State>(*lambda)(InflateStream* self, void* buffer, ULONG countBytes);
|
||||
InflateHandler(lambda f): Handler(f) {}
|
||||
lambda Handler;
|
||||
};
|
||||
|
||||
std::array<InflateHandler, static_cast<size_t>(InflateStream::State::MAX)> stateMachine =
|
||||
{
|
||||
// State::UNINITIALIZED
|
||||
InflateHandler([](InflateStream* self, void*, ULONG)
|
||||
{
|
||||
ThrowHrIfFailed(self->m_stream->Seek({0}, StreamBase::START, nullptr));
|
||||
self->m_zstrm = { 0 };
|
||||
self->m_fileCurrentPosition = 0;
|
||||
self->m_fileCurrentWindowPositionEnd = 0;
|
||||
|
||||
int ret = inflateInit2(&(self->m_zstrm), -MAX_WBITS);
|
||||
ThrowErrorIfNot(Error::InflateInitialize, (ret == Z_OK), "inflateInit2 failed");
|
||||
return std::make_pair(true, InflateStream::State::READY_TO_READ);
|
||||
}), // State::UNINITIALIZED
|
||||
|
||||
// State::READY_TO_READ
|
||||
InflateHandler([](InflateStream* self, void*, ULONG)
|
||||
{
|
||||
ThrowErrorIfNot(Error::InflateRead,(self->m_zstrm.avail_in == 0), "uninflated bytes overwritten");
|
||||
ULONG available = 0;
|
||||
self->m_compressedBuffer = std::make_unique<std::vector<std::uint8_t>>(BufferSize);
|
||||
ThrowHrIfFailed(self->m_stream->Read(self->m_compressedBuffer->data(), static_cast<ULONG>(self->m_compressedBuffer->size()), &available));
|
||||
ThrowErrorIf(Error::FileRead, (available == 0), "Getting nothing back is unexpected here.");
|
||||
self->m_zstrm.avail_in = static_cast<uInt>(available);
|
||||
self->m_zstrm.next_in = self->m_compressedBuffer->data();
|
||||
return std::make_pair(true, InflateStream::State::READY_TO_INFLATE);
|
||||
}), // State::READY_TO_READ
|
||||
|
||||
// State::READY_TO_INFLATE
|
||||
InflateHandler([](InflateStream* self, void*, ULONG)
|
||||
{
|
||||
self->m_inflateWindow = std::make_unique<std::vector<std::uint8_t>>(BufferSize);
|
||||
self->m_inflateWindowPosition = 0;
|
||||
self->m_zstrm.avail_out = static_cast<uInt>(self->m_inflateWindow->size());
|
||||
self->m_zstrm.next_out = self->m_inflateWindow->data();
|
||||
self->m_zret = inflate(&(self->m_zstrm), Z_NO_FLUSH);
|
||||
switch (self->m_zret)
|
||||
{
|
||||
case Z_NEED_DICT:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
self->Cleanup();
|
||||
ThrowErrorIfNot(Error::InflateCorruptData, false, "inflate failed unexpectedly.");
|
||||
case Z_STREAM_END:
|
||||
default:
|
||||
self->m_fileCurrentWindowPositionEnd += (BufferSize - self->m_zstrm.avail_out);
|
||||
return std::make_pair(true, InflateStream::State::READY_TO_COPY);
|
||||
}
|
||||
}), // State::READY_TO_INFLATE
|
||||
|
||||
// State::READY_TO_COPY
|
||||
InflateHandler([](InflateStream* self, void* buffer, ULONG countBytes)
|
||||
{
|
||||
// Check if we're actually at the end of stream.
|
||||
if (self->m_fileCurrentPosition >= self->m_uncompressedSize)
|
||||
{
|
||||
ThrowErrorIfNot(Error::InflateCorruptData, ((self->m_zret == Z_STREAM_END) && (self->m_zstrm.avail_in == 0)), "unexpected extra data");
|
||||
return std::make_pair(true, InflateStream::State::CLEANUP);
|
||||
}
|
||||
|
||||
// If the end of the current window position is less than the seek position, keep inflating
|
||||
if (self->m_fileCurrentWindowPositionEnd < self->m_seekPosition)
|
||||
{
|
||||
self->m_fileCurrentPosition = self->m_fileCurrentWindowPositionEnd;
|
||||
return std::make_pair(true, (self->m_zstrm.avail_in == 0) ? InflateStream::State::READY_TO_READ : InflateStream::State::READY_TO_INFLATE);
|
||||
}
|
||||
|
||||
// now that we're within the window between current file position and seek position
|
||||
// calculate the number of bytes to skip ahead within this window
|
||||
ULONG bytesToSkipInWindow = (ULONG)(self->m_seekPosition - self->m_fileCurrentPosition);
|
||||
self->m_inflateWindowPosition += bytesToSkipInWindow;
|
||||
|
||||
// Calculate the difference between the beginning of the window and the seek position.
|
||||
// if there's nothing left in the window to copy, then we need to fetch another window.
|
||||
ULONG bytesRemainingInWindow = (BufferSize - self->m_zstrm.avail_out) - self->m_inflateWindowPosition;
|
||||
if (bytesRemainingInWindow == 0)
|
||||
{
|
||||
return std::make_pair(true, (self->m_zstrm.avail_in == 0) ? InflateStream::State::READY_TO_READ : InflateStream::State::READY_TO_INFLATE);
|
||||
}
|
||||
|
||||
ULONG bytesToCopy = std::min(countBytes, bytesRemainingInWindow);
|
||||
if (bytesToCopy > 0)
|
||||
{
|
||||
memcpy(buffer, &(self->m_inflateWindow->at(self->m_inflateWindowPosition)), bytesToCopy);
|
||||
self->m_bytesRead += bytesToCopy;
|
||||
self->m_seekPosition += bytesToCopy;
|
||||
self->m_inflateWindowPosition += bytesToCopy;
|
||||
self->m_fileCurrentPosition += bytesToCopy;
|
||||
}
|
||||
|
||||
if (self->m_fileCurrentPosition == self->m_uncompressedSize)
|
||||
{
|
||||
self->Cleanup();
|
||||
return std::make_pair(false, InflateStream::State::UNINITIALIZED);
|
||||
}
|
||||
return std::make_pair(countBytes != 0, InflateStream::State::READY_TO_COPY);
|
||||
}), // State::READY_TO_COPY
|
||||
|
||||
// State::CLEANUP
|
||||
InflateHandler([](InflateStream* self, void*, ULONG)
|
||||
{
|
||||
self->Cleanup();
|
||||
return std::make_pair(false, InflateStream::State::UNINITIALIZED);
|
||||
}) // State::CLEANUP
|
||||
};
|
||||
|
||||
InflateStream::InflateStream(const ComPtr<IStream>& stream, std::uint64_t uncompressedSize) :
|
||||
m_stream(stream),
|
||||
m_state(State::UNINITIALIZED),
|
||||
m_uncompressedSize(uncompressedSize)
|
||||
{
|
||||
m_zstrm = {0};
|
||||
}
|
||||
|
||||
InflateStream::~InflateStream()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
HRESULT InflateStream::Read(void* buffer, ULONG countBytes, ULONG* bytesRead) noexcept try
|
||||
{
|
||||
m_bytesRead = 0;
|
||||
m_startCurrentBuffer = reinterpret_cast<std::uint8_t*>(buffer);
|
||||
if (m_seekPosition < m_uncompressedSize)
|
||||
{
|
||||
bool stayInLoop = true;
|
||||
while (stayInLoop && (m_bytesRead < countBytes))
|
||||
{
|
||||
auto&& result = stateMachine[static_cast<size_t>(m_state)].Handler(this, m_startCurrentBuffer + m_bytesRead, countBytes - m_bytesRead);
|
||||
stayInLoop = std::get<0>(result);
|
||||
m_previous = m_state;
|
||||
m_state = std::get<1>(result);
|
||||
}
|
||||
}
|
||||
m_startCurrentBuffer = nullptr;
|
||||
if (bytesRead) { *bytesRead = m_bytesRead; }
|
||||
return static_cast<HRESULT>(Error::OK);
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT InflateStream::Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) noexcept try
|
||||
{
|
||||
LARGE_INTEGER seekPosition = { 0 };
|
||||
switch (origin)
|
||||
{
|
||||
case Reference::CURRENT:
|
||||
seekPosition.QuadPart = m_seekPosition + move.QuadPart;
|
||||
break;
|
||||
case Reference::START:
|
||||
seekPosition.QuadPart = move.QuadPart;
|
||||
break;
|
||||
case Reference::END:
|
||||
seekPosition.QuadPart = m_uncompressedSize + move.QuadPart;
|
||||
break;
|
||||
}
|
||||
|
||||
// Can't seek beyond the end of the uncompressed stream
|
||||
seekPosition.QuadPart = std::min(seekPosition.QuadPart, static_cast<LONGLONG>(m_uncompressedSize));
|
||||
|
||||
if (seekPosition.QuadPart != m_seekPosition)
|
||||
{
|
||||
m_seekPosition = seekPosition.QuadPart;
|
||||
// If the caller is trying to seek back to an earlier
|
||||
// point in the inflated stream, we will need to reset
|
||||
// zlib and start inflating from the beginning of the
|
||||
// stream; otherwise, seeking forward is fine: We will
|
||||
// catch up to the seek pointer during the ::Read operation.
|
||||
if (m_seekPosition < m_fileCurrentPosition)
|
||||
{
|
||||
m_fileCurrentPosition = 0;
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
if (newPosition) { newPosition->QuadPart = m_seekPosition; }
|
||||
return static_cast<HRESULT>(Error::OK);
|
||||
} CATCH_RETURN();
|
||||
|
||||
void InflateStream::Cleanup()
|
||||
{
|
||||
if (m_state != State::UNINITIALIZED)
|
||||
{
|
||||
inflateEnd(&m_zstrm);
|
||||
m_state = State::UNINITIALIZED;
|
||||
}
|
||||
}
|
||||
} /* msix */
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#pragma once
|
||||
#ifdef WIN32
|
||||
#include "zlib.h"
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "ComHelper.hpp"
|
||||
|
||||
// Windows.h defines max and min...
|
||||
#undef max
|
||||
#undef min
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
// This represents a LZW-compressed stream
|
||||
class InflateStream final : public StreamBase
|
||||
{
|
||||
public:
|
||||
InflateStream(const ComPtr<IStream>& stream, std::uint64_t uncompressedSize);
|
||||
~InflateStream();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) noexcept override;
|
||||
HRESULT STDMETHODCALLTYPE Read(void* buffer, ULONG countBytes, ULONG* bytesRead) noexcept override;
|
||||
HRESULT STDMETHODCALLTYPE Write(void const *buffer, ULONG countBytes, ULONG *bytesWritten) noexcept override
|
||||
{
|
||||
return static_cast<HRESULT>(Error::NotImplemented);
|
||||
}
|
||||
|
||||
// IStreamInternal
|
||||
std::uint64_t GetSizeOnZip() override
|
||||
{ // The underlying ZipFileStream object knows, so go ask it.
|
||||
return m_stream.As<IStreamInternal>()->GetSizeOnZip();
|
||||
}
|
||||
|
||||
bool IsCompressed() override
|
||||
{ // The underlying ZipFileStream object knows, so go ask it.
|
||||
return m_stream.As<IStreamInternal>()->IsCompressed();
|
||||
}
|
||||
|
||||
std::string GetName() override
|
||||
{ // The underlying ZipFileStream object knows, so go ask it.
|
||||
return m_stream.As<IStreamInternal>()->GetName();
|
||||
}
|
||||
|
||||
void Cleanup();
|
||||
|
||||
enum class State : size_t
|
||||
{
|
||||
UNINITIALIZED = 0,
|
||||
READY_TO_READ,
|
||||
READY_TO_INFLATE,
|
||||
READY_TO_COPY,
|
||||
CLEANUP,
|
||||
MAX = CLEANUP + 1
|
||||
};
|
||||
|
||||
State m_previous = State::UNINITIALIZED;
|
||||
State m_state = State::UNINITIALIZED;
|
||||
|
||||
ComPtr<IStream> m_stream;
|
||||
ULONGLONG m_seekPosition = 0;
|
||||
ULONGLONG m_uncompressedSize = 0;
|
||||
ULONG m_bytesRead = 0;
|
||||
std::uint8_t* m_startCurrentBuffer = nullptr;
|
||||
ULONG m_inflateWindowPosition = 0;
|
||||
ULONGLONG m_fileCurrentWindowPositionEnd = 0;
|
||||
ULONGLONG m_fileCurrentPosition = 0;
|
||||
|
||||
z_stream m_zstrm;
|
||||
int m_zret;
|
||||
|
||||
std::unique_ptr<std::vector<std::uint8_t>> m_compressedBuffer;
|
||||
std::unique_ptr<std::vector<std::uint8_t>> m_inflateWindow;
|
||||
};
|
||||
}
|
Загрузка…
Ссылка в новой задаче