| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/pack.cc |
| Date: | 2025-12-21 02:39:23 |
| 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 | 3368 | void InitializeHeader(const int version, const int num_objects, | |
| 23 | const size_t pack_size, std::string *header) { | ||
| 24 |
1/2✓ Branch 0 taken 3368 times.
✗ Branch 1 not taken.
|
3368 | if (header) { |
| 25 |
2/4✓ Branch 2 taken 3368 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3368 times.
✗ Branch 6 not taken.
|
3368 | *header = "V" + StringifyInt(version) + "\n"; |
| 26 |
3/6✓ Branch 2 taken 3368 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3368 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3368 times.
✗ Branch 9 not taken.
|
3368 | *header += "S" + StringifyInt(pack_size) + "\n"; |
| 27 |
3/6✓ Branch 2 taken 3368 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3368 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3368 times.
✗ Branch 9 not taken.
|
3368 | *header += "N" + StringifyInt(num_objects) + "\n"; |
| 28 | 3368 | *header += "--\n"; | |
| 29 | } | ||
| 30 | 3368 | } | |
| 31 | |||
| 32 | 271664 | 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 126 times.
✓ Branch 1 taken 271538 times.
✓ Branch 2 taken 126 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 126 times.
✗ Branch 6 not taken.
|
271664 | assert((object_type == ObjectPack::kCas) |
| 37 | || ((object_type == ObjectPack::kNamed) && (!object_name.empty()))); | ||
| 38 |
1/2✓ Branch 2 taken 271664 times.
✗ Branch 3 not taken.
|
271664 | std::string line_prefix = ""; |
| 39 |
1/2✓ Branch 2 taken 271664 times.
✗ Branch 3 not taken.
|
271664 | std::string line_suffix = ""; |
| 40 |
2/3✓ Branch 0 taken 126 times.
✓ Branch 1 taken 271538 times.
✗ Branch 2 not taken.
|
271664 | switch (object_type) { |
| 41 | 126 | case ObjectPack::kNamed: | |
| 42 |
1/2✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
|
126 | line_prefix = "N "; |
| 43 |
3/6✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 126 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 126 times.
✗ Branch 9 not taken.
|
126 | line_suffix = std::string(" ") + Base64Url(object_name); |
| 44 | 126 | break; | |
| 45 | 271538 | case ObjectPack::kCas: | |
| 46 |
1/2✓ Branch 1 taken 271538 times.
✗ Branch 2 not taken.
|
271538 | line_prefix = "C "; |
| 47 | 271538 | break; | |
| 48 | ✗ | default: | |
| 49 | ✗ | PANIC(kLogStderr, "Unknown object pack type to be added to header."); | |
| 50 | } | ||
| 51 |
1/2✓ Branch 0 taken 271664 times.
✗ Branch 1 not taken.
|
271664 | if (header) { |
| 52 |
4/8✓ Branch 1 taken 271664 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 271664 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 271664 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 271664 times.
✗ Branch 11 not taken.
|
543328 | *header += line_prefix + hash_str + " " + StringifyInt(object_size) |
| 53 |
3/6✓ Branch 1 taken 271664 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 271664 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 271664 times.
✗ Branch 8 not taken.
|
271664 | + line_suffix + "\n"; |
| 54 | } | ||
| 55 | 271664 | } | |
| 56 | |||
| 57 | } // namespace | ||
| 58 | |||
| 59 | 4215506 | ObjectPack::Bucket::Bucket() | |
| 60 | 4215506 | : content(reinterpret_cast<unsigned char *>(smalloc(kInitialSize))) | |
| 61 | 4215506 | , size(0) | |
| 62 | 4215506 | , capacity(kInitialSize) | |
| 63 | 4215506 | , content_type(kEmpty) | |
| 64 | 8431012 | , name() { } | |
| 65 | |||
| 66 | 15044 | void ObjectPack::Bucket::Add(const void *buf, const uint64_t buf_size) { | |
| 67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15044 times.
|
15044 | if (buf_size == 0) |
| 68 | ✗ | return; | |
| 69 | |||
| 70 |
2/2✓ Branch 0 taken 170392 times.
✓ Branch 1 taken 15044 times.
|
185436 | while (size + buf_size > capacity) { |
| 71 | 170392 | capacity *= 2; | |
| 72 | 170392 | content = reinterpret_cast<unsigned char *>(srealloc(content, capacity)); | |
| 73 | } | ||
| 74 | 15044 | memcpy(content + size, buf, buf_size); | |
| 75 | 15044 | size += buf_size; | |
| 76 | } | ||
| 77 | |||
| 78 | 4215506 | ObjectPack::Bucket::~Bucket() { free(content); } | |
| 79 | |||
| 80 | //------------------------------------------------------------------------------ | ||
| 81 | |||
| 82 | 2822 | ObjectPack::ObjectPack(const uint64_t limit) : limit_(limit), size_(0) { | |
| 83 | 2822 | InitLock(); | |
| 84 | 2822 | } | |
| 85 | |||
| 86 | 2780 | ObjectPack::~ObjectPack() { | |
| 87 | 5560 | for (std::set<BucketHandle>::const_iterator i = open_buckets_.begin(), | |
| 88 | 2780 | iEnd = open_buckets_.end(); | |
| 89 |
2/2✓ Branch 1 taken 2772 times.
✓ Branch 2 taken 2780 times.
|
5552 | i != iEnd; |
| 90 | 2772 | ++i) { | |
| 91 |
1/2✓ Branch 1 taken 2772 times.
✗ Branch 2 not taken.
|
2772 | delete *i; |
| 92 | } | ||
| 93 | |||
| 94 |
2/2✓ Branch 1 taken 4212566 times.
✓ Branch 2 taken 2780 times.
|
4215346 | for (unsigned i = 0; i < buckets_.size(); ++i) |
| 95 |
1/2✓ Branch 1 taken 4212566 times.
✗ Branch 2 not taken.
|
4212566 | delete buckets_[i]; |
| 96 | 2780 | pthread_mutex_destroy(lock_); | |
| 97 | 2780 | free(lock_); | |
| 98 | 2780 | } | |
| 99 | |||
| 100 | 3914 | void ObjectPack::AddToBucket(const void *buf, const uint64_t size, | |
| 101 | const ObjectPack::BucketHandle handle) { | ||
| 102 | 3914 | handle->Add(buf, size); | |
| 103 | 3914 | } | |
| 104 | |||
| 105 | 4215464 | ObjectPack::BucketHandle ObjectPack::NewBucket() { | |
| 106 |
2/4✓ Branch 1 taken 4215464 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4215464 times.
✗ Branch 5 not taken.
|
4215464 | BucketHandle handle = new Bucket(); |
| 107 | |||
| 108 | 4215464 | const MutexLockGuard mutex_guard(lock_); | |
| 109 |
1/2✓ Branch 1 taken 4215464 times.
✗ Branch 2 not taken.
|
4215464 | open_buckets_.insert(handle); |
| 110 | 4215464 | return handle; | |
| 111 | 4215464 | } | |
| 112 | |||
| 113 | /** | ||
| 114 | * Can only fail due to insufficient remaining space in the ObjectPack. | ||
| 115 | */ | ||
| 116 | 4216052 | bool ObjectPack::CommitBucket(const BucketContentType type, | |
| 117 | const shash::Any &id, | ||
| 118 | const ObjectPack::BucketHandle handle, | ||
| 119 | const std::string &name) { | ||
| 120 | 4216052 | handle->id = id; | |
| 121 | |||
| 122 | 4216052 | handle->content_type = type; | |
| 123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4216052 times.
|
4216052 | if (type == kNamed) { |
| 124 | ✗ | handle->name = name; | |
| 125 | } | ||
| 126 | |||
| 127 | 4216052 | const MutexLockGuard mutex_guard(lock_); | |
| 128 |
2/2✓ Branch 1 taken 42 times.
✓ Branch 2 taken 4216010 times.
|
4216052 | if (buckets_.size() >= kMaxObjects) |
| 129 | 42 | return false; | |
| 130 |
2/2✓ Branch 0 taken 3444 times.
✓ Branch 1 taken 4212566 times.
|
4216010 | if (size_ + handle->size > limit_) |
| 131 | 3444 | return false; | |
| 132 |
1/2✓ Branch 1 taken 4212566 times.
✗ Branch 2 not taken.
|
4212566 | open_buckets_.erase(handle); |
| 133 |
1/2✓ Branch 1 taken 4212566 times.
✗ Branch 2 not taken.
|
4212566 | buckets_.push_back(handle); |
| 134 | 4212566 | size_ += handle->size; | |
| 135 | 4212566 | return true; | |
| 136 | 4216052 | } | |
| 137 | |||
| 138 | 126 | void ObjectPack::DiscardBucket(const BucketHandle handle) { | |
| 139 | 126 | const MutexLockGuard mutex_guard(lock_); | |
| 140 |
1/2✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
|
126 | open_buckets_.erase(handle); |
| 141 |
1/2✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
|
126 | delete handle; |
| 142 | 126 | } | |
| 143 | |||
| 144 | 2822 | void ObjectPack::InitLock() { | |
| 145 | 2822 | lock_ = reinterpret_cast<pthread_mutex_t *>(smalloc(sizeof(pthread_mutex_t))); | |
| 146 | 2822 | const int retval = pthread_mutex_init(lock_, NULL); | |
| 147 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2822 times.
|
2822 | assert(retval == 0); |
| 148 | 2822 | } | |
| 149 | |||
| 150 | /** | ||
| 151 | * If a commit failed, an open Bucket can be transferred to another ObjectPack | ||
| 152 | * with more space. | ||
| 153 | */ | ||
| 154 | 966 | void ObjectPack::TransferBucket(const ObjectPack::BucketHandle handle, | |
| 155 | ObjectPack *other) { | ||
| 156 | 966 | const MutexLockGuard mutex_guard(lock_); | |
| 157 |
1/2✓ Branch 1 taken 966 times.
✗ Branch 2 not taken.
|
966 | open_buckets_.erase(handle); |
| 158 |
1/2✓ Branch 1 taken 966 times.
✗ Branch 2 not taken.
|
966 | other->open_buckets_.insert(handle); |
| 159 | 966 | } | |
| 160 | |||
| 161 | 1570984 | unsigned char *ObjectPack::BucketContent(size_t idx) const { | |
| 162 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1570984 times.
|
1570984 | assert(idx < buckets_.size()); |
| 163 | 1570984 | return buckets_[idx]->content; | |
| 164 | } | ||
| 165 | |||
| 166 | 1842522 | uint64_t ObjectPack::BucketSize(size_t idx) const { | |
| 167 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1842522 times.
|
1842522 | assert(idx < buckets_.size()); |
| 168 | 1842522 | return buckets_[idx]->size; | |
| 169 | } | ||
| 170 | |||
| 171 | 271538 | const shash::Any &ObjectPack::BucketId(size_t idx) const { | |
| 172 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 271538 times.
|
271538 | assert(idx < buckets_.size()); |
| 173 | 271538 | 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 | 3074 | void ObjectPackProducer::GetDigest(shash::Any *hash) { | |
| 182 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3074 times.
|
3074 | assert(hash); |
| 183 | 3074 | shash::HashString(header_, hash); | |
| 184 | 3074 | } | |
| 185 | |||
| 186 | 3242 | ObjectPackProducer::ObjectPackProducer(ObjectPack *pack) | |
| 187 | 3242 | : pack_(pack), big_file_(NULL), pos_(0), idx_(0), pos_in_bucket_(0) { | |
| 188 | 3242 | const unsigned N = pack->GetNoObjects(); | |
| 189 | // rough guess, most likely a little too much | ||
| 190 |
1/2✓ Branch 1 taken 3242 times.
✗ Branch 2 not taken.
|
3242 | header_.reserve(30 + N * (2 * shash::kMaxDigestSize + 5)); |
| 191 | |||
| 192 |
1/2✓ Branch 2 taken 3242 times.
✗ Branch 3 not taken.
|
3242 | InitializeHeader(2, N, pack->size(), &header_); |
| 193 | |||
| 194 |
2/2✓ Branch 0 taken 271538 times.
✓ Branch 1 taken 3242 times.
|
274780 | for (unsigned i = 0; i < N; ++i) { |
| 195 |
3/6✓ Branch 2 taken 271538 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 271538 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 271538 times.
✗ Branch 11 not taken.
|
271538 | AppendItemToHeader(ObjectPack::kCas, pack->BucketId(i).ToString(true), |
| 196 | pack->BucketSize(i), "", &header_); | ||
| 197 | } | ||
| 198 | 3242 | } | |
| 199 | |||
| 200 | 126 | ObjectPackProducer::ObjectPackProducer(const shash::Any &id, FILE *big_file, | |
| 201 | 126 | const std::string &file_name) | |
| 202 | 126 | : pack_(NULL), big_file_(big_file), pos_(0), idx_(0), pos_in_bucket_(0) { | |
| 203 | 126 | const int fd = fileno(big_file_); | |
| 204 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
|
126 | assert(fd >= 0); |
| 205 | platform_stat64 info; | ||
| 206 | 126 | const int retval = platform_fstat(fd, &info); | |
| 207 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
|
126 | assert(retval == 0); |
| 208 | |||
| 209 |
1/2✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
|
126 | InitializeHeader(2, 1, info.st_size, &header_); |
| 210 | |||
| 211 |
2/4✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 126 times.
✗ Branch 5 not taken.
|
126 | AppendItemToHeader(ObjectPack::kNamed, id.ToString(true), info.st_size, |
| 212 | file_name, &header_); | ||
| 213 | |||
| 214 |
1/2✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
|
126 | rewind(big_file); |
| 215 | 126 | } | |
| 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 | 1314456 | unsigned ObjectPackProducer::ProduceNext(const unsigned buf_size, | |
| 222 | unsigned char *buf) { | ||
| 223 | 1314456 | const unsigned remaining_in_header = (pos_ < header_.size()) | |
| 224 | 13910 | ? (header_.size() - pos_) | |
| 225 |
2/2✓ Branch 0 taken 13910 times.
✓ Branch 1 taken 1300546 times.
|
1328366 | : 0; |
| 226 | 1314456 | const unsigned nbytes_header = std::min(remaining_in_header, buf_size); | |
| 227 |
2/2✓ Branch 0 taken 13910 times.
✓ Branch 1 taken 1300546 times.
|
1314456 | if (nbytes_header) { |
| 228 | 13910 | memcpy(buf, header_.data() + pos_, nbytes_header); | |
| 229 | 13910 | pos_ += nbytes_header; | |
| 230 | } | ||
| 231 | |||
| 232 | 1314456 | unsigned remaining_in_buf = buf_size - nbytes_header; | |
| 233 |
2/2✓ Branch 0 taken 10668 times.
✓ Branch 1 taken 1303788 times.
|
1314456 | if (remaining_in_buf == 0) |
| 234 | 10668 | return nbytes_header; | |
| 235 | 1303788 | unsigned nbytes_payload = 0; | |
| 236 | |||
| 237 |
2/2✓ Branch 0 taken 798 times.
✓ Branch 1 taken 1302990 times.
|
1303788 | if (big_file_) { |
| 238 |
1/2✓ Branch 1 taken 798 times.
✗ Branch 2 not taken.
|
798 | const size_t nbytes = fread(buf + nbytes_header, 1, remaining_in_buf, |
| 239 | big_file_); | ||
| 240 | 798 | nbytes_payload = nbytes; | |
| 241 | 798 | pos_ += nbytes_payload; | |
| 242 |
2/2✓ Branch 1 taken 1302604 times.
✓ Branch 2 taken 386 times.
|
1302990 | } else if (idx_ < pack_->GetNoObjects()) { |
| 243 | // Copy a few buckets more | ||
| 244 |
6/6✓ Branch 0 taken 1574058 times.
✓ Branch 1 taken 1299530 times.
✓ Branch 3 taken 1570984 times.
✓ Branch 4 taken 3074 times.
✓ Branch 5 taken 1570984 times.
✓ Branch 6 taken 1302604 times.
|
2873588 | while ((remaining_in_buf) > 0 && (idx_ < pack_->GetNoObjects())) { |
| 245 | 1570984 | const unsigned remaining_in_bucket = pack_->BucketSize(idx_) | |
| 246 | 1570984 | - pos_in_bucket_; | |
| 247 | 1570984 | const unsigned nbytes = std::min(remaining_in_buf, remaining_in_bucket); | |
| 248 | 3141968 | memcpy(buf + nbytes_header + nbytes_payload, | |
| 249 | 1570984 | pack_->BucketContent(idx_) + pos_in_bucket_, nbytes); | |
| 250 | |||
| 251 | 1570984 | pos_in_bucket_ += nbytes; | |
| 252 | 1570984 | nbytes_payload += nbytes; | |
| 253 | 1570984 | remaining_in_buf -= nbytes; | |
| 254 |
2/2✓ Branch 0 taken 271538 times.
✓ Branch 1 taken 1299446 times.
|
1570984 | if (nbytes == remaining_in_bucket) { |
| 255 | 271538 | pos_in_bucket_ = 0; | |
| 256 | 271538 | idx_++; | |
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | 1303788 | return nbytes_header + nbytes_payload; | |
| 262 | } | ||
| 263 | |||
| 264 | //------------------------------------------------------------------------------ | ||
| 265 | |||
| 266 | 3116 | ObjectPackConsumer::ObjectPackConsumer(const shash::Any &expected_digest, | |
| 267 | 3116 | const unsigned expected_header_size) | |
| 268 | 3116 | : expected_digest_(expected_digest) | |
| 269 | 3116 | , expected_header_size_(expected_header_size) | |
| 270 | 3116 | , pos_(0) | |
| 271 | 3116 | , idx_(0) | |
| 272 | 3116 | , pos_in_object_(0) | |
| 273 | 3116 | , pos_in_accu_(0) | |
| 274 | 3116 | , state_(ObjectPackBuild::kStateContinue) | |
| 275 | 3116 | , size_(0) { | |
| 276 | // Upper limit of 100B per entry | ||
| 277 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3116 times.
|
3116 | if (expected_header_size > (100 * ObjectPack::kMaxObjects)) { |
| 278 | ✗ | state_ = ObjectPackBuild::kStateHeaderTooBig; | |
| 279 | ✗ | return; | |
| 280 | } | ||
| 281 | |||
| 282 |
1/2✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
|
3116 | 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 | 1312382 | ObjectPackBuild::State ObjectPackConsumer::ConsumeNext( | |
| 290 | const unsigned buf_size, const unsigned char *buf) { | ||
| 291 |
2/2✓ Branch 0 taken 2814 times.
✓ Branch 1 taken 1309568 times.
|
1312382 | if (buf_size == 0) |
| 292 | 2814 | return state_; | |
| 293 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1309568 times.
|
1309568 | if (state_ == ObjectPackBuild::kStateDone) { |
| 294 | ✗ | state_ = ObjectPackBuild::kStateTrailingBytes; | |
| 295 | ✗ | return state_; | |
| 296 | } | ||
| 297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1309568 times.
|
1309568 | if (state_ != ObjectPackBuild::kStateContinue) |
| 298 | ✗ | return state_; | |
| 299 | |||
| 300 |
2/2✓ Branch 0 taken 10340 times.
✓ Branch 1 taken 1299228 times.
|
1309568 | const unsigned remaining_in_header = (pos_ < expected_header_size_) |
| 301 | 10340 | ? (expected_header_size_ - pos_) | |
| 302 | : 0; | ||
| 303 | 1309568 | const unsigned nbytes_header = std::min(remaining_in_header, buf_size); | |
| 304 |
2/2✓ Branch 0 taken 10340 times.
✓ Branch 1 taken 1299228 times.
|
1309568 | if (nbytes_header) { |
| 305 |
2/4✓ Branch 2 taken 10340 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10340 times.
✗ Branch 6 not taken.
|
10340 | raw_header_ += string(reinterpret_cast<const char *>(buf), nbytes_header); |
| 306 | 10340 | pos_ += nbytes_header; | |
| 307 | } | ||
| 308 | |||
| 309 |
2/2✓ Branch 0 taken 7224 times.
✓ Branch 1 taken 1302344 times.
|
1309568 | if (pos_ < expected_header_size_) |
| 310 | 7224 | 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 3116 times.
✓ Branch 1 taken 1299228 times.
✓ Branch 2 taken 3116 times.
✗ Branch 3 not taken.
|
1302344 | if (nbytes_header && (pos_ == expected_header_size_)) { |
| 315 |
1/2✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
|
3116 | shash::Any digest(expected_digest_.algorithm); |
| 316 |
1/2✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
|
3116 | shash::HashString(raw_header_, &digest); |
| 317 |
2/4✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3116 times.
|
3116 | if (digest != expected_digest_) { |
| 318 | ✗ | state_ = ObjectPackBuild::kStateCorrupt; | |
| 319 | 84 | return state_; | |
| 320 | } else { | ||
| 321 |
1/2✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
|
3116 | const bool retval = ParseHeader(); |
| 322 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3116 times.
|
3116 | if (!retval) { |
| 323 | ✗ | state_ = ObjectPackBuild::kStateBadFormat; | |
| 324 | ✗ | return state_; | |
| 325 | } | ||
| 326 | // We don't need the raw string anymore | ||
| 327 | 3116 | raw_header_.clear(); | |
| 328 | } | ||
| 329 | |||
| 330 | // Empty pack? | ||
| 331 |
6/6✓ Branch 0 taken 126 times.
✓ Branch 1 taken 2990 times.
✓ Branch 3 taken 84 times.
✓ Branch 4 taken 42 times.
✓ Branch 5 taken 84 times.
✓ Branch 6 taken 3032 times.
|
3116 | if ((buf_size == nbytes_header) && (index_.size() == 0)) { |
| 332 | 84 | state_ = ObjectPackBuild::kStateDone; | |
| 333 | 84 | return state_; | |
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | 1302260 | const unsigned remaining_in_buf = buf_size - nbytes_header; | |
| 338 | 1302260 | const unsigned char *payload = buf + nbytes_header; | |
| 339 |
1/2✓ Branch 1 taken 1302260 times.
✗ Branch 2 not taken.
|
1302260 | 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 | 1302260 | ObjectPackBuild::State ObjectPackConsumer::ConsumePayload( | |
| 351 | const unsigned buf_size, const unsigned char *buf) { | ||
| 352 | 1302260 | uint64_t pos_in_buf = 0; | |
| 353 | 3061564 | while ((idx_ < index_.size()) | |
| 354 |
8/8✓ Branch 0 taken 3058532 times.
✓ Branch 1 taken 3032 times.
✓ Branch 2 taken 1299270 times.
✓ Branch 3 taken 1759262 times.
✓ Branch 5 taken 42 times.
✓ Branch 6 taken 1299228 times.
✓ Branch 7 taken 1759304 times.
✓ Branch 8 taken 1302260 times.
|
3061564 | && ((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 | 1759304 | const uint64_t remaining_in_buf = buf_size - pos_in_buf; | |
| 358 | 1759304 | const uint64_t remaining_in_object = index_[idx_].size - pos_in_object_; | |
| 359 | 1759304 | 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 | 1759304 | nbytes = std::min(remaining_in_object, remaining_in_buf); | |
| 364 |
2/2✓ Branch 0 taken 1339136 times.
✓ Branch 1 taken 420168 times.
|
1759304 | if ((pos_in_accu_ > 0) |
| 365 |
4/4✓ Branch 0 taken 1073100 times.
✓ Branch 1 taken 266036 times.
✓ Branch 2 taken 194124 times.
✓ Branch 3 taken 878976 times.
|
1339136 | || ((remaining_in_buf < remaining_in_object) && is_small_rest)) { |
| 366 | 614292 | const uint64_t remaining_in_accu = kAccuSize - pos_in_accu_; | |
| 367 | 614292 | nbytes = std::min(remaining_in_accu, nbytes); | |
| 368 | 614292 | memcpy(accumulator_ + pos_in_accu_, buf + pos_in_buf, nbytes); | |
| 369 | 614292 | pos_in_accu_ += nbytes; | |
| 370 |
4/4✓ Branch 0 taken 425460 times.
✓ Branch 1 taken 188832 times.
✓ Branch 2 taken 5292 times.
✓ Branch 3 taken 420168 times.
|
614292 | if ((pos_in_accu_ == kAccuSize) || (nbytes == remaining_in_object)) { |
| 371 |
1/2✓ Branch 1 taken 194124 times.
✗ Branch 2 not taken.
|
194124 | NotifyListeners(ObjectPackBuild::Event( |
| 372 |
1/2✓ Branch 3 taken 194124 times.
✗ Branch 4 not taken.
|
194124 | index_[idx_].id, index_[idx_].size, pos_in_accu_, accumulator_, |
| 373 | 194124 | index_[idx_].entry_type, index_[idx_].entry_name)); | |
| 374 | 194124 | pos_in_accu_ = 0; | |
| 375 | } | ||
| 376 | 614292 | } else { // directly trigger listeners using buf | |
| 377 |
1/2✓ Branch 1 taken 1145012 times.
✗ Branch 2 not taken.
|
1145012 | NotifyListeners(ObjectPackBuild::Event( |
| 378 |
1/2✓ Branch 3 taken 1145012 times.
✗ Branch 4 not taken.
|
1145012 | index_[idx_].id, index_[idx_].size, nbytes, buf + pos_in_buf, |
| 379 | 1145012 | index_[idx_].entry_type, index_[idx_].entry_name)); | |
| 380 | } | ||
| 381 | |||
| 382 | 1759304 | pos_in_buf += nbytes; | |
| 383 | 1759304 | pos_in_object_ += nbytes; | |
| 384 |
2/2✓ Branch 0 taken 271370 times.
✓ Branch 1 taken 1487934 times.
|
1759304 | if (nbytes == remaining_in_object) { |
| 385 | 271370 | idx_++; | |
| 386 | 271370 | pos_in_object_ = 0; | |
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | 1302260 | pos_ += buf_size; | |
| 391 | |||
| 392 |
2/2✓ Branch 1 taken 3032 times.
✓ Branch 2 taken 1299228 times.
|
1302260 | if (idx_ == index_.size()) |
| 393 |
1/2✓ Branch 0 taken 3032 times.
✗ Branch 1 not taken.
|
3032 | state_ = (pos_in_buf == buf_size) ? ObjectPackBuild::kStateDone |
| 394 | : ObjectPackBuild::kStateTrailingBytes; | ||
| 395 | else | ||
| 396 | 1299228 | state_ = ObjectPackBuild::kStateContinue; | |
| 397 | 1302260 | return state_; | |
| 398 | } | ||
| 399 | |||
| 400 | 3116 | bool ObjectPackConsumer::ParseHeader() { | |
| 401 | 3116 | map<char, string> header; | |
| 402 | const unsigned char *data = reinterpret_cast<const unsigned char *>( | ||
| 403 | 3116 | raw_header_.data()); | |
| 404 |
1/2✓ Branch 2 taken 3116 times.
✗ Branch 3 not taken.
|
3116 | ParseKeyvalMem(data, raw_header_.size(), &header); |
| 405 |
2/5✓ Branch 2 taken 3116 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 3116 times.
|
3116 | if (header.find('V') == header.end()) |
| 406 | ✗ | return false; | |
| 407 |
3/7✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3116 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3116 times.
|
3116 | if (header['V'] != "2") |
| 408 | ✗ | return false; | |
| 409 |
2/4✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3116 times.
✗ Branch 5 not taken.
|
3116 | size_ = String2Uint64(header['S']); |
| 410 |
2/4✓ Branch 1 taken 3116 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3116 times.
✗ Branch 5 not taken.
|
3116 | const unsigned nobjects = String2Uint64(header['N']); |
| 411 | |||
| 412 |
2/2✓ Branch 0 taken 84 times.
✓ Branch 1 taken 3032 times.
|
3116 | if (nobjects == 0) |
| 413 | 84 | return true; | |
| 414 | |||
| 415 | // Build the object index | ||
| 416 | 3032 | const size_t separator_idx = raw_header_.find("--\n"); | |
| 417 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3032 times.
|
3032 | if (separator_idx == string::npos) |
| 418 | ✗ | return false; | |
| 419 | 3032 | unsigned index_idx = separator_idx + 3; | |
| 420 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3032 times.
|
3032 | if (index_idx >= raw_header_.size()) |
| 421 | ✗ | return false; | |
| 422 | |||
| 423 | 3032 | uint64_t sum_size = 0; | |
| 424 | do { | ||
| 425 | 271370 | const unsigned remaining_in_header = raw_header_.size() - index_idx; | |
| 426 | 542740 | const string line = GetLineMem(raw_header_.data() + index_idx, | |
| 427 |
1/2✓ Branch 2 taken 271370 times.
✗ Branch 3 not taken.
|
271370 | remaining_in_header); |
| 428 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 271370 times.
|
271370 | if (line == "") |
| 429 | ✗ | break; | |
| 430 | |||
| 431 |
1/2✓ Branch 1 taken 271370 times.
✗ Branch 2 not taken.
|
271370 | IndexEntry entry; |
| 432 |
2/4✓ Branch 1 taken 271370 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 271370 times.
|
271370 | if (!ParseItem(line, &entry, &sum_size)) { |
| 433 | ✗ | break; | |
| 434 | } | ||
| 435 | |||
| 436 |
1/2✓ Branch 1 taken 271370 times.
✗ Branch 2 not taken.
|
271370 | index_.push_back(entry); |
| 437 | 271370 | index_idx += line.size() + 1; | |
| 438 |
4/6✓ Branch 1 taken 271370 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 271370 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 268338 times.
✓ Branch 8 taken 3032 times.
|
542740 | } while (index_idx < raw_header_.size()); |
| 439 | |||
| 440 |
2/4✓ Branch 1 taken 3032 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3032 times.
✗ Branch 4 not taken.
|
3032 | return (nobjects == index_.size()) && (size_ == sum_size); |
| 441 | 3116 | } | |
| 442 | |||
| 443 | 271370 | bool ObjectPackConsumer::ParseItem(const std::string &line, | |
| 444 | ObjectPackConsumer::IndexEntry *entry, | ||
| 445 | uint64_t *sum_size) { | ||
| 446 |
2/4✓ Branch 0 taken 271370 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 271370 times.
|
271370 | if (!entry || !sum_size) { |
| 447 | ✗ | return false; | |
| 448 | } | ||
| 449 | |||
| 450 |
2/2✓ Branch 1 taken 271328 times.
✓ Branch 2 taken 42 times.
|
271370 | if (line[0] == 'C') { // CAS blob |
| 451 | 271328 | 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 | 271328 | const size_t separator = line.find(' ', 2); | |
| 456 |
3/6✓ Branch 0 taken 271328 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 271328 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 271328 times.
|
271328 | if ((separator == string::npos) || (separator == (line.size() - 1))) { |
| 457 | ✗ | return false; | |
| 458 | } | ||
| 459 | |||
| 460 |
2/4✓ Branch 1 taken 271328 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 271328 times.
✗ Branch 5 not taken.
|
271328 | const uint64_t size = String2Uint64(line.substr(separator + 1)); |
| 461 | 271328 | *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 271328 times.
✗ Branch 2 not taken.
|
271328 | const std::string hash_string = line.substr(2, separator - 2); |
| 466 | 271328 | const shash::HexPtr hex_ptr(hash_string); | |
| 467 | |||
| 468 |
1/2✓ Branch 1 taken 271328 times.
✗ Branch 2 not taken.
|
271328 | entry->id = shash::MkFromSuffixedHexPtr(hex_ptr); |
| 469 | 271328 | entry->size = size; | |
| 470 | 271328 | entry->entry_type = entry_type; | |
| 471 |
1/2✓ Branch 1 taken 271328 times.
✗ Branch 2 not taken.
|
271328 | entry->entry_name = ""; |
| 472 |
1/2✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
|
271370 | } else if (line[0] == 'N') { // Named file |
| 473 | 42 | const ObjectPack::BucketContentType entry_type = ObjectPack::kNamed; | |
| 474 | |||
| 475 | // First separator, before the size field | ||
| 476 | 42 | const size_t separator1 = line.find(' ', 2); | |
| 477 |
3/6✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 42 times.
|
42 | if ((separator1 == string::npos) || (separator1 == (line.size() - 1))) { |
| 478 | ✗ | return false; | |
| 479 | } | ||
| 480 | |||
| 481 | // Second separator, before the name field | ||
| 482 | 42 | const size_t separator2 = line.find(' ', separator1 + 1); | |
| 483 |
1/2✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
|
42 | if ((separator1 == 0) || (separator1 == string::npos) |
| 484 |
3/6✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 42 times.
|
84 | || (separator1 == (line.size() - 1))) { |
| 485 | ✗ | return false; | |
| 486 | } | ||
| 487 | |||
| 488 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | const uint64_t size = String2Uint64( |
| 489 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
84 | line.substr(separator1 + 1, separator2 - separator1 - 1)); |
| 490 | |||
| 491 | 42 | std::string name; | |
| 492 |
3/7✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 42 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 42 times.
|
42 | if (!Debase64(line.substr(separator2 + 1), &name)) { |
| 493 | ✗ | return false; | |
| 494 | } | ||
| 495 | |||
| 496 | 42 | *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 42 times.
✗ Branch 2 not taken.
|
42 | const std::string hash_string = line.substr(2, separator1 - 2); |
| 501 | 42 | const shash::HexPtr hex_ptr(hash_string); | |
| 502 | |||
| 503 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | entry->id = shash::MkFromSuffixedHexPtr(hex_ptr); |
| 504 | 42 | entry->size = size; | |
| 505 | 42 | entry->entry_type = entry_type; | |
| 506 |
1/2✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
|
42 | entry->entry_name = name; |
| 507 |
1/2✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
|
42 | } else { // Error |
| 508 | ✗ | return false; | |
| 509 | } | ||
| 510 | |||
| 511 | 271370 | return true; | |
| 512 | } | ||
| 513 |