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

PredatorCZ / PreCore / 530

15 Aug 2024 04:12PM UTC coverage: 53.343% (-0.01%) from 53.356%
530

push

github

PredatorCZ
better throw messages for FindFile

0 of 2 new or added lines in 1 file covered. (0.0%)

3 existing lines in 1 file now uncovered.

4237 of 7943 relevant lines covered (53.34%)

10449.91 hits per line

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

9.39
/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) {
×
NEW
264
        throw std::runtime_error("Too many files found for pattern: " +
×
NEW
265
                                 pattern);
×
266
      }
267

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

×
285
    if (!winner) {
286
      throw std::runtime_error("Too many files found for pattern: " + pattern);
287
    }
UNCOV
288

×
289
    return {OpenFile(*winner), this, AFileInfo(*winner)};
×
290
  }
×
291

292
  return {OpenFile(sc.Files().front()), this, AFileInfo(sc.Files().front())};
293
}
294

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

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

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

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

×
311
    index++;
312
  }
×
313

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

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

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

327
  return buffer;
28✔
328
}
329

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

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

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

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

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

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

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

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

×
410
    return ectx.get();
411
  }
×
412

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

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

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

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

436
static std::mutex ZIPLock;
437

×
438
struct ZIPDataHolder {
×
439
  virtual ~ZIPDataHolder() = default;
×
440
};
441

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

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

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

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

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

474
struct ZIPMemoryStream : ZIPDataHolder {
×
475
  std::istringstream stream;
×
476

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

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

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

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

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

510
  return {dataBegin, dataEnd};
511
}
512

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

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

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

543
  const map_type *base;
×
544
  mutable map_type::const_iterator current;
×
545
  map_type::const_iterator end;
×
546
};
×
547

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

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

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

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

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

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

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

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

×
584
  return {OpenFile(found->second), this};
585
}
586

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

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

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

×
602
  throw es::FileNotFoundError(pattern);
603
}
604

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

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

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

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

×
629
  uint64 dirOffset = 0;
630
  uint64 numEntries = 0;
631
  uint64 dirSize = 0;
632

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

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

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

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

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

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

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

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

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

696
        if (!hdr.compressedSize) {
697
          return;
×
698
        }
×
699

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

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

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

712
        size_t entrySize = hdr.compressedSize;
×
713
        size_t localOffset = hdr.localHeaderOffset;
×
714

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

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

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

742
          rd.Pop();
743
        }
744

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

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

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

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

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

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

×
781
    auto found = cache.RequestFile(path);
×
782

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

×
787
    return {OpenFile(found), this};
788
  }
×
789

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

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

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

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

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

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

822
private:
×
823
  Cache cache;
×
824
  es::MappedFile cacheMount;
825
};
×
826

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

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