GCC Code Coverage Report


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