archive_r_ruby 0.1.21 → 0.1.22

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -21
  3. data/NOTICE +116 -116
  4. data/README.md +106 -106
  5. data/VERSION +1 -1
  6. data/ext/archive_r/archive_r_ext.cc +1098 -1098
  7. data/ext/archive_r/extconf.rb +125 -125
  8. data/ext/archive_r/vendor/archive_r/LICENSE +21 -21
  9. data/ext/archive_r/vendor/archive_r/NOTICE +116 -116
  10. data/ext/archive_r/vendor/archive_r/include/archive_r/data_stream.h +42 -42
  11. data/ext/archive_r/vendor/archive_r/include/archive_r/entry.h +180 -180
  12. data/ext/archive_r/vendor/archive_r/include/archive_r/entry_fault.h +34 -34
  13. data/ext/archive_r/vendor/archive_r/include/archive_r/entry_metadata.h +56 -56
  14. data/ext/archive_r/vendor/archive_r/include/archive_r/multi_volume_stream_base.h +46 -46
  15. data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy.h +92 -92
  16. data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy_utils.h +36 -36
  17. data/ext/archive_r/vendor/archive_r/include/archive_r/platform_compat.h +34 -34
  18. data/ext/archive_r/vendor/archive_r/include/archive_r/traverser.h +156 -156
  19. data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.cc +300 -300
  20. data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.h +110 -110
  21. data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.cc +161 -161
  22. data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.h +53 -53
  23. data/ext/archive_r/vendor/archive_r/src/archive_type.cc +545 -545
  24. data/ext/archive_r/vendor/archive_r/src/archive_type.h +77 -77
  25. data/ext/archive_r/vendor/archive_r/src/data_stream.cc +35 -35
  26. data/ext/archive_r/vendor/archive_r/src/entry.cc +238 -238
  27. data/ext/archive_r/vendor/archive_r/src/entry_fault.cc +26 -26
  28. data/ext/archive_r/vendor/archive_r/src/entry_fault_error.cc +54 -54
  29. data/ext/archive_r/vendor/archive_r/src/entry_fault_error.h +32 -32
  30. data/ext/archive_r/vendor/archive_r/src/entry_impl.h +56 -56
  31. data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.cc +76 -76
  32. data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.h +39 -39
  33. data/ext/archive_r/vendor/archive_r/src/multi_volume_stream_base.cc +208 -208
  34. data/ext/archive_r/vendor/archive_r/src/path_hierarchy.cc +127 -127
  35. data/ext/archive_r/vendor/archive_r/src/path_hierarchy_utils.cc +251 -251
  36. data/ext/archive_r/vendor/archive_r/src/simple_profiler.h +109 -109
  37. data/ext/archive_r/vendor/archive_r/src/system_file_stream.cc +294 -294
  38. data/ext/archive_r/vendor/archive_r/src/system_file_stream.h +46 -46
  39. data/ext/archive_r/vendor/archive_r/src/traverser.cc +295 -295
  40. data/lib/archive_r.rb +120 -120
  41. metadata +3 -6
@@ -1,300 +1,300 @@
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
- std::shared_ptr<StreamArchive> StreamArchive::parent_archive() const {
64
- auto entry_stream = std::dynamic_pointer_cast<EntryPayloadStream>(_stream);
65
- return entry_stream ? entry_stream->parent_archive() : nullptr;
66
- }
67
-
68
- la_ssize_t StreamArchive::read_callback_bridge(struct archive *a, void *client_data, const void **buff) {
69
- auto *archive = static_cast<StreamArchive *>(client_data);
70
-
71
- ssize_t bytes_read = 0;
72
- try {
73
- bytes_read = archive->_stream->read(archive->_buffer.data(), archive->_buffer.size());
74
- } catch (const std::exception &) {
75
- return -1;
76
- }
77
- if (bytes_read < 0) {
78
- return static_cast<la_ssize_t>(bytes_read);
79
- }
80
-
81
- *buff = archive->_buffer.data();
82
-
83
- return static_cast<la_ssize_t>(bytes_read);
84
- }
85
-
86
- la_int64_t StreamArchive::seek_callback_bridge(struct archive *a, void *client_data, la_int64_t request, int whence) {
87
- auto *archive = static_cast<StreamArchive *>(client_data);
88
- try {
89
- return archive->_stream->seek(request, whence);
90
- } catch (const std::exception &) {
91
- return -1;
92
- }
93
- }
94
-
95
- la_int64_t StreamArchive::skip_callback_bridge(struct archive *a, void *client_data, la_int64_t request) {
96
- auto *archive = static_cast<StreamArchive *>(client_data);
97
- try {
98
- la_int64_t current = archive->_stream->tell();
99
- if (current < 0) {
100
- current = archive->_stream->seek(0, SEEK_CUR);
101
- }
102
- if (current < 0) {
103
- return 0;
104
- }
105
-
106
- auto result = archive->_stream->seek(request, SEEK_CUR);
107
- if (result >= 0) {
108
- return result - current;
109
- }
110
- } catch (const std::exception &) {
111
- return 0;
112
- }
113
- return 0;
114
- }
115
-
116
- // ============================================================================
117
- // EntryPayloadStream Implementation
118
- // ============================================================================
119
-
120
- EntryPayloadStream::EntryPayloadStream(std::shared_ptr<StreamArchive> parent_archive, PathHierarchy logical_path)
121
- : MultiVolumeStreamBase(std::move(logical_path), false)
122
- , _parent_archive(std::move(parent_archive)) {
123
- if (!_parent_archive) {
124
- throw std::invalid_argument("Invalid parent archive context");
125
- }
126
- }
127
-
128
- EntryPayloadStream::~EntryPayloadStream() { deactivate_active_part(); }
129
-
130
- std::shared_ptr<StreamArchive> EntryPayloadStream::parent_archive() const { return _parent_archive; }
131
-
132
- void EntryPayloadStream::rewind() {
133
- MultiVolumeStreamBase::rewind();
134
- const PathHierarchy first_part = pathhierarchy_select_single_part(_logical_path, 0);
135
- const std::string entry_name = first_part.back().single_value();
136
-
137
- if (!_parent_archive->skip_to_entry(entry_name)) {
138
- throw_entry_fault("Parent archive does not contain requested stream part", first_part);
139
- }
140
- // leave parent positioned at the beginning of the first part so subsequent reads start cleanly
141
- }
142
-
143
- void EntryPayloadStream::open_single_part(const PathHierarchy &single_part) {
144
- const std::string entry_name = single_part.back().single_value();
145
- if (!_parent_archive->skip_to_entry(entry_name)) {
146
- throw_entry_fault("Parent archive does not contain requested stream part", single_part);
147
- }
148
- }
149
-
150
- void EntryPayloadStream::close_single_part() {
151
- // libarchive automatically skips unread data when reading the next header,
152
- // so explicit skipping here is unnecessary and avoids potential exceptions in destructor.
153
- }
154
-
155
- ssize_t EntryPayloadStream::read_from_single_part(void *buffer, size_t size) { return _parent_archive->read_current(buffer, size); }
156
-
157
- // ============================================================================
158
- // ArchiveStackCursor Implementation
159
- // ============================================================================
160
-
161
- ArchiveStackCursor::ArchiveStackCursor()
162
- : options_snapshot()
163
- , _current_stream(nullptr)
164
- , _current_archive(nullptr) {}
165
-
166
- void ArchiveStackCursor::configure(const ArchiveOption &options) { options_snapshot = options; }
167
-
168
- void ArchiveStackCursor::reset() {
169
- options_snapshot = ArchiveOption{};
170
- _current_stream = nullptr;
171
- _current_archive = nullptr;
172
- }
173
-
174
- bool ArchiveStackCursor::descend() {
175
- if (!_current_stream) {
176
- throw std::logic_error("current stream is empty");
177
- }
178
-
179
- auto stream = _current_stream;
180
- if (auto *archive = current_archive()) {
181
- if (stream && !archive->current_entry_content_ready()) {
182
- stream->rewind();
183
- }
184
- }
185
-
186
- PathHierarchy dummy_hierarchy = stream->source_hierarchy();
187
- auto archive_ptr = std::make_shared<StreamArchive>(std::move(stream), options_snapshot);
188
- _current_archive = archive_ptr;
189
- _current_stream = nullptr;
190
- return true;
191
- }
192
-
193
- bool ArchiveStackCursor::ascend() {
194
- if (!_current_archive) {
195
- return false;
196
- }
197
-
198
- _current_stream = _current_archive->get_stream();
199
- _current_archive = _current_archive->parent_archive();
200
- return true;
201
- }
202
-
203
- bool ArchiveStackCursor::next() {
204
- StreamArchive *archive = current_archive();
205
- if (!archive) {
206
- return false;
207
- }
208
-
209
- _current_stream = nullptr;
210
-
211
- while (true) {
212
- if (!archive->skip_to_next_header()) {
213
- return false;
214
- }
215
- if (!archive->current_entryname.empty()) {
216
- break;
217
- }
218
- }
219
-
220
- _current_stream = create_stream(current_entry_hierarchy());
221
- return true;
222
- }
223
-
224
- bool ArchiveStackCursor::synchronize_to_hierarchy(const PathHierarchy &target_hierarchy) {
225
- if (target_hierarchy.empty()) {
226
- throw_entry_fault("target hierarchy cannot be empty", {});
227
- }
228
-
229
- // 1. Ascend until we find a common ancestor
230
- while (depth() > 0) {
231
- auto current_h = _current_archive->source_hierarchy();
232
- if (current_h.size() <= target_hierarchy.size() && hierarchies_equal(current_h, pathhierarchy_prefix_until(target_hierarchy, current_h.size() - 1))) {
233
- break;
234
- }
235
- ascend();
236
- }
237
-
238
- // 2. Descend to target
239
- for (size_t d = depth(); d < target_hierarchy.size(); ++d) {
240
- auto prefix = pathhierarchy_prefix_until(target_hierarchy, d);
241
-
242
- if (!_current_stream || !hierarchies_equal(_current_stream->source_hierarchy(), prefix)) {
243
- _current_stream = create_stream(prefix);
244
- _current_stream->rewind();
245
- }
246
-
247
- if (d < target_hierarchy.size() - 1) {
248
- descend();
249
- }
250
- }
251
-
252
- return true;
253
- }
254
-
255
- ssize_t ArchiveStackCursor::read(void *buff, size_t len) {
256
- if (len == 0) {
257
- return 0;
258
- }
259
-
260
- if (StreamArchive *archive = current_archive()) {
261
- return archive->read_current(buff, len);
262
- }
263
-
264
- if (_current_stream) {
265
- return _current_stream->read(buff, len);
266
- }
267
- return 0;
268
- }
269
-
270
- StreamArchive *ArchiveStackCursor::current_archive() { return _current_archive.get(); }
271
-
272
- PathHierarchy ArchiveStackCursor::current_entry_hierarchy() {
273
- if (!_current_stream && !_current_archive) {
274
- return {};
275
- }
276
-
277
- if (StreamArchive *archive = current_archive()) {
278
- PathHierarchy path = archive->source_hierarchy();
279
- if (!archive->current_entryname.empty()) {
280
- append_single(path, archive->current_entryname);
281
- }
282
- return path;
283
- }
284
-
285
- return _current_stream ? _current_stream->source_hierarchy() : PathHierarchy{};
286
- }
287
-
288
- std::shared_ptr<IDataStream> ArchiveStackCursor::create_stream(const PathHierarchy &hierarchy) {
289
- if (hierarchy.size() == 1) {
290
- if (auto factory = get_root_stream_factory()) {
291
- if (auto stream = factory(hierarchy)) {
292
- return stream;
293
- }
294
- }
295
- return std::make_shared<SystemFileStream>(hierarchy);
296
- }
297
- return std::make_shared<EntryPayloadStream>(_current_archive, hierarchy);
298
- }
299
-
300
- } // 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
+ std::shared_ptr<StreamArchive> StreamArchive::parent_archive() const {
64
+ auto entry_stream = std::dynamic_pointer_cast<EntryPayloadStream>(_stream);
65
+ return entry_stream ? entry_stream->parent_archive() : nullptr;
66
+ }
67
+
68
+ la_ssize_t StreamArchive::read_callback_bridge(struct archive *a, void *client_data, const void **buff) {
69
+ auto *archive = static_cast<StreamArchive *>(client_data);
70
+
71
+ ssize_t bytes_read = 0;
72
+ try {
73
+ bytes_read = archive->_stream->read(archive->_buffer.data(), archive->_buffer.size());
74
+ } catch (const std::exception &) {
75
+ return -1;
76
+ }
77
+ if (bytes_read < 0) {
78
+ return static_cast<la_ssize_t>(bytes_read);
79
+ }
80
+
81
+ *buff = archive->_buffer.data();
82
+
83
+ return static_cast<la_ssize_t>(bytes_read);
84
+ }
85
+
86
+ la_int64_t StreamArchive::seek_callback_bridge(struct archive *a, void *client_data, la_int64_t request, int whence) {
87
+ auto *archive = static_cast<StreamArchive *>(client_data);
88
+ try {
89
+ return archive->_stream->seek(request, whence);
90
+ } catch (const std::exception &) {
91
+ return -1;
92
+ }
93
+ }
94
+
95
+ la_int64_t StreamArchive::skip_callback_bridge(struct archive *a, void *client_data, la_int64_t request) {
96
+ auto *archive = static_cast<StreamArchive *>(client_data);
97
+ try {
98
+ la_int64_t current = archive->_stream->tell();
99
+ if (current < 0) {
100
+ current = archive->_stream->seek(0, SEEK_CUR);
101
+ }
102
+ if (current < 0) {
103
+ return 0;
104
+ }
105
+
106
+ auto result = archive->_stream->seek(request, SEEK_CUR);
107
+ if (result >= 0) {
108
+ return result - current;
109
+ }
110
+ } catch (const std::exception &) {
111
+ return 0;
112
+ }
113
+ return 0;
114
+ }
115
+
116
+ // ============================================================================
117
+ // EntryPayloadStream Implementation
118
+ // ============================================================================
119
+
120
+ EntryPayloadStream::EntryPayloadStream(std::shared_ptr<StreamArchive> parent_archive, PathHierarchy logical_path)
121
+ : MultiVolumeStreamBase(std::move(logical_path), false)
122
+ , _parent_archive(std::move(parent_archive)) {
123
+ if (!_parent_archive) {
124
+ throw std::invalid_argument("Invalid parent archive context");
125
+ }
126
+ }
127
+
128
+ EntryPayloadStream::~EntryPayloadStream() { deactivate_active_part(); }
129
+
130
+ std::shared_ptr<StreamArchive> EntryPayloadStream::parent_archive() const { return _parent_archive; }
131
+
132
+ void EntryPayloadStream::rewind() {
133
+ MultiVolumeStreamBase::rewind();
134
+ const PathHierarchy first_part = pathhierarchy_select_single_part(_logical_path, 0);
135
+ const std::string entry_name = first_part.back().single_value();
136
+
137
+ if (!_parent_archive->skip_to_entry(entry_name)) {
138
+ throw_entry_fault("Parent archive does not contain requested stream part", first_part);
139
+ }
140
+ // leave parent positioned at the beginning of the first part so subsequent reads start cleanly
141
+ }
142
+
143
+ void EntryPayloadStream::open_single_part(const PathHierarchy &single_part) {
144
+ const std::string entry_name = single_part.back().single_value();
145
+ if (!_parent_archive->skip_to_entry(entry_name)) {
146
+ throw_entry_fault("Parent archive does not contain requested stream part", single_part);
147
+ }
148
+ }
149
+
150
+ void EntryPayloadStream::close_single_part() {
151
+ // libarchive automatically skips unread data when reading the next header,
152
+ // so explicit skipping here is unnecessary and avoids potential exceptions in destructor.
153
+ }
154
+
155
+ ssize_t EntryPayloadStream::read_from_single_part(void *buffer, size_t size) { return _parent_archive->read_current(buffer, size); }
156
+
157
+ // ============================================================================
158
+ // ArchiveStackCursor Implementation
159
+ // ============================================================================
160
+
161
+ ArchiveStackCursor::ArchiveStackCursor()
162
+ : options_snapshot()
163
+ , _current_stream(nullptr)
164
+ , _current_archive(nullptr) {}
165
+
166
+ void ArchiveStackCursor::configure(const ArchiveOption &options) { options_snapshot = options; }
167
+
168
+ void ArchiveStackCursor::reset() {
169
+ options_snapshot = ArchiveOption{};
170
+ _current_stream = nullptr;
171
+ _current_archive = nullptr;
172
+ }
173
+
174
+ bool ArchiveStackCursor::descend() {
175
+ if (!_current_stream) {
176
+ throw std::logic_error("current stream is empty");
177
+ }
178
+
179
+ auto stream = _current_stream;
180
+ if (auto *archive = current_archive()) {
181
+ if (stream && !archive->current_entry_content_ready()) {
182
+ stream->rewind();
183
+ }
184
+ }
185
+
186
+ PathHierarchy dummy_hierarchy = stream->source_hierarchy();
187
+ auto archive_ptr = std::make_shared<StreamArchive>(std::move(stream), options_snapshot);
188
+ _current_archive = archive_ptr;
189
+ _current_stream = nullptr;
190
+ return true;
191
+ }
192
+
193
+ bool ArchiveStackCursor::ascend() {
194
+ if (!_current_archive) {
195
+ return false;
196
+ }
197
+
198
+ _current_stream = _current_archive->get_stream();
199
+ _current_archive = _current_archive->parent_archive();
200
+ return true;
201
+ }
202
+
203
+ bool ArchiveStackCursor::next() {
204
+ StreamArchive *archive = current_archive();
205
+ if (!archive) {
206
+ return false;
207
+ }
208
+
209
+ _current_stream = nullptr;
210
+
211
+ while (true) {
212
+ if (!archive->skip_to_next_header()) {
213
+ return false;
214
+ }
215
+ if (!archive->current_entryname.empty()) {
216
+ break;
217
+ }
218
+ }
219
+
220
+ _current_stream = create_stream(current_entry_hierarchy());
221
+ return true;
222
+ }
223
+
224
+ bool ArchiveStackCursor::synchronize_to_hierarchy(const PathHierarchy &target_hierarchy) {
225
+ if (target_hierarchy.empty()) {
226
+ throw_entry_fault("target hierarchy cannot be empty", {});
227
+ }
228
+
229
+ // 1. Ascend until we find a common ancestor
230
+ while (depth() > 0) {
231
+ auto current_h = _current_archive->source_hierarchy();
232
+ if (current_h.size() <= target_hierarchy.size() && hierarchies_equal(current_h, pathhierarchy_prefix_until(target_hierarchy, current_h.size() - 1))) {
233
+ break;
234
+ }
235
+ ascend();
236
+ }
237
+
238
+ // 2. Descend to target
239
+ for (size_t d = depth(); d < target_hierarchy.size(); ++d) {
240
+ auto prefix = pathhierarchy_prefix_until(target_hierarchy, d);
241
+
242
+ if (!_current_stream || !hierarchies_equal(_current_stream->source_hierarchy(), prefix)) {
243
+ _current_stream = create_stream(prefix);
244
+ _current_stream->rewind();
245
+ }
246
+
247
+ if (d < target_hierarchy.size() - 1) {
248
+ descend();
249
+ }
250
+ }
251
+
252
+ return true;
253
+ }
254
+
255
+ ssize_t ArchiveStackCursor::read(void *buff, size_t len) {
256
+ if (len == 0) {
257
+ return 0;
258
+ }
259
+
260
+ if (StreamArchive *archive = current_archive()) {
261
+ return archive->read_current(buff, len);
262
+ }
263
+
264
+ if (_current_stream) {
265
+ return _current_stream->read(buff, len);
266
+ }
267
+ return 0;
268
+ }
269
+
270
+ StreamArchive *ArchiveStackCursor::current_archive() { return _current_archive.get(); }
271
+
272
+ PathHierarchy ArchiveStackCursor::current_entry_hierarchy() {
273
+ if (!_current_stream && !_current_archive) {
274
+ return {};
275
+ }
276
+
277
+ if (StreamArchive *archive = current_archive()) {
278
+ PathHierarchy path = archive->source_hierarchy();
279
+ if (!archive->current_entryname.empty()) {
280
+ append_single(path, archive->current_entryname);
281
+ }
282
+ return path;
283
+ }
284
+
285
+ return _current_stream ? _current_stream->source_hierarchy() : PathHierarchy{};
286
+ }
287
+
288
+ std::shared_ptr<IDataStream> ArchiveStackCursor::create_stream(const PathHierarchy &hierarchy) {
289
+ if (hierarchy.size() == 1) {
290
+ if (auto factory = get_root_stream_factory()) {
291
+ if (auto stream = factory(hierarchy)) {
292
+ return stream;
293
+ }
294
+ }
295
+ return std::make_shared<SystemFileStream>(hierarchy);
296
+ }
297
+ return std::make_shared<EntryPayloadStream>(_current_archive, hierarchy);
298
+ }
299
+
300
+ } // namespace archive_r