-
-
Notifications
You must be signed in to change notification settings - Fork 301
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1st attempt to implement brotli (todo: use single threaded brotli API…
… instead brotli-mt, because of framing)
- Loading branch information
Showing
2 changed files
with
349 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
// BrotliHandler.cpp | ||
|
||
#include "StdAfx.h" | ||
|
||
#include "../../../C/CpuArch.h" | ||
#include "../../Common/ComTry.h" | ||
#include "../../Common/Defs.h" | ||
|
||
#include "../Common/ProgressUtils.h" | ||
#include "../Common/RegisterArc.h" | ||
#include "../Common/StreamUtils.h" | ||
|
||
#include "../Compress/BrotliDecoder.h" | ||
#include "../Compress/BrotliEncoder.h" | ||
#include "../Compress/CopyCoder.h" | ||
|
||
#include "Common/DummyOutStream.h" | ||
#include "Common/HandlerOut.h" | ||
|
||
using namespace NWindows; | ||
|
||
namespace NArchive { | ||
namespace NBROTLI { | ||
|
||
class CHandler: | ||
public IInArchive, | ||
public IArchiveOpenSeq, | ||
public IOutArchive, | ||
public ISetProperties, | ||
public CMyUnknownImp | ||
{ | ||
CMyComPtr<IInStream> _stream; | ||
CMyComPtr<ISequentialInStream> _seqStream; | ||
|
||
bool _isArc; | ||
bool _dataAfterEnd; | ||
bool _needMoreInput; | ||
|
||
bool _packSize_Defined; | ||
bool _unpackSize_Defined; | ||
|
||
UInt64 _packSize; | ||
UInt64 _unpackSize; | ||
UInt64 _numStreams; | ||
UInt64 _numBlocks; | ||
|
||
CSingleMethodProps _props; | ||
|
||
public: | ||
MY_UNKNOWN_IMP4( | ||
IInArchive, | ||
IArchiveOpenSeq, | ||
IOutArchive, | ||
ISetProperties) | ||
INTERFACE_IInArchive(;) | ||
INTERFACE_IOutArchive(;) | ||
STDMETHOD(OpenSeq)(ISequentialInStream *stream); | ||
STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps); | ||
|
||
CHandler() { } | ||
}; | ||
|
||
static const Byte kProps[] = | ||
{ | ||
kpidSize, | ||
kpidPackSize | ||
}; | ||
|
||
static const Byte kArcProps[] = | ||
{ | ||
kpidNumStreams, | ||
kpidNumBlocks | ||
}; | ||
|
||
IMP_IInArchive_Props | ||
IMP_IInArchive_ArcProps | ||
|
||
STDMETHODIMP CHandler::GetArchiveProperty(PROPID /*propID*/, PROPVARIANT * /*value*/) | ||
{ | ||
return S_OK; | ||
} | ||
|
||
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | ||
{ | ||
*numItems = 1; | ||
return S_OK; | ||
} | ||
|
||
STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) | ||
{ | ||
NCOM::CPropVariant prop; | ||
switch (propID) | ||
{ | ||
case kpidPackSize: if (_packSize_Defined) prop = _packSize; break; | ||
case kpidSize: if (_unpackSize_Defined) prop = _unpackSize; break; | ||
} | ||
prop.Detach(value); | ||
return S_OK; | ||
} | ||
|
||
// brotli format has no signature | ||
// brotli stream can't be veriefied unless errors by unpack | ||
#if 0 | ||
API_FUNC_static_IsArc IsArc_Brotli(const Byte *p, size_t size) | ||
{ | ||
return k_IsArc_Res_YES; | ||
} | ||
} | ||
#endif | ||
|
||
STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *) | ||
{ | ||
COM_TRY_BEGIN | ||
Close(); | ||
{ | ||
_isArc = true; | ||
_stream = stream; | ||
_seqStream = stream; | ||
} | ||
return S_OK; | ||
COM_TRY_END | ||
} | ||
|
||
STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) | ||
{ | ||
Close(); | ||
_isArc = true; | ||
_seqStream = stream; | ||
return S_OK; | ||
} | ||
|
||
STDMETHODIMP CHandler::Close() | ||
{ | ||
_isArc = false; | ||
_dataAfterEnd = false; | ||
_needMoreInput = false; | ||
|
||
_packSize_Defined = false; | ||
_unpackSize_Defined = false; | ||
|
||
_packSize = 0; | ||
|
||
_seqStream.Release(); | ||
_stream.Release(); | ||
return S_OK; | ||
} | ||
|
||
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, | ||
Int32 testMode, IArchiveExtractCallback *extractCallback) | ||
{ | ||
COM_TRY_BEGIN | ||
if (numItems == 0) | ||
return S_OK; | ||
if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) | ||
return E_INVALIDARG; | ||
|
||
if (_packSize_Defined) | ||
extractCallback->SetTotal(_packSize); | ||
|
||
CMyComPtr<ISequentialOutStream> realOutStream; | ||
Int32 askMode = testMode ? | ||
NExtract::NAskMode::kTest : | ||
NExtract::NAskMode::kExtract; | ||
RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); | ||
if (!testMode && !realOutStream) | ||
return S_OK; | ||
|
||
extractCallback->PrepareOperation(askMode); | ||
|
||
Int32 opRes; | ||
|
||
{ | ||
|
||
NCompress::NBROTLI::CDecoder *decoderSpec = new NCompress::NBROTLI::CDecoder; | ||
CMyComPtr<ICompressCoder> decoder = decoderSpec; | ||
decoderSpec->SetInStream(_seqStream); | ||
|
||
CDummyOutStream *outStreamSpec = new CDummyOutStream; | ||
CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); | ||
outStreamSpec->SetStream(realOutStream); | ||
outStreamSpec->Init(); | ||
|
||
realOutStream.Release(); | ||
|
||
CLocalProgress *lps = new CLocalProgress; | ||
CMyComPtr<ICompressProgressInfo> progress = lps; | ||
lps->Init(extractCallback, true); | ||
|
||
UInt64 packSize = 0; | ||
UInt64 unpackedSize = 0; | ||
|
||
HRESULT result = S_OK; | ||
|
||
for (;;) | ||
{ | ||
lps->InSize = packSize; | ||
lps->OutSize = unpackedSize; | ||
|
||
RINOK(lps->SetCur()); | ||
result = decoderSpec->CodeResume(outStream, &unpackedSize, progress); | ||
UInt64 streamSize = decoderSpec->GetInputProcessedSize(); | ||
|
||
if (result != S_FALSE && result != S_OK) | ||
return result; | ||
|
||
if (unpackedSize == 0) | ||
break; | ||
|
||
if (streamSize == packSize) | ||
{ | ||
// no new bytes in input stream, So it's good end of archive. | ||
result = S_OK; | ||
break; | ||
} | ||
|
||
if (packSize > streamSize) | ||
return E_FAIL; | ||
|
||
if (result != S_OK) | ||
break; | ||
} | ||
|
||
decoderSpec->ReleaseInStream(); | ||
outStream.Release(); | ||
|
||
if (!_isArc) | ||
opRes = NExtract::NOperationResult::kIsNotArc; | ||
else if (_needMoreInput) | ||
opRes = NExtract::NOperationResult::kUnexpectedEnd; | ||
else if (_dataAfterEnd) | ||
opRes = NExtract::NOperationResult::kDataAfterEnd; | ||
else if (result == S_FALSE) | ||
opRes = NExtract::NOperationResult::kDataError; | ||
else if (result == S_OK) | ||
opRes = NExtract::NOperationResult::kOK; | ||
else | ||
return result; | ||
|
||
} | ||
|
||
return extractCallback->SetOperationResult(opRes); | ||
|
||
COM_TRY_END | ||
} | ||
|
||
static HRESULT UpdateArchive( | ||
UInt64 unpackSize, | ||
ISequentialOutStream *outStream, | ||
const CProps &props, | ||
IArchiveUpdateCallback *updateCallback) | ||
{ | ||
RINOK(updateCallback->SetTotal(unpackSize)); | ||
CMyComPtr<ISequentialInStream> fileInStream; | ||
RINOK(updateCallback->GetStream(0, &fileInStream)); | ||
CLocalProgress *localProgressSpec = new CLocalProgress; | ||
CMyComPtr<ICompressProgressInfo> localProgress = localProgressSpec; | ||
localProgressSpec->Init(updateCallback, true); | ||
NCompress::NBROTLI::CEncoder *encoderSpec = new NCompress::NBROTLI::CEncoder; | ||
CMyComPtr<ICompressCoder> encoder = encoderSpec; | ||
RINOK(props.SetCoderProps(encoderSpec, NULL)); | ||
RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, localProgress)); | ||
return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); | ||
} | ||
|
||
STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) | ||
{ | ||
*type = NFileTimeType::kUnix; | ||
return S_OK; | ||
} | ||
|
||
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, | ||
IArchiveUpdateCallback *updateCallback) | ||
{ | ||
COM_TRY_BEGIN | ||
|
||
if (numItems != 1) | ||
return E_INVALIDARG; | ||
|
||
Int32 newData, newProps; | ||
UInt32 indexInArchive; | ||
if (!updateCallback) | ||
return E_FAIL; | ||
RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); | ||
|
||
if ((newProps)) | ||
{ | ||
{ | ||
NCOM::CPropVariant prop; | ||
RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); | ||
if (prop.vt != VT_EMPTY) | ||
if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) | ||
return E_INVALIDARG; | ||
} | ||
} | ||
|
||
if ((newData)) | ||
{ | ||
UInt64 size; | ||
{ | ||
NCOM::CPropVariant prop; | ||
RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); | ||
if (prop.vt != VT_UI8) | ||
return E_INVALIDARG; | ||
size = prop.uhVal.QuadPart; | ||
} | ||
return UpdateArchive(size, outStream, _props, updateCallback); | ||
} | ||
|
||
if (indexInArchive != 0) | ||
return E_INVALIDARG; | ||
|
||
CLocalProgress *lps = new CLocalProgress; | ||
CMyComPtr<ICompressProgressInfo> progress = lps; | ||
lps->Init(updateCallback, true); | ||
|
||
CMyComPtr<IArchiveUpdateCallbackFile> opCallback; | ||
updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback); | ||
if (opCallback) | ||
{ | ||
RINOK(opCallback->ReportOperation( | ||
NEventIndexType::kInArcIndex, 0, | ||
NUpdateNotifyOp::kReplicate)) | ||
} | ||
|
||
if (_stream) | ||
RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); | ||
|
||
return NCompress::CopyStream(_stream, outStream, progress); | ||
|
||
COM_TRY_END | ||
} | ||
|
||
STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps) | ||
{ | ||
return _props.SetProperties(names, values, numProps); | ||
} | ||
|
||
IMP_CreateArcIn | ||
IMP_CreateArcOut | ||
REGISTER_ARC_R( | ||
"brotli", "br brotli tbr", 0, 0x1F, | ||
0, NULL, | ||
0, | ||
NArcInfoFlags::kKeepName | NArcInfoFlags::kPureStartOpen | NArcInfoFlags::kByExtOnlyOpen, | ||
0, | ||
CreateArc, CreateArcOut, | ||
/* IsArc_Brotli */) | ||
}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters