• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PredatorCZ / PreCore / 460

pending completion
460

push

github-actions-ci

PredatorCZ
try fix coverage

3204 of 6095 relevant lines covered (52.57%)

354.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/app/in_context.cpp
1
/*  Spike is universal dedicated module handler
2
    This source contains context for input data
3
    Part of PreCore project
4

5
    Copyright 2021-2022 Lukas Cone
6

7
    Licensed under the Apache License, Version 2.0 (the "License");
8
    you may not use this file except in compliance with the License.
9
    You may obtain a copy of the License at
10

11
        http://www.apache.org/licenses/LICENSE-2.0
12

13
    Unless required by applicable law or agreed to in writing, software
14
    distributed under the License is distributed on an "AS IS" BASIS,
15
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
    See the License for the specific language governing permissions and
17
    limitations under the License.
18
*/
19

20
#include "spike/app/context.hpp"
21
#include "spike/app/out_context.hpp"
22
#include "spike/app/tmp_storage.hpp"
23
#include "spike/format/ZIP_istream.inl"
24
#include "spike/io/binreader.hpp"
25
#include "spike/io/binwritter.hpp"
26
#include "spike/io/directory_scanner.hpp"
27
#include "spike/io/fileinfo.hpp"
28
#include "spike/io/stat.hpp"
29
#include "spike/master_printer.hpp"
30
#include <list>
31
#include <mutex>
32
#include <optional>
33
#include <spanstream>
34

35
static std::mutex simpleIOLock;
36

37
const std::vector<std::string> &AppContextShare::SupplementalFiles() {
×
38
  if (!supplementals) {
×
39
    throw std::runtime_error(
×
40
        "Invalid call of SupplementalFiles, module is not for batch.");
×
41
  }
42

43
  return supplementals.value();
×
44
}
45

46
const std::vector<std::string> &ZIPIOContext::SupplementalFiles() {
×
47
  if (!supplementals) {
×
48
    throw std::runtime_error(
×
49
        "Invalid call of SupplementalFiles, module is not for batch.");
×
50
  }
51

52
  return supplementals.value();
×
53
}
54

55
struct AppContextShareImpl : AppContextShare {
56
  NewFileContext NewFile(const std::string &path) override {
×
57
    std::string filePath;
58
    size_t delimeter = 0;
59

60
    if (basePathParts.empty()) {
×
61
      filePath = std::string(basePath.GetFullPath()) + path;
×
62
      delimeter = basePath.GetFullPath().size();
63
    } else {
64
      AFileInfo pathInfo(path);
×
65
      auto exploded = pathInfo.Explode();
×
66
      const size_t numItems = std::min(exploded.size(), basePathParts.size());
×
67

68
      if (basePath.GetFullPath().at(0) == '/') {
×
69
        filePath.push_back('/');
×
70
      }
71

72
      size_t i = 0;
73

74
      for (; i < numItems; i++) {
×
75
        if (basePathParts[i] == exploded[i]) {
×
76
          filePath.append(exploded[i]);
77
          filePath.push_back('/');
×
78
          continue;
79
        }
80

81
        break;
82
      }
83

84
      for (size_t j = i; j < basePathParts.size(); j++) {
×
85
        filePath.append(basePathParts[j]);
86
        filePath.push_back('/');
×
87
      }
88

89
      delimeter = filePath.size();
90

91
      for (; i < exploded.size(); i++) {
×
92
        filePath.append(exploded[i]);
93
        filePath.push_back('/');
×
94
      }
95

96
      filePath.pop_back();
97
    }
98

99
    try {
100
      outFile = BinWritter(filePath);
×
101
    } catch (const es::FileInvalidAccessError &e) {
×
102
      mkdirs(filePath);
×
103
      outFile = BinWritter(filePath);
×
104
    }
×
105
    return {outFile.BaseStream(), filePath, delimeter};
×
106
  }
107

108
  void BaseOutputPath(std::string basePath_) override {
×
109
    if (basePath_.back() != '/') {
×
110
      basePath_.push_back('/');
×
111
    }
112
    basePath = AFileInfo(basePath_);
×
113
    basePathParts = basePath.Explode();
×
114
  }
115

116
  JenHash Hash() override { return JenHash(FullPath()); }
×
117

118
  std::string FullPath() override {
×
119
    return std::string(basePath.GetFullPath()) +
×
120
           std::string(workingFile.GetFullPath());
×
121
  }
122

123
  BinWritter outFile;
124
  AFileInfo basePath;
125
  std::vector<std::string_view> basePathParts;
126
};
127

128
struct SimpleIOContext : AppContextShareImpl {
129
  SimpleIOContext(const std::string &path,
×
130
                  std::optional<std::vector<std::string>> supplementals_) {
×
131
    mainFile.Open(path);
×
132
    workingFile.Load(path);
×
133
    supplementals = std::move(supplementals_);
134

135
    if (!cliSettings.out.empty()) {
×
136
      BaseOutputPath(cliSettings.out);
×
137
    }
138
  }
139
  std::istream *OpenFile(const std::string &path);
140

141
  AppContextStream RequestFile(const std::string &path) override;
142

143
  AppContextFoundStream FindFile(const std::string &rootFolder,
144
                                 const std::string &pattern) override;
145
  std::istream &GetStream() override;
146
  std::string GetBuffer(size_t size, size_t begin) override;
147

148
  void DisposeFile(std::istream *str) override;
149

150
  AppExtractContext *ExtractContext(std::string_view name) override {
×
151
    if (ectx) [[unlikely]] {
×
152
      return ectx.get();
×
153
    }
154

155
    std::string outPath(basePath.GetFullPath());
×
156
    outPath += workingFile.GetFolder();
157
    outPath += name;
158

159
    if (mainSettings.extractSettings.makeZIP) {
×
160
      if (workingFile.GetExtension() == ".zip") {
×
161
        outPath.append("_out");
×
162
      }
163

164
      outPath.append(".zip");
×
165

166
      auto uniq = std::make_unique<ZIPExtactContext>(outPath);
×
167
      uniq->forEachFile = forEachFile;
×
168
      ectx = std::move(uniq);
169
    } else {
170
      if (!mainSettings.extractSettings.folderPerArc) {
×
171
        outPath = workingFile.GetFolder();
172
      } else {
173
        es::mkdir(outPath);
174
        outPath.push_back('/');
×
175
      }
176

177
      auto uniq = std::make_unique<IOExtractContext>(outPath);
×
178
      uniq->forEachFile = forEachFile;
×
179
      ectx = std::move(uniq);
180
    }
181

182
    return ectx.get();
183
  }
184

185
  AppExtractContext *ExtractContext() override {
×
186
    return ExtractContext(workingFile.GetFilename());
×
187
  }
188

189
  void Finish() override {
×
190
    if (ectx && mainSettings.extractSettings.makeZIP) {
×
191
      static_cast<ZIPExtactContext *>(ectx.get())->FinishZIP([] {
192
        printinfo("Generating cache.");
×
193
      });
×
194
    }
195
  }
196

197
private:
198
  BinReader mainFile;
199
  BinReader streamedFiles[32];
200
  uint32 usedFiles = 0;
201
  std::unique_ptr<AppExtractContext> ectx;
202
};
203

204
std::istream *SimpleIOContext::OpenFile(const std::string &path) {
×
205
  std::lock_guard<std::mutex> guard(simpleIOLock);
206
  for (size_t b = 0; b < 32; b++) {
×
207
    uint32 bit = 1 << b;
×
208
    if (!(usedFiles & bit)) {
×
209
      streamedFiles[b].Open(path);
×
210
      usedFiles ^= bit;
×
211
      return &streamedFiles[b].BaseStream();
×
212
    }
213
  }
214

215
  throw std::out_of_range("Maximum opened files reached!");
×
216
}
217

218
AppContextStream SimpleIOContext::RequestFile(const std::string &path) {
×
219
  AFileInfo wFile(workingFile);
220
  AFileInfo pFile(path);
×
221
  auto catchedFile = pFile.CatchBranch(wFile.GetFolder());
×
222
  return {OpenFile(catchedFile), this};
×
223
}
224

225
AppContextFoundStream SimpleIOContext::FindFile(const std::string &rootFolder,
×
226
                                                const std::string &pattern) {
227
  DirectoryScanner sc;
×
228
  sc.AddFilter(pattern);
×
229
  sc.Scan(rootFolder);
×
230

231
  if (sc.Files().empty()) {
×
232
    throw es::FileNotFoundError(pattern);
×
233
  } else if (sc.Files().size() > 1) {
×
234
    std::string *winner = nullptr;
235
    size_t minFolder = 0x10000;
236
    size_t minLevel = 0x10000;
237

238
    for (auto &f : sc) {
×
239
      size_t foundIdx = f.find_last_of('/');
240

241
      if (foundIdx == f.npos) {
×
242
        throw std::runtime_error("Too many files found.");
×
243
      }
244

245
      if (foundIdx < minFolder) {
×
246
        winner = &f;
247
        minFolder = foundIdx;
248
        minLevel = std::count(f.begin(), f.end(), '/');
×
249
      } else if (foundIdx == minFolder) {
×
250
        if (auto clevel = std::count(f.begin(), f.end(), '/');
×
251
            clevel < minLevel) {
×
252
          winner = &f;
253
          minFolder = foundIdx;
254
          minLevel = clevel;
255
        } else if (clevel == minLevel) {
×
256
          throw std::runtime_error("Too many files found.");
×
257
        }
258
      }
259
    }
260

261
    if (!winner) {
×
262
      throw std::runtime_error("Too many files found.");
×
263
    }
264

265
    return {OpenFile(*winner), this, AFileInfo(*winner)};
×
266
  }
267

268
  return {OpenFile(sc.Files().front()), this, AFileInfo(sc.Files().front())};
×
269
}
270

271
void SimpleIOContext::DisposeFile(std::istream *str) {
×
272
  size_t index = 0;
273

274
  for (auto &f : streamedFiles) {
×
275
    if (&f.BaseStream() == str) {
×
276
      uint32 bit = 1 << index;
×
277

278
      if (!(usedFiles & bit)) {
×
279
        throw std::runtime_error("Stream already freed.");
×
280
      }
281

282
      es::Dispose(f);
×
283
      usedFiles ^= bit;
×
284
      return;
×
285
    }
286

287
    index++;
×
288
  }
289

290
  throw std::runtime_error("Requested stream not found!");
×
291
}
292

293
std::istream &SimpleIOContext::GetStream() { return mainFile.BaseStream(); }
×
294

295
std::string SimpleIOContext::GetBuffer(size_t size, size_t begin) {
×
296
  mainFile.Push();
×
297
  mainFile.Seek(begin);
298
  std::string buffer;
299
  mainFile.ReadContainer(buffer,
×
300
                         size == size_t(-1) ? mainFile.GetSize() : size);
×
301
  mainFile.Pop();
302

303
  return buffer;
×
304
}
305

306
std::shared_ptr<AppContextShare>
307
MakeIOContext(const std::string &path,
×
308
              std::optional<std::vector<std::string>> supplementals) {
309
  return std::make_unique<SimpleIOContext>(path, supplementals);
×
310
}
311

312
struct ZIPIOContextInstance : AppContextShareImpl {
313
  ZIPIOContextInstance(const ZIPIOContextInstance &) = delete;
314
  ZIPIOContextInstance(ZIPIOContextInstance &&) = delete;
315
  ZIPIOContextInstance(ZIPIOContext *base_, ZIPIOEntry entry_)
×
316
      : base(base_), entry(entry_) {
×
317
    workingFile.Load(entry_.AsView());
×
318
    BaseOutputPath(base->basePath);
×
319
  }
320

321
  ~ZIPIOContextInstance() {
×
322
    if (stream) {
×
323
      base->DisposeFile(stream);
×
324
    }
325
  }
326

×
327
  AppContextStream RequestFile(const std::string &path) override {
328
    return base->RequestFile(path);
329
  }
330
  void DisposeFile(std::istream *file) override { base->DisposeFile(file); }
331
  AppContextFoundStream FindFile(const std::string &rootFolder,
×
332
                                 const std::string &pattern) override {
×
333
    return base->FindFile(rootFolder, pattern);
×
334
  }
335

336
  std::istream &GetStream() override {
337
    if (!stream) {
×
338
      stream = base->OpenFile(entry);
×
339
    }
340
    return *stream;
×
341
  }
×
342
  std::string GetBuffer(size_t size, size_t begin) override {
343
    BinReaderRef rd(GetStream());
×
344
    rd.Push();
345
    rd.Seek(begin);
346
    std::string buffer;
×
347
    rd.ReadContainer(buffer, size == size_t(-1) ? rd.GetSize() : size);
×
348
    rd.Pop();
×
349
    return buffer;
350
  }
×
351

352
  AppExtractContext *ExtractContext(std::string_view name) override {
×
353
    if (ectx) [[unlikely]] {
×
354
      return ectx.get();
355
    }
356

357
    if (mainSettings.extractSettings.makeZIP) {
×
358
      base->InitMerger();
359
      entriesPath = RequestTempFile();
×
360
      auto uniq = std::make_unique<ZIPExtactContext>(entriesPath, false);
361
      if (mainSettings.extractSettings.folderPerArc) {
362
        uniq->prefixPath = workingFile.GetFolder();
×
363
        uniq->prefixPath += name;
×
364
        uniq->prefixPath.push_back('/');
×
365
      }
366
      uniq->forEachFile = forEachFile;
367
      ectx = std::move(uniq);
×
368
    } else {
×
369
      std::string outPath;
×
370
      if (!mainSettings.extractSettings.folderPerArc) {
×
371
        outPath = workingFile.GetFolder();
×
372
      } else {
×
373
        outPath = std::string(basePath.GetFullPath());
374
        outPath += workingFile.GetFolder();
×
375
        outPath += name;
376
        mkdirs(outPath);
×
377
        outPath.push_back('/');
378
      }
379

380
      auto uniq = std::make_unique<IOExtractContext>(outPath);
×
381
      uniq->forEachFile = forEachFile;
382
      ectx = std::move(uniq);
383
    }
×
384

385
    return ectx.get();
386
  }
×
387

×
388
  AppExtractContext *ExtractContext() override {
389
    return ExtractContext(workingFile.GetFilename());
390
  }
×
391

×
392
  void Finish() override {
393
    if (ectx && mainSettings.extractSettings.makeZIP) {
394
      base->Merge(static_cast<ZIPExtactContext *>(ectx.get()), entriesPath);
395
      es::Dispose(ectx);
×
396
      es::RemoveFile(entriesPath);
397
    }
398
  }
×
399

×
400
  ZIPIOContext *base;
401
  std::istream *stream = nullptr;
402
  ZipEntry entry;
×
403
  std::unique_ptr<AppExtractContext> ectx;
×
404
  std::string entriesPath;
×
405
};
406

×
407
std::shared_ptr<AppContextShare> ZIPIOContext::Instance(ZIPIOEntry entry) {
408
  return std::make_unique<ZIPIOContextInstance>(this, entry);
409
}
410

411
static std::mutex ZIPLock;
412

413
struct ZIPDataHolder {
414
  virtual ~ZIPDataHolder() = default;
415
};
416

417
struct ZIPIOContext_implbase : ZIPIOContext {
×
418
  ZIPIOContext_implbase(const std::string &file) : zipMount(file) {}
×
419
  std::istream *OpenFile(const ZipEntry &entry) override;
420
  std::string GetChunk(const ZipEntry &entry, size_t offset,
421
                       size_t size) const override;
422
  void DisposeFile(std::istream *str) override;
423

424
  void Merge(ZIPExtactContext *eCtx, const std::string &records) override {
425
    std::lock_guard<std::mutex> lg(mergerMtx);
426
    merger->Merge(*eCtx, records);
427
  }
428

×
429
  void InitMerger() override {
430
    std::lock_guard<std::mutex> lg(mergerMtx);
431
    if (!merger) {
432
      merger.emplace(basePath + "_out.zip", RequestTempFile());
433
    }
434
  }
×
435

×
436
  void Finish() override {
×
437
    if (merger) {
438
      merger->FinishMerge([] { printinfo("Generating cache."); });
439
    }
×
440
  }
×
441

×
442
protected:
×
443
  std::list<std::spanstream> openedFiles;
444
  es::MappedFile zipMount;
445
  std::optional<ZIPMerger> merger;
446
  std::mutex mergerMtx;
×
447
};
×
448

×
449
struct ZIPMemoryStream : ZIPDataHolder {
450
  std::istringstream stream;
451

452
  ZIPMemoryStream(std::string &&input)
453
      : stream(input, std::ios::in | std::ios::binary) {}
454
};
455

456
struct ZIPFileStream : ZIPDataHolder {
457
  BinReader rd;
458
  std::string path;
459
  ZIPFileStream(const std::string &path_) : rd(path_), path(path_) {}
460
  ~ZIPFileStream() {
461
    es::Dispose(rd);
462
    try {
463
      es::RemoveFile(path);
464
    } catch (const std::exception &e) {
465
      printerror(e.what());
466
    }
467
  }
468
};
469

470
std::istream *ZIPIOContext_implbase::OpenFile(const ZipEntry &entry) {
471
  auto dataBegin = static_cast<char *>(zipMount.data) + entry.offset;
472
  auto dataEnd = dataBegin + entry.size;
473

474
  std::lock_guard<std::mutex> guard(ZIPLock);
475
  auto &str = openedFiles.emplace_back(std::span<char>(dataBegin, dataEnd),
476
                                       std::ios::binary | std::ios::in);
477
  return &str;
478
}
479

480
std::string ZIPIOContext_implbase::GetChunk(const ZipEntry &entry,
×
481
                                            size_t offset, size_t size) const {
×
482
  auto dataBegin = static_cast<char *>(zipMount.data) + entry.offset + offset;
×
483
  auto dataEnd = dataBegin + size;
484

485
  return {dataBegin, dataEnd};
×
486
}
×
487

×
488
void ZIPIOContext_implbase::DisposeFile(std::istream *str) {
489
  std::lock_guard<std::mutex> guard(ZIPLock);
490
  openedFiles.remove_if([&](auto &spanStr) {
×
491
    return static_cast<std::istream *>(&spanStr) == str;
492
  });
×
493
}
×
494

495
struct ZIPIOContextIter_impl : ZIPIOEntryRawIterator {
×
496
  using map_type = std::map<std::string_view, ZipEntry>;
497
  ZIPIOContextIter_impl(const map_type &map)
498
      : base(&map), current(map.begin()), end(map.end()) {}
×
499
  ZIPIOEntry Fist() const override {
500
    if (current == end) {
×
501
      return {};
×
502
    }
503
    return {current->second, current->first};
504
  }
505
  ZIPIOEntry Next() const override {
506
    if (current == end) {
507
      return {};
508
    }
×
509
    current++;
×
510
    if (current == end) {
×
511
      return {};
×
512
    }
513

514
    return {current->second, current->first};
515
  }
×
516
  size_t Count() const override { return base->size(); }
×
517

×
518
  const map_type *base;
519
  mutable map_type::const_iterator current;
520
  map_type::const_iterator end;
×
521
};
×
522

523
struct ZIPIOContext_impl : ZIPIOContext_implbase {
524
  AppContextStream RequestFile(const std::string &path) override;
525

526
  AppContextFoundStream FindFile(const std::string &rootFolder,
×
527
                                 const std::string &pattern) override;
528

529
  ZIPIOContextIterator Iter(ZIPIOEntryType) const override {
530
    return {std::make_unique<ZIPIOContextIter_impl>(vfs)};
531
  }
532

533
  ZIPIOContext_impl(const std::string &file, const PathFilter &pathFilter_,
534
                    const PathFilter &moduleFilter_)
535
      : ZIPIOContext_implbase(file), pathFilter(&pathFilter_),
536
        moduleFilter(&moduleFilter_) {
537
    Read();
538
    pathFilter = moduleFilter = nullptr;
539
  }
×
540

×
541
  ZIPIOContext_impl(const std::string &file) : ZIPIOContext_implbase(file) {
542
    Read();
543
  }
×
544

545
private:
×
546
  void Read();
×
547
  const PathFilter *pathFilter = nullptr;
×
548
  const PathFilter *moduleFilter = nullptr;
×
549
  std::map<std::string_view, ZipEntry> vfs;
550
};
551

×
552
AppContextStream ZIPIOContext_impl::RequestFile(const std::string &path) {
×
553
  auto found = vfs.find(path);
554

555
  if (es::IsEnd(vfs, found)) {
556
    throw es::FileNotFoundError(path);
557
  }
558

559
  return {OpenFile(found->second), this};
560
}
561

562
AppContextFoundStream ZIPIOContext_impl::FindFile(const std::string &,
×
563
                                                  const std::string &pattern) {
×
564
  PathFilter filter;
565
  filter.AddFilter(pattern);
×
566

×
567
  for (auto &f : vfs) {
568
    std::string_view kvi(f.first);
569
    size_t lastSlash = kvi.find_last_of('/');
×
570
    kvi.remove_prefix(lastSlash + 1);
571

572
    if (filter.IsFiltered(kvi)) {
×
573
      return {OpenFile(f.second), this, AFileInfo(f.first)};
574
    }
×
575
  }
×
576

577
  throw es::FileNotFoundError(pattern);
×
578
}
×
579

580
// Warning: Unaligned accesses
×
581
// Note: Multiple central directories? (unlikely)
582
void ZIPIOContext_impl::Read() {
×
583
  auto curEnd = static_cast<char *>(zipMount.data) + zipMount.fileSize -
×
584
                (sizeof(ZIPCentralDir) - 2);
585
  auto curLocator = reinterpret_cast<const ZIPCentralDir *>(curEnd);
586

587
  if (curLocator->id != ZIPCentralDir::ID) {
×
588
    int numIters = 4096;
589
    while (numIters > 0) {
590
      curEnd--;
591
      numIters--;
592

×
593
      curLocator = reinterpret_cast<const ZIPCentralDir *>(curEnd);
×
594
      if (curLocator->id == ZIPCentralDir::ID) {
595
        break;
596
      }
597
    }
×
598
  }
599

×
600
  if (curLocator->id != ZIPCentralDir::ID) {
×
601
    throw std::runtime_error("Cannot find ZIP central directory");
×
602
  }
603

604
  uint64 dirOffset = 0;
×
605
  uint64 numEntries = 0;
606
  uint64 dirSize = 0;
607

608
  if (curLocator->dirOffset == -1U || curLocator->numDirEntries == uint16(-1) ||
609
      curLocator->dirSize == -1U) {
610
    curEnd -= sizeof(ZIP64CentralDir) - 8;
×
611
    auto curLocatorX64 = reinterpret_cast<const ZIP64CentralDir *>(curEnd);
×
612
    if (curLocatorX64->id != ZIP64CentralDir::ID) {
613
      int numIters = 4096;
614
      while (numIters > 0) {
615
        curEnd--;
616
        numIters--;
617

618
        curLocatorX64 = reinterpret_cast<const ZIP64CentralDir *>(curEnd);
×
619
        if (curLocatorX64->id == ZIP64CentralDir::ID) {
×
620
          break;
×
621
        }
622
      }
×
623
    }
624

×
625
    if (curLocatorX64->id != ZIP64CentralDir::ID) {
×
626
      throw std::runtime_error("Cannot find ZIPx64 central directory");
×
627
    }
628

629
    std::spanstream entriesSpan(
×
630
        std::span<char>(curEnd,
631
                        static_cast<char *>(zipMount.data) + zipMount.fileSize),
632
        std::ios::binary | std::ios::in);
633
    BinReaderRef rd(entriesSpan);
634
    ZIP64CentralDir x64CentraDir;
635
    rd.Read(x64CentraDir);
×
636

×
637
    dirOffset = x64CentraDir.dirOffset;
638
    numEntries = x64CentraDir.numDirEntries;
639
    dirSize = x64CentraDir.dirSize;
640
  } else {
641
    dirOffset = curLocator->dirOffset;
642
    numEntries = curLocator->numDirEntries;
×
643
    dirSize = curLocator->dirSize;
644
  }
645

646
  auto entriesBegin = static_cast<char *>(zipMount.data) + dirOffset;
647
  auto entriesEnd = entriesBegin + dirSize;
×
648
  std::spanstream entriesSpan(std::span<char>(entriesBegin, entriesEnd),
×
649
                              std::ios::binary | std::ios::in);
×
650
  BinReaderRef rd(entriesSpan);
×
651
  std::spanstream localStreamSpan(
×
652
      std::span<char>(static_cast<char *>(zipMount.data), entriesBegin),
×
653
      std::ios::binary | std::ios::in);
×
654
  BinReaderRef localRd(localStreamSpan);
655

656
  for (size_t d = 0; d < numEntries; d++) {
×
657
    uint32 id;
658
    rd.Push();
659
    rd.Read(id);
×
660
    rd.Pop();
661

662
    switch (id) {
×
663
    case ZIPFile::ID: {
×
664
      ZIPFile hdr;
665
      rd.Read(hdr);
666

×
667
      [&] {
668
        std::string_view entryName(entriesBegin + rd.Tell(), hdr.fileNameSize);
669
        rd.Skip(hdr.fileNameSize);
670

671
        if (!hdr.compressedSize) {
672
          return;
×
673
        }
674

675
        if (hdr.flags[ZIPLocalFlag::Encrypted]) {
×
676
          throw std::runtime_error("ZIP cannot have encrypted files!");
677
        }
×
678

×
679
        if (hdr.compression != ZIPCompressionMethod::Store) {
×
680
          throw std::runtime_error("ZIP cannot have compressed files!");
681
        }
×
682

×
683
        if (!hdr.fileNameSize) {
684
          throw std::runtime_error("ZIP local file's path must be specified!");
685
        }
×
686

×
687
        size_t entrySize = hdr.compressedSize;
688
        size_t localOffset = hdr.localHeaderOffset;
689

×
690
        if (hdr.compressedSize == -1U || hdr.uncompressedSize == -1U ||
×
691
            hdr.localHeaderOffset == -1U) {
692
          const size_t extraEnd = rd.Push() + hdr.extraFieldSize;
693

×
694
          while (rd.Tell() < extraEnd) {
×
695
            ZIP64Extra extra;
696
            rd.Read(extra.id);
697
            rd.Read(extra.size);
×
698

×
699
            if (extra.id == 1) {
700
              if (hdr.uncompressedSize == -1U) {
×
701
                rd.Read(extra.uncompressedSize);
702
                entrySize = extra.uncompressedSize;
×
703
              }
704
              if (hdr.compressedSize == -1U) {
×
705
                rd.Read(extra.compressedSize);
706
              }
707
              if (hdr.localHeaderOffset == -1U) {
708
                rd.Read(extra.localHeaderOffset);
709
                localOffset = extra.localHeaderOffset;
×
710
              }
×
711
              break;
712
            } else {
×
713
              rd.Skip(extra.size);
714
            }
×
715
          }
716

717
          rd.Pop();
×
718
        }
719

×
720
        if (pathFilter && !pathFilter->IsFiltered(entryName)) {
721
          return;
×
722
        }
723

×
724
        if (moduleFilter && !moduleFilter->IsFiltered(entryName)) {
725
          return;
726
        }
727

728
        ZIPLocalFile localEntry;
729
        localRd.Seek(localOffset);
730
        localRd.Read(localEntry);
×
731
        ZipEntry entry;
732
        entry.size = entrySize;
733
        entry.offset = localRd.Tell() + localEntry.extraFieldSize +
734
                       localEntry.fileNameSize;
×
735
        vfs.emplace(entryName, entry);
736
      }();
737

738
      rd.Skip(hdr.extraFieldSize + hdr.fileCommentSize);
739
      break;
×
740
    }
×
741
    default:
742
      using std::to_string;
×
743
      throw std::runtime_error("Invalid dir entry " + to_string(id) + " at " +
×
744
                               to_string(rd.Tell() + dirOffset));
×
745
    }
×
746
  }
×
747
}
748

×
749
ZIPIOEntry::operator bool() const {
750
  return std::visit([](auto &name) { return !name.empty(); }, name);
751
}
×
752

753
struct ZIPIOContextCached : ZIPIOContext_implbase {
×
754
  AppContextStream RequestFile(const std::string &path) override {
×
755

756
    auto found = cache.RequestFile(path);
757

758
    if (!found.size) {
759
      throw es::FileNotFoundError(path);
×
760
    }
×
761

762
    return {OpenFile(found), this};
763
  }
764

×
765
  AppContextFoundStream FindFile(const std::string &,
766
                                 const std::string &pattern) override {
×
767
    auto found = cache.FindFile(pattern);
768

×
769
    if (!found.size) {
×
770
      throw es::FileNotFoundError(pattern);
771
    }
772

×
773
    return std::visit(
774
        [&](auto &item) -> AppContextFoundStream {
775
          return {OpenFile(found), this, AFileInfo(item)};
×
776
        },
777
        found.name);
×
778
  }
779

×
780
  ZIPIOContextIterator Iter(ZIPIOEntryType type) const override {
×
781
    return {cache.Iter(type)};
782
  }
783

784
  ZIPIOContextCached(const std::string &file, es::MappedFile &&cacheFile)
×
785
      : ZIPIOContext_implbase(file), cacheMount(std::move(cacheFile)) {
×
786
    cache.Mount(cacheMount.data);
787
    auto &cacheHdr = reinterpret_cast<const CacheBaseHeader &>(cache.Header());
×
788
    auto zipData = static_cast<const char *>(zipMount.data);
×
789
    auto zipHeader = reinterpret_cast<const CacheBaseHeader *>(
790
        zipData + cacheHdr.zipCheckupOffset);
×
791

×
792
    if (memcmp(zipHeader, &cacheHdr, sizeof(cacheHdr))) {
793
      throw std::runtime_error("Cache header and zip checkup are different.");
×
794
    }
795
  }
796

×
797
private:
×
798
  Cache cache;
799
  es::MappedFile cacheMount;
800
};
×
801

×
802
std::unique_ptr<ZIPIOContext> MakeZIPContext(const std::string &file,
×
803
                                             const PathFilter &pathFilter,
×
804
                                             const PathFilter &moduleFilter) {
×
805
  return std::make_unique<ZIPIOContext_impl>(file, pathFilter, moduleFilter);
×
806
}
×
807

808
std::unique_ptr<ZIPIOContext> MakeZIPContext(const std::string &file) {
×
809
  std::string cacheFile = file + ".cache";
×
810
  es::MappedFile mf;
811
  try {
812
    mf = es::MappedFile(cacheFile);
813
  } catch (const std::exception &e) {
814
    printwarning("Failed loading cache: " << e.what());
815
    return std::make_unique<ZIPIOContext_impl>(file);
816
  }
817
  try {
818
    printinfo("Found zip cache: " << cacheFile);
×
819
    return std::make_unique<ZIPIOContextCached>(file, std::move(mf));
820
  } catch (const std::exception &e) {
821
    printwarning("Failed loading cache: " << e.what());
×
822
    return std::make_unique<ZIPIOContext_impl>(file);
823
  }
824
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc