Coverage Report

Created: 2026-07-20 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libqxp/src/lib/libqxp_utils.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 libqxp 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 "libqxp_utils.h"
11
12
#include <unicode/ucnv.h>
13
#include <unicode/utypes.h>
14
15
#ifdef DEBUG
16
#include <cstdarg>
17
#include <cstdio>
18
#endif
19
20
#include <boost/math/constants/constants.hpp>
21
22
using std::string;
23
24
namespace libqxp
25
{
26
27
namespace
28
{
29
30
void checkStream(librevenge::RVNGInputStream *const input)
31
49.4M
{
32
49.4M
  if (!input || input->isEnd())
33
21.9k
    throw EndOfStreamException();
34
49.4M
}
35
36
struct SeekFailedException {};
37
38
static void _appendUCS4(librevenge::RVNGString &text, unsigned ucs4Character)
39
779k
{
40
779k
  unsigned char first;
41
779k
  int len;
42
779k
  if (ucs4Character < 0x80)
43
594k
  {
44
594k
    first = 0;
45
594k
    len = 1;
46
594k
  }
47
184k
  else if (ucs4Character < 0x800)
48
175k
  {
49
175k
    first = 0xc0;
50
175k
    len = 2;
51
175k
  }
52
9.26k
  else if (ucs4Character < 0x10000)
53
9.26k
  {
54
9.26k
    first = 0xe0;
55
9.26k
    len = 3;
56
9.26k
  }
57
0
  else if (ucs4Character < 0x200000)
58
0
  {
59
0
    first = 0xf0;
60
0
    len = 4;
61
0
  }
62
0
  else if (ucs4Character < 0x4000000)
63
0
  {
64
0
    first = 0xf8;
65
0
    len = 5;
66
0
  }
67
0
  else
68
0
  {
69
0
    first = 0xfc;
70
0
    len = 6;
71
0
  }
72
73
779k
  unsigned char outbuf[6] = { 0, 0, 0, 0, 0, 0 };
74
779k
  int i;
75
972k
  for (i = len - 1; i > 0; --i)
76
193k
  {
77
193k
    outbuf[i] = (ucs4Character & 0x3f) | 0x80;
78
193k
    ucs4Character >>= 6;
79
193k
  }
80
779k
  outbuf[0] = (ucs4Character & 0xff) | first;
81
82
1.75M
  for (i = 0; i < len; i++)
83
972k
    text.append(char(outbuf[i]));
84
779k
}
85
86
87
}
88
89
#ifdef DEBUG
90
void debugPrint(const char *format, ...)
91
{
92
  va_list args;
93
  va_start(args, format);
94
  std::vfprintf(stderr, format, args);
95
  va_end(args);
96
}
97
#endif
98
99
uint8_t readU8(librevenge::RVNGInputStream *input, bool /* bigEndian */)
100
19.7M
{
101
19.7M
  checkStream(input);
102
103
19.7M
  unsigned long numBytesRead;
104
19.7M
  uint8_t const *p = input->read(sizeof(uint8_t), numBytesRead);
105
106
19.7M
  if (p && numBytesRead == sizeof(uint8_t))
107
19.7M
    return *(uint8_t const *)(p);
108
974
  throw EndOfStreamException();
109
19.7M
}
110
111
uint16_t readU16(librevenge::RVNGInputStream *input, bool bigEndian)
112
9.47M
{
113
9.47M
  checkStream(input);
114
115
9.47M
  unsigned long numBytesRead;
116
9.47M
  uint8_t const *p = input->read(sizeof(uint16_t), numBytesRead);
117
118
9.47M
  if (p && numBytesRead == sizeof(uint16_t))
119
9.47M
  {
120
9.47M
    if (bigEndian)
121
8.05M
      return static_cast<uint16_t>((uint16_t)p[1]|((uint16_t)p[0]<<8));
122
1.42M
    return static_cast<uint16_t>((uint16_t)p[0]|((uint16_t)p[1]<<8));
123
9.47M
  }
124
1.40k
  throw EndOfStreamException();
125
9.47M
}
126
127
uint32_t readU32(librevenge::RVNGInputStream *input, bool bigEndian)
128
13.5M
{
129
13.5M
  checkStream(input);
130
131
13.5M
  unsigned long numBytesRead;
132
13.5M
  uint8_t const *p = input->read(sizeof(uint32_t), numBytesRead);
133
134
13.5M
  if (p && numBytesRead == sizeof(uint32_t))
135
13.5M
  {
136
13.5M
    if (bigEndian)
137
10.6M
      return (uint32_t)p[3]|((uint32_t)p[2]<<8)|((uint32_t)p[1]<<16)|((uint32_t)p[0]<<24);
138
2.90M
    return (uint32_t)p[0]|((uint32_t)p[1]<<8)|((uint32_t)p[2]<<16)|((uint32_t)p[3]<<24);
139
13.5M
  }
140
3.49k
  throw EndOfStreamException();
141
13.5M
}
142
143
uint64_t readU64(librevenge::RVNGInputStream *input, bool bigEndian)
144
0
{
145
0
  checkStream(input);
146
147
0
  unsigned long numBytesRead;
148
0
  uint8_t const *p = input->read(sizeof(uint64_t), numBytesRead);
149
150
0
  if (p && numBytesRead == sizeof(uint64_t))
151
0
  {
152
0
    if (bigEndian)
153
0
      return (uint64_t)p[7]|((uint64_t)p[6]<<8)|((uint64_t)p[5]<<16)|((uint64_t)p[4]<<24)|((uint64_t)p[3]<<32)|((uint64_t)p[2]<<40)|((uint64_t)p[1]<<48)|((uint64_t)p[0]<<56);
154
0
    return (uint64_t)p[0]|((uint64_t)p[1]<<8)|((uint64_t)p[2]<<16)|((uint64_t)p[3]<<24)|((uint64_t)p[4]<<32)|((uint64_t)p[5]<<40)|((uint64_t)p[6]<<48)|((uint64_t)p[7]<<56);
155
0
  }
156
0
  throw EndOfStreamException();
157
0
}
158
159
int16_t readS16(librevenge::RVNGInputStream *input, bool bigEndian)
160
1.26M
{
161
1.26M
  return int16_t(readU16(input, bigEndian));
162
1.26M
}
163
164
int32_t readS32(librevenge::RVNGInputStream *input, bool bigEndian)
165
4.84M
{
166
4.84M
  return int32_t(readU32(input, bigEndian));
167
4.84M
}
168
169
double readFloat16(librevenge::RVNGInputStream *input, bool bigEndian)
170
36.5k
{
171
36.5k
  return readU16(input, bigEndian) / double(0x10000);
172
36.5k
}
173
174
double readFraction(librevenge::RVNGInputStream *input, bool bigEndian)
175
4.79M
{
176
4.79M
  int32_t num = readS32(input, bigEndian);
177
4.79M
  return (num >> 16) + ((num & 0xffff) / double(0x10000));
178
4.79M
}
179
180
const unsigned char *readNBytes(librevenge::RVNGInputStream *const input, const unsigned long numBytes)
181
2.43k
{
182
2.43k
  checkStream(input);
183
184
2.43k
  unsigned long readBytes = 0;
185
2.43k
  const unsigned char *const s = input->read(numBytes, readBytes);
186
187
2.43k
  if (numBytes != readBytes)
188
0
    throw EndOfStreamException();
189
190
2.43k
  return s;
191
2.43k
}
192
193
string readCString(librevenge::RVNGInputStream *input)
194
1.75M
{
195
1.75M
  checkStream(input);
196
197
1.75M
  string str;
198
1.75M
  unsigned char c = readU8(input);
199
3.90M
  while (0 != c)
200
2.14M
  {
201
2.14M
    str.push_back(c);
202
2.14M
    c = readU8(input);
203
2.14M
  }
204
205
1.75M
  return str;
206
1.75M
}
207
208
string readPascalString(librevenge::RVNGInputStream *input)
209
766k
{
210
766k
  checkStream(input);
211
212
766k
  const unsigned length = readU8(input);
213
214
766k
  return readString(input, length);
215
766k
}
216
217
std::string readString(librevenge::RVNGInputStream *input, const unsigned length)
218
791k
{
219
791k
  checkStream(input);
220
221
791k
  string str;
222
791k
  str.reserve(length);
223
10.8M
  for (unsigned i = 0; length != i; ++i)
224
10.0M
    str.push_back(readU8(input));
225
226
791k
  return str;
227
791k
}
228
229
std::string readPlatformString(librevenge::RVNGInputStream *input, bool bigEndian)
230
2.52M
{
231
2.52M
  return bigEndian ? readPascalString(input) : readCString(input);
232
2.52M
}
233
234
void skip(librevenge::RVNGInputStream *input, unsigned long numBytes)
235
3.37M
{
236
3.37M
  checkStream(input);
237
238
3.37M
  seekRelative(input, static_cast<long>(numBytes));
239
3.37M
}
240
241
void seek(librevenge::RVNGInputStream *const input, const unsigned long pos)
242
544k
{
243
544k
  if (!input)
244
0
    throw EndOfStreamException();
245
246
544k
  if (0 != input->seek(static_cast<long>(pos), librevenge::RVNG_SEEK_SET))
247
449
    throw SeekFailedException();
248
544k
}
249
250
void seekRelative(librevenge::RVNGInputStream *const input, const long pos)
251
3.35M
{
252
3.35M
  if (!input)
253
0
    throw EndOfStreamException();
254
255
3.35M
  if (0 != input->seek(pos, librevenge::RVNG_SEEK_CUR))
256
3.65k
    throw SeekFailedException();
257
3.35M
}
258
259
unsigned long getRemainingLength(librevenge::RVNGInputStream *const input)
260
314k
{
261
314k
  if (!input || input->tell() < 0)
262
1.52k
    throw SeekFailedException();
263
264
313k
  const unsigned long begin = input->tell();
265
313k
  unsigned long end = begin;
266
267
313k
  if (0 == input->seek(0, librevenge::RVNG_SEEK_END))
268
313k
    end = input->tell();
269
0
  else
270
0
  {
271
    // librevenge::RVNG_SEEK_END does not work. Use the harder way.
272
0
    while (!input->isEnd())
273
0
    {
274
0
      readU8(input);
275
0
      ++end;
276
0
    }
277
0
  }
278
279
313k
  seek(input, begin);
280
281
313k
  return end - begin;
282
314k
}
283
284
uint8_t readU8(const std::shared_ptr<librevenge::RVNGInputStream> input, bool)
285
5.03M
{
286
5.03M
  return readU8(input.get());
287
5.03M
}
288
289
uint16_t readU16(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian)
290
8.17M
{
291
8.17M
  return readU16(input.get(), bigEndian);
292
8.17M
}
293
294
uint32_t readU32(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian)
295
8.74M
{
296
8.74M
  return readU32(input.get(), bigEndian);
297
8.74M
}
298
299
uint64_t readU64(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian)
300
0
{
301
0
  return readU64(input.get(), bigEndian);
302
0
}
303
304
int16_t readS16(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
305
1.26M
{
306
1.26M
  return readS16(input.get(), bigEndian);
307
1.26M
}
308
309
int32_t readS32(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
310
50.9k
{
311
50.9k
  return readS32(input.get(), bigEndian);
312
50.9k
}
313
314
double readFloat16(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
315
36.5k
{
316
36.5k
  return readFloat16(input.get(), bigEndian);
317
36.5k
}
318
319
double readFraction(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
320
4.79M
{
321
4.79M
  return readFraction(input.get(), bigEndian);
322
4.79M
}
323
324
const unsigned char *readNBytes(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes)
325
2.43k
{
326
2.43k
  return readNBytes(input.get(), numBytes);
327
2.43k
}
328
329
std::string readCString(const std::shared_ptr<librevenge::RVNGInputStream> input)
330
0
{
331
0
  return readCString(input.get());
332
0
}
333
334
std::string readPascalString(const std::shared_ptr<librevenge::RVNGInputStream> input)
335
0
{
336
0
  return readPascalString(input.get());
337
0
}
338
339
std::string readString(std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned length)
340
24.5k
{
341
24.5k
  return readString(input.get(), length);
342
24.5k
}
343
344
std::string readPlatformString(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
345
2.52M
{
346
2.52M
  return readPlatformString(input.get(), bigEndian);
347
2.52M
}
348
349
void skip(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes)
350
3.37M
{
351
3.37M
  return skip(input.get(), numBytes);
352
3.37M
}
353
354
void seek(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long pos)
355
202k
{
356
202k
  seek(input.get(), pos);
357
202k
}
358
359
void seekRelative(const std::shared_ptr<librevenge::RVNGInputStream> input, const long pos)
360
6.97k
{
361
6.97k
  seekRelative(input.get(), pos);
362
6.97k
}
363
364
unsigned long getRemainingLength(const std::shared_ptr<librevenge::RVNGInputStream> &input)
365
300k
{
366
300k
  return getRemainingLength(input.get());
367
300k
}
368
369
EndOfStreamException::EndOfStreamException()
370
24.2k
{
371
24.2k
  QXP_DEBUG_MSG(("Throwing EndOfStreamException\n"));
372
24.2k
}
373
374
double deg2rad(double value)
375
455k
{
376
455k
  using namespace boost::math::double_constants;
377
378
455k
  return normalizeDegAngle(value) * degree;
379
455k
}
380
381
double normalizeRadAngle(double radAngle)
382
0
{
383
0
  using namespace boost::math::double_constants;
384
0
  radAngle = std::fmod(radAngle, two_pi);
385
0
  if (radAngle < 0)
386
0
    radAngle += two_pi;
387
0
  return radAngle;
388
0
}
389
390
double normalizeDegAngle(double degAngle)
391
476k
{
392
476k
  degAngle = std::fmod(degAngle, 360.0);
393
476k
  if (degAngle < 0.0)
394
392k
    degAngle += 360.0;
395
476k
  return degAngle;
396
476k
}
397
398
void appendCharacters(librevenge::RVNGString &text, const char *characters, const size_t size,
399
                      const char *encoding)
400
27.3k
{
401
27.3k
  if (size == 0)
402
15.6k
  {
403
15.6k
    QXP_DEBUG_MSG(("Attempt to append 0 characters!"));
404
15.6k
    return;
405
15.6k
  }
406
407
11.6k
  UErrorCode status = U_ZERO_ERROR;
408
11.6k
  std::unique_ptr<UConverter, void(*)(UConverter *)> conv(ucnv_open(encoding, &status), ucnv_close);
409
11.6k
  if (U_SUCCESS(status))
410
11.6k
  {
411
    // ICU documentation claims that character-by-character processing is faster "for small amounts of data" and "'normal' charsets"
412
    // (in any case, it is more convenient :) )
413
11.6k
    const char *src = &characters[0];
414
11.6k
    const char *srcLimit = (const char *)src + size;
415
790k
    while (src < srcLimit)
416
779k
    {
417
779k
      uint32_t ucs4Character = (uint32_t)ucnv_getNextUChar(conv.get(), &src, srcLimit, &status);
418
779k
      if (U_SUCCESS(status))
419
779k
      {
420
779k
        _appendUCS4(text, ucs4Character);
421
779k
      }
422
779k
    }
423
11.6k
  }
424
11.6k
}
425
426
}
427
428
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */