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

PredatorCZ / PreCore / 461

pending completion
461

push

github-actions-ci

PredatorCZ
update readme

3204 of 6096 relevant lines covered (52.56%)

354.05 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

4
    Copyright 2021-2023 Lukas Cone
5

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

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

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

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

34
static std::mutex simpleIOLock;
35

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

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

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

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

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

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

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

71
      size_t i = 0;
72

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

80
        break;
81
      }
82

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

88
      delimeter = filePath.size();
89

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

95
      filePath.pop_back();
96
    }
97

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

181
    return ectx.get();
182
  }
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

286
    index++;
×
287
  }
288

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

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

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

302
  return buffer;
×
303
}
304

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

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

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

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

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

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

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

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

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

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

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

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

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

410
static std::mutex ZIPLock;
411

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

716
          rd.Pop();
×
717
        }
718

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

807
std::unique_ptr<ZIPIOContext> MakeZIPContext(const std::string &file) {
×
808
  std::string cacheFile = file + ".cache";
×
809
  es::MappedFile mf;
810
  try {
811
    mf = es::MappedFile(cacheFile);
812
  } catch (const std::exception &e) {
813
    printwarning("Failed loading cache: " << e.what());
814
    return std::make_unique<ZIPIOContext_impl>(file);
815
  }
816
  try {
817
    printinfo("Found zip cache: " << cacheFile);
×
818
    return std::make_unique<ZIPIOContextCached>(file, std::move(mf));
819
  } catch (const std::exception &e) {
820
    printwarning("Failed loading cache: " << e.what());
×
821
    return std::make_unique<ZIPIOContext_impl>(file);
822
  }
823
}
×
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