Stroika Library 3.0d20
 
Loading...
Searching...
No Matches
Archive/Zip/Writer.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
7#include "Stroika/Foundation/DataExchange/Archive/Zip/Private_minizip_.h"
8#include "Stroika/Foundation/Execution/Finally.h" // not needed yet, but maybe still - wait til working
9#include "Stroika/Foundation/Streams/MemoryStream.h"
10
11#include "Writer.h"
12
13using namespace Stroika::Foundation;
16using namespace Stroika::Foundation::DataExchange::Archive;
17using namespace Stroika::Foundation::DataExchange::Archive::Writer;
18using namespace Stroika::Foundation::Execution;
19using namespace Stroika::Foundation::Memory;
20using namespace Stroika::Foundation::Streams;
21
22#if qStroika_HasComponent_zlib
23using namespace Stroika::Foundation::DataExchange::Archive::Zip::PrivateMinizip_;
24
25using Memory::BLOB;
26using std::byte;
27
28namespace {
29 void ThrowIfMinizipErr_ (int err, const String& doing)
30 {
31 if (err != UNZ_OK) [[unlikely]] {
32 Throw (RuntimeErrorException{Format ("error {} with zipfile in {}"_f, err, doing)});
33 }
34 }
35 struct MyZipLibOutStream_ final : zlib_filefunc64_def {
36 OutputStream::Ptr<byte> fOutSteram_;
37#if qStroika_Foundation_Debug_AssertionsChecked
38 bool fOpened_{false};
39#endif
40 MyZipLibOutStream_ (const OutputStream::Ptr<byte>& in)
41 : fOutSteram_{in}
42 {
43 this->zopen64_file = [] (voidpf opaqueStream, const void* /*filename*/, int /*mode*/) -> voidpf {
44 MyZipLibOutStream_* myThis = reinterpret_cast<MyZipLibOutStream_*> (opaqueStream);
45#if qStroika_Foundation_Debug_AssertionsChecked
46 Assert (not myThis->fOpened_);
47 myThis->fOpened_ = true;
48#endif
49 return myThis;
50 };
51 this->zread_file = [] ([[maybe_unused]] voidpf opaqueStream, [[maybe_unused]] voidpf stream, [[maybe_unused]] void* buf,
52 [[maybe_unused]] uLong size) -> uLong {
53 RequireNotReached (); // read only zip
54 return static_cast<uLong> (UNZ_PARAMERROR);
55 };
56 this->zwrite_file = [] (voidpf opaqueStream, [[maybe_unused]] voidpf stream, const void* buf, uLong size) -> uLong {
57 Require (opaqueStream == stream); // our use is one stream per zlib_filefunc64_def object
58 MyZipLibOutStream_* myThis = reinterpret_cast<MyZipLibOutStream_*> (opaqueStream);
59#if qStroika_Foundation_Debug_AssertionsChecked
60 Assert (myThis->fOpened_);
61#endif
62 myThis->fOutSteram_.Write (span{reinterpret_cast<const byte*> (buf), size});
63 return static_cast<uLong> (size);
64 };
65 this->ztell64_file = [] (voidpf opaqueStream, [[maybe_unused]] voidpf stream) -> ZPOS64_T {
66 Require (opaqueStream == stream); // our use is one stream per zlib_filefunc64_def object
67 MyZipLibOutStream_* myThis = reinterpret_cast<MyZipLibOutStream_*> (opaqueStream);
68#if qStroika_Foundation_Debug_AssertionsChecked
69 Assert (myThis->fOpened_);
70#endif
71 return myThis->fOutSteram_.GetOffset ();
72 };
73 this->zseek64_file = [] (voidpf opaqueStream, [[maybe_unused]] voidpf stream, ZPOS64_T offset, int origin) -> long {
74 Require (opaqueStream == stream); // our use is one stream per zlib_filefunc64_def object
75 MyZipLibOutStream_* myThis = reinterpret_cast<MyZipLibOutStream_*> (opaqueStream);
76#if qStroika_Foundation_Debug_AssertionsChecked
77 Assert (myThis->fOpened_);
78#endif
79 switch (origin) {
80 case ZLIB_FILEFUNC_SEEK_SET:
81 myThis->fOutSteram_.Seek (offset);
82 break;
83 case ZLIB_FILEFUNC_SEEK_CUR:
84 myThis->fOutSteram_.Seek (Streams::eFromCurrent, offset);
85 break;
86 case ZLIB_FILEFUNC_SEEK_END:
87 myThis->fOutSteram_.Seek (Streams::eFromEnd, offset);
88 break;
89 default:
91 return UNZ_PARAMERROR;
92 }
93 return UNZ_OK;
94 };
95 this->zclose_file = [] ([[maybe_unused]] voidpf opaqueStream, [[maybe_unused]] voidpf stream) -> int {
96#if qStroika_Foundation_Debug_AssertionsChecked
97 Require (opaqueStream == stream); // our use is one stream per zlib_filefunc64_def object
98 MyZipLibOutStream_* myThis = reinterpret_cast<MyZipLibOutStream_*> (opaqueStream);
99 Assert (myThis->fOpened_);
100 myThis->fOutSteram_.Flush ();
101 myThis->fOpened_ = false;
102#endif
103 return UNZ_OK;
104 };
105 this->zerror_file = [] (voidpf opaqueStream, [[maybe_unused]] voidpf stream) -> int {
106 Require (opaqueStream == stream); // our use is one stream per zlib_filefunc64_def object
107 [[maybe_unused]] MyZipLibOutStream_* myThis = reinterpret_cast<MyZipLibOutStream_*> (opaqueStream);
108#if qStroika_Foundation_Debug_AssertionsChecked
109 Assert (myThis->fOpened_);
110#endif
111 return UNZ_OK; // @todo - see what this means?
112 };
113 this->opaque = this;
114 }
115 ~MyZipLibOutStream_ ()
116 {
117#if qStroika_Foundation_Debug_AssertionsChecked
118 Assert (not fOpened_);
119#endif
120 }
121 };
122}
123
124namespace {
125 struct MyRep_ final : Archive::Writer::IRep {
126 MyZipLibOutStream_ fOutZipStream_;
127 zipFile fZipFile_;
128 MyRep_ (const OutputStream::Ptr<byte>& out)
129 : fOutZipStream_{out}
130 , fZipFile_{zipOpen2_64 ("", APPEND_STATUS_CREATE, nullptr, &fOutZipStream_)}
131 {
132 if (fZipFile_ == nullptr) [[unlikely]] {
133 static const RuntimeErrorException kException_{"failed to open zipfile"sv};
134 Throw (kException_);
135 }
136 }
137 ~MyRep_ ()
138 {
139 AssertNotNull (fZipFile_);
140 zipClose (fZipFile_, nullptr);
141 }
142 virtual void Add (const String& fileName, const span<const byte>& data) override
143 {
144 // password support NYI - add options param for some of these options
145 const char* password = nullptr;
146 uint32_t crcFile = password == nullptr ? 0 : crc32_z (0, reinterpret_cast<const Bytef*> (data.data ()), data.size ());
147
148 bool zip64 = data.size () > numeric_limits<uint16_t>::max ();
149 // @todo figure out about code page for encoding filenames
150 zip_fileinfo zi{}; //???
151 uInt opt_compress_level = 0;
152 constexpr int VERSIONMADEBY = (0x0); /* platform dependent */
153 // see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT - Bit 11: Language encoding flag (EFS).
154 constexpr uLong flagBase = 1 << 11; // UTF-8 filenames
155 ThrowIfMinizipErr_ (zipOpenNewFileInZip4_64 (fZipFile_, fileName.AsUTF8<string> ().c_str (), &zi, NULL, 0, NULL, 0,
156 NULL /* comment*/, (opt_compress_level != 0) ? Z_DEFLATED : 0, opt_compress_level, 0,
157 /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
158 -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password, crcFile, VERSIONMADEBY, flagBase, zip64),
159 "zipOpenNewFileInZip4_64"sv);
160
161 [[maybe_unused]] auto&& cleanup = Finally ([this] () noexcept { zipCloseFileInZip (fZipFile_); });
162
163 ThrowIfMinizipErr_ (zipWriteInFileInZip (fZipFile_, reinterpret_cast<const Bytef*> (data.data ()), static_cast<unsigned int> (data.size ())),
164 "zipWriteInFileInZip"sv);
165 }
166 };
167}
168
169/*
170 ********************************************************************************
171 ******************* DataExchange::Archive::Zip::Writer::New ********************
172 ********************************************************************************
173 */
174Archive::Writer::Ptr Archive::Zip::Writer::New (const OutputStream::Ptr<byte>& writeTo)
175{
176 Require (writeTo.IsSeekable ());
177 return Archive::Writer::Ptr{make_shared<MyRep_> (writeTo)};
178}
179#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotReached()
Definition Assertions.h:385
#define AssertNotReached()
Definition Assertions.h:355
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
nonvirtual bool IsSeekable() const
Returns true iff this object was constructed with a seekable input stream rep.
Definition Stream.inl:44
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31