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:
Adrian Mascarenhas 2018-09-05 16:26:51 +00:00 коммит произвёл msftrubengu
Родитель eb5b7dd841
Коммит fad5fc8b2a
12 изменённых файлов: 280 добавлений и 350 удалений

Просмотреть файл

@ -4,8 +4,8 @@
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)
@ -18,9 +18,9 @@ IF((NOT ((MACOS) OR (IOS))) OR USE_ZLIB)
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build dll" FORCE)
ELSE()
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build dll" FORCE)
ENDIF()
ENDIF()
add_subdirectory(zlib)
ENDIF()
ENDIF()
# Xerces
IF(XML_PARSER MATCHES xerces)

Просмотреть файл

@ -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,15 +264,19 @@ 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)
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}
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/zlib
${CMAKE_PROJECT_ROOT}/lib/zlib
${CMAKE_PROJECT_ROOT}/lib/zlib
${CMAKE_PROJECT_ROOT}/src/msix/PAL/DataCompression/Zlib
)
if(USE_SHARED_ZLIB)

Просмотреть файл

@ -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;
};
}