Coverage Report

Created: 2026-03-12 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libe-book/src/lib/SoftBookResourceDir.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/*
3
 * This file is part of the libe-book project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#include <cassert>
11
#include <string>
12
#include <unordered_map>
13
14
#include <boost/optional.hpp>
15
16
#include "libebook_utils.h"
17
#include "EBOOKMemoryStream.h"
18
#include "SoftBookHeader.h"
19
#include "SoftBookResourceDir.h"
20
21
using boost::optional;
22
using std::shared_ptr;
23
24
using std::string;
25
26
namespace libebook
27
{
28
29
namespace
30
{
31
32
template<class Selector>
33
class ResourceStream : public librevenge::RVNGInputStream
34
{
35
public:
36
  ResourceStream(shared_ptr<librevenge::RVNGInputStream> strm, shared_ptr<SoftBookResourceDirImpl> resourceDir);
37
38
  bool isStructured() override;
39
  unsigned subStreamCount() override;
40
  const char *subStreamName(unsigned id) override;
41
  bool existsSubStream(const char *name) override;
42
  librevenge::RVNGInputStream *getSubStreamByName(const char *name) override;
43
  RVNGInputStream *getSubStreamById(unsigned id) override;
44
45
  const unsigned char *read(unsigned long numBytes, unsigned long &numBytesRead) override;
46
  int seek(long offset, librevenge::RVNG_SEEK_TYPE seekType) override;
47
  long tell() override;
48
  bool isEnd() override;
49
50
private:
51
  const shared_ptr<librevenge::RVNGInputStream> m_stream;
52
  const shared_ptr<SoftBookResourceDirImpl> m_resourceDir;
53
};
54
55
struct NameSelector
56
{
57
  static librevenge::RVNGInputStream *getStream(shared_ptr<SoftBookResourceDirImpl> resourceDir, const char *name);
58
};
59
60
struct TypeSelector
61
{
62
  static librevenge::RVNGInputStream *getStream(shared_ptr<SoftBookResourceDirImpl> resourceDir, const char *name);
63
};
64
65
}
66
67
class SoftBookResourceDirImpl
68
{
69
  // -Weffc++
70
  SoftBookResourceDirImpl(const SoftBookResourceDirImpl &other);
71
  SoftBookResourceDirImpl &operator=(const SoftBookResourceDirImpl &other);
72
73
  struct ResourceInfo
74
  {
75
    ResourceInfo();
76
77
    unsigned offset;
78
    unsigned length;
79
    optional<string> type;
80
  };
81
82
  typedef std::unordered_map<string, ResourceInfo> ResourceMap_t;
83
  typedef std::unordered_map<string, ResourceMap_t::const_iterator> TypeMap_t;
84
85
public:
86
  SoftBookResourceDirImpl(librevenge::RVNGInputStream *input, unsigned files, unsigned version);
87
88
  librevenge::RVNGInputStream *getDirStream() const;
89
90
  librevenge::RVNGInputStream *getResourceByName(const char *name) const;
91
  librevenge::RVNGInputStream *getResourceByType(const char *type) const;
92
93
private:
94
  ResourceMap_t::const_iterator findResourceByType(const char *type) const;
95
96
  librevenge::RVNGInputStream *createStream(const ResourceInfo &info) const;
97
98
private:
99
  librevenge::RVNGInputStream *m_stream;
100
  unsigned m_start;
101
  unsigned m_length;
102
  mutable ResourceMap_t m_resourceMap;
103
  mutable TypeMap_t m_typeMap;
104
};
105
106
namespace
107
{
108
109
string readFileType(librevenge::RVNGInputStream *const input)
110
580k
{
111
580k
  const unsigned char *const data = readNBytes(input, 4);
112
580k
  const string fileName(data, data + ((0 == data[3]) ? 3 : 4));
113
580k
  return fileName;
114
580k
}
115
116
librevenge::RVNGInputStream *NameSelector::getStream(const shared_ptr<SoftBookResourceDirImpl> resourceDir, const char *const name)
117
0
{
118
0
  return resourceDir->getResourceByName(name);
119
0
}
120
121
librevenge::RVNGInputStream *TypeSelector::getStream(const shared_ptr<SoftBookResourceDirImpl> resourceDir, const char *const name)
122
227
{
123
227
  return resourceDir->getResourceByType(name);
124
227
}
125
126
template<class Selector>
127
ResourceStream<Selector>::ResourceStream(const shared_ptr<librevenge::RVNGInputStream> strm, const shared_ptr<SoftBookResourceDirImpl> resourceDir)
128
226
  : m_stream(strm)
129
226
  , m_resourceDir(resourceDir)
130
226
{
131
226
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::ResourceStream(std::__1::shared_ptr<librevenge::RVNGInputStream>, std::__1::shared_ptr<libebook::SoftBookResourceDirImpl>)
SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::ResourceStream(std::__1::shared_ptr<librevenge::RVNGInputStream>, std::__1::shared_ptr<libebook::SoftBookResourceDirImpl>)
Line
Count
Source
128
226
  : m_stream(strm)
129
226
  , m_resourceDir(resourceDir)
130
226
{
131
226
}
132
133
template<class Selector>
134
bool ResourceStream<Selector>::isStructured()
135
0
{
136
0
  return true;
137
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::isStructured()
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::isStructured()
138
139
template<class Selector>
140
unsigned ResourceStream<Selector>::subStreamCount()
141
0
{
142
  // TODO: implement me
143
0
  return 0;
144
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::subStreamCount()
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::subStreamCount()
145
146
template<class Selector>
147
const char *ResourceStream<Selector>::subStreamName(const unsigned id)
148
0
{
149
  // TODO: implement me
150
0
  (void) id;
151
0
  return nullptr;
152
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::subStreamName(unsigned int)
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::subStreamName(unsigned int)
153
154
template<class Selector>
155
bool ResourceStream<Selector>::existsSubStream(const char *const name)
156
0
{
157
  // TODO: implement me
158
0
  (void) name;
159
0
  return true;
160
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::existsSubStream(char const*)
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::existsSubStream(char const*)
161
162
template<class Selector>
163
librevenge::RVNGInputStream *ResourceStream<Selector>::getSubStreamByName(const char *const name)
164
227
{
165
227
  return Selector::getStream(m_resourceDir, name);
166
227
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::getSubStreamByName(char const*)
SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::getSubStreamByName(char const*)
Line
Count
Source
164
227
{
165
227
  return Selector::getStream(m_resourceDir, name);
166
227
}
167
168
template<class Selector>
169
librevenge::RVNGInputStream *ResourceStream<Selector>::getSubStreamById(const unsigned id)
170
0
{
171
  // TODO: implement me
172
0
  (void) id;
173
0
  return nullptr;
174
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::getSubStreamById(unsigned int)
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::getSubStreamById(unsigned int)
175
176
template<class Selector>
177
const unsigned char *ResourceStream<Selector>::read(const unsigned long numBytes, unsigned long &numBytesRead)
178
0
{
179
0
  return m_stream->read(numBytes, numBytesRead);
180
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::read(unsigned long, unsigned long&)
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::read(unsigned long, unsigned long&)
181
182
template<class Selector>
183
int ResourceStream<Selector>::seek(const long offset, const librevenge::RVNG_SEEK_TYPE seekType)
184
0
{
185
0
  return m_stream->seek(offset, seekType);
186
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::seek(long, librevenge::RVNG_SEEK_TYPE)
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::seek(long, librevenge::RVNG_SEEK_TYPE)
187
188
template<class Selector>
189
long ResourceStream<Selector>::tell()
190
0
{
191
0
  return m_stream->tell();
192
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::tell()
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::tell()
193
194
template<class Selector>
195
bool ResourceStream<Selector>::isEnd()
196
0
{
197
0
  return m_stream->isEnd();
198
0
}
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::NameSelector>::isEnd()
Unexecuted instantiation: SoftBookResourceDir.cpp:libebook::(anonymous namespace)::ResourceStream<libebook::(anonymous namespace)::TypeSelector>::isEnd()
199
200
}
201
202
SoftBookResourceDir::SoftBookResourceDir(librevenge::RVNGInputStream *const input, const SoftBookHeader &header)
203
947
  : m_impl()
204
947
{
205
947
  input->seek((long) header.getTOCOffset(), librevenge::RVNG_SEEK_SET);
206
947
  m_impl.reset(new SoftBookResourceDirImpl(input, header.getFileCount(), header.getVersion()));
207
947
}
208
209
shared_ptr<librevenge::RVNGInputStream> SoftBookResourceDir::getNameStream() const
210
0
{
211
0
  const shared_ptr<librevenge::RVNGInputStream> strm(m_impl->getDirStream());
212
0
  const shared_ptr<librevenge::RVNGInputStream> resource(new ResourceStream<NameSelector>(strm, m_impl));
213
0
  return resource;
214
0
}
215
216
shared_ptr<librevenge::RVNGInputStream> SoftBookResourceDir::getTypeStream() const
217
298
{
218
298
  const shared_ptr<librevenge::RVNGInputStream> strm(m_impl->getDirStream());
219
298
  const shared_ptr<librevenge::RVNGInputStream> resource(new ResourceStream<TypeSelector>(strm, m_impl));
220
298
  return resource;
221
298
}
222
223
SoftBookResourceDirImpl::ResourceInfo::ResourceInfo()
224
336k
  : offset(0)
225
336k
  , length(0)
226
336k
  , type()
227
336k
{
228
336k
}
229
230
SoftBookResourceDirImpl::SoftBookResourceDirImpl(librevenge::RVNGInputStream *const input, const unsigned files, const unsigned version)
231
947
  : m_stream(input)
232
947
  , m_start(0)
233
947
  , m_length(0)
234
947
  , m_resourceMap()
235
947
  , m_typeMap()
236
947
{
237
947
  m_start = static_cast<unsigned>(input->tell());
238
947
  unsigned headerLength = 0;
239
240
947
  switch (version)
241
947
  {
242
251
  case 1 :
243
251
    headerLength = 10;
244
251
    break;
245
696
  case 2 :
246
696
    headerLength = 20;
247
696
    break;
248
0
  default :
249
0
    EBOOK_DEBUG_MSG(("unknown version %d\n", version));
250
0
    throw GenericException();
251
947
  }
252
253
947
  const unsigned tocLength = headerLength * files;
254
947
  unsigned fileOffset = m_start + tocLength;
255
947
  m_length = tocLength;
256
257
337k
  for (unsigned i = 0; i != files; ++i)
258
336k
  {
259
336k
    const string fileName = readFileType(input);
260
336k
    ResourceInfo info;
261
262
336k
    switch (version)
263
336k
    {
264
92.9k
    case 1 :
265
92.9k
      skip(input, 2);
266
92.9k
      info.length = readU32(input, true);
267
92.9k
      break;
268
243k
    case 2 :
269
243k
      skip(input, 4);
270
243k
      info.length = readU32(input, true);
271
243k
      info.type = readFileType(input);
272
243k
      skip(input, 4);
273
243k
      break;
274
0
    default :
275
0
      throw GenericException();
276
336k
    }
277
278
336k
    info.length += headerLength;
279
336k
    info.offset = fileOffset;
280
336k
    fileOffset += info.length;
281
336k
    m_length += info.length;
282
283
336k
    const ResourceMap_t::const_iterator it = m_resourceMap.insert(ResourceMap_t::value_type(fileName, info)).first;
284
336k
    if (info.type)
285
243k
      m_typeMap.insert(TypeMap_t::value_type(get(info.type), it));
286
336k
  }
287
947
}
288
289
librevenge::RVNGInputStream *SoftBookResourceDirImpl::getDirStream() const
290
298
{
291
298
  m_stream->seek((long) m_start, librevenge::RVNG_SEEK_SET);
292
298
  const unsigned char *const data = readNBytes(m_stream, m_length);
293
298
  return new EBOOKMemoryStream(data, m_length);
294
298
}
295
296
librevenge::RVNGInputStream *SoftBookResourceDirImpl::getResourceByName(const char *const name) const
297
0
{
298
0
  librevenge::RVNGInputStream *resource = nullptr;
299
300
0
  ResourceMap_t::const_iterator it = m_resourceMap.find(name);
301
0
  if (m_resourceMap.end() != it)
302
0
    resource = createStream(it->second);
303
304
0
  return resource;
305
0
}
306
307
librevenge::RVNGInputStream *SoftBookResourceDirImpl::getResourceByType(const char *const type) const
308
227
{
309
227
  TypeMap_t::const_iterator it = m_typeMap.find(type);
310
227
  if (m_typeMap.end() == it)
311
60
  {
312
60
    const ResourceMap_t::const_iterator resIt = findResourceByType(type);
313
60
    it = m_typeMap.insert(TypeMap_t::value_type(type, resIt)).first;
314
60
  }
315
227
  assert(m_typeMap.end() != it);
316
317
227
  librevenge::RVNGInputStream *resource = nullptr;
318
227
  if (m_resourceMap.end() != it->second)
319
167
    resource = createStream(it->second->second);
320
321
227
  return resource;
322
227
}
323
324
SoftBookResourceDirImpl::ResourceMap_t::const_iterator SoftBookResourceDirImpl::findResourceByType(const char *const type) const
325
60
{
326
60
  auto it = m_resourceMap.begin();
327
328
190
  for (; m_resourceMap.end() != it; ++it)
329
130
  {
330
130
    ResourceInfo &info = it->second;
331
130
    if (!info.type)
332
28
    {
333
      // sniff the content
334
28
      m_stream->seek((long) info.offset, librevenge::RVNG_SEEK_SET);
335
28
      if (1 == readU16(m_stream))
336
3
        info.type = readFileType(m_stream);
337
25
      else
338
25
        info.type = string();
339
28
    }
340
341
130
    assert(info.type);
342
343
130
    if (type == get(info.type))
344
0
      break;
345
130
  }
346
347
60
  return it;
348
60
}
349
350
librevenge::RVNGInputStream *SoftBookResourceDirImpl::createStream(const ResourceInfo &info) const
351
167
{
352
167
  m_stream->seek((long) info.offset, librevenge::RVNG_SEEK_SET);
353
167
  const unsigned char *const data = readNBytes(m_stream, info.length);
354
167
  return new EBOOKMemoryStream(data, info.length);
355
167
}
356
357
}
358
359
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */