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

PredatorCZ / PreCore / 488

02 Oct 2023 03:56PM UTC coverage: 55.265% (-0.05%) from 55.317%
488

push

github-actions-ci

PredatorCZ
fix sending wrong paths to pack context

7 of 7 new or added lines in 1 file covered. (100.0%)

4146 of 7502 relevant lines covered (55.27%)

8926.43 hits per line

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

9.44
/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/texel.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
  NewTexelContext *NewImage(NewTexelContextCreate ctx,
56✔
57
                            const std::string *path) override {
58
    if (texelContext) {
56✔
59
      texelContext->Finish();
28✔
60
    }
61

62
    texelContext = CreateTexelContext(ctx, this);
56✔
63

64
    if (path) {
56✔
65
      texelContext->pathOverride.Load(*path);
28✔
66
    }
67

68
    return texelContext.get();
56✔
69
  }
70

71
  NewFileContext NewFile(const std::string &path) override {
190✔
72
    std::string filePath;
73
    size_t delimeter = 0;
74

75
    if (basePathParts.empty()) {
190✔
76
      filePath = std::string(basePath.GetFullPath()) + path;
380✔
77
      delimeter = basePath.GetFullPath().size();
78
    } else {
79
      AFileInfo pathInfo(path);
×
80
      auto exploded = pathInfo.Explode();
×
81
      const size_t numItems = std::min(exploded.size(), basePathParts.size());
×
82

83
      if (basePath.GetFullPath().at(0) == '/') {
×
84
        filePath.push_back('/');
×
85
      }
86

87
      size_t i = 0;
88

89
      for (; i < numItems; i++) {
×
90
        if (basePathParts[i] == exploded[i]) {
×
91
          filePath.append(exploded[i]);
92
          filePath.push_back('/');
×
93
          continue;
94
        }
95

96
        break;
97
      }
98

99
      for (size_t j = i; j < basePathParts.size(); j++) {
×
100
        filePath.append(basePathParts[j]);
101
        filePath.push_back('/');
×
102
      }
103

104
      delimeter = filePath.size();
105

106
      for (; i < exploded.size(); i++) {
×
107
        filePath.append(exploded[i]);
108
        filePath.push_back('/');
×
109
      }
110

111
      filePath.pop_back();
112
    }
113

114
    try {
115
      outFile = BinWritter(filePath);
379✔
116
    } catch (const es::FileInvalidAccessError &e) {
1✔
117
      mkdirs(filePath);
1✔
118
      outFile = BinWritter(filePath);
1✔
119
    }
1✔
120
    return {outFile.BaseStream(), filePath, delimeter};
190✔
121
  }
380✔
122

123
  void BaseOutputPath(std::string basePath_) override {
×
124
    if (basePath_.back() != '/') {
×
125
      basePath_.push_back('/');
×
126
    }
127
    basePath = AFileInfo(basePath_);
×
128
    basePathParts = basePath.Explode();
×
129
  }
130

131
  JenHash Hash() override { return JenHash(FullPath()); }
×
132

133
  std::string FullPath() override {
×
134
    return std::string(basePath.GetFullPath()) +
×
135
           std::string(workingFile.GetFullPath());
×
136
  }
137

138
  ~AppContextShareImpl() {
28✔
139
    if (texelContext) {
28✔
140
      texelContext->Finish();
28✔
141
    }
142
  }
28✔
143

×
144
  BinWritter outFile;
145
  AFileInfo basePath;
146
  std::vector<std::string_view> basePathParts;
147
  std::unique_ptr<NewTexelContextImpl> texelContext;
148
};
28✔
149

28✔
150
struct SimpleIOContext : AppContextShareImpl {
28✔
151
  SimpleIOContext(const std::string &path,
152
                  std::optional<std::vector<std::string>> supplementals_) {
28✔
153
    mainFile.Open(path);
154
    workingFile.Load(path);
155
    supplementals = std::move(supplementals_);
156

157
    if (!cliSettings.out.empty()) {
158
      BaseOutputPath(cliSettings.out);
159
    }
160
  }
161
  std::istream *OpenFile(const std::string &path);
28✔
162

924✔
163
  AppContextStream RequestFile(const std::string &path) override;
28✔
164

28✔
165
  AppContextFoundStream FindFile(const std::string &rootFolder,
166
                                 const std::string &pattern) override;
167
  std::istream &GetStream() override;
28✔
168
  std::string GetBuffer(size_t size, size_t begin) override;
×
169

170
  void DisposeFile(std::istream *str) override;
28✔
171

172
  AppExtractContext *ExtractContext(std::string_view name) override {
173
    if (ectx) [[unlikely]] {
174
      return ectx.get();
175
    }
176

177
    std::string outPath(basePath.GetFullPath());
178
    outPath += workingFile.GetFolder();
179
    outPath += name;
180

181
    if (mainSettings.extractSettings.makeZIP) {
182
      if (workingFile.GetExtension() == ".zip") {
×
183
        outPath.append("_out");
×
184
      }
×
185

186
      outPath.append(".zip");
187

×
188
      auto uniq = std::make_unique<ZIPExtactContext>(outPath);
189
      uniq->forEachFile = forEachFile;
190
      ectx = std::move(uniq);
191
    } else {
×
192
      if (!mainSettings.extractSettings.folderPerArc) {
×
193
        outPath = workingFile.GetFolder();
×
194
      } else {
195
        es::mkdir(outPath);
196
        outPath.push_back('/');
×
197
      }
198

×
199
      auto uniq = std::make_unique<IOExtractContext>(outPath);
×
200
      uniq->forEachFile = forEachFile;
201
      ectx = std::move(uniq);
202
    }
×
203

204
    return ectx.get();
205
  }
206

×
207
  AppExtractContext *ExtractContext() override {
208
    return ExtractContext(workingFile.GetFilename());
209
  }
×
210

×
211
  void Finish() override {
212
    if (ectx && mainSettings.extractSettings.makeZIP) {
213
      static_cast<ZIPExtactContext *>(ectx.get())->FinishZIP([] {
214
        printinfo("Generating cache.");
215
      });
216
    }
217
  }
×
218

×
219
private:
220
  BinReader mainFile;
221
  BinReader streamedFiles[32];
×
222
  uint32 usedFiles = 0;
×
223
  std::unique_ptr<AppExtractContext> ectx;
224
};
×
225

×
226
std::istream *SimpleIOContext::OpenFile(const std::string &path) {
227
  std::lock_guard<std::mutex> guard(simpleIOLock);
228
  for (size_t b = 0; b < 32; b++) {
229
    uint32 bit = 1 << b;
230
    if (!(usedFiles & bit)) {
231
      streamedFiles[b].Open(path);
232
      usedFiles ^= bit;
233
      return &streamedFiles[b].BaseStream();
234
    }
235
  }
236

×
237
  throw std::out_of_range("Maximum opened files reached!");
238
}
×
239

×
240
AppContextStream SimpleIOContext::RequestFile(const std::string &path) {
×
241
  AFileInfo wFile(workingFile);
×
242
  AFileInfo pFile(path);
×
243
  auto catchedFile = pFile.CatchBranch(wFile.GetFolder());
×
244
  return {OpenFile(catchedFile), this};
245
}
246

247
AppContextFoundStream SimpleIOContext::FindFile(const std::string &rootFolder,
×
248
                                                const std::string &pattern) {
249
  DirectoryScanner sc;
250
  sc.AddFilter(pattern);
×
251
  sc.Scan(rootFolder);
252

×
253
  if (sc.Files().empty()) {
×
254
    throw es::FileNotFoundError(pattern);
×
255
  } else if (sc.Files().size() > 1) {
256
    std::string *winner = nullptr;
257
    size_t minFolder = 0x10000;
×
258
    size_t minLevel = 0x10000;
259

×
260
    for (auto &f : sc) {
×
261
      size_t foundIdx = f.find_last_of('/');
×
262

263
      if (foundIdx == f.npos) {
×
264
        throw std::runtime_error("Too many files found.");
×
265
      }
×
266

267
      if (foundIdx < minFolder) {
268
        winner = &f;
269
        minFolder = foundIdx;
270
        minLevel = std::count(f.begin(), f.end(), '/');
×
271
      } else if (foundIdx == minFolder) {
272
        if (auto clevel = std::count(f.begin(), f.end(), '/');
273
            clevel < minLevel) {
×
274
          winner = &f;
×
275
          minFolder = foundIdx;
276
          minLevel = clevel;
277
        } else if (clevel == minLevel) {
×
278
          throw std::runtime_error("Too many files found.");
279
        }
280
      }
×
281
    }
×
282

×
283
    if (!winner) {
×
284
      throw std::runtime_error("Too many files found.");
285
    }
286

287
    return {OpenFile(*winner), this, AFileInfo(*winner)};
×
288
  }
×
289

290
  return {OpenFile(sc.Files().front()), this, AFileInfo(sc.Files().front())};
291
}
292

293
void SimpleIOContext::DisposeFile(std::istream *str) {
×
294
  size_t index = 0;
×
295

296
  for (auto &f : streamedFiles) {
297
    if (&f.BaseStream() == str) {
×
298
      uint32 bit = 1 << index;
299

300
      if (!(usedFiles & bit)) {
×
301
        throw std::runtime_error("Stream already freed.");
302
      }
303

×
304
      es::Dispose(f);
305
      usedFiles ^= bit;
306
      return;
×
307
    }
×
308

×
309
    index++;
310
  }
×
311

×
312
  throw std::runtime_error("Requested stream not found!");
313
}
314

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

×
317
std::string SimpleIOContext::GetBuffer(size_t size, size_t begin) {
318
  mainFile.Push();
319
  mainFile.Seek(begin);
×
320
  std::string buffer;
321
  mainFile.ReadContainer(
322
      buffer, size == size_t(-1) ? (mainFile.GetSize() - begin) : size);
×
323
  mainFile.Pop();
324

325
  return buffer;
28✔
326
}
327

×
328
std::shared_ptr<AppContextShare>
×
329
MakeIOContext(const std::string &path,
330
              std::optional<std::vector<std::string>> supplementals) {
331
  return std::make_unique<SimpleIOContext>(path, supplementals);
×
332
}
×
333

334
struct ZIPIOContextInstance : AppContextShareImpl {
335
  ZIPIOContextInstance(const ZIPIOContextInstance &) = delete;
×
336
  ZIPIOContextInstance(ZIPIOContextInstance &&) = delete;
337
  ZIPIOContextInstance(ZIPIOContext *base_, ZIPIOEntry entry_)
338
      : base(base_), entry(entry_) {
339
    workingFile.Load(entry_.AsView());
28✔
340
    BaseOutputPath(base->basePath);
341
  }
56✔
342

343
  ~ZIPIOContextInstance() {
344
    if (stream) {
345
      base->DisposeFile(stream);
346
    }
347
  }
×
348

×
349
  AppContextStream RequestFile(const std::string &path) override {
×
350
    return base->RequestFile(path);
×
351
  }
352
  void DisposeFile(std::istream *file) override { base->DisposeFile(file); }
353
  AppContextFoundStream FindFile(const std::string &rootFolder,
×
354
                                 const std::string &pattern) override {
×
355
    return base->FindFile(rootFolder, pattern);
×
356
  }
357

358
  std::istream &GetStream() override {
×
359
    if (!stream) {
360
      stream = base->OpenFile(entry);
361
    }
362
    return *stream;
363
  }
×
364
  std::string GetBuffer(size_t size, size_t begin) override {
×
365
    BinReaderRef rd(GetStream());
×
366
    rd.Push();
367
    rd.Seek(begin);
368
    std::string buffer;
369
    rd.ReadContainer(buffer,
×
370
                     size == size_t(-1) ? (rd.GetSize() - begin) : size);
×
371
    rd.Pop();
372
    return buffer;
×
373
  }
×
374

375
  AppExtractContext *ExtractContext(std::string_view name) override {
×
376
    if (ectx) [[unlikely]] {
377
      return ectx.get();
378
    }
×
379

×
380
    if (mainSettings.extractSettings.makeZIP) {
×
381
      base->InitMerger();
382
      entriesPath = RequestTempFile();
×
383
      auto uniq = std::make_unique<ZIPExtactContext>(entriesPath, false);
384
      if (mainSettings.extractSettings.folderPerArc) {
×
385
        uniq->prefixPath = workingFile.GetFolder();
×
386
        uniq->prefixPath += name;
387
        uniq->prefixPath.push_back('/');
388
      }
389
      uniq->forEachFile = forEachFile;
×
390
      ectx = std::move(uniq);
×
391
    } else {
392
      std::string outPath;
×
393
      if (!mainSettings.extractSettings.folderPerArc) {
394
        outPath = workingFile.GetFolder();
395
      } else {
×
396
        outPath = std::string(basePath.GetFullPath());
×
397
        outPath += workingFile.GetFolder();
×
398
        outPath += name;
399
        mkdirs(outPath);
400
        outPath.push_back('/');
×
401
      }
×
402

×
403
      auto uniq = std::make_unique<IOExtractContext>(outPath);
×
404
      uniq->forEachFile = forEachFile;
×
405
      ectx = std::move(uniq);
×
406
    }
407

×
408
    return ectx.get();
409
  }
×
410

411
  AppExtractContext *ExtractContext() override {
412
    return ExtractContext(workingFile.GetFilename());
413
  }
×
414

415
  void Finish() override {
416
    if (ectx && mainSettings.extractSettings.makeZIP) {
×
417
      base->Merge(static_cast<ZIPExtactContext *>(ectx.get()), entriesPath);
418
      es::Dispose(ectx);
419
      es::RemoveFile(entriesPath);
×
420
    }
×
421
  }
422

423
  ZIPIOContext *base;
×
424
  std::istream *stream = nullptr;
×
425
  ZipEntry entry;
426
  std::unique_ptr<AppExtractContext> ectx;
427
  std::string entriesPath;
428
};
×
429

430
std::shared_ptr<AppContextShare> ZIPIOContext::Instance(ZIPIOEntry entry) {
431
  return std::make_unique<ZIPIOContextInstance>(this, entry);
×
432
}
×
433

434
static std::mutex ZIPLock;
435

×
436
struct ZIPDataHolder {
×
437
  virtual ~ZIPDataHolder() = default;
×
438
};
439

×
440
struct ZIPIOContext_implbase : ZIPIOContext {
441
  ZIPIOContext_implbase(const std::string &file) : zipMount(file) {}
442
  std::istream *OpenFile(const ZipEntry &entry) override;
443
  std::string GetChunk(const ZipEntry &entry, size_t offset,
444
                       size_t size) const override;
445
  void DisposeFile(std::istream *str) override;
446

447
  void Merge(ZIPExtactContext *eCtx, const std::string &records) override {
448
    std::lock_guard<std::mutex> lg(mergerMtx);
449
    merger->Merge(*eCtx, records);
450
  }
×
451

×
452
  void InitMerger() override {
453
    std::lock_guard<std::mutex> lg(mergerMtx);
454
    if (!merger) {
455
      merger.emplace(basePath + "_out.zip", RequestTempFile());
456
    }
457
  }
458

459
  void Finish() override {
460
    if (merger) {
461
      merger->FinishMerge([] { printinfo("Generating cache."); });
×
462
    }
463
  }
464

465
protected:
466
  std::list<std::spanstream> openedFiles;
467
  es::MappedFile zipMount;
×
468
  std::optional<ZIPMerger> merger;
×
469
  std::mutex mergerMtx;
×
470
};
471

472
struct ZIPMemoryStream : ZIPDataHolder {
×
473
  std::istringstream stream;
×
474

×
475
  ZIPMemoryStream(std::string &&input)
×
476
      : stream(input, std::ios::in | std::ios::binary) {}
477
};
478

479
struct ZIPFileStream : ZIPDataHolder {
×
480
  BinReader rd;
×
481
  std::string path;
×
482
  ZIPFileStream(const std::string &path_) : rd(path_), path(path_) {}
483
  ~ZIPFileStream() {
484
    es::Dispose(rd);
485
    try {
486
      es::RemoveFile(path);
487
    } catch (const std::exception &e) {
488
      printerror(e.what());
489
    }
490
  }
491
};
492

493
std::istream *ZIPIOContext_implbase::OpenFile(const ZipEntry &entry) {
494
  auto dataBegin = static_cast<char *>(zipMount.data) + entry.offset;
495
  auto dataEnd = dataBegin + entry.size;
496

497
  std::lock_guard<std::mutex> guard(ZIPLock);
498
  auto &str = openedFiles.emplace_back(std::span<char>(dataBegin, dataEnd),
499
                                       std::ios::binary | std::ios::in);
500
  return &str;
501
}
502

503
std::string ZIPIOContext_implbase::GetChunk(const ZipEntry &entry,
504
                                            size_t offset, size_t size) const {
505
  auto dataBegin = static_cast<char *>(zipMount.data) + entry.offset + offset;
506
  auto dataEnd = dataBegin + size;
507

508
  return {dataBegin, dataEnd};
509
}
510

511
void ZIPIOContext_implbase::DisposeFile(std::istream *str) {
512
  std::lock_guard<std::mutex> guard(ZIPLock);
513
  openedFiles.remove_if([&](auto &spanStr) {
×
514
    return static_cast<std::istream *>(&spanStr) == str;
×
515
  });
×
516
}
517

518
struct ZIPIOContextIter_impl : ZIPIOEntryRawIterator {
×
519
  using map_type = std::map<std::string_view, ZipEntry>;
×
520
  ZIPIOContextIter_impl(const map_type &map)
×
521
      : base(&map), current(map.begin()), end(map.end()) {}
522
  ZIPIOEntry Fist() const override {
523
    if (current == end) {
×
524
      return {};
525
    }
×
526
    return {current->second, current->first};
×
527
  }
528
  ZIPIOEntry Next() const override {
×
529
    if (current == end) {
530
      return {};
531
    }
×
532
    current++;
533
    if (current == end) {
×
534
      return {};
×
535
    }
536

537
    return {current->second, current->first};
538
  }
539
  size_t Count() const override { return base->size(); }
540

541
  const map_type *base;
×
542
  mutable map_type::const_iterator current;
×
543
  map_type::const_iterator end;
×
544
};
×
545

546
struct ZIPIOContext_impl : ZIPIOContext_implbase {
547
  AppContextStream RequestFile(const std::string &path) override;
548

×
549
  AppContextFoundStream FindFile(const std::string &rootFolder,
×
550
                                 const std::string &pattern) override;
×
551

552
  ZIPIOContextIterator Iter(ZIPIOEntryType) const override {
553
    return {std::make_unique<ZIPIOContextIter_impl>(vfs)};
×
554
  }
×
555

556
  ZIPIOContext_impl(const std::string &file, const PathFilter &pathFilter_,
557
                    const PathFilter &moduleFilter_)
558
      : ZIPIOContext_implbase(file), pathFilter(&pathFilter_),
559
        moduleFilter(&moduleFilter_) {
×
560
    Read();
561
    pathFilter = moduleFilter = nullptr;
562
  }
563

564
  ZIPIOContext_impl(const std::string &file) : ZIPIOContext_implbase(file) {
565
    Read();
566
  }
567

568
private:
569
  void Read();
570
  const PathFilter *pathFilter = nullptr;
571
  const PathFilter *moduleFilter = nullptr;
572
  std::map<std::string_view, ZipEntry> vfs;
×
573
};
×
574

575
AppContextStream ZIPIOContext_impl::RequestFile(const std::string &path) {
576
  auto found = vfs.find(path);
×
577

578
  if (es::IsEnd(vfs, found)) {
×
579
    throw es::FileNotFoundError(path);
×
580
  }
×
581

×
582
  return {OpenFile(found->second), this};
583
}
584

×
585
AppContextFoundStream ZIPIOContext_impl::FindFile(const std::string &,
×
586
                                                  const std::string &pattern) {
587
  PathFilter filter;
588
  filter.AddFilter(pattern);
589

590
  for (auto &f : vfs) {
591
    std::string_view kvi(f.first);
592
    size_t lastSlash = kvi.find_last_of('/');
593
    kvi.remove_prefix(lastSlash + 1);
594

595
    if (filter.IsFiltered(kvi)) {
×
596
      return {OpenFile(f.second), this, AFileInfo(f.first)};
×
597
    }
598
  }
×
599

×
600
  throw es::FileNotFoundError(pattern);
601
}
602

×
603
// Warning: Unaligned accesses
604
// Note: Multiple central directories? (unlikely)
605
void ZIPIOContext_impl::Read() {
×
606
  auto curEnd = static_cast<char *>(zipMount.data) + zipMount.fileSize -
607
                (sizeof(ZIPCentralDir) - 2);
×
608
  auto curLocator = reinterpret_cast<const ZIPCentralDir *>(curEnd);
×
609

610
  if (curLocator->id != ZIPCentralDir::ID) {
×
611
    int numIters = 4096;
×
612
    while (numIters > 0) {
613
      curEnd--;
×
614
      numIters--;
615

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

623
  if (curLocator->id != ZIPCentralDir::ID) {
624
    throw std::runtime_error("Cannot find ZIP central directory");
625
  }
×
626

×
627
  uint64 dirOffset = 0;
628
  uint64 numEntries = 0;
629
  uint64 dirSize = 0;
630

×
631
  if (curLocator->dirOffset == -1U || curLocator->numDirEntries == uint16(-1) ||
632
      curLocator->dirSize == -1U) {
×
633
    curEnd -= sizeof(ZIP64CentralDir) - 8;
×
634
    auto curLocatorX64 = reinterpret_cast<const ZIP64CentralDir *>(curEnd);
×
635
    if (curLocatorX64->id != ZIP64CentralDir::ID) {
636
      int numIters = 4096;
637
      while (numIters > 0) {
×
638
        curEnd--;
639
        numIters--;
640

641
        curLocatorX64 = reinterpret_cast<const ZIP64CentralDir *>(curEnd);
642
        if (curLocatorX64->id == ZIP64CentralDir::ID) {
643
          break;
×
644
        }
×
645
      }
646
    }
647

648
    if (curLocatorX64->id != ZIP64CentralDir::ID) {
649
      throw std::runtime_error("Cannot find ZIPx64 central directory");
650
    }
651

×
652
    std::spanstream entriesSpan(
×
653
        std::span<char>(curEnd,
×
654
                        static_cast<char *>(zipMount.data) + zipMount.fileSize),
655
        std::ios::binary | std::ios::in);
×
656
    BinReaderRef rd(entriesSpan);
657
    ZIP64CentralDir x64CentraDir;
×
658
    rd.Read(x64CentraDir);
×
659

×
660
    dirOffset = x64CentraDir.dirOffset;
661
    numEntries = x64CentraDir.numDirEntries;
662
    dirSize = x64CentraDir.dirSize;
×
663
  } else {
664
    dirOffset = curLocator->dirOffset;
665
    numEntries = curLocator->numDirEntries;
666
    dirSize = curLocator->dirSize;
667
  }
668

×
669
  auto entriesBegin = static_cast<char *>(zipMount.data) + dirOffset;
×
670
  auto entriesEnd = entriesBegin + dirSize;
671
  std::spanstream entriesSpan(std::span<char>(entriesBegin, entriesEnd),
672
                              std::ios::binary | std::ios::in);
673
  BinReaderRef rd(entriesSpan);
674
  std::spanstream localStreamSpan(
675
      std::span<char>(static_cast<char *>(zipMount.data), entriesBegin),
×
676
      std::ios::binary | std::ios::in);
677
  BinReaderRef localRd(localStreamSpan);
678

679
  for (size_t d = 0; d < numEntries; d++) {
680
    uint32 id;
×
681
    rd.Push();
×
682
    rd.Read(id);
×
683
    rd.Pop();
×
684

×
685
    switch (id) {
×
686
    case ZIPFile::ID: {
×
687
      ZIPFile hdr;
688
      rd.Read(hdr);
689

×
690
      [&] {
691
        std::string_view entryName(entriesBegin + rd.Tell(), hdr.fileNameSize);
692
        rd.Skip(hdr.fileNameSize);
×
693

694
        if (!hdr.compressedSize) {
695
          return;
×
696
        }
×
697

698
        if (hdr.flags[ZIPLocalFlag::Encrypted]) {
699
          throw std::runtime_error("ZIP cannot have encrypted files!");
×
700
        }
701

702
        if (hdr.compression != ZIPCompressionMethod::Store) {
703
          throw std::runtime_error("ZIP cannot have compressed files!");
704
        }
705

×
706
        if (!hdr.fileNameSize) {
707
          throw std::runtime_error("ZIP local file's path must be specified!");
708
        }
×
709

710
        size_t entrySize = hdr.compressedSize;
×
711
        size_t localOffset = hdr.localHeaderOffset;
×
712

×
713
        if (hdr.compressedSize == -1U || hdr.uncompressedSize == -1U ||
714
            hdr.localHeaderOffset == -1U) {
×
715
          const size_t extraEnd = rd.Push() + hdr.extraFieldSize;
×
716

717
          while (rd.Tell() < extraEnd) {
718
            ZIP64Extra extra;
×
719
            rd.Read(extra.id);
×
720
            rd.Read(extra.size);
721

722
            if (extra.id == 1) {
×
723
              if (hdr.uncompressedSize == -1U) {
×
724
                rd.Read(extra.uncompressedSize);
725
                entrySize = extra.uncompressedSize;
726
              }
×
727
              if (hdr.compressedSize == -1U) {
×
728
                rd.Read(extra.compressedSize);
729
              }
730
              if (hdr.localHeaderOffset == -1U) {
×
731
                rd.Read(extra.localHeaderOffset);
×
732
                localOffset = extra.localHeaderOffset;
733
              }
×
734
              break;
735
            } else {
×
736
              rd.Skip(extra.size);
737
            }
×
738
          }
739

740
          rd.Pop();
741
        }
742

×
743
        if (pathFilter && !pathFilter->IsFiltered(entryName)) {
×
744
          return;
745
        }
×
746

747
        if (moduleFilter && !moduleFilter->IsFiltered(entryName)) {
×
748
          return;
749
        }
750

×
751
        ZIPLocalFile localEntry;
752
        localRd.Seek(localOffset);
×
753
        localRd.Read(localEntry);
754
        ZipEntry entry;
×
755
        entry.size = entrySize;
756
        entry.offset = localRd.Tell() + localEntry.extraFieldSize +
×
757
                       localEntry.fileNameSize;
758
        vfs.emplace(entryName, entry);
759
      }();
760

761
      rd.Skip(hdr.extraFieldSize + hdr.fileCommentSize);
762
      break;
763
    }
×
764
    default:
765
      using std::to_string;
766
      throw std::runtime_error("Invalid dir entry " + to_string(id) + " at " +
767
                               to_string(rd.Tell() + dirOffset));
×
768
    }
769
  }
770
}
771

772
ZIPIOEntry::operator bool() const {
×
773
  return std::visit([](auto &name) { return !name.empty(); }, name);
×
774
}
775

×
776
struct ZIPIOContextCached : ZIPIOContext_implbase {
×
777
  AppContextStream RequestFile(const std::string &path) override {
×
778

×
779
    auto found = cache.RequestFile(path);
×
780

781
    if (!found.size) {
×
782
      throw es::FileNotFoundError(path);
783
    }
784

×
785
    return {OpenFile(found), this};
786
  }
×
787

×
788
  AppContextFoundStream FindFile(const std::string &,
789
                                 const std::string &pattern) override {
790
    auto found = cache.FindFile(pattern);
791

792
    if (!found.size) {
×
793
      throw es::FileNotFoundError(pattern);
×
794
    }
795

796
    return std::visit(
797
        [&](auto &item) -> AppContextFoundStream {
×
798
          return {OpenFile(found), this, AFileInfo(item)};
799
        },
×
800
        found.name);
801
  }
×
802

×
803
  ZIPIOContextIterator Iter(ZIPIOEntryType type) const override {
804
    return {cache.Iter(type)};
805
  }
×
806

807
  ZIPIOContextCached(const std::string &file, es::MappedFile &&cacheFile)
808
      : ZIPIOContext_implbase(file), cacheMount(std::move(cacheFile)) {
×
809
    cache.Mount(cacheMount.data);
810
    auto &cacheHdr = reinterpret_cast<const CacheBaseHeader &>(cache.Header());
×
811
    auto zipData = static_cast<const char *>(zipMount.data);
812
    auto zipHeader = reinterpret_cast<const CacheBaseHeader *>(
×
813
        zipData + cacheHdr.zipCheckupOffset);
×
814

815
    if (memcmp(zipHeader, &cacheHdr, sizeof(cacheHdr))) {
816
      throw std::runtime_error("Cache header and zip checkup are different.");
817
    }
×
818
  }
×
819

820
private:
×
821
  Cache cache;
×
822
  es::MappedFile cacheMount;
823
};
×
824

×
825
std::unique_ptr<ZIPIOContext> MakeZIPContext(const std::string &file,
826
                                             const PathFilter &pathFilter,
×
827
                                             const PathFilter &moduleFilter) {
828
  return std::make_unique<ZIPIOContext_impl>(file, pathFilter, moduleFilter);
829
}
×
830

×
831
std::unique_ptr<ZIPIOContext> MakeZIPContext(const std::string &file) {
832
  std::string cacheFile = file + ".cache";
833
  es::MappedFile mf;
×
834
  try {
×
835
    mf = es::MappedFile(cacheFile);
×
836
  } catch (const std::exception &e) {
×
837
    printwarning("Failed loading cache: " << e.what());
×
838
    return std::make_unique<ZIPIOContext_impl>(file);
×
839
  }
×
840
  try {
841
    printinfo("Found zip cache: " << cacheFile);
×
842
    return std::make_unique<ZIPIOContextCached>(file, std::move(mf));
×
843
  } catch (const std::exception &e) {
844
    printwarning("Failed loading cache: " << e.what());
845
    return std::make_unique<ZIPIOContext_impl>(file);
846
  }
847
}
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