archive_r_ruby 0.1.0
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 +7 -0
- data/LICENSE +56 -0
- data/README.md +103 -0
- data/ext/archive_r/archive_r_ext.cc +910 -0
- data/ext/archive_r/extconf.rb +90 -0
- data/ext/archive_r/vendor/archive_r/LICENSE.txt +56 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/data_stream.h +41 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry.h +161 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry_fault.h +34 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/entry_metadata.h +56 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/multi_volume_stream_base.h +46 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy.h +109 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/path_hierarchy_utils.h +37 -0
- data/ext/archive_r/vendor/archive_r/include/archive_r/traverser.h +122 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.cc +330 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_cursor.h +98 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.cc +162 -0
- data/ext/archive_r/vendor/archive_r/src/archive_stack_orchestrator.h +54 -0
- data/ext/archive_r/vendor/archive_r/src/archive_type.cc +552 -0
- data/ext/archive_r/vendor/archive_r/src/archive_type.h +76 -0
- data/ext/archive_r/vendor/archive_r/src/data_stream.cc +35 -0
- data/ext/archive_r/vendor/archive_r/src/entry.cc +253 -0
- data/ext/archive_r/vendor/archive_r/src/entry_fault.cc +26 -0
- data/ext/archive_r/vendor/archive_r/src/entry_fault_error.cc +54 -0
- data/ext/archive_r/vendor/archive_r/src/entry_fault_error.h +32 -0
- data/ext/archive_r/vendor/archive_r/src/entry_impl.h +58 -0
- data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.cc +81 -0
- data/ext/archive_r/vendor/archive_r/src/multi_volume_manager.h +41 -0
- data/ext/archive_r/vendor/archive_r/src/multi_volume_stream_base.cc +199 -0
- data/ext/archive_r/vendor/archive_r/src/path_hierarchy.cc +151 -0
- data/ext/archive_r/vendor/archive_r/src/path_hierarchy_utils.cc +304 -0
- data/ext/archive_r/vendor/archive_r/src/simple_profiler.h +120 -0
- data/ext/archive_r/vendor/archive_r/src/system_file_stream.cc +263 -0
- data/ext/archive_r/vendor/archive_r/src/system_file_stream.h +46 -0
- data/ext/archive_r/vendor/archive_r/src/traverser.cc +314 -0
- data/lib/archive_r.rb +80 -0
- metadata +112 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "archive_r/multi_volume_stream_base.h"
|
|
5
|
+
|
|
6
|
+
#include "archive_r/path_hierarchy_utils.h"
|
|
7
|
+
|
|
8
|
+
#include <algorithm>
|
|
9
|
+
#include <memory>
|
|
10
|
+
#include <stdexcept>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
namespace archive_r {
|
|
14
|
+
|
|
15
|
+
struct MultiVolumeStreamBase::Impl {
|
|
16
|
+
explicit Impl(MultiVolumeStreamBase &owner)
|
|
17
|
+
: self(owner) {}
|
|
18
|
+
|
|
19
|
+
MultiVolumeStreamBase &self;
|
|
20
|
+
std::vector<int64_t> part_offsets;
|
|
21
|
+
std::size_t total_parts = 0;
|
|
22
|
+
std::size_t active_part_index = 0;
|
|
23
|
+
bool part_open = false;
|
|
24
|
+
int64_t logical_offset = 0;
|
|
25
|
+
int64_t total_size = -1;
|
|
26
|
+
|
|
27
|
+
void ensure_part_active(std::size_t part_index);
|
|
28
|
+
bool advance_to_next_part();
|
|
29
|
+
bool ensure_size_metadata();
|
|
30
|
+
int64_t compute_target_offset(int64_t offset, int whence) const;
|
|
31
|
+
std::size_t locate_part_for_offset(int64_t target, int64_t &offset_within_part) const;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
MultiVolumeStreamBase::MultiVolumeStreamBase(PathHierarchy logical_path, bool supports_seek)
|
|
35
|
+
: _logical_path(std::move(logical_path))
|
|
36
|
+
, _supports_seek(supports_seek)
|
|
37
|
+
, _impl(std::make_unique<Impl>(*this)) {
|
|
38
|
+
_impl->total_parts = pathhierarchy_volume_size(_logical_path);
|
|
39
|
+
if (_impl->total_parts == 0) {
|
|
40
|
+
throw std::invalid_argument("MultiVolumeStreamBase requires at least one volume component");
|
|
41
|
+
}
|
|
42
|
+
_impl->part_offsets.assign(_impl->total_parts + 1, 0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
MultiVolumeStreamBase::~MultiVolumeStreamBase() = default;
|
|
46
|
+
|
|
47
|
+
ssize_t MultiVolumeStreamBase::read(void *buffer, size_t size) {
|
|
48
|
+
if (size == 0) {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
std::size_t total_read = 0;
|
|
53
|
+
auto *out = static_cast<char *>(buffer);
|
|
54
|
+
|
|
55
|
+
while (total_read < size) {
|
|
56
|
+
if (_impl->active_part_index >= _impl->total_parts) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_impl->ensure_part_active(_impl->active_part_index);
|
|
61
|
+
const ssize_t bytes = read_from_single_part(out + total_read, size - total_read);
|
|
62
|
+
if (bytes > 0) {
|
|
63
|
+
total_read += static_cast<std::size_t>(bytes);
|
|
64
|
+
_impl->logical_offset += bytes;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (bytes < 0) {
|
|
69
|
+
return bytes;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!_impl->advance_to_next_part()) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return static_cast<ssize_t>(total_read);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
void MultiVolumeStreamBase::rewind() {
|
|
81
|
+
deactivate_active_part();
|
|
82
|
+
_impl->active_part_index = 0;
|
|
83
|
+
_impl->logical_offset = 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
bool MultiVolumeStreamBase::at_end() const {
|
|
87
|
+
return (_impl->active_part_index >= _impl->total_parts) && !_impl->part_open;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
int64_t MultiVolumeStreamBase::seek(int64_t offset, int whence) {
|
|
91
|
+
if (!_supports_seek) {
|
|
92
|
+
return -1;
|
|
93
|
+
}
|
|
94
|
+
if (!_impl->ensure_size_metadata()) {
|
|
95
|
+
return -1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const int64_t target = _impl->compute_target_offset(offset, whence);
|
|
99
|
+
if (target < 0 || target > _impl->total_size) {
|
|
100
|
+
return -1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (target == _impl->total_size) {
|
|
104
|
+
deactivate_active_part();
|
|
105
|
+
_impl->active_part_index = _impl->total_parts;
|
|
106
|
+
_impl->logical_offset = target;
|
|
107
|
+
return _impl->logical_offset;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
int64_t offset_within_part = 0;
|
|
111
|
+
const std::size_t part_index = _impl->locate_part_for_offset(target, offset_within_part);
|
|
112
|
+
_impl->ensure_part_active(part_index);
|
|
113
|
+
if (seek_within_single_part(offset_within_part, SEEK_SET) < 0) {
|
|
114
|
+
return -1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_impl->logical_offset = target;
|
|
118
|
+
_impl->active_part_index = part_index;
|
|
119
|
+
return _impl->logical_offset;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
int64_t MultiVolumeStreamBase::tell() const { return _impl->logical_offset; }
|
|
123
|
+
|
|
124
|
+
void MultiVolumeStreamBase::Impl::ensure_part_active(std::size_t part_index) {
|
|
125
|
+
if (part_open && active_part_index == part_index) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
self.deactivate_active_part();
|
|
130
|
+
PathHierarchy single_part = pathhierarchy_select_single_part(self._logical_path, part_index);
|
|
131
|
+
self.open_single_part(single_part);
|
|
132
|
+
active_part_index = part_index;
|
|
133
|
+
part_open = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void MultiVolumeStreamBase::deactivate_active_part() {
|
|
137
|
+
if (_impl->part_open) {
|
|
138
|
+
close_single_part();
|
|
139
|
+
_impl->part_open = false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
bool MultiVolumeStreamBase::Impl::advance_to_next_part() {
|
|
144
|
+
if (active_part_index >= total_parts) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
self.deactivate_active_part();
|
|
148
|
+
++active_part_index;
|
|
149
|
+
return active_part_index < total_parts;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
bool MultiVolumeStreamBase::Impl::ensure_size_metadata() {
|
|
153
|
+
if (total_size >= 0) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
int64_t prefix = 0;
|
|
158
|
+
part_offsets[0] = 0;
|
|
159
|
+
for (std::size_t index = 0; index < total_parts; ++index) {
|
|
160
|
+
PathHierarchy single_part = pathhierarchy_select_single_part(self._logical_path, index);
|
|
161
|
+
const int64_t size = self.size_of_single_part(single_part);
|
|
162
|
+
if (size < 0) {
|
|
163
|
+
total_size = -1;
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
prefix += size;
|
|
167
|
+
part_offsets[index + 1] = prefix;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
total_size = prefix;
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
int64_t MultiVolumeStreamBase::Impl::compute_target_offset(int64_t offset, int whence) const {
|
|
175
|
+
switch (whence) {
|
|
176
|
+
case SEEK_SET:
|
|
177
|
+
return offset;
|
|
178
|
+
case SEEK_CUR:
|
|
179
|
+
return logical_offset + offset;
|
|
180
|
+
case SEEK_END:
|
|
181
|
+
return total_size + offset;
|
|
182
|
+
default:
|
|
183
|
+
return -1;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
std::size_t MultiVolumeStreamBase::Impl::locate_part_for_offset(int64_t target, int64_t &offset_within_part) const {
|
|
188
|
+
auto it = std::upper_bound(part_offsets.begin(), part_offsets.end(), target);
|
|
189
|
+
if (it == part_offsets.begin()) {
|
|
190
|
+
offset_within_part = target;
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const std::size_t index = static_cast<std::size_t>(std::distance(part_offsets.begin(), it) - 1);
|
|
195
|
+
offset_within_part = target - part_offsets[index];
|
|
196
|
+
return index;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "archive_r/path_hierarchy.h"
|
|
5
|
+
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <utility>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
namespace archive_r {
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
int entry_type_rank(const PathEntry &entry) {
|
|
14
|
+
if (entry.is_single()) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
if (entry.is_multi_volume()) {
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
return 2;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
int compare_node_lists_impl(const PathEntry::NodeList &lhs, const PathEntry::NodeList &rhs);
|
|
24
|
+
|
|
25
|
+
int compare_entries_impl(const PathEntry &lhs, const PathEntry &rhs) {
|
|
26
|
+
const int lhs_rank = entry_type_rank(lhs);
|
|
27
|
+
const int rhs_rank = entry_type_rank(rhs);
|
|
28
|
+
if (lhs_rank != rhs_rank) {
|
|
29
|
+
return lhs_rank < rhs_rank ? -1 : 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (lhs.is_single()) {
|
|
33
|
+
const auto &lval = lhs.single_value();
|
|
34
|
+
const auto &rval = rhs.single_value();
|
|
35
|
+
if (lval < rval) {
|
|
36
|
+
return -1;
|
|
37
|
+
}
|
|
38
|
+
if (rval < lval) {
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (lhs.is_multi_volume()) {
|
|
45
|
+
const auto &lparts = lhs.multi_volume_parts();
|
|
46
|
+
const auto &rparts = rhs.multi_volume_parts();
|
|
47
|
+
if (lparts.ordering != rparts.ordering) {
|
|
48
|
+
return static_cast<int>(lparts.ordering) < static_cast<int>(rparts.ordering) ? -1 : 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const std::size_t lsize = lparts.values.size();
|
|
52
|
+
const std::size_t rsize = rparts.values.size();
|
|
53
|
+
const std::size_t compare_count = lsize < rsize ? lsize : rsize;
|
|
54
|
+
for (std::size_t i = 0; i < compare_count; ++i) {
|
|
55
|
+
const std::string &lvalue = lparts.values[i];
|
|
56
|
+
const std::string &rvalue = rparts.values[i];
|
|
57
|
+
if (lvalue < rvalue) {
|
|
58
|
+
return -1;
|
|
59
|
+
}
|
|
60
|
+
if (rvalue < lvalue) {
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (lsize != rsize) {
|
|
66
|
+
return lsize < rsize ? -1 : 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return compare_node_lists_impl(lhs.nested_nodes(), rhs.nested_nodes());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
int compare_node_lists_impl(const PathEntry::NodeList &lhs, const PathEntry::NodeList &rhs) {
|
|
76
|
+
const std::size_t lsize = lhs.size();
|
|
77
|
+
const std::size_t rsize = rhs.size();
|
|
78
|
+
const std::size_t compare_count = lsize < rsize ? lsize : rsize;
|
|
79
|
+
for (std::size_t i = 0; i < compare_count; ++i) {
|
|
80
|
+
const int cmp = compare_entries_impl(lhs[i], rhs[i]);
|
|
81
|
+
if (cmp != 0) {
|
|
82
|
+
return cmp;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (lsize != rsize) {
|
|
86
|
+
return lsize < rsize ? -1 : 1;
|
|
87
|
+
}
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
int compare_hierarchies_impl(const PathHierarchy &lhs, const PathHierarchy &rhs) {
|
|
92
|
+
const std::size_t lhs_size = lhs.size();
|
|
93
|
+
const std::size_t rhs_size = rhs.size();
|
|
94
|
+
const std::size_t compare_count = lhs_size < rhs_size ? lhs_size : rhs_size;
|
|
95
|
+
for (std::size_t i = 0; i < compare_count; ++i) {
|
|
96
|
+
const int cmp = compare_entries_impl(lhs[i], rhs[i]);
|
|
97
|
+
if (cmp != 0) {
|
|
98
|
+
return cmp;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (lhs_size != rhs_size) {
|
|
102
|
+
return lhs_size < rhs_size ? -1 : 1;
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
bool entries_equal_impl(const PathEntry &lhs, const PathEntry &rhs) { return compare_entries_impl(lhs, rhs) == 0; }
|
|
108
|
+
|
|
109
|
+
bool hierarchies_equal_impl(const PathHierarchy &lhs, const PathHierarchy &rhs) { return compare_hierarchies_impl(lhs, rhs) == 0; }
|
|
110
|
+
|
|
111
|
+
} // namespace
|
|
112
|
+
|
|
113
|
+
int compare_entries(const PathEntry &lhs, const PathEntry &rhs) { return compare_entries_impl(lhs, rhs); }
|
|
114
|
+
|
|
115
|
+
int compare_hierarchies(const PathHierarchy &lhs, const PathHierarchy &rhs) { return compare_hierarchies_impl(lhs, rhs); }
|
|
116
|
+
|
|
117
|
+
bool entries_equal(const PathEntry &lhs, const PathEntry &rhs) { return entries_equal_impl(lhs, rhs); }
|
|
118
|
+
|
|
119
|
+
bool hierarchies_equal(const PathHierarchy &lhs, const PathHierarchy &rhs) { return hierarchies_equal_impl(lhs, rhs); }
|
|
120
|
+
|
|
121
|
+
bool PathHierarchyLess::operator()(const PathHierarchy &lhs, const PathHierarchy &rhs) const { return compare_hierarchies(lhs, rhs) < 0; }
|
|
122
|
+
|
|
123
|
+
PathHierarchy make_single_path(const std::string &root) {
|
|
124
|
+
PathHierarchy hierarchy;
|
|
125
|
+
hierarchy.emplace_back(PathEntry::single(root));
|
|
126
|
+
return hierarchy;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void append_single(PathHierarchy &hierarchy, std::string value) { hierarchy.emplace_back(PathEntry::single(std::move(value))); }
|
|
130
|
+
|
|
131
|
+
void append_multi_volume(PathHierarchy &hierarchy, std::vector<std::string> parts, PathEntry::Parts::Ordering ordering) {
|
|
132
|
+
hierarchy.emplace_back(PathEntry::multi_volume(std::move(parts), ordering));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
PathHierarchy pathhierarchy_prefix_until(const PathHierarchy &hierarchy, size_t inclusive_index) {
|
|
136
|
+
if (hierarchy.empty() || inclusive_index >= hierarchy.size()) {
|
|
137
|
+
return {};
|
|
138
|
+
}
|
|
139
|
+
const auto prefix_end = hierarchy.begin() + static_cast<PathHierarchy::difference_type>(inclusive_index + 1);
|
|
140
|
+
return PathHierarchy(hierarchy.begin(), prefix_end);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
PathHierarchy parent_hierarchy(const PathHierarchy &hierarchy) {
|
|
144
|
+
if (hierarchy.size() <= 1) {
|
|
145
|
+
return {};
|
|
146
|
+
}
|
|
147
|
+
return pathhierarchy_prefix_until(hierarchy, hierarchy.size() - 2);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
} // namespace archive_r
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2025 archive_r Team
|
|
3
|
+
|
|
4
|
+
#include "archive_r/path_hierarchy_utils.h"
|
|
5
|
+
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <filesystem>
|
|
8
|
+
|
|
9
|
+
namespace archive_r {
|
|
10
|
+
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
struct CollapseSegments {
|
|
14
|
+
PathHierarchy prefix;
|
|
15
|
+
PathHierarchy suffix;
|
|
16
|
+
std::size_t multi_volume_depth = 0;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
PathHierarchy hierarchy_suffix_from(const PathHierarchy &hierarchy, std::size_t start_index) {
|
|
20
|
+
if (hierarchy.empty() || start_index >= hierarchy.size()) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
const auto begin = hierarchy.begin() + static_cast<PathHierarchy::difference_type>(start_index);
|
|
24
|
+
return PathHierarchy(begin, hierarchy.end());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
bool determine_multi_volume_segments(const std::vector<PathHierarchy> &sources, CollapseSegments &segments) {
|
|
28
|
+
if (sources.empty()) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const PathHierarchy &reference = sources.front();
|
|
33
|
+
if (reference.empty()) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
std::size_t multi_volume_depth = reference.size() - 1;
|
|
38
|
+
bool difference_found = false;
|
|
39
|
+
|
|
40
|
+
for (std::size_t depth = 0; depth < reference.size(); ++depth) {
|
|
41
|
+
const PathEntry &reference_entry = reference[depth];
|
|
42
|
+
for (std::size_t i = 1; i < sources.size(); ++i) {
|
|
43
|
+
const PathHierarchy &candidate = sources[i];
|
|
44
|
+
if (candidate.size() <= depth) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (!archive_r::entries_equal(reference_entry, candidate[depth])) {
|
|
48
|
+
multi_volume_depth = depth;
|
|
49
|
+
difference_found = true;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (difference_found) {
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!difference_found) {
|
|
59
|
+
for (std::size_t i = 1; i < sources.size(); ++i) {
|
|
60
|
+
if (sources[i].size() != reference.size()) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
segments.multi_volume_depth = multi_volume_depth;
|
|
67
|
+
segments.prefix.clear();
|
|
68
|
+
if (multi_volume_depth > 0) {
|
|
69
|
+
segments.prefix = pathhierarchy_prefix_until(reference, multi_volume_depth - 1);
|
|
70
|
+
}
|
|
71
|
+
segments.suffix = hierarchy_suffix_from(reference, multi_volume_depth + 1);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
bool collect_multi_volume_parts(const std::vector<PathHierarchy> &sources, const CollapseSegments &segments, std::vector<std::string> &parts) {
|
|
76
|
+
if (sources.empty()) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const std::size_t suffix_size = segments.suffix.size();
|
|
81
|
+
const std::size_t required_size = segments.multi_volume_depth + 1 + suffix_size;
|
|
82
|
+
|
|
83
|
+
parts.clear();
|
|
84
|
+
parts.reserve(sources.size());
|
|
85
|
+
|
|
86
|
+
for (const auto &hierarchy : sources) {
|
|
87
|
+
if (hierarchy.empty() || hierarchy.size() <= segments.multi_volume_depth) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (hierarchy.size() != required_size) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const PathEntry &component = hierarchy[segments.multi_volume_depth];
|
|
96
|
+
if (!component.is_single()) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (std::size_t offset = 0; offset < suffix_size; ++offset) {
|
|
101
|
+
const PathEntry &reference_entry = segments.suffix[offset];
|
|
102
|
+
const PathEntry &candidate_entry = hierarchy[segments.multi_volume_depth + 1 + offset];
|
|
103
|
+
if (!archive_r::entries_equal(reference_entry, candidate_entry)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
parts.push_back(component.single_value());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
bool flatten_single_entry(const PathEntry &entry, std::string &output) {
|
|
115
|
+
if (entry.is_single()) {
|
|
116
|
+
output = entry.single_value();
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return flatten_entry_to_string(entry, output);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
} // namespace
|
|
123
|
+
|
|
124
|
+
const std::string *path_entry_component_at(const PathEntry &entry, std::size_t index) {
|
|
125
|
+
if (entry.is_single()) {
|
|
126
|
+
if (index == 0) {
|
|
127
|
+
return &entry.single_value();
|
|
128
|
+
}
|
|
129
|
+
return nullptr;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (entry.is_multi_volume()) {
|
|
133
|
+
const auto &parts = entry.multi_volume_parts().values;
|
|
134
|
+
if (index < parts.size()) {
|
|
135
|
+
return &parts[index];
|
|
136
|
+
}
|
|
137
|
+
return nullptr;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return nullptr;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
std::size_t pathhierarchy_volume_size(const PathHierarchy &logical) {
|
|
144
|
+
if (logical.empty()) {
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
const PathEntry &tail = logical.back();
|
|
148
|
+
if (!tail.is_multi_volume()) {
|
|
149
|
+
return 1;
|
|
150
|
+
}
|
|
151
|
+
const auto &parts = tail.multi_volume_parts().values;
|
|
152
|
+
return parts.size();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
std::string pathhierarchy_volume_entry_name(const PathHierarchy &logical, std::size_t index) {
|
|
156
|
+
if (logical.empty()) {
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
const PathEntry &tail = logical.back();
|
|
160
|
+
if (!tail.is_multi_volume()) {
|
|
161
|
+
if (index != 0) {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
std::string entry_name;
|
|
166
|
+
if (!flatten_single_entry(tail, entry_name)) {
|
|
167
|
+
return {};
|
|
168
|
+
}
|
|
169
|
+
return entry_name;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const auto &parts = tail.multi_volume_parts().values;
|
|
173
|
+
if (index >= parts.size()) {
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
return parts[index];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
bool pathhierarchy_is_multivolume(const PathHierarchy &hierarchy) {
|
|
180
|
+
if (hierarchy.empty()) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
return hierarchy.back().is_multi_volume();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
PathHierarchy pathhierarchy_select_single_part(const PathHierarchy &logical, std::size_t index) {
|
|
187
|
+
if (logical.empty()) {
|
|
188
|
+
return {};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
PathHierarchy result = parent_hierarchy(logical);
|
|
192
|
+
append_single(result, pathhierarchy_volume_entry_name(logical, index));
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
void sort_hierarchies(std::vector<PathHierarchy> &hierarchies) {
|
|
197
|
+
std::sort(hierarchies.begin(), hierarchies.end(), [](const PathHierarchy &lhs, const PathHierarchy &rhs) { return archive_r::compare_hierarchies(lhs, rhs) < 0; });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
bool flatten_entry_to_string(const PathEntry &entry, std::string &output) {
|
|
201
|
+
if (entry.is_single()) {
|
|
202
|
+
output = entry.single_value();
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (entry.is_nested()) {
|
|
207
|
+
std::string result;
|
|
208
|
+
bool first = true;
|
|
209
|
+
for (const auto &child : entry.nested_nodes()) {
|
|
210
|
+
std::string component;
|
|
211
|
+
if (!flatten_entry_to_string(child, component)) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (component.empty()) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
if (!first) {
|
|
218
|
+
result.push_back('/');
|
|
219
|
+
}
|
|
220
|
+
result += component;
|
|
221
|
+
first = false;
|
|
222
|
+
}
|
|
223
|
+
output = result;
|
|
224
|
+
return !result.empty();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
bool entry_name_from_component(const PathEntry &entry, std::string &output) {
|
|
231
|
+
if (entry.is_multi_volume()) {
|
|
232
|
+
const auto &parts = entry.multi_volume_parts().values;
|
|
233
|
+
if (parts.empty()) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
output = parts.front();
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return flatten_entry_to_string(entry, output);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
PathHierarchy merge_multi_volume_sources(const std::vector<PathHierarchy> &sources) {
|
|
244
|
+
CollapseSegments segments;
|
|
245
|
+
if (!determine_multi_volume_segments(sources, segments)) {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
std::vector<std::string> parts;
|
|
250
|
+
if (!collect_multi_volume_parts(sources, segments, parts)) {
|
|
251
|
+
return {};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
PathHierarchy result = segments.prefix;
|
|
255
|
+
result.reserve(result.size() + 1 + segments.suffix.size());
|
|
256
|
+
result.emplace_back(PathEntry::multi_volume(std::move(parts)));
|
|
257
|
+
result.insert(result.end(), segments.suffix.begin(), segments.suffix.end());
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
std::string path_entry_display(const PathEntry &entry) {
|
|
262
|
+
if (entry.is_single()) {
|
|
263
|
+
return entry.single_value();
|
|
264
|
+
}
|
|
265
|
+
if (entry.is_multi_volume()) {
|
|
266
|
+
std::string value = "[";
|
|
267
|
+
bool first = true;
|
|
268
|
+
for (const auto &part : entry.multi_volume_parts().values) {
|
|
269
|
+
if (!first) {
|
|
270
|
+
value.push_back('|');
|
|
271
|
+
}
|
|
272
|
+
value += part;
|
|
273
|
+
first = false;
|
|
274
|
+
}
|
|
275
|
+
value.push_back(']');
|
|
276
|
+
return value;
|
|
277
|
+
}
|
|
278
|
+
std::string value = "{";
|
|
279
|
+
bool first = true;
|
|
280
|
+
for (const auto &child : entry.nested_nodes()) {
|
|
281
|
+
if (!first) {
|
|
282
|
+
value.push_back('/');
|
|
283
|
+
}
|
|
284
|
+
value += path_entry_display(child);
|
|
285
|
+
first = false;
|
|
286
|
+
}
|
|
287
|
+
value.push_back('}');
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
std::string hierarchy_display(const PathHierarchy &hierarchy) {
|
|
292
|
+
std::string result;
|
|
293
|
+
bool first = true;
|
|
294
|
+
for (const auto &component : hierarchy) {
|
|
295
|
+
if (!first) {
|
|
296
|
+
result.push_back('/');
|
|
297
|
+
}
|
|
298
|
+
result += path_entry_display(component);
|
|
299
|
+
first = false;
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
} // namespace archive_r
|