• 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

29.2
/src/app/in_cache.cpp
1
/*  Cache format loader for seekless zip loading
2
    Part of PreCore's Spike project
3

4
    Copyright 2021-2022 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/cache.hpp"
20
#include "spike/except.hpp"
21
#include "spike/io/binreader_stream.hpp"
22
#include "spike/io/fileinfo.hpp"
23

24
template <class C, size_t Align> struct CachePointer {
25
  using value_type = C;
26
  static constexpr size_t TYPE_ALIGN = Align;
27

28
private:
29
  int32 varPtr;
30

31
public:
32
  CachePointer() = default;
33
  CachePointer(const CachePointer &) = delete;
34
  CachePointer(CachePointer &&) = delete;
35
  operator C *() {
36
    return varPtr ? reinterpret_cast<C *>(reinterpret_cast<char *>(&varPtr) +
37
                                          (varPtr * TYPE_ALIGN))
38
                  : nullptr;
39
  }
40

41
  C &operator*() { return *static_cast<C *>(*this); }
42
  C *operator->() { return *this; }
43

44
  operator const C *() const {
45
    return varPtr ? reinterpret_cast<const C *>(
75,656✔
46
                        reinterpret_cast<const char *>(&varPtr) +
77,394✔
47
                        (varPtr * TYPE_ALIGN))
75,656✔
48
                  : nullptr;
49
  }
50
  const C &operator*() const { return *static_cast<const C *>(*this); }
51
  const C *operator->() const { return *this; }
52
};
53

54
using HybridLeafPtr = CachePointer<HybridLeaf, 4>;
55
using TextPtr = CachePointer<const char, 1>;
56

57
struct ZipEntryLeaf : ZipEntry {
58
  HybridLeafPtr parent;
59
  uint16 fileNameSize;
60
  uint16 totalFileNameSize;
61
  union {
62
    TextPtr fileNamePtr;
63
    const char fileName[8];
64
  };
65

66
  std::string_view Name() const {
67
    if (fileNameSize < 9) {
1,738✔
68
      return {fileName, fileNameSize};
401✔
69
    } else {
70
      return {fileNamePtr, fileNameSize};
12,135✔
71
    }
72
  }
73

74
  bool operator<(std::string_view sw) const { return Name() < sw; }
10,798✔
75
};
76

77
using ZipEntryLeafPtr = CachePointer<ZipEntryLeaf, 4>;
78

79
struct ChildrenIter {
80
  using value_type = HybridLeafPtr;
81
  value_type const *begin_;
82
  value_type const *end_;
83

84
  auto begin() const { return begin_; }
6,881✔
85
  auto end() const { return end_; }
86
};
87

88
struct FinalsIter {
89
  using value_type = ZipEntryLeafPtr;
90
  value_type const *begin_;
91
  value_type const *end_;
92

93
  auto begin() const { return begin_; }
1,738✔
94
  auto end() const { return end_; }
95
};
96

97
struct HybridLeaf {
98
  HybridLeafPtr children[1];
99
  HybridLeafPtr parent;
100
  union {
101
    TextPtr pathPartPtr;
102
    const char pathPart[4];
103
  };
104
  uint16 numChildren;
105
  uint16 pathPartSize;
106
  uint32 numFinals;
107
  ZipEntryLeafPtr entries[1];
108

109
  ChildrenIter Children() const {
110
    return {children - numChildren + 1, children + 1};
6,881✔
111
  }
112

113
  FinalsIter Finals() const { return {entries, entries + numFinals}; }
1,738✔
114

115
  std::string_view Name() const {
116
    if (pathPartSize < 5) {
6,881✔
117
      return {pathPart, pathPartSize};
8,886✔
118
    } else {
119
      return {pathPartPtr, pathPartSize};
16,740✔
120
    }
121
  }
122
};
123

124
struct CacheHeader : CacheBaseHeader {
125
  uint32 cacheSize;
126
  HybridLeafPtr root;
127
  ZipEntryLeafPtr entries;
128
};
129

130
const CacheHeader &Cache::Header() const {
×
131
  return *static_cast<const CacheHeader *>(data);
1,738✔
132
}
133

134
ZIPIOEntry Cache::FindFile(std::string_view pattern) {
×
135
  const ZipEntryLeaf *begin = Header().entries;
136
  auto end = begin + Header().numFiles;
×
137
  bool clampBegin = pattern.front() == '^';
×
138
  bool clampEnd = pattern.back() == '$';
×
139

140
  if (clampBegin) {
×
141
    pattern.remove_prefix(1);
142
  }
143

144
  if (clampEnd) {
×
145
    pattern.remove_suffix(1);
146
  }
147

148
  auto wildcharPos = pattern.find_first_of('*');
149
  bool useWildchar = wildcharPos != pattern.npos;
150

151
  if (useWildchar) {
×
152
    auto part1 = pattern.substr(0, wildcharPos);
×
153
    auto part2 = pattern.substr(wildcharPos + 1);
×
154

155
    // cases ^foo*bar or ^foo*bar$
156
    if (clampBegin) {
×
157
      auto found = std::lower_bound(begin, end, part1);
158

159
      if (found == end) {
×
160
        return {};
×
161
      }
162

163
      auto foundName = found->Name();
×
164

165
      while (foundName.starts_with(part1)) {
×
166
        if (clampEnd) {
×
167
          if (foundName.ends_with(part2)) {
×
168
            return {*found, foundName};
169
          }
170
        } else if (foundName.find(part2, part1.size()) != foundName.npos) {
×
171
          return {*found, foundName};
172
        }
173

174
        found++;
×
175

176
        if (found == end) {
×
177
          return {};
×
178
        }
179

180
        foundName = found->Name();
×
181
      }
182

183
      return {};
×
184
    }
185

186
    // cases foo*bar$ only
187
    if (clampEnd) {
×
188
      for (auto p = begin; p != end; p++) {
×
189
        const auto foundName = p->Name();
×
190

191
        if (foundName.ends_with(part2)) {
×
192
          auto foundName2 = foundName;
×
193
          foundName2.remove_suffix(part2.size());
194

195
          if (foundName2.find(part1) != foundName.npos) {
×
196
            return {*p, foundName};
×
197
          }
198
        }
199
      }
200

201
      return {};
×
202
    }
203

204
    // cases foo*bar only
205
    for (auto p = begin; p != end; p++) {
×
206
      const auto foundName = p->Name();
×
207

208
      if (auto found = foundName.find(part1); found != foundName.npos) {
×
209
        auto foundName2 = foundName;
210
        foundName2.remove_prefix(found + part1.size());
×
211

212
        if (foundName2.find(part2) != foundName.npos) {
×
213
          return {*p, foundName};
×
214
        }
215
      }
216
    }
217

218
    return {};
×
219
  }
220

221
  if (clampBegin && clampEnd) {
×
222
    auto found = std::lower_bound(begin, end, pattern);
223

224
    if (found == end) {
×
225
      return {};
×
226
    }
227

228
    auto foundName = found->Name();
229

230
    if (foundName == pattern) {
×
231
      return {*found, foundName};
232
    }
233

234
    return {};
×
235
  } else if (clampBegin) {
×
236
    auto found = std::lower_bound(begin, end, pattern);
237

238
    if (found == end) {
×
239
      return {};
×
240
    }
241

242
    auto foundName = found->Name();
×
243

244
    if (foundName.starts_with(pattern)) {
×
245
      return {*found, foundName};
246
    }
247

248
    return {};
×
249
  } else if (clampEnd) {
×
250
    for (auto p = begin; p != end; p++) {
×
251
      auto foundName = p->Name();
×
252
      if (foundName.ends_with(pattern)) {
×
253
        return {*p, foundName};
×
254
      }
255
    }
256

257
    return {};
×
258
  }
259

260
  for (auto p = begin; p != end; p++) {
×
261
    auto foundName = p->Name();
×
262
    if (foundName.find(pattern) != foundName.npos) {
×
263
      return {*p, foundName};
×
264
    }
265
  }
266

267
  return {};
×
268
}
269

270
bool operator<(const HybridLeaf *leaf, std::string_view sw) {
18,745✔
271
  return leaf->Name() < sw;
18,745✔
272
}
273

274
bool operator<(const ZipEntryLeaf *leaf, std::string_view sw) {
×
275
  return leaf->operator<(sw);
10,798✔
276
}
277

278
ZipEntry Cache::RequestFile(std::string_view path) {
1,738✔
279
  AFileInfo pp(path);
1,738✔
280
  auto parts = pp.Explode();
1,738✔
281

282
  auto find = [&](auto children, size_t level) {
8,619✔
283
    auto found =
284
        std::lower_bound(children.begin(), children.end(), parts.at(level));
8,619✔
285

286
    if (es::IsEnd(children, found) || (*found)->Name() != parts.at(level)) {
17,238✔
287
      static const typename decltype(children)::value_type null{};
288
      return std::ref(null);
×
289
    }
290

291
    return std::ref(*found);
292
  };
1,738✔
293

6,881✔
294
  if (parts.size() > Header().numLevels) {
295
    return {};
6,881✔
296
  } else if (parts.size() == 1) {
297
    auto rootFinals = Header().root->Finals();
13,762✔
298
    auto foundFinal = find(rootFinals, parts.size() - 1);
299

×
300
    if (!foundFinal.get()) {
301
      return {};
302
    }
303

304
    return *foundFinal.get();
1,738✔
305
  }
306

1,738✔
307
  auto rootChildren = Header().root->Children();
308
  auto foundLevel = find(rootChildren, 0);
3,476✔
309

310
  if (!foundLevel.get()) {
×
311
    return {};
312
  }
313

314
  for (size_t l = 1; l < parts.size() - 1; l++) {
315
    foundLevel = find(foundLevel.get()->Children(), l);
316

1,738✔
317
    if (!foundLevel.get()) {
×
318
      return {};
1,738✔
319
    }
320
  }
7✔
321

322
  auto foundFinal = find(foundLevel.get()->Finals(), parts.size() - 1);
323

×
324
  if (!foundFinal.get()) {
325
    return {};
326
  }
7✔
327

328
  return *foundFinal.get();
329
}
330

1,731✔
331
struct ZIPIOEntryRawIterator_impl : ZIPIOEntryRawIterator {
332
  ZIPIOEntryRawIterator_impl(const Cache &map, ZIPIOEntryType type_)
333
      : base(&map), type(type_), current(map.Header().entries),
×
334
        end(current + map.Header().numFiles) {}
335

336
  void Make(std::string &buff, const HybridLeaf *parent) const {
6,881✔
337
    if (parent) {
5,150✔
338
      Make(buff, parent->parent);
339
      auto parentName = parent->Name();
340
      buff.append(parentName.data(), parentName.size());
×
341
      if (parent->parent) {
342
        buff.push_back('/');
343
      }
344
    }
1,731✔
345
  }
346

347
  ZIPIOEntry Make() const {
×
348
    if (type == ZIPIOEntryType::View) {
349
      return {*current, current->Name()};
350
    }
1,731✔
351

352
    std::string fullPath;
353
    fullPath.reserve(current->totalFileNameSize);
354
    Make(fullPath, current->parent);
355
    fullPath.append(current->Name());
×
356

×
357
    return {*current, fullPath};
358
  }
×
359

×
360
  ZIPIOEntry Fist() const override {
×
361
    if (current == end) {
362
      return {};
×
363
    }
364
    return Make();
×
365
  }
366

367
  ZIPIOEntry Next() const override {
368
    if (current == end) {
369
      return {};
×
370
    }
×
371
    current++;
×
372
    if (current == end) {
373
      return {};
374
    }
375

×
376
    return Make();
×
377
  }
×
378
  size_t Count() const override { return base->Header().numFiles; }
379

×
380
  const Cache *base;
381
  ZIPIOEntryType type;
382
  mutable const ZipEntryLeaf *current;
×
383
  const ZipEntryLeaf *end;
×
384
};
×
385

386
std::unique_ptr<ZIPIOEntryRawIterator> Cache::Iter(ZIPIOEntryType type) const {
×
387
  return std::make_unique<ZIPIOEntryRawIterator_impl>(*this, type);
388
}
389

×
390
#ifndef NDEBUG
×
391
#include "spike/master_printer.hpp"
×
392
#include <cstring>
393

×
394
void Dump(const HybridLeaf *leaf, char *buffer, const char *wholeBuffer) {
×
395
  buffer[0] = '/';
×
396
  buffer++;
397
  for (auto &f : leaf->Finals()) {
398
    std::string_view sw(wholeBuffer, buffer);
×
399
    printinfo(sw << f->Name());
400
  }
×
401

402
  for (auto &c : leaf->Children()) {
403
    auto name = c->Name();
404
    memcpy(buffer, name.data(), name.size());
405
    char *newBuffer = buffer + name.size();
406
    Dump(c, newBuffer, wholeBuffer);
407
  }
408
}
×
409

×
410
void DumpEntries(const CacheHeader &cc) {
411
  const ZipEntryLeaf *begin = cc.entries;
412
  auto end = begin + cc.numFiles;
413

414
  while (begin < end) {
415
    printinfo(begin->Name());
416
    begin++;
417
  }
418
}
419
#endif
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