Directory: | cvmfs/ |
---|---|
File: | cvmfs/pack.cc |
Date: | 2025-06-29 02:35:41 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 283 | 307 | 92.2% |
Branches: | 202 | 332 | 60.8% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #include "pack.h" | ||
6 | |||
7 | #include <algorithm> | ||
8 | #include <cassert> | ||
9 | #include <cstring> | ||
10 | #include <map> | ||
11 | |||
12 | #include "util/concurrency.h" | ||
13 | #include "util/exception.h" | ||
14 | #include "util/platform.h" | ||
15 | #include "util/smalloc.h" | ||
16 | #include "util/string.h" | ||
17 | |||
18 | using namespace std; // NOLINT | ||
19 | |||
20 | namespace { // some private utility functions used by ObjectPackProducer | ||
21 | |||
22 | 3009 | void InitializeHeader(const int version, const int num_objects, | |
23 | const size_t pack_size, std::string *header) { | ||
24 |
1/2✓ Branch 0 taken 3009 times.
✗ Branch 1 not taken.
|
3009 | if (header) { |
25 |
2/4✓ Branch 2 taken 3009 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3009 times.
✗ Branch 6 not taken.
|
3009 | *header = "V" + StringifyInt(version) + "\n"; |
26 |
3/6✓ Branch 2 taken 3009 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3009 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3009 times.
✗ Branch 9 not taken.
|
3009 | *header += "S" + StringifyInt(pack_size) + "\n"; |
27 |
3/6✓ Branch 2 taken 3009 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3009 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3009 times.
✗ Branch 9 not taken.
|
3009 | *header += "N" + StringifyInt(num_objects) + "\n"; |
28 | 3009 | *header += "--\n"; | |
29 | } | ||
30 | 3009 | } | |
31 | |||
32 | 254208 | void AppendItemToHeader(ObjectPack::BucketContentType object_type, | |
33 | const std::string &hash_str, const size_t object_size, | ||
34 | const std::string &object_name, std::string *header) { | ||
35 | // If the item type is kName, the "item_name" parameter should not be empty | ||
36 |
4/6✓ Branch 0 taken 117 times.
✓ Branch 1 taken 254091 times.
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 117 times.
✗ Branch 6 not taken.
|
254208 | assert((object_type == ObjectPack::kCas) |
37 | || ((object_type == ObjectPack::kNamed) && (!object_name.empty()))); | ||
38 |
1/2✓ Branch 2 taken 254208 times.
✗ Branch 3 not taken.
|
254208 | std::string line_prefix = ""; |
39 |
1/2✓ Branch 2 taken 254208 times.
✗ Branch 3 not taken.
|
254208 | std::string line_suffix = ""; |
40 |
2/3✓ Branch 0 taken 117 times.
✓ Branch 1 taken 254091 times.
✗ Branch 2 not taken.
|
254208 | switch (object_type) { |
41 | 117 | case ObjectPack::kNamed: | |
42 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | line_prefix = "N "; |
43 |
3/6✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 117 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 117 times.
✗ Branch 9 not taken.
|
117 | line_suffix = std::string(" ") + Base64Url(object_name); |
44 | 117 | break; | |
45 | 254091 | case ObjectPack::kCas: | |
46 |
1/2✓ Branch 1 taken 254091 times.
✗ Branch 2 not taken.
|
254091 | line_prefix = "C "; |
47 | 254091 | break; | |
48 | ✗ | default: | |
49 | ✗ | PANIC(kLogStderr, "Unknown object pack type to be added to header."); | |
50 | } | ||
51 |
1/2✓ Branch 0 taken 254208 times.
✗ Branch 1 not taken.
|
254208 | if (header) { |
52 |
4/8✓ Branch 1 taken 254208 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 254208 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 254208 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 254208 times.
✗ Branch 11 not taken.
|
508416 | *header += line_prefix + hash_str + " " + StringifyInt(object_size) |
53 |
3/6✓ Branch 1 taken 254208 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 254208 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 254208 times.
✗ Branch 8 not taken.
|
254208 | + line_suffix + "\n"; |
54 | } | ||
55 | 254208 | } | |
56 | |||
57 | } // namespace | ||
58 | |||
59 | 3913473 | ObjectPack::Bucket::Bucket() | |
60 | 3913473 | : content(reinterpret_cast<unsigned char *>(smalloc(kInitialSize))) | |
61 | 3913473 | , size(0) | |
62 | 3913473 | , capacity(kInitialSize) | |
63 | 3913473 | , content_type(kEmpty) | |
64 | 7826946 | , name() { } | |
65 | |||
66 | 13044 | void ObjectPack::Bucket::Add(const void *buf, const uint64_t buf_size) { | |
67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13044 times.
|
13044 | if (buf_size == 0) |
68 | ✗ | return; | |
69 | |||
70 |
2/2✓ Branch 0 taken 156051 times.
✓ Branch 1 taken 13044 times.
|
169095 | while (size + buf_size > capacity) { |
71 | 156051 | capacity *= 2; | |
72 | 156051 | content = reinterpret_cast<unsigned char *>(srealloc(content, capacity)); | |
73 | } | ||
74 | 13044 | memcpy(content + size, buf, buf_size); | |
75 | 13044 | size += buf_size; | |
76 | } | ||
77 | |||
78 | 3913473 | ObjectPack::Bucket::~Bucket() { free(content); } | |
79 | |||
80 | //------------------------------------------------------------------------------ | ||
81 | |||
82 | 1727 | ObjectPack::ObjectPack(const uint64_t limit) : limit_(limit), size_(0) { | |
83 | 1727 | InitLock(); | |
84 | 1727 | } | |
85 | |||
86 | 1713 | ObjectPack::~ObjectPack() { | |
87 | 3426 | for (std::set<BucketHandle>::const_iterator i = open_buckets_.begin(), | |
88 | 1713 | iEnd = open_buckets_.end(); | |
89 |
2/2✓ Branch 1 taken 2574 times.
✓ Branch 2 taken 1713 times.
|
4287 | i != iEnd; |
90 | 2574 | ++i) { | |
91 |
1/2✓ Branch 1 taken 2574 times.
✗ Branch 2 not taken.
|
2574 | delete *i; |
92 | } | ||
93 | |||
94 |
2/2✓ Branch 1 taken 3910743 times.
✓ Branch 2 taken 1713 times.
|
3912456 | for (unsigned i = 0; i < buckets_.size(); ++i) |
95 |
1/2✓ Branch 1 taken 3910743 times.
✗ Branch 2 not taken.
|
3910743 | delete buckets_[i]; |
96 | 1713 | pthread_mutex_destroy(lock_); | |
97 | 1713 | free(lock_); | |
98 | 1713 | } | |
99 | |||
100 | 2241 | void ObjectPack::AddToBucket(const void *buf, const uint64_t size, | |
101 | const ObjectPack::BucketHandle handle) { | ||
102 | 2241 | handle->Add(buf, size); | |
103 | 2241 | } | |
104 | |||
105 | 3913434 | ObjectPack::BucketHandle ObjectPack::NewBucket() { | |
106 |
2/4✓ Branch 1 taken 3913434 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3913434 times.
✗ Branch 5 not taken.
|
3913434 | BucketHandle handle = new Bucket(); |
107 | |||
108 | 3913434 | const MutexLockGuard mutex_guard(lock_); | |
109 |
1/2✓ Branch 1 taken 3913434 times.
✗ Branch 2 not taken.
|
3913434 | open_buckets_.insert(handle); |
110 | 3913434 | return handle; | |
111 | 3913434 | } | |
112 | |||
113 | /** | ||
114 | * Can only fail due to insufficient remaining space in the ObjectPack. | ||
115 | */ | ||
116 | 3913580 | bool ObjectPack::CommitBucket(const BucketContentType type, | |
117 | const shash::Any &id, | ||
118 | const ObjectPack::BucketHandle handle, | ||
119 | const std::string &name) { | ||
120 | 3913580 | handle->id = id; | |
121 | |||
122 | 3913580 | handle->content_type = type; | |
123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3913580 times.
|
3913580 | if (type == kNamed) { |
124 | ✗ | handle->name = name; | |
125 | } | ||
126 | |||
127 | 3913580 | const MutexLockGuard mutex_guard(lock_); | |
128 |
2/2✓ Branch 1 taken 39 times.
✓ Branch 2 taken 3913541 times.
|
3913580 | if (buckets_.size() >= kMaxObjects) |
129 | 39 | return false; | |
130 |
2/2✓ Branch 0 taken 2798 times.
✓ Branch 1 taken 3910743 times.
|
3913541 | if (size_ + handle->size > limit_) |
131 | 2798 | return false; | |
132 |
1/2✓ Branch 1 taken 3910743 times.
✗ Branch 2 not taken.
|
3910743 | open_buckets_.erase(handle); |
133 |
1/2✓ Branch 1 taken 3910743 times.
✗ Branch 2 not taken.
|
3910743 | buckets_.push_back(handle); |
134 | 3910743 | size_ += handle->size; | |
135 | 3910743 | return true; | |
136 | 3913580 | } | |
137 | |||
138 | 117 | void ObjectPack::DiscardBucket(const BucketHandle handle) { | |
139 | 117 | const MutexLockGuard mutex_guard(lock_); | |
140 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | open_buckets_.erase(handle); |
141 |
1/2✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
|
117 | delete handle; |
142 | 117 | } | |
143 | |||
144 | 1727 | void ObjectPack::InitLock() { | |
145 | 1727 | lock_ = reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t))); | |
146 | 1727 | const int retval = pthread_mutex_init(lock_, NULL); | |
147 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1727 times.
|
1727 | assert(retval == 0); |
148 | 1727 | } | |
149 | |||
150 | /** | ||
151 | * If a commit failed, an open Bucket can be transferred to another ObjectPack | ||
152 | * with more space. | ||
153 | */ | ||
154 | 347 | void ObjectPack::TransferBucket(const ObjectPack::BucketHandle handle, | |
155 | ObjectPack *other) { | ||
156 | 347 | const MutexLockGuard mutex_guard(lock_); | |
157 |
1/2✓ Branch 1 taken 347 times.
✗ Branch 2 not taken.
|
347 | open_buckets_.erase(handle); |
158 |
1/2✓ Branch 1 taken 347 times.
✗ Branch 2 not taken.
|
347 | other->open_buckets_.insert(handle); |
159 | 347 | } | |
160 | |||
161 | 1466532 | unsigned char *ObjectPack::BucketContent(size_t idx) const { | |
162 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1466532 times.
|
1466532 | assert(idx < buckets_.size()); |
163 | 1466532 | return buckets_[idx]->content; | |
164 | } | ||
165 | |||
166 | 1720623 | uint64_t ObjectPack::BucketSize(size_t idx) const { | |
167 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1720623 times.
|
1720623 | assert(idx < buckets_.size()); |
168 | 1720623 | return buckets_[idx]->size; | |
169 | } | ||
170 | |||
171 | 254091 | const shash::Any &ObjectPack::BucketId(size_t idx) const { | |
172 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 254091 times.
|
254091 | assert(idx < buckets_.size()); |
173 | 254091 | return buckets_[idx]->id; | |
174 | } | ||
175 | |||
176 | //------------------------------------------------------------------------------ | ||
177 | |||
178 | /** | ||
179 | * Hash over the header. The hash algorithm needs to be provided by hash. | ||
180 | */ | ||
181 | 2786 | void ObjectPackProducer::GetDigest(shash::Any *hash) { | |
182 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2786 times.
|
2786 | assert(hash); |
183 | 2786 | shash::HashString(header_, hash); | |
184 | 2786 | } | |
185 | |||
186 | 2892 | ObjectPackProducer::ObjectPackProducer(ObjectPack *pack) | |
187 | 2892 | : pack_(pack), big_file_(NULL), pos_(0), idx_(0), pos_in_bucket_(0) { | |
188 | 2892 | const unsigned N = pack->GetNoObjects(); | |
189 | // rough guess, most likely a little too much | ||
190 |
1/2✓ Branch 1 taken 2892 times.
✗ Branch 2 not taken.
|
2892 | header_.reserve(30 + N * (2 * shash::kMaxDigestSize + 5)); |
191 | |||
192 |
1/2✓ Branch 2 taken 2892 times.
✗ Branch 3 not taken.
|
2892 | InitializeHeader(2, N, pack->size(), &header_); |
193 | |||
194 |
2/2✓ Branch 0 taken 254091 times.
✓ Branch 1 taken 2892 times.
|
256983 | for (unsigned i = 0; i < N; ++i) { |
195 |
3/6✓ Branch 2 taken 254091 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 254091 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 254091 times.
✗ Branch 11 not taken.
|
254091 | AppendItemToHeader(ObjectPack::kCas, pack->BucketId(i).ToString(true), |
196 | pack->BucketSize(i), "", &header_); | ||
197 | } | ||
198 | 2892 | } | |
199 | |||
200 | 117 | ObjectPackProducer::ObjectPackProducer(const shash::Any &id, FILE *big_file, | |
201 | 117 | const std::string &file_name) | |
202 | 117 | : pack_(NULL), big_file_(big_file), pos_(0), idx_(0), pos_in_bucket_(0) { | |
203 | 117 | const int fd = fileno(big_file_); | |
204 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
|
117 | assert(fd >= 0); |
205 | platform_stat64 info; | ||
206 | 117 | const int retval = platform_fstat(fd, &info); | |
207 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
|
117 | assert(retval == 0); |
208 | |||
209 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | InitializeHeader(2, 1, info.st_size, &header_); |
210 | |||
211 |
2/4✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 117 times.
✗ Branch 5 not taken.
|
117 | AppendItemToHeader(ObjectPack::kNamed, id.ToString(true), info.st_size, |
212 | file_name, &header_); | ||
213 | |||
214 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | rewind(big_file); |
215 | 117 | } | |
216 | |||
217 | /** | ||
218 | * Copies as many bytes as possible into buf. If the returned number of bytes | ||
219 | * is shorter than buf_size, everything has been produced. | ||
220 | */ | ||
221 | 1226078 | unsigned ObjectPackProducer::ProduceNext(const unsigned buf_size, | |
222 | unsigned char *buf) { | ||
223 | 1226078 | const unsigned remaining_in_header = (pos_ < header_.size()) | |
224 | 12759 | ? (header_.size() - pos_) | |
225 |
2/2✓ Branch 0 taken 12759 times.
✓ Branch 1 taken 1213319 times.
|
1238837 | : 0; |
226 | 1226078 | const unsigned nbytes_header = std::min(remaining_in_header, buf_size); | |
227 |
2/2✓ Branch 0 taken 12759 times.
✓ Branch 1 taken 1213319 times.
|
1226078 | if (nbytes_header) { |
228 | 12759 | memcpy(buf, header_.data() + pos_, nbytes_header); | |
229 | 12759 | pos_ += nbytes_header; | |
230 | } | ||
231 | |||
232 | 1226078 | unsigned remaining_in_buf = buf_size - nbytes_header; | |
233 |
2/2✓ Branch 0 taken 9867 times.
✓ Branch 1 taken 1216211 times.
|
1226078 | if (remaining_in_buf == 0) |
234 | 9867 | return nbytes_header; | |
235 | 1216211 | unsigned nbytes_payload = 0; | |
236 | |||
237 |
2/2✓ Branch 0 taken 741 times.
✓ Branch 1 taken 1215470 times.
|
1216211 | if (big_file_) { |
238 |
1/2✓ Branch 1 taken 741 times.
✗ Branch 2 not taken.
|
741 | const size_t nbytes = fread(buf + nbytes_header, 1, remaining_in_buf, |
239 | big_file_); | ||
240 | 741 | nbytes_payload = nbytes; | |
241 | 741 | pos_ += nbytes_payload; | |
242 |
2/2✓ Branch 1 taken 1215255 times.
✓ Branch 2 taken 215 times.
|
1215470 | } else if (idx_ < pack_->GetNoObjects()) { |
243 | // Copy a few buckets more | ||
244 |
6/6✓ Branch 0 taken 1469268 times.
✓ Branch 1 taken 1212519 times.
✓ Branch 3 taken 1466532 times.
✓ Branch 4 taken 2736 times.
✓ Branch 5 taken 1466532 times.
✓ Branch 6 taken 1215255 times.
|
2681787 | while ((remaining_in_buf) > 0 && (idx_ < pack_->GetNoObjects())) { |
245 | 1466532 | const unsigned remaining_in_bucket = pack_->BucketSize(idx_) | |
246 | 1466532 | - pos_in_bucket_; | |
247 | 1466532 | const unsigned nbytes = std::min(remaining_in_buf, remaining_in_bucket); | |
248 | 2933064 | memcpy(buf + nbytes_header + nbytes_payload, | |
249 | 1466532 | pack_->BucketContent(idx_) + pos_in_bucket_, nbytes); | |
250 | |||
251 | 1466532 | pos_in_bucket_ += nbytes; | |
252 | 1466532 | nbytes_payload += nbytes; | |
253 | 1466532 | remaining_in_buf -= nbytes; | |
254 |
2/2✓ Branch 0 taken 254091 times.
✓ Branch 1 taken 1212441 times.
|
1466532 | if (nbytes == remaining_in_bucket) { |
255 | 254091 | pos_in_bucket_ = 0; | |
256 | 254091 | idx_++; | |
257 | } | ||
258 | } | ||
259 | } | ||
260 | |||
261 | 1216211 | return nbytes_header + nbytes_payload; | |
262 | } | ||
263 | |||
264 | //------------------------------------------------------------------------------ | ||
265 | |||
266 | 2825 | ObjectPackConsumer::ObjectPackConsumer(const shash::Any &expected_digest, | |
267 | 2825 | const unsigned expected_header_size) | |
268 | 2825 | : expected_digest_(expected_digest) | |
269 | 2825 | , expected_header_size_(expected_header_size) | |
270 | 2825 | , pos_(0) | |
271 | 2825 | , idx_(0) | |
272 | 2825 | , pos_in_object_(0) | |
273 | 2825 | , pos_in_accu_(0) | |
274 | 2825 | , state_(ObjectPackBuild::kStateContinue) | |
275 | 2825 | , size_(0) { | |
276 | // Upper limit of 100B per entry | ||
277 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2825 times.
|
2825 | if (expected_header_size > (100 * ObjectPack::kMaxObjects)) { |
278 | ✗ | state_ = ObjectPackBuild::kStateHeaderTooBig; | |
279 | ✗ | return; | |
280 | } | ||
281 | |||
282 |
1/2✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
|
2825 | raw_header_.reserve(expected_header_size); |
283 | } | ||
284 | |||
285 | /** | ||
286 | * At the end of the function, pos_ will have progressed by buf_size (unless | ||
287 | * the buffer contains trailing garbage bytes. | ||
288 | */ | ||
289 | 1224539 | ObjectPackBuild::State ObjectPackConsumer::ConsumeNext( | |
290 | const unsigned buf_size, const unsigned char *buf) { | ||
291 |
2/2✓ Branch 0 taken 2613 times.
✓ Branch 1 taken 1221926 times.
|
1224539 | if (buf_size == 0) |
292 | 2613 | return state_; | |
293 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1221926 times.
|
1221926 | if (state_ == ObjectPackBuild::kStateDone) { |
294 | ✗ | state_ = ObjectPackBuild::kStateTrailingBytes; | |
295 | ✗ | return state_; | |
296 | } | ||
297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1221926 times.
|
1221926 | if (state_ != ObjectPackBuild::kStateContinue) |
298 | ✗ | return state_; | |
299 | |||
300 |
2/2✓ Branch 0 taken 9494 times.
✓ Branch 1 taken 1212432 times.
|
1221926 | const unsigned remaining_in_header = (pos_ < expected_header_size_) |
301 | 9494 | ? (expected_header_size_ - pos_) | |
302 | : 0; | ||
303 | 1221926 | const unsigned nbytes_header = std::min(remaining_in_header, buf_size); | |
304 |
2/2✓ Branch 0 taken 9494 times.
✓ Branch 1 taken 1212432 times.
|
1221926 | if (nbytes_header) { |
305 |
2/4✓ Branch 2 taken 9494 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 9494 times.
✗ Branch 6 not taken.
|
9494 | raw_header_ += string(reinterpret_cast<const char *>(buf), nbytes_header); |
306 | 9494 | pos_ += nbytes_header; | |
307 | } | ||
308 | |||
309 |
2/2✓ Branch 0 taken 6669 times.
✓ Branch 1 taken 1215257 times.
|
1221926 | if (pos_ < expected_header_size_) |
310 | 6669 | return ObjectPackBuild::kStateContinue; | |
311 | |||
312 | // This condition can only be true once through the lifetime of the | ||
313 | // Consumer. | ||
314 |
3/4✓ Branch 0 taken 2825 times.
✓ Branch 1 taken 1212432 times.
✓ Branch 2 taken 2825 times.
✗ Branch 3 not taken.
|
1215257 | if (nbytes_header && (pos_ == expected_header_size_)) { |
315 |
1/2✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
|
2825 | shash::Any digest(expected_digest_.algorithm); |
316 |
1/2✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
|
2825 | shash::HashString(raw_header_, &digest); |
317 |
2/4✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2825 times.
|
2825 | if (digest != expected_digest_) { |
318 | ✗ | state_ = ObjectPackBuild::kStateCorrupt; | |
319 | 78 | return state_; | |
320 | } else { | ||
321 |
1/2✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
|
2825 | const bool retval = ParseHeader(); |
322 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2825 times.
|
2825 | if (!retval) { |
323 | ✗ | state_ = ObjectPackBuild::kStateBadFormat; | |
324 | ✗ | return state_; | |
325 | } | ||
326 | // We don't need the raw string anymore | ||
327 | 2825 | raw_header_.clear(); | |
328 | } | ||
329 | |||
330 | // Empty pack? | ||
331 |
6/6✓ Branch 0 taken 117 times.
✓ Branch 1 taken 2708 times.
✓ Branch 3 taken 78 times.
✓ Branch 4 taken 39 times.
✓ Branch 5 taken 78 times.
✓ Branch 6 taken 2747 times.
|
2825 | if ((buf_size == nbytes_header) && (index_.size() == 0)) { |
332 | 78 | state_ = ObjectPackBuild::kStateDone; | |
333 | 78 | return state_; | |
334 | } | ||
335 | } | ||
336 | |||
337 | 1215179 | const unsigned remaining_in_buf = buf_size - nbytes_header; | |
338 | 1215179 | const unsigned char *payload = buf + nbytes_header; | |
339 |
1/2✓ Branch 1 taken 1215179 times.
✗ Branch 2 not taken.
|
1215179 | return ConsumePayload(remaining_in_buf, payload); |
340 | } | ||
341 | |||
342 | /** | ||
343 | * Informs listeners for small complete objects. For large objects, buffers | ||
344 | * the | ||
345 | * input into reasonably sized chunks. buf can contain both a chunk of data | ||
346 | * that needs to be added to the consumer's accumulator and a bunch of | ||
347 | * complete small objects. We use the accumulator only if necessary to avoid | ||
348 | * unnecessary memory copies. | ||
349 | */ | ||
350 | 1215179 | ObjectPackBuild::State ObjectPackConsumer::ConsumePayload( | |
351 | const unsigned buf_size, const unsigned char *buf) { | ||
352 | 1215179 | uint64_t pos_in_buf = 0; | |
353 | 2859982 | while ((idx_ < index_.size()) | |
354 |
8/8✓ Branch 0 taken 2857235 times.
✓ Branch 1 taken 2747 times.
✓ Branch 2 taken 1212471 times.
✓ Branch 3 taken 1644764 times.
✓ Branch 5 taken 39 times.
✓ Branch 6 taken 1212432 times.
✓ Branch 7 taken 1644803 times.
✓ Branch 8 taken 1215179 times.
|
2859982 | && ((pos_in_buf < buf_size) || (index_[idx_].size == 0))) { |
355 | // Fill the accumulator or process next small object | ||
356 | uint64_t nbytes; // How many bytes are consumed in this iteration | ||
357 | 1644803 | const uint64_t remaining_in_buf = buf_size - pos_in_buf; | |
358 | 1644803 | const uint64_t remaining_in_object = index_[idx_].size - pos_in_object_; | |
359 | 1644803 | const bool is_small_rest = remaining_in_buf < kAccuSize; | |
360 | |||
361 | // We use the accumulator if there is already something in or if we have a | ||
362 | // small piece of data of a larger object. | ||
363 | 1644803 | nbytes = std::min(remaining_in_object, remaining_in_buf); | |
364 |
2/2✓ Branch 0 taken 1252658 times.
✓ Branch 1 taken 392145 times.
|
1644803 | if ((pos_in_accu_ > 0) |
365 |
4/4✓ Branch 0 taken 1001988 times.
✓ Branch 1 taken 250670 times.
✓ Branch 2 taken 181779 times.
✓ Branch 3 taken 820209 times.
|
1252658 | || ((remaining_in_buf < remaining_in_object) && is_small_rest)) { |
366 | 573924 | const uint64_t remaining_in_accu = kAccuSize - pos_in_accu_; | |
367 | 573924 | nbytes = std::min(remaining_in_accu, nbytes); | |
368 | 573924 | memcpy(accumulator_ + pos_in_accu_, buf + pos_in_buf, nbytes); | |
369 | 573924 | pos_in_accu_ += nbytes; | |
370 |
4/4✓ Branch 0 taken 395421 times.
✓ Branch 1 taken 178503 times.
✓ Branch 2 taken 3276 times.
✓ Branch 3 taken 392145 times.
|
573924 | if ((pos_in_accu_ == kAccuSize) || (nbytes == remaining_in_object)) { |
371 |
1/2✓ Branch 1 taken 181779 times.
✗ Branch 2 not taken.
|
181779 | NotifyListeners(ObjectPackBuild::Event( |
372 |
1/2✓ Branch 3 taken 181779 times.
✗ Branch 4 not taken.
|
181779 | index_[idx_].id, index_[idx_].size, pos_in_accu_, accumulator_, |
373 | 181779 | index_[idx_].entry_type, index_[idx_].entry_name)); | |
374 | 181779 | pos_in_accu_ = 0; | |
375 | } | ||
376 | 573924 | } else { // directly trigger listeners using buf | |
377 |
1/2✓ Branch 1 taken 1070879 times.
✗ Branch 2 not taken.
|
1070879 | NotifyListeners(ObjectPackBuild::Event( |
378 |
1/2✓ Branch 3 taken 1070879 times.
✗ Branch 4 not taken.
|
1070879 | index_[idx_].id, index_[idx_].size, nbytes, buf + pos_in_buf, |
379 | 1070879 | index_[idx_].entry_type, index_[idx_].entry_name)); | |
380 | } | ||
381 | |||
382 | 1644803 | pos_in_buf += nbytes; | |
383 | 1644803 | pos_in_object_ += nbytes; | |
384 |
2/2✓ Branch 0 taken 253985 times.
✓ Branch 1 taken 1390818 times.
|
1644803 | if (nbytes == remaining_in_object) { |
385 | 253985 | idx_++; | |
386 | 253985 | pos_in_object_ = 0; | |
387 | } | ||
388 | } | ||
389 | |||
390 | 1215179 | pos_ += buf_size; | |
391 | |||
392 |
2/2✓ Branch 1 taken 2747 times.
✓ Branch 2 taken 1212432 times.
|
1215179 | if (idx_ == index_.size()) |
393 |
1/2✓ Branch 0 taken 2747 times.
✗ Branch 1 not taken.
|
2747 | state_ = (pos_in_buf == buf_size) ? ObjectPackBuild::kStateDone |
394 | : ObjectPackBuild::kStateTrailingBytes; | ||
395 | else | ||
396 | 1212432 | state_ = ObjectPackBuild::kStateContinue; | |
397 | 1215179 | return state_; | |
398 | } | ||
399 | |||
400 | 2825 | bool ObjectPackConsumer::ParseHeader() { | |
401 | 2825 | map<char, string> header; | |
402 | const unsigned char *data = reinterpret_cast<const unsigned char *>( | ||
403 | 2825 | raw_header_.data()); | |
404 |
1/2✓ Branch 2 taken 2825 times.
✗ Branch 3 not taken.
|
2825 | ParseKeyvalMem(data, raw_header_.size(), &header); |
405 |
2/5✓ Branch 2 taken 2825 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2825 times.
|
2825 | if (header.find('V') == header.end()) |
406 | ✗ | return false; | |
407 |
3/7✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2825 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2825 times.
|
2825 | if (header['V'] != "2") |
408 | ✗ | return false; | |
409 |
2/4✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2825 times.
✗ Branch 5 not taken.
|
2825 | size_ = String2Uint64(header['S']); |
410 |
2/4✓ Branch 1 taken 2825 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2825 times.
✗ Branch 5 not taken.
|
2825 | const unsigned nobjects = String2Uint64(header['N']); |
411 | |||
412 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 2747 times.
|
2825 | if (nobjects == 0) |
413 | 78 | return true; | |
414 | |||
415 | // Build the object index | ||
416 | 2747 | const size_t separator_idx = raw_header_.find("--\n"); | |
417 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2747 times.
|
2747 | if (separator_idx == string::npos) |
418 | ✗ | return false; | |
419 | 2747 | unsigned index_idx = separator_idx + 3; | |
420 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2747 times.
|
2747 | if (index_idx >= raw_header_.size()) |
421 | ✗ | return false; | |
422 | |||
423 | 2747 | uint64_t sum_size = 0; | |
424 | do { | ||
425 | 253985 | const unsigned remaining_in_header = raw_header_.size() - index_idx; | |
426 | 507970 | const string line = GetLineMem(raw_header_.data() + index_idx, | |
427 |
1/2✓ Branch 2 taken 253985 times.
✗ Branch 3 not taken.
|
253985 | remaining_in_header); |
428 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 253985 times.
|
253985 | if (line == "") |
429 | ✗ | break; | |
430 | |||
431 |
1/2✓ Branch 1 taken 253985 times.
✗ Branch 2 not taken.
|
253985 | IndexEntry entry; |
432 |
2/4✓ Branch 1 taken 253985 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 253985 times.
|
253985 | if (!ParseItem(line, &entry, &sum_size)) { |
433 | ✗ | break; | |
434 | } | ||
435 | |||
436 |
1/2✓ Branch 1 taken 253985 times.
✗ Branch 2 not taken.
|
253985 | index_.push_back(entry); |
437 | 253985 | index_idx += line.size() + 1; | |
438 |
4/6✓ Branch 1 taken 253985 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 253985 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 251238 times.
✓ Branch 8 taken 2747 times.
|
507970 | } while (index_idx < raw_header_.size()); |
439 | |||
440 |
2/4✓ Branch 1 taken 2747 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2747 times.
✗ Branch 4 not taken.
|
2747 | return (nobjects == index_.size()) && (size_ == sum_size); |
441 | 2825 | } | |
442 | |||
443 | 253985 | bool ObjectPackConsumer::ParseItem(const std::string &line, | |
444 | ObjectPackConsumer::IndexEntry *entry, | ||
445 | uint64_t *sum_size) { | ||
446 |
2/4✓ Branch 0 taken 253985 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 253985 times.
|
253985 | if (!entry || !sum_size) { |
447 | ✗ | return false; | |
448 | } | ||
449 | |||
450 |
2/2✓ Branch 1 taken 253946 times.
✓ Branch 2 taken 39 times.
|
253985 | if (line[0] == 'C') { // CAS blob |
451 | 253946 | const ObjectPack::BucketContentType entry_type = ObjectPack::kCas; | |
452 | |||
453 | // We could use SplitString but we can have many lines so we do something | ||
454 | // more efficient here | ||
455 | 253946 | const size_t separator = line.find(' ', 2); | |
456 |
3/6✓ Branch 0 taken 253946 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 253946 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 253946 times.
|
253946 | if ((separator == string::npos) || (separator == (line.size() - 1))) { |
457 | ✗ | return false; | |
458 | } | ||
459 | |||
460 |
2/4✓ Branch 1 taken 253946 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 253946 times.
✗ Branch 5 not taken.
|
253946 | const uint64_t size = String2Uint64(line.substr(separator + 1)); |
461 | 253946 | *sum_size += size; | |
462 | |||
463 | // Warning do not construct a HexPtr with an rvalue! | ||
464 | // The constructor takes the address of its argument. | ||
465 |
1/2✓ Branch 1 taken 253946 times.
✗ Branch 2 not taken.
|
253946 | const std::string hash_string = line.substr(2, separator - 2); |
466 | 253946 | const shash::HexPtr hex_ptr(hash_string); | |
467 | |||
468 |
1/2✓ Branch 1 taken 253946 times.
✗ Branch 2 not taken.
|
253946 | entry->id = shash::MkFromSuffixedHexPtr(hex_ptr); |
469 | 253946 | entry->size = size; | |
470 | 253946 | entry->entry_type = entry_type; | |
471 |
1/2✓ Branch 1 taken 253946 times.
✗ Branch 2 not taken.
|
253946 | entry->entry_name = ""; |
472 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
253985 | } else if (line[0] == 'N') { // Named file |
473 | 39 | const ObjectPack::BucketContentType entry_type = ObjectPack::kNamed; | |
474 | |||
475 | // First separator, before the size field | ||
476 | 39 | const size_t separator1 = line.find(' ', 2); | |
477 |
3/6✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 39 times.
|
39 | if ((separator1 == string::npos) || (separator1 == (line.size() - 1))) { |
478 | ✗ | return false; | |
479 | } | ||
480 | |||
481 | // Second separator, before the name field | ||
482 | 39 | const size_t separator2 = line.find(' ', separator1 + 1); | |
483 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | if ((separator1 == 0) || (separator1 == string::npos) |
484 |
3/6✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 39 times.
|
78 | || (separator1 == (line.size() - 1))) { |
485 | ✗ | return false; | |
486 | } | ||
487 | |||
488 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | const uint64_t size = String2Uint64( |
489 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
78 | line.substr(separator1 + 1, separator2 - separator1 - 1)); |
490 | |||
491 | 39 | std::string name; | |
492 |
3/7✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 39 times.
|
39 | if (!Debase64(line.substr(separator2 + 1), &name)) { |
493 | ✗ | return false; | |
494 | } | ||
495 | |||
496 | 39 | *sum_size += size; | |
497 | |||
498 | // Warning do not construct a HexPtr with an rvalue! | ||
499 | // The constructor takes the address of its argument. | ||
500 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | const std::string hash_string = line.substr(2, separator1 - 2); |
501 | 39 | const shash::HexPtr hex_ptr(hash_string); | |
502 | |||
503 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | entry->id = shash::MkFromSuffixedHexPtr(hex_ptr); |
504 | 39 | entry->size = size; | |
505 | 39 | entry->entry_type = entry_type; | |
506 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | entry->entry_name = name; |
507 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | } else { // Error |
508 | ✗ | return false; | |
509 | } | ||
510 | |||
511 | 253985 | return true; | |
512 | } | ||
513 |