archive_r_ruby 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/{LICENSE → LICENSE.txt} +77 -77
- data/README.md +103 -103
- data/ext/archive_r/Makefile +48 -45
- data/ext/archive_r/archive_r-x64-mingw-ucrt.def +2 -0
- data/ext/archive_r/archive_r_ext.cc +1106 -1106
- data/ext/archive_r/archive_r_ext.o +0 -0
- data/ext/archive_r/extconf.rb +120 -120
- data/ext/archive_r/mkmf.log +23 -18
- data/ext/archive_r/vendor/archive_r/LICENSE.txt +77 -77
- data/ext/archive_r/vendor/archive_r/include/archive_r/data_stream.h +52 -52
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry.h +166 -166
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry_fault.h +34 -34
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry_metadata.h +56 -56
- data/ext/archive_r/vendor/archive_r/include/archive_r/multi_volume_stream_base.h +46 -46
- data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy.h +109 -109
- data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy_utils.h +37 -37
- data/ext/archive_r/vendor/archive_r/include/archive_r/platform_compat.h +19 -19
- data/ext/archive_r/vendor/archive_r/include/archive_r/traverser.h +122 -122
- data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.cc +330 -330
- data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.h +97 -97
- data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.cc +162 -162
- data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.h +54 -54
- data/ext/archive_r/vendor/archive_r/src/archive_type.cc +552 -552
- data/ext/archive_r/vendor/archive_r/src/archive_type.h +77 -77
- data/ext/archive_r/vendor/archive_r/src/data_stream.cc +35 -35
- data/ext/archive_r/vendor/archive_r/src/entry.cc +253 -253
- data/ext/archive_r/vendor/archive_r/src/entry_fault.cc +26 -26
- data/ext/archive_r/vendor/archive_r/src/entry_fault_error.cc +54 -54
- data/ext/archive_r/vendor/archive_r/src/entry_fault_error.h +32 -32
- data/ext/archive_r/vendor/archive_r/src/entry_impl.h +57 -57
- data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.cc +81 -81
- data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.h +41 -41
- data/ext/archive_r/vendor/archive_r/src/multi_volume_stream_base.cc +199 -199
- data/ext/archive_r/vendor/archive_r/src/path_hierarchy.cc +151 -151
- data/ext/archive_r/vendor/archive_r/src/path_hierarchy_utils.cc +304 -304
- data/ext/archive_r/vendor/archive_r/src/simple_profiler.h +120 -120
- data/ext/archive_r/vendor/archive_r/src/system_file_stream.cc +295 -295
- data/ext/archive_r/vendor/archive_r/src/system_file_stream.h +46 -46
- data/ext/archive_r/vendor/archive_r/src/traverser.cc +314 -314
- data/lib/archive_r.rb +105 -105
- metadata +11 -8
- data/ext/archive_r/archive_r.bundle +0 -0
|
@@ -1,330 +1,330 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
// Copyright (c) 2025 archive_r Team
|
|
3
|
-
|
|
4
|
-
#include "archive_stack_cursor.h"
|
|
5
|
-
|
|
6
|
-
#include "archive_r/path_hierarchy_utils.h"
|
|
7
|
-
#include "system_file_stream.h"
|
|
8
|
-
#include <exception>
|
|
9
|
-
#include <memory>
|
|
10
|
-
#include <stdexcept>
|
|
11
|
-
#include <typeinfo>
|
|
12
|
-
#include <utility>
|
|
13
|
-
|
|
14
|
-
namespace archive_r {
|
|
15
|
-
|
|
16
|
-
namespace {
|
|
17
|
-
|
|
18
|
-
[[noreturn]] void throw_entry_fault(const std::string &message, const PathHierarchy &hierarchy) { throw make_entry_fault_error(message, hierarchy); }
|
|
19
|
-
|
|
20
|
-
} // namespace
|
|
21
|
-
|
|
22
|
-
// ============================================================================
|
|
23
|
-
// StreamArchive Implementation
|
|
24
|
-
// ============================================================================
|
|
25
|
-
|
|
26
|
-
StreamArchive::StreamArchive(std::shared_ptr<IDataStream> stream, ArchiveOption options)
|
|
27
|
-
: Archive()
|
|
28
|
-
, _stream(std::move(stream))
|
|
29
|
-
, _options(std::move(options)) {
|
|
30
|
-
if (!_stream) {
|
|
31
|
-
throw std::invalid_argument("StreamArchive requires a valid data stream");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
open_archive();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
StreamArchive::~StreamArchive() = default;
|
|
38
|
-
|
|
39
|
-
void StreamArchive::open_archive() {
|
|
40
|
-
archive_ptr ar = new_read_archive_common(_options.passphrases, _options.formats, [this](struct archive *ar) -> int {
|
|
41
|
-
archive_read_set_callback_data(ar, this);
|
|
42
|
-
archive_read_set_read_callback(ar, read_callback_bridge);
|
|
43
|
-
if (_stream->can_seek()) {
|
|
44
|
-
archive_read_set_skip_callback(ar, skip_callback_bridge);
|
|
45
|
-
archive_read_set_seek_callback(ar, seek_callback_bridge);
|
|
46
|
-
}
|
|
47
|
-
return archive_read_open1(ar);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
_ar = ar.release();
|
|
51
|
-
current_entryname.clear();
|
|
52
|
-
_at_eof = false;
|
|
53
|
-
_current_entry_content_ready = false;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
void StreamArchive::rewind() {
|
|
57
|
-
_stream->rewind();
|
|
58
|
-
Archive::rewind();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
PathHierarchy StreamArchive::source_hierarchy() const { return _stream->source_hierarchy(); }
|
|
62
|
-
|
|
63
|
-
la_ssize_t StreamArchive::read_callback_bridge(struct archive *a, void *client_data, const void **buff) {
|
|
64
|
-
auto *archive = static_cast<StreamArchive *>(client_data);
|
|
65
|
-
|
|
66
|
-
ssize_t bytes_read = 0;
|
|
67
|
-
try {
|
|
68
|
-
bytes_read = archive->_stream->read(archive->_buffer.data(), archive->_buffer.size());
|
|
69
|
-
} catch (const std::exception &) {
|
|
70
|
-
return -1;
|
|
71
|
-
}
|
|
72
|
-
if (bytes_read < 0) {
|
|
73
|
-
return static_cast<la_ssize_t>(bytes_read);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
*buff = archive->_buffer.data();
|
|
77
|
-
|
|
78
|
-
return static_cast<la_ssize_t>(bytes_read);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
la_int64_t StreamArchive::seek_callback_bridge(struct archive *a, void *client_data, la_int64_t request, int whence) {
|
|
82
|
-
auto *archive = static_cast<StreamArchive *>(client_data);
|
|
83
|
-
try {
|
|
84
|
-
return archive->_stream->seek(request, whence);
|
|
85
|
-
} catch (const std::exception &) {
|
|
86
|
-
return -1;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
la_int64_t StreamArchive::skip_callback_bridge(struct archive *a, void *client_data, la_int64_t request) {
|
|
91
|
-
auto *archive = static_cast<StreamArchive *>(client_data);
|
|
92
|
-
try {
|
|
93
|
-
la_int64_t current = archive->_stream->tell();
|
|
94
|
-
if (current < 0) {
|
|
95
|
-
current = archive->_stream->seek(0, SEEK_CUR);
|
|
96
|
-
}
|
|
97
|
-
if (current < 0) {
|
|
98
|
-
return 0;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
auto result = archive->_stream->seek(request, SEEK_CUR);
|
|
102
|
-
if (result >= 0) {
|
|
103
|
-
return result - current;
|
|
104
|
-
}
|
|
105
|
-
} catch (const std::exception &) {
|
|
106
|
-
return 0;
|
|
107
|
-
}
|
|
108
|
-
return 0;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// ============================================================================
|
|
112
|
-
// EntryPayloadStream Implementation
|
|
113
|
-
// ============================================================================
|
|
114
|
-
|
|
115
|
-
EntryPayloadStream::EntryPayloadStream(std::shared_ptr<StreamArchive> parent_archive, PathHierarchy logical_path)
|
|
116
|
-
: MultiVolumeStreamBase(std::move(logical_path), false)
|
|
117
|
-
, _parent_archive(std::move(parent_archive)) {
|
|
118
|
-
if (!_parent_archive) {
|
|
119
|
-
throw std::invalid_argument("Invalid parent archive context");
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
EntryPayloadStream::~EntryPayloadStream() = default;
|
|
124
|
-
|
|
125
|
-
std::shared_ptr<StreamArchive> EntryPayloadStream::parent_archive() const { return _parent_archive; }
|
|
126
|
-
|
|
127
|
-
void EntryPayloadStream::rewind() {
|
|
128
|
-
MultiVolumeStreamBase::rewind();
|
|
129
|
-
const PathHierarchy first_part = pathhierarchy_select_single_part(_logical_path, 0);
|
|
130
|
-
const std::string entry_name = first_part.back().single_value();
|
|
131
|
-
|
|
132
|
-
if (!_parent_archive->skip_to_entry(entry_name)) {
|
|
133
|
-
throw_entry_fault("Parent archive does not contain requested stream part", first_part);
|
|
134
|
-
}
|
|
135
|
-
// leave parent positioned at the beginning of the first part so subsequent reads start cleanly
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
void EntryPayloadStream::open_single_part(const PathHierarchy &single_part) {
|
|
139
|
-
const std::string entry_name = single_part.back().single_value();
|
|
140
|
-
if (!_parent_archive->skip_to_entry(entry_name)) {
|
|
141
|
-
throw_entry_fault("Parent archive does not contain requested stream part", single_part);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
void EntryPayloadStream::close_single_part() {
|
|
146
|
-
if (_parent_archive->current_entryname.empty()) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
_parent_archive->skip_data();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
ssize_t EntryPayloadStream::read_from_single_part(void *buffer, size_t size) {
|
|
154
|
-
if (size == 0) {
|
|
155
|
-
return 0;
|
|
156
|
-
}
|
|
157
|
-
return _parent_archive->read_current(buffer, size);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
int64_t EntryPayloadStream::seek_within_single_part(int64_t offset, int whence) { return -1; }
|
|
161
|
-
|
|
162
|
-
int64_t EntryPayloadStream::size_of_single_part(const PathHierarchy &single_part) {
|
|
163
|
-
(void)single_part;
|
|
164
|
-
return -1;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ============================================================================
|
|
168
|
-
// ArchiveStackCursor Implementation
|
|
169
|
-
// ============================================================================
|
|
170
|
-
|
|
171
|
-
ArchiveStackCursor::ArchiveStackCursor()
|
|
172
|
-
: options_snapshot()
|
|
173
|
-
, stream_stack() {}
|
|
174
|
-
|
|
175
|
-
void ArchiveStackCursor::configure(const ArchiveOption &options) {
|
|
176
|
-
options_snapshot = options;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
void ArchiveStackCursor::reset() {
|
|
180
|
-
options_snapshot = ArchiveOption{};
|
|
181
|
-
stream_stack.clear();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
bool ArchiveStackCursor::descend() {
|
|
185
|
-
if (stream_stack.empty()) {
|
|
186
|
-
throw std::logic_error("stream stack is empty");
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
auto stream = stream_stack.back();
|
|
190
|
-
|
|
191
|
-
if (auto *archive = current_archive()) {
|
|
192
|
-
if (stream && !archive->current_entry_content_ready()) {
|
|
193
|
-
stream->rewind();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
PathHierarchy dummy_hierarchy = stream->source_hierarchy();
|
|
198
|
-
auto archive_ptr = std::make_shared<StreamArchive>(std::move(stream), options_snapshot);
|
|
199
|
-
append_single(dummy_hierarchy, std::string{});
|
|
200
|
-
stream_stack.emplace_back(std::make_shared<EntryPayloadStream>(archive_ptr, std::move(dummy_hierarchy)));
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
bool ArchiveStackCursor::ascend() {
|
|
205
|
-
if (stream_stack.size() <= 0) {
|
|
206
|
-
return false;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
stream_stack.pop_back();
|
|
210
|
-
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
bool ArchiveStackCursor::next() {
|
|
215
|
-
StreamArchive *archive = current_archive();
|
|
216
|
-
if (!archive) {
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
while (true) {
|
|
221
|
-
if (!archive->skip_to_next_header()) {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
if (!archive->current_entryname.empty()) {
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
stream_stack.back() = create_stream(current_entry_hierarchy());
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
bool ArchiveStackCursor::synchronize_to_hierarchy(const PathHierarchy &target_hierarchy) {
|
|
233
|
-
if (target_hierarchy.empty()) {
|
|
234
|
-
throw_entry_fault("target hierarchy cannot be empty", {});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const size_t last_depth = target_hierarchy.size() - 1;
|
|
238
|
-
if (stream_stack.size() < target_hierarchy.size()) {
|
|
239
|
-
stream_stack.resize(target_hierarchy.size());
|
|
240
|
-
}
|
|
241
|
-
for (size_t depth = 0; depth < target_hierarchy.size(); ++depth) {
|
|
242
|
-
auto prefix = pathhierarchy_prefix_until(target_hierarchy, depth);
|
|
243
|
-
auto stream = stream_stack[depth];
|
|
244
|
-
|
|
245
|
-
// Reuse the existing stream when it already matches this prefix.
|
|
246
|
-
if (stream && hierarchies_equal(stream->source_hierarchy(), prefix)) {
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
// Shrink the stack to the current depth before creating a fresh stream.
|
|
250
|
-
stream_stack.resize(depth+1);
|
|
251
|
-
stream = create_stream(prefix);
|
|
252
|
-
stream_stack.back() = stream;
|
|
253
|
-
stream->rewind();
|
|
254
|
-
|
|
255
|
-
if (depth == last_depth) {
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
// Descend into the archive for the next level of the hierarchy.
|
|
259
|
-
descend();
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return true;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
ssize_t ArchiveStackCursor::read(void *buff, size_t len) {
|
|
266
|
-
if (len == 0) {
|
|
267
|
-
return 0;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (stream_stack.empty()) {
|
|
271
|
-
throw_entry_fault("Stream stack is empty", {});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
auto stream = stream_stack.back();
|
|
275
|
-
ssize_t bytes = 0;
|
|
276
|
-
bytes = stream->read(buff, len);
|
|
277
|
-
|
|
278
|
-
if (bytes < 0) {
|
|
279
|
-
const std::string message = "Failed to read from active stream";
|
|
280
|
-
throw_entry_fault(message, current_entry_hierarchy());
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return bytes;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
StreamArchive *ArchiveStackCursor::current_archive() {
|
|
287
|
-
if (stream_stack.size() <= 0) {
|
|
288
|
-
return nullptr;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const auto stream = std::dynamic_pointer_cast<EntryPayloadStream>(stream_stack.back());
|
|
292
|
-
if (!stream) {
|
|
293
|
-
return nullptr;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
auto parent_archive = stream->parent_archive();
|
|
297
|
-
return parent_archive ? parent_archive.get() : nullptr;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
PathHierarchy ArchiveStackCursor::current_entry_hierarchy() {
|
|
301
|
-
if (stream_stack.empty() || !stream_stack.front()) {
|
|
302
|
-
return {};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (StreamArchive *archive = current_archive()) {
|
|
306
|
-
PathHierarchy path = archive->source_hierarchy();
|
|
307
|
-
if (!archive->current_entryname.empty()) {
|
|
308
|
-
append_single(path, archive->current_entryname);
|
|
309
|
-
}
|
|
310
|
-
return path;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return stream_stack.front()->source_hierarchy();
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
std::shared_ptr<IDataStream> ArchiveStackCursor::create_stream(const PathHierarchy &hierarchy) {
|
|
317
|
-
if (hierarchy.size() == 1) {
|
|
318
|
-
if (auto factory = get_root_stream_factory()) {
|
|
319
|
-
if (auto stream = factory(hierarchy)) {
|
|
320
|
-
return stream;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return std::make_shared<SystemFileStream>(hierarchy);
|
|
324
|
-
}
|
|
325
|
-
auto stream = std::dynamic_pointer_cast<EntryPayloadStream>(stream_stack.back());
|
|
326
|
-
|
|
327
|
-
return std::make_shared<EntryPayloadStream>(stream->parent_archive(), hierarchy);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
} // namespace archive_r
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "archive_stack_cursor.h"
|
|
5
|
+
|
|
6
|
+
#include "archive_r/path_hierarchy_utils.h"
|
|
7
|
+
#include "system_file_stream.h"
|
|
8
|
+
#include <exception>
|
|
9
|
+
#include <memory>
|
|
10
|
+
#include <stdexcept>
|
|
11
|
+
#include <typeinfo>
|
|
12
|
+
#include <utility>
|
|
13
|
+
|
|
14
|
+
namespace archive_r {
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
[[noreturn]] void throw_entry_fault(const std::string &message, const PathHierarchy &hierarchy) { throw make_entry_fault_error(message, hierarchy); }
|
|
19
|
+
|
|
20
|
+
} // namespace
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// StreamArchive Implementation
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
StreamArchive::StreamArchive(std::shared_ptr<IDataStream> stream, ArchiveOption options)
|
|
27
|
+
: Archive()
|
|
28
|
+
, _stream(std::move(stream))
|
|
29
|
+
, _options(std::move(options)) {
|
|
30
|
+
if (!_stream) {
|
|
31
|
+
throw std::invalid_argument("StreamArchive requires a valid data stream");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
open_archive();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
StreamArchive::~StreamArchive() = default;
|
|
38
|
+
|
|
39
|
+
void StreamArchive::open_archive() {
|
|
40
|
+
archive_ptr ar = new_read_archive_common(_options.passphrases, _options.formats, [this](struct archive *ar) -> int {
|
|
41
|
+
archive_read_set_callback_data(ar, this);
|
|
42
|
+
archive_read_set_read_callback(ar, read_callback_bridge);
|
|
43
|
+
if (_stream->can_seek()) {
|
|
44
|
+
archive_read_set_skip_callback(ar, skip_callback_bridge);
|
|
45
|
+
archive_read_set_seek_callback(ar, seek_callback_bridge);
|
|
46
|
+
}
|
|
47
|
+
return archive_read_open1(ar);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
_ar = ar.release();
|
|
51
|
+
current_entryname.clear();
|
|
52
|
+
_at_eof = false;
|
|
53
|
+
_current_entry_content_ready = false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void StreamArchive::rewind() {
|
|
57
|
+
_stream->rewind();
|
|
58
|
+
Archive::rewind();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
PathHierarchy StreamArchive::source_hierarchy() const { return _stream->source_hierarchy(); }
|
|
62
|
+
|
|
63
|
+
la_ssize_t StreamArchive::read_callback_bridge(struct archive *a, void *client_data, const void **buff) {
|
|
64
|
+
auto *archive = static_cast<StreamArchive *>(client_data);
|
|
65
|
+
|
|
66
|
+
ssize_t bytes_read = 0;
|
|
67
|
+
try {
|
|
68
|
+
bytes_read = archive->_stream->read(archive->_buffer.data(), archive->_buffer.size());
|
|
69
|
+
} catch (const std::exception &) {
|
|
70
|
+
return -1;
|
|
71
|
+
}
|
|
72
|
+
if (bytes_read < 0) {
|
|
73
|
+
return static_cast<la_ssize_t>(bytes_read);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
*buff = archive->_buffer.data();
|
|
77
|
+
|
|
78
|
+
return static_cast<la_ssize_t>(bytes_read);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
la_int64_t StreamArchive::seek_callback_bridge(struct archive *a, void *client_data, la_int64_t request, int whence) {
|
|
82
|
+
auto *archive = static_cast<StreamArchive *>(client_data);
|
|
83
|
+
try {
|
|
84
|
+
return archive->_stream->seek(request, whence);
|
|
85
|
+
} catch (const std::exception &) {
|
|
86
|
+
return -1;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
la_int64_t StreamArchive::skip_callback_bridge(struct archive *a, void *client_data, la_int64_t request) {
|
|
91
|
+
auto *archive = static_cast<StreamArchive *>(client_data);
|
|
92
|
+
try {
|
|
93
|
+
la_int64_t current = archive->_stream->tell();
|
|
94
|
+
if (current < 0) {
|
|
95
|
+
current = archive->_stream->seek(0, SEEK_CUR);
|
|
96
|
+
}
|
|
97
|
+
if (current < 0) {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
auto result = archive->_stream->seek(request, SEEK_CUR);
|
|
102
|
+
if (result >= 0) {
|
|
103
|
+
return result - current;
|
|
104
|
+
}
|
|
105
|
+
} catch (const std::exception &) {
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// EntryPayloadStream Implementation
|
|
113
|
+
// ============================================================================
|
|
114
|
+
|
|
115
|
+
EntryPayloadStream::EntryPayloadStream(std::shared_ptr<StreamArchive> parent_archive, PathHierarchy logical_path)
|
|
116
|
+
: MultiVolumeStreamBase(std::move(logical_path), false)
|
|
117
|
+
, _parent_archive(std::move(parent_archive)) {
|
|
118
|
+
if (!_parent_archive) {
|
|
119
|
+
throw std::invalid_argument("Invalid parent archive context");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
EntryPayloadStream::~EntryPayloadStream() = default;
|
|
124
|
+
|
|
125
|
+
std::shared_ptr<StreamArchive> EntryPayloadStream::parent_archive() const { return _parent_archive; }
|
|
126
|
+
|
|
127
|
+
void EntryPayloadStream::rewind() {
|
|
128
|
+
MultiVolumeStreamBase::rewind();
|
|
129
|
+
const PathHierarchy first_part = pathhierarchy_select_single_part(_logical_path, 0);
|
|
130
|
+
const std::string entry_name = first_part.back().single_value();
|
|
131
|
+
|
|
132
|
+
if (!_parent_archive->skip_to_entry(entry_name)) {
|
|
133
|
+
throw_entry_fault("Parent archive does not contain requested stream part", first_part);
|
|
134
|
+
}
|
|
135
|
+
// leave parent positioned at the beginning of the first part so subsequent reads start cleanly
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
void EntryPayloadStream::open_single_part(const PathHierarchy &single_part) {
|
|
139
|
+
const std::string entry_name = single_part.back().single_value();
|
|
140
|
+
if (!_parent_archive->skip_to_entry(entry_name)) {
|
|
141
|
+
throw_entry_fault("Parent archive does not contain requested stream part", single_part);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
void EntryPayloadStream::close_single_part() {
|
|
146
|
+
if (_parent_archive->current_entryname.empty()) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_parent_archive->skip_data();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
ssize_t EntryPayloadStream::read_from_single_part(void *buffer, size_t size) {
|
|
154
|
+
if (size == 0) {
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
return _parent_archive->read_current(buffer, size);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
int64_t EntryPayloadStream::seek_within_single_part(int64_t offset, int whence) { return -1; }
|
|
161
|
+
|
|
162
|
+
int64_t EntryPayloadStream::size_of_single_part(const PathHierarchy &single_part) {
|
|
163
|
+
(void)single_part;
|
|
164
|
+
return -1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ============================================================================
|
|
168
|
+
// ArchiveStackCursor Implementation
|
|
169
|
+
// ============================================================================
|
|
170
|
+
|
|
171
|
+
ArchiveStackCursor::ArchiveStackCursor()
|
|
172
|
+
: options_snapshot()
|
|
173
|
+
, stream_stack() {}
|
|
174
|
+
|
|
175
|
+
void ArchiveStackCursor::configure(const ArchiveOption &options) {
|
|
176
|
+
options_snapshot = options;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void ArchiveStackCursor::reset() {
|
|
180
|
+
options_snapshot = ArchiveOption{};
|
|
181
|
+
stream_stack.clear();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
bool ArchiveStackCursor::descend() {
|
|
185
|
+
if (stream_stack.empty()) {
|
|
186
|
+
throw std::logic_error("stream stack is empty");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
auto stream = stream_stack.back();
|
|
190
|
+
|
|
191
|
+
if (auto *archive = current_archive()) {
|
|
192
|
+
if (stream && !archive->current_entry_content_ready()) {
|
|
193
|
+
stream->rewind();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
PathHierarchy dummy_hierarchy = stream->source_hierarchy();
|
|
198
|
+
auto archive_ptr = std::make_shared<StreamArchive>(std::move(stream), options_snapshot);
|
|
199
|
+
append_single(dummy_hierarchy, std::string{});
|
|
200
|
+
stream_stack.emplace_back(std::make_shared<EntryPayloadStream>(archive_ptr, std::move(dummy_hierarchy)));
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
bool ArchiveStackCursor::ascend() {
|
|
205
|
+
if (stream_stack.size() <= 0) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
stream_stack.pop_back();
|
|
210
|
+
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
bool ArchiveStackCursor::next() {
|
|
215
|
+
StreamArchive *archive = current_archive();
|
|
216
|
+
if (!archive) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
while (true) {
|
|
221
|
+
if (!archive->skip_to_next_header()) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
if (!archive->current_entryname.empty()) {
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
stream_stack.back() = create_stream(current_entry_hierarchy());
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
bool ArchiveStackCursor::synchronize_to_hierarchy(const PathHierarchy &target_hierarchy) {
|
|
233
|
+
if (target_hierarchy.empty()) {
|
|
234
|
+
throw_entry_fault("target hierarchy cannot be empty", {});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const size_t last_depth = target_hierarchy.size() - 1;
|
|
238
|
+
if (stream_stack.size() < target_hierarchy.size()) {
|
|
239
|
+
stream_stack.resize(target_hierarchy.size());
|
|
240
|
+
}
|
|
241
|
+
for (size_t depth = 0; depth < target_hierarchy.size(); ++depth) {
|
|
242
|
+
auto prefix = pathhierarchy_prefix_until(target_hierarchy, depth);
|
|
243
|
+
auto stream = stream_stack[depth];
|
|
244
|
+
|
|
245
|
+
// Reuse the existing stream when it already matches this prefix.
|
|
246
|
+
if (stream && hierarchies_equal(stream->source_hierarchy(), prefix)) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
// Shrink the stack to the current depth before creating a fresh stream.
|
|
250
|
+
stream_stack.resize(depth+1);
|
|
251
|
+
stream = create_stream(prefix);
|
|
252
|
+
stream_stack.back() = stream;
|
|
253
|
+
stream->rewind();
|
|
254
|
+
|
|
255
|
+
if (depth == last_depth) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
// Descend into the archive for the next level of the hierarchy.
|
|
259
|
+
descend();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
ssize_t ArchiveStackCursor::read(void *buff, size_t len) {
|
|
266
|
+
if (len == 0) {
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (stream_stack.empty()) {
|
|
271
|
+
throw_entry_fault("Stream stack is empty", {});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
auto stream = stream_stack.back();
|
|
275
|
+
ssize_t bytes = 0;
|
|
276
|
+
bytes = stream->read(buff, len);
|
|
277
|
+
|
|
278
|
+
if (bytes < 0) {
|
|
279
|
+
const std::string message = "Failed to read from active stream";
|
|
280
|
+
throw_entry_fault(message, current_entry_hierarchy());
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return bytes;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
StreamArchive *ArchiveStackCursor::current_archive() {
|
|
287
|
+
if (stream_stack.size() <= 0) {
|
|
288
|
+
return nullptr;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const auto stream = std::dynamic_pointer_cast<EntryPayloadStream>(stream_stack.back());
|
|
292
|
+
if (!stream) {
|
|
293
|
+
return nullptr;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
auto parent_archive = stream->parent_archive();
|
|
297
|
+
return parent_archive ? parent_archive.get() : nullptr;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
PathHierarchy ArchiveStackCursor::current_entry_hierarchy() {
|
|
301
|
+
if (stream_stack.empty() || !stream_stack.front()) {
|
|
302
|
+
return {};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (StreamArchive *archive = current_archive()) {
|
|
306
|
+
PathHierarchy path = archive->source_hierarchy();
|
|
307
|
+
if (!archive->current_entryname.empty()) {
|
|
308
|
+
append_single(path, archive->current_entryname);
|
|
309
|
+
}
|
|
310
|
+
return path;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return stream_stack.front()->source_hierarchy();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
std::shared_ptr<IDataStream> ArchiveStackCursor::create_stream(const PathHierarchy &hierarchy) {
|
|
317
|
+
if (hierarchy.size() == 1) {
|
|
318
|
+
if (auto factory = get_root_stream_factory()) {
|
|
319
|
+
if (auto stream = factory(hierarchy)) {
|
|
320
|
+
return stream;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return std::make_shared<SystemFileStream>(hierarchy);
|
|
324
|
+
}
|
|
325
|
+
auto stream = std::dynamic_pointer_cast<EntryPayloadStream>(stream_stack.back());
|
|
326
|
+
|
|
327
|
+
return std::make_shared<EntryPayloadStream>(stream->parent_archive(), hierarchy);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
} // namespace archive_r
|