Coverage Report

Created: 2026-09-14 06:37

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