Coverage Report

Created: 2026-10-28 06:34

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
16.1M
{
32
16.1M
  if (!input || input->isEnd())
33
8.80k
    throw EndOfStreamException();
34
16.1M
}
35
36
struct SeekFailedException {};
37
38
static void _appendUCS4(librevenge::RVNGString &text, unsigned ucs4Character)
39
883k
{
40
883k
  unsigned char first;
41
883k
  int len;
42
883k
  if (ucs4Character < 0x80)
43
814k
  {
44
814k
    first = 0;
45
814k
    len = 1;
46
814k
  }
47
69.0k
  else if (ucs4Character < 0x800)
48
53.8k
  {
49
53.8k
    first = 0xc0;
50
53.8k
    len = 2;
51
53.8k
  }
52
15.2k
  else if (ucs4Character < 0x10000)
53
15.2k
  {
54
15.2k
    first = 0xe0;
55
15.2k
    len = 3;
56
15.2k
  }
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
883k
  unsigned char outbuf[6] = { 0, 0, 0, 0, 0, 0 };
74
883k
  int i;
75
967k
  for (i = len - 1; i > 0; --i)
76
84.2k
  {
77
84.2k
    outbuf[i] = (ucs4Character & 0x3f) | 0x80;
78
84.2k
    ucs4Character >>= 6;
79
84.2k
  }
80
883k
  outbuf[0] = (ucs4Character & 0xff) | first;
81
82
1.85M
  for (i = 0; i < len; i++)
83
967k
    text.append(char(outbuf[i]));
84
883k
}
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
5.03M
{
101
5.03M
  checkStream(input);
102
103
5.03M
  unsigned long numBytesRead;
104
5.03M
  uint8_t const *p = input->read(sizeof(uint8_t), numBytesRead);
105
106
5.03M
  if (p && numBytesRead == sizeof(uint8_t))
107
5.03M
    return *(uint8_t const *)(p);
108
699
  throw EndOfStreamException();
109
5.03M
}
110
111
uint16_t readU16(librevenge::RVNGInputStream *input, bool bigEndian)
112
3.68M
{
113
3.68M
  checkStream(input);
114
115
3.68M
  unsigned long numBytesRead;
116
3.68M
  uint8_t const *p = input->read(sizeof(uint16_t), numBytesRead);
117
118
3.68M
  if (p && numBytesRead == sizeof(uint16_t))
119
3.68M
  {
120
3.68M
    if (bigEndian)
121
2.92M
      return static_cast<uint16_t>((uint16_t)p[1]|((uint16_t)p[0]<<8));
122
759k
    return static_cast<uint16_t>((uint16_t)p[0]|((uint16_t)p[1]<<8));
123
3.68M
  }
124
652
  throw EndOfStreamException();
125
3.68M
}
126
127
uint32_t readU32(librevenge::RVNGInputStream *input, bool bigEndian)
128
5.16M
{
129
5.16M
  checkStream(input);
130
131
5.16M
  unsigned long numBytesRead;
132
5.16M
  uint8_t const *p = input->read(sizeof(uint32_t), numBytesRead);
133
134
5.16M
  if (p && numBytesRead == sizeof(uint32_t))
135
5.16M
  {
136
5.16M
    if (bigEndian)
137
4.30M
      return (uint32_t)p[3]|((uint32_t)p[2]<<8)|((uint32_t)p[1]<<16)|((uint32_t)p[0]<<24);
138
857k
    return (uint32_t)p[0]|((uint32_t)p[1]<<8)|((uint32_t)p[2]<<16)|((uint32_t)p[3]<<24);
139
5.16M
  }
140
2.86k
  throw EndOfStreamException();
141
5.16M
}
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
517k
{
161
517k
  return int16_t(readU16(input, bigEndian));
162
517k
}
163
164
int32_t readS32(librevenge::RVNGInputStream *input, bool bigEndian)
165
1.66M
{
166
1.66M
  return int32_t(readU32(input, bigEndian));
167
1.66M
}
168
169
double readFloat16(librevenge::RVNGInputStream *input, bool bigEndian)
170
39.2k
{
171
39.2k
  return readU16(input, bigEndian) / double(0x10000);
172
39.2k
}
173
174
double readFraction(librevenge::RVNGInputStream *input, bool bigEndian)
175
1.60M
{
176
1.60M
  int32_t num = readS32(input, bigEndian);
177
1.60M
  return (num >> 16) + ((num & 0xffff) / double(0x10000));
178
1.60M
}
179
180
const unsigned char *readNBytes(librevenge::RVNGInputStream *const input, const unsigned long numBytes)
181
1.69k
{
182
1.69k
  checkStream(input);
183
184
1.69k
  unsigned long readBytes = 0;
185
1.69k
  const unsigned char *const s = input->read(numBytes, readBytes);
186
187
1.69k
  if (numBytes != readBytes)
188
0
    throw EndOfStreamException();
189
190
1.69k
  return s;
191
1.69k
}
192
193
string readCString(librevenge::RVNGInputStream *input)
194
1.01M
{
195
1.01M
  checkStream(input);
196
197
1.01M
  string str;
198
1.01M
  unsigned char c = readU8(input);
199
3.25M
  while (0 != c)
200
2.23M
  {
201
2.23M
    str.push_back(c);
202
2.23M
    c = readU8(input);
203
2.23M
  }
204
205
1.01M
  return str;
206
1.01M
}
207
208
string readPascalString(librevenge::RVNGInputStream *input)
209
20.8k
{
210
20.8k
  checkStream(input);
211
212
20.8k
  const unsigned length = readU8(input);
213
214
20.8k
  return readString(input, length);
215
20.8k
}
216
217
std::string readString(librevenge::RVNGInputStream *input, const unsigned length)
218
41.5k
{
219
41.5k
  checkStream(input);
220
221
41.5k
  string str;
222
41.5k
  str.reserve(length);
223
777k
  for (unsigned i = 0; length != i; ++i)
224
735k
    str.push_back(readU8(input));
225
226
41.5k
  return str;
227
41.5k
}
228
229
std::string readPlatformString(librevenge::RVNGInputStream *input, bool bigEndian)
230
1.03M
{
231
1.03M
  return bigEndian ? readPascalString(input) : readCString(input);
232
1.03M
}
233
234
void skip(librevenge::RVNGInputStream *input, unsigned long numBytes)
235
1.15M
{
236
1.15M
  checkStream(input);
237
238
1.15M
  seekRelative(input, static_cast<long>(numBytes));
239
1.15M
}
240
241
void seek(librevenge::RVNGInputStream *const input, const unsigned long pos)
242
263k
{
243
263k
  if (!input)
244
0
    throw EndOfStreamException();
245
246
263k
  if (0 != input->seek(static_cast<long>(pos), librevenge::RVNG_SEEK_SET))
247
414
    throw SeekFailedException();
248
263k
}
249
250
void seekRelative(librevenge::RVNGInputStream *const input, const long pos)
251
1.15M
{
252
1.15M
  if (!input)
253
0
    throw EndOfStreamException();
254
255
1.15M
  if (0 != input->seek(pos, librevenge::RVNG_SEEK_CUR))
256
1.72k
    throw SeekFailedException();
257
1.15M
}
258
259
unsigned long getRemainingLength(librevenge::RVNGInputStream *const input)
260
84.1k
{
261
84.1k
  if (!input || input->tell() < 0)
262
1.60k
    throw SeekFailedException();
263
264
82.5k
  const unsigned long begin = input->tell();
265
82.5k
  unsigned long end = begin;
266
267
82.5k
  if (0 == input->seek(0, librevenge::RVNG_SEEK_END))
268
82.5k
    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
82.5k
  seek(input, begin);
280
281
82.5k
  return end - begin;
282
84.1k
}
283
284
uint8_t readU8(const std::shared_ptr<librevenge::RVNGInputStream> input, bool)
285
1.02M
{
286
1.02M
  return readU8(input.get());
287
1.02M
}
288
289
uint16_t readU16(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian)
290
3.13M
{
291
3.13M
  return readU16(input.get(), bigEndian);
292
3.13M
}
293
294
uint32_t readU32(const std::shared_ptr<librevenge::RVNGInputStream> input, const bool bigEndian)
295
3.50M
{
296
3.50M
  return readU32(input.get(), bigEndian);
297
3.50M
}
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
517k
{
306
517k
  return readS16(input.get(), bigEndian);
307
517k
}
308
309
int32_t readS32(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
310
56.8k
{
311
56.8k
  return readS32(input.get(), bigEndian);
312
56.8k
}
313
314
double readFloat16(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
315
39.2k
{
316
39.2k
  return readFloat16(input.get(), bigEndian);
317
39.2k
}
318
319
double readFraction(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
320
1.60M
{
321
1.60M
  return readFraction(input.get(), bigEndian);
322
1.60M
}
323
324
const unsigned char *readNBytes(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes)
325
1.69k
{
326
1.69k
  return readNBytes(input.get(), numBytes);
327
1.69k
}
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
20.6k
{
341
20.6k
  return readString(input.get(), length);
342
20.6k
}
343
344
std::string readPlatformString(std::shared_ptr<librevenge::RVNGInputStream> input, bool bigEndian)
345
1.03M
{
346
1.03M
  return readPlatformString(input.get(), bigEndian);
347
1.03M
}
348
349
void skip(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long numBytes)
350
1.15M
{
351
1.15M
  return skip(input.get(), numBytes);
352
1.15M
}
353
354
void seek(const std::shared_ptr<librevenge::RVNGInputStream> input, const unsigned long pos)
355
157k
{
356
157k
  seek(input.get(), pos);
357
157k
}
358
359
void seekRelative(const std::shared_ptr<librevenge::RVNGInputStream> input, const long pos)
360
5.86k
{
361
5.86k
  seekRelative(input.get(), pos);
362
5.86k
}
363
364
unsigned long getRemainingLength(const std::shared_ptr<librevenge::RVNGInputStream> &input)
365
72.2k
{
366
72.2k
  return getRemainingLength(input.get());
367
72.2k
}
368
369
EndOfStreamException::EndOfStreamException()
370
10.5k
{
371
10.5k
  QXP_DEBUG_MSG(("Throwing EndOfStreamException\n"));
372
10.5k
}
373
374
double deg2rad(double value)
375
183k
{
376
183k
  using namespace boost::math::double_constants;
377
378
183k
  return normalizeDegAngle(value) * degree;
379
183k
}
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
195k
{
392
195k
  degAngle = std::fmod(degAngle, 360.0);
393
195k
  if (degAngle < 0.0)
394
167k
    degAngle += 360.0;
395
195k
  return degAngle;
396
195k
}
397
398
void appendCharacters(librevenge::RVNGString &text, const char *characters, const size_t size,
399
                      const char *encoding)
400
37.3k
{
401
37.3k
  if (size == 0)
402
30.1k
  {
403
30.1k
    QXP_DEBUG_MSG(("Attempt to append 0 characters!"));
404
30.1k
    return;
405
30.1k
  }
406
407
7.22k
  UErrorCode status = U_ZERO_ERROR;
408
7.22k
  std::unique_ptr<UConverter, void(*)(UConverter *)> conv(ucnv_open(encoding, &status), ucnv_close);
409
7.22k
  if (U_SUCCESS(status))
410
7.22k
  {
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
7.22k
    const char *src = &characters[0];
414
7.22k
    const char *srcLimit = (const char *)src + size;
415
890k
    while (src < srcLimit)
416
883k
    {
417
883k
      uint32_t ucs4Character = (uint32_t)ucnv_getNextUChar(conv.get(), &src, srcLimit, &status);
418
883k
      if (U_SUCCESS(status))
419
883k
      {
420
883k
        _appendUCS4(text, ucs4Character);
421
883k
      }
422
883k
    }
423
7.22k
  }
424
7.22k
}
425
426
}
427
428
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */