Coverage Report

Created: 2025-07-12 07:23

/src/poppler/poppler/Object.h
Line
Count
Source (jump to first uncovered line)
1
//========================================================================
2
//
3
// Object.h
4
//
5
// Copyright 1996-2003 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
//========================================================================
10
//
11
// Modified under the Poppler project - http://poppler.freedesktop.org
12
//
13
// All changes made under the Poppler project to this file are licensed
14
// under GPL version 2 or later
15
//
16
// Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
17
// Copyright (C) 2008 Kees Cook <kees@outflux.net>
18
// Copyright (C) 2008, 2010, 2017-2021, 2023, 2024 Albert Astals Cid <aacid@kde.org>
19
// Copyright (C) 2009 Jakub Wilk <jwilk@jwilk.net>
20
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
21
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
22
// Copyright (C) 2013, 2017, 2018 Adrian Johnson <ajohnson@redneon.com>
23
// Copyright (C) 2013 Adrian Perez de Castro <aperez@igalia.com>
24
// Copyright (C) 2016, 2020 Jakub Alba <jakubalba@gmail.com>
25
// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
26
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
27
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
28
// Copyright (C) 2023 Oliver Sander <oliver.sander@tu-dresden.de>
29
// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
30
//
31
// To see a description of the changes please see the Changelog file that
32
// came with your tarball or type make ChangeLog if you are building from git
33
//
34
//========================================================================
35
36
#ifndef OBJECT_H
37
#define OBJECT_H
38
39
#include <cassert>
40
#include <set>
41
#include <cstdio>
42
#include <cstring>
43
#include <climits>
44
#include "goo/gmem.h"
45
#include "goo/GooString.h"
46
#include "goo/GooLikely.h"
47
#include "Error.h"
48
#include "poppler_private_export.h"
49
50
#define OBJECT_TYPE_CHECK(wanted_type)                                                                                                                                                                                                         \
51
15.0G
    if (unlikely(type != (wanted_type))) {                                                                                                                                                                                                     \
52
0
        ::error(errInternal, 0,                                                                                                                                                                                                                \
53
0
                "Call to Object where the object was type {0:d}, "                                                                                                                                                                             \
54
0
                "not the expected type {1:d}",                                                                                                                                                                                                 \
55
0
                type, wanted_type);                                                                                                                                                                                                            \
56
0
        abort();                                                                                                                                                                                                                               \
57
0
    }
58
59
#define OBJECT_2TYPES_CHECK(wanted_type1, wanted_type2)                                                                                                                                                                                        \
60
0
    if (unlikely(type != (wanted_type1)) && unlikely(type != (wanted_type2))) {                                                                                                                                                                \
61
0
        ::error(errInternal, 0,                                                                                                                                                                                                                \
62
0
                "Call to Object where the object was type {0:d}, "                                                                                                                                                                             \
63
0
                "not the expected type {1:d} or {2:d}",                                                                                                                                                                                        \
64
0
                type, wanted_type1, wanted_type2);                                                                                                                                                                                             \
65
0
        abort();                                                                                                                                                                                                                               \
66
0
    }
67
68
#define OBJECT_3TYPES_CHECK(wanted_type1, wanted_type2, wanted_type3)                                                                                                                                                                          \
69
328M
    if (unlikely(type != (wanted_type1)) && unlikely(type != (wanted_type2)) && unlikely(type != (wanted_type3))) {                                                                                                                            \
70
0
        ::error(errInternal, 0,                                                                                                                                                                                                                \
71
0
                "Call to Object where the object was type {0:d}, "                                                                                                                                                                             \
72
0
                "not the expected type {1:d}, {2:d} or {3:d}",                                                                                                                                                                                 \
73
0
                type, wanted_type1, wanted_type2, wanted_type3);                                                                                                                                                                               \
74
0
        abort();                                                                                                                                                                                                                               \
75
0
    }
76
77
#define CHECK_NOT_DEAD                                                                                                                                                                                                                         \
78
17.1G
    if (unlikely(type == objDead)) {                                                                                                                                                                                                           \
79
0
        ::error(errInternal, 0, "Call to dead object");                                                                                                                                                                                        \
80
0
        abort();                                                                                                                                                                                                                               \
81
0
    }
82
83
class XRef;
84
class Array;
85
class Dict;
86
class Stream;
87
88
//------------------------------------------------------------------------
89
// Ref
90
//------------------------------------------------------------------------
91
92
struct Ref
93
{
94
    int num; // object number
95
    int gen; // generation number
96
97
22.8M
    static constexpr Ref INVALID() { return { -1, -1 }; };
98
};
99
100
inline bool operator==(const Ref lhs, const Ref rhs) noexcept
101
45.7M
{
102
45.7M
    return lhs.num == rhs.num && lhs.gen == rhs.gen;
103
45.7M
}
104
105
inline bool operator!=(const Ref lhs, const Ref rhs) noexcept
106
2.56M
{
107
2.56M
    return lhs.num != rhs.num || lhs.gen != rhs.gen;
108
2.56M
}
109
110
inline bool operator<(const Ref lhs, const Ref rhs) noexcept
111
150k
{
112
150k
    if (lhs.num != rhs.num) {
113
150k
        return lhs.num < rhs.num;
114
150k
    }
115
650
    return lhs.gen < rhs.gen;
116
150k
}
117
118
struct RefRecursionChecker
119
{
120
453k
    RefRecursionChecker() = default;
121
122
    RefRecursionChecker(const RefRecursionChecker &) = delete;
123
    RefRecursionChecker &operator=(const RefRecursionChecker &) = delete;
124
125
    bool insert(Ref ref)
126
14.8M
    {
127
14.8M
        if (ref == Ref::INVALID()) {
128
977k
            return true;
129
977k
        }
130
131
        // insert returns std::pair<iterator,bool>
132
        // where the bool is whether the insert succeeded
133
13.8M
        return alreadySeenRefs.insert(ref.num).second;
134
14.8M
    }
135
136
13.4M
    void remove(Ref ref) { alreadySeenRefs.erase(ref.num); }
137
138
private:
139
    std::set<int> alreadySeenRefs;
140
};
141
142
struct RefRecursionCheckerRemover
143
{
144
    // Removes ref from c when this object is removed
145
13.4M
    RefRecursionCheckerRemover(RefRecursionChecker &c, Ref r) : checker(c), ref(r) { }
146
13.4M
    ~RefRecursionCheckerRemover() { checker.remove(ref); }
147
148
    RefRecursionCheckerRemover(const RefRecursionCheckerRemover &) = delete;
149
    RefRecursionCheckerRemover &operator=(const RefRecursionCheckerRemover &) = delete;
150
151
private:
152
    RefRecursionChecker &checker;
153
    Ref ref;
154
};
155
156
namespace std {
157
158
template<>
159
struct hash<Ref>
160
{
161
    using argument_type = Ref;
162
    using result_type = size_t;
163
164
652k
    result_type operator()(const argument_type ref) const noexcept { return std::hash<int> {}(ref.num) ^ (std::hash<int> {}(ref.gen) << 1); }
165
};
166
167
}
168
169
//------------------------------------------------------------------------
170
// object types
171
//------------------------------------------------------------------------
172
173
enum ObjType
174
{
175
    // simple objects
176
    objBool, // boolean
177
    objInt, // integer
178
    objReal, // real
179
    objString, // string
180
    objName, // name
181
    objNull, // null
182
183
    // complex objects
184
    objArray, // array
185
    objDict, // dictionary
186
    objStream, // stream
187
    objRef, // indirect reference
188
189
    // special objects
190
    objCmd, // command name
191
    objError, // error return from Lexer
192
    objEOF, // end of file return from Lexer
193
    objNone, // uninitialized object
194
195
    // poppler-only objects
196
    objInt64, // integer with at least 64-bits
197
    objHexString, // hex string
198
    objDead // and object after shallowCopy
199
};
200
201
constexpr int numObjTypes = 17; // total number of object types
202
203
//------------------------------------------------------------------------
204
// Object
205
//------------------------------------------------------------------------
206
207
class POPPLER_PRIVATE_EXPORT Object
208
{
209
public:
210
2.51G
    static Object null() { return Object(objNull); }
211
20.8M
    static Object eof() { return Object(objEOF); }
212
15.1M
    static Object error() { return Object(objError); }
213
4.03G
    Object() : type(objNone) { }
214
10.8G
    ~Object() { free(); }
215
216
    explicit Object(bool boolnA)
217
1.36M
    {
218
1.36M
        type = objBool;
219
1.36M
        booln = boolnA;
220
1.36M
    }
221
    explicit Object(int intgA)
222
1.04G
    {
223
1.04G
        type = objInt;
224
1.04G
        intg = intgA;
225
1.04G
    }
226
    explicit Object(double realA)
227
106M
    {
228
106M
        type = objReal;
229
106M
        real = realA;
230
106M
    }
231
    explicit Object(std::unique_ptr<GooString> stringA)
232
486k
    {
233
486k
        assert(stringA);
234
486k
        type = objString;
235
486k
        string = stringA.release();
236
486k
    }
237
    explicit Object(std::string &&stringA)
238
47.6M
    {
239
47.6M
        type = objString;
240
47.6M
        string = new GooString(stringA);
241
47.6M
    }
242
    Object(ObjType typeA, std::string &&stringA)
243
0
    {
244
0
        assert(typeA == objHexString);
245
0
        type = typeA;
246
0
        string = new GooString(stringA);
247
0
    }
248
    Object(ObjType typeA, const char *stringA)
249
700M
    {
250
700M
        assert(typeA == objName || typeA == objCmd);
251
700M
        assert(stringA);
252
700M
        type = typeA;
253
700M
        cString = copyString(stringA);
254
700M
    }
255
    explicit Object(long long int64gA)
256
967k
    {
257
967k
        type = objInt64;
258
967k
        int64g = int64gA;
259
967k
    }
260
    explicit Object(Array *arrayA)
261
34.6M
    {
262
34.6M
        assert(arrayA);
263
34.6M
        type = objArray;
264
34.6M
        array = arrayA;
265
34.6M
    }
266
    explicit Object(Dict *dictA)
267
23.5M
    {
268
23.5M
        assert(dictA);
269
23.5M
        type = objDict;
270
23.5M
        dict = dictA;
271
23.5M
    }
272
    template<typename StreamType>
273
        requires(std::is_base_of_v<Stream, StreamType>)
274
    explicit Object(std::unique_ptr<StreamType> &&streamA)
275
21.9M
    {
276
21.9M
        assert(streamA);
277
21.9M
        type = objStream;
278
21.9M
        stream = streamA.release();
279
21.9M
    }
_ZN6ObjectC2I17AutoFreeMemStreamQsr3stdE12is_base_of_vI6StreamT_EEEONSt3__110unique_ptrIS3_NS4_14default_deleteIS3_EEEE
Line
Count
Source
275
81.2k
    {
276
81.2k
        assert(streamA);
277
81.2k
        type = objStream;
278
81.2k
        stream = streamA.release();
279
81.2k
    }
Unexecuted instantiation: _ZN6ObjectC2I10FileStreamQsr3stdE12is_base_of_vI6StreamT_EEEONSt3__110unique_ptrIS3_NS4_14default_deleteIS3_EEEE
_ZN6ObjectC2I6StreamQsr3stdE12is_base_of_vIS1_T_EEEONSt3__110unique_ptrIS2_NS3_14default_deleteIS2_EEEE
Line
Count
Source
275
21.9M
    {
276
21.9M
        assert(streamA);
277
21.9M
        type = objStream;
278
21.9M
        stream = streamA.release();
279
21.9M
    }
_ZN6ObjectC2I9MemStreamQsr3stdE12is_base_of_vI6StreamT_EEEONSt3__110unique_ptrIS3_NS4_14default_deleteIS3_EEEE
Line
Count
Source
275
528
    {
276
528
        assert(streamA);
277
528
        type = objStream;
278
528
        stream = streamA.release();
279
528
    }
280
    explicit Object(const Ref r)
281
52.7M
    {
282
52.7M
        type = objRef;
283
52.7M
        ref = r;
284
52.7M
    }
285
286
    template<typename T>
287
    Object(T) = delete;
288
289
    Object(Object &&other) noexcept
290
2.33G
    {
291
2.33G
        std::memcpy(reinterpret_cast<void *>(this), &other, sizeof(Object)); // NOLINT(bugprone-undefined-memory-manipulation)
292
2.33G
        other.type = objDead;
293
2.33G
    }
294
295
    Object &operator=(Object &&other) noexcept
296
6.90G
    {
297
6.90G
        free();
298
299
6.90G
        std::memcpy(reinterpret_cast<void *>(this), &other, sizeof(Object)); // NOLINT(bugprone-undefined-memory-manipulation)
300
6.90G
        other.type = objDead;
301
302
6.90G
        return *this;
303
6.90G
    }
304
305
    Object &operator=(const Object &other) = delete;
306
    Object(const Object &other) = delete;
307
308
    // Set object to null.
309
    void setToNull()
310
152M
    {
311
152M
        free();
312
152M
        type = objNull;
313
152M
    }
314
315
    // Copies all object types except
316
    // objArray, objDict, objStream whose refcount is increased by 1
317
    Object copy() const;
318
319
    // Deep copies all object types (recursively)
320
    // except objStream whose refcount is increased by 1
321
    Object deepCopy() const;
322
323
    // If object is a Ref, fetch and return the referenced object.
324
    // Otherwise, return a copy of the object.
325
    Object fetch(XRef *xref, int recursion = 0) const;
326
327
    // Type checking.
328
    ObjType getType() const
329
54.6M
    {
330
54.6M
        CHECK_NOT_DEAD;
331
54.6M
        return type;
332
54.6M
    }
333
    bool isBool() const
334
4.03M
    {
335
4.03M
        CHECK_NOT_DEAD;
336
4.03M
        return type == objBool;
337
4.03M
    }
338
    bool isInt() const
339
1.49G
    {
340
1.49G
        CHECK_NOT_DEAD;
341
1.49G
        return type == objInt;
342
1.49G
    }
343
    bool isReal() const
344
8.95k
    {
345
8.95k
        CHECK_NOT_DEAD;
346
8.95k
        return type == objReal;
347
8.95k
    }
348
    bool isNum() const
349
324M
    {
350
324M
        CHECK_NOT_DEAD;
351
324M
        return type == objInt || type == objReal || type == objInt64;
352
324M
    }
353
    bool isString() const
354
456M
    {
355
456M
        CHECK_NOT_DEAD;
356
456M
        return type == objString;
357
456M
    }
358
    bool isHexString() const
359
0
    {
360
0
        CHECK_NOT_DEAD;
361
0
        return type == objHexString;
362
0
    }
363
    bool isName() const
364
365M
    {
365
365M
        CHECK_NOT_DEAD;
366
365M
        return type == objName;
367
365M
    }
368
    bool isNull() const
369
48.5M
    {
370
48.5M
        CHECK_NOT_DEAD;
371
48.5M
        return type == objNull;
372
48.5M
    }
373
    bool isArray() const
374
33.1M
    {
375
33.1M
        CHECK_NOT_DEAD;
376
33.1M
        return type == objArray;
377
33.1M
    }
378
    bool isDict() const
379
53.4M
    {
380
53.4M
        CHECK_NOT_DEAD;
381
53.4M
        return type == objDict;
382
53.4M
    }
383
    bool isStream() const
384
11.8G
    {
385
11.8G
        CHECK_NOT_DEAD;
386
11.8G
        return type == objStream;
387
11.8G
    }
388
    bool isRef() const
389
8.55M
    {
390
8.55M
        CHECK_NOT_DEAD;
391
8.55M
        return type == objRef;
392
8.55M
    }
393
    bool isCmd() const
394
229M
    {
395
229M
        CHECK_NOT_DEAD;
396
229M
        return type == objCmd;
397
229M
    }
398
    bool isError() const
399
177M
    {
400
177M
        CHECK_NOT_DEAD;
401
177M
        return type == objError;
402
177M
    }
403
    bool isEOF() const
404
1.29G
    {
405
1.29G
        CHECK_NOT_DEAD;
406
1.29G
        return type == objEOF;
407
1.29G
    }
408
    bool isNone() const
409
2.28M
    {
410
2.28M
        CHECK_NOT_DEAD;
411
2.28M
        return type == objNone;
412
2.28M
    }
413
    bool isInt64() const
414
5.07M
    {
415
5.07M
        CHECK_NOT_DEAD;
416
5.07M
        return type == objInt64;
417
5.07M
    }
418
    bool isIntOrInt64() const
419
0
    {
420
0
        CHECK_NOT_DEAD;
421
0
        return type == objInt || type == objInt64;
422
0
    }
423
424
    // Special type checking.
425
190M
    bool isName(const char *nameA) const { return type == objName && !strcmp(cString, nameA); }
426
    bool isDict(const char *dictType) const;
427
4.60G
    bool isCmd(const char *cmdA) const { return type == objCmd && !strcmp(cString, cmdA); }
428
429
    // Accessors.
430
    bool getBool() const
431
964k
    {
432
964k
        OBJECT_TYPE_CHECK(objBool);
433
964k
        return booln;
434
964k
    }
435
    int getInt() const
436
634M
    {
437
634M
        OBJECT_TYPE_CHECK(objInt);
438
634M
        return intg;
439
634M
    }
440
    double getReal() const
441
651k
    {
442
651k
        OBJECT_TYPE_CHECK(objReal);
443
651k
        return real;
444
651k
    }
445
446
    // Note: integers larger than 2^53 can not be exactly represented by a double.
447
    // Where the exact value of integers up to 2^63 is required, use isInt64()/getInt64().
448
    double getNum() const
449
328M
    {
450
328M
        OBJECT_3TYPES_CHECK(objInt, objInt64, objReal);
451
328M
        return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real;
452
328M
    }
453
    double getNum(bool *ok) const
454
164k
    {
455
164k
        if (unlikely(type != objInt && type != objInt64 && type != objReal)) {
456
9.34k
            *ok = false;
457
9.34k
            return 0.;
458
9.34k
        }
459
155k
        return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real;
460
164k
    }
461
    const GooString *getString() const
462
30.0M
    {
463
30.0M
        OBJECT_TYPE_CHECK(objString);
464
30.0M
        return string;
465
30.0M
    }
466
    const GooString *getHexString() const
467
0
    {
468
0
        OBJECT_TYPE_CHECK(objHexString);
469
0
        return string;
470
0
    }
471
    const char *getName() const
472
210M
    {
473
210M
        OBJECT_TYPE_CHECK(objName);
474
210M
        return cString;
475
210M
    }
476
    std::string getNameString() const
477
995k
    {
478
995k
        OBJECT_TYPE_CHECK(objName);
479
995k
        return std::string { cString };
480
995k
    }
481
    Array *getArray() const
482
7.69M
    {
483
7.69M
        OBJECT_TYPE_CHECK(objArray);
484
7.69M
        return array;
485
7.69M
    }
486
    Dict *getDict() const
487
21.4M
    {
488
21.4M
        OBJECT_TYPE_CHECK(objDict);
489
21.4M
        return dict;
490
21.4M
    }
491
    Stream *getStream() const
492
488M
    {
493
488M
        OBJECT_TYPE_CHECK(objStream);
494
488M
        return stream;
495
488M
    }
496
    Ref getRef() const
497
6.36M
    {
498
6.36M
        OBJECT_TYPE_CHECK(objRef);
499
6.36M
        return ref;
500
6.36M
    }
501
    int getRefNum() const
502
1.08M
    {
503
1.08M
        OBJECT_TYPE_CHECK(objRef);
504
1.08M
        return ref.num;
505
1.08M
    }
506
    int getRefGen() const
507
586k
    {
508
586k
        OBJECT_TYPE_CHECK(objRef);
509
586k
        return ref.gen;
510
586k
    }
511
    const char *getCmd() const
512
79.2M
    {
513
79.2M
        OBJECT_TYPE_CHECK(objCmd);
514
79.2M
        return cString;
515
79.2M
    }
516
    long long getInt64() const
517
68.7k
    {
518
68.7k
        OBJECT_TYPE_CHECK(objInt64);
519
68.7k
        return int64g;
520
68.7k
    }
521
    long long getIntOrInt64() const
522
0
    {
523
0
        OBJECT_2TYPES_CHECK(objInt, objInt64);
524
0
        return type == objInt ? intg : int64g;
525
0
    }
526
527
    // Array accessors.
528
    int arrayGetLength() const;
529
    void arrayAdd(Object &&elem);
530
    void arrayRemove(int i);
531
    Object arrayGet(int i, int recursion) const;
532
    const Object &arrayGetNF(int i) const;
533
534
    // Dict accessors.
535
    int dictGetLength() const;
536
    void dictAdd(char *key, Object &&val) = delete;
537
    void dictAdd(const char *key, Object &&val);
538
    void dictSet(const char *key, Object &&val);
539
    void dictRemove(const char *key);
540
    bool dictIs(const char *dictType) const;
541
    Object dictLookup(const char *key, int recursion = 0) const;
542
    const Object &dictLookupNF(const char *key) const;
543
    const char *dictGetKey(int i) const;
544
    Object dictGetVal(int i) const;
545
    const Object &dictGetValNF(int i) const;
546
547
    // Stream accessors.
548
    [[nodiscard]] bool streamReset();
549
    void streamClose();
550
    int streamGetChar();
551
    int streamGetChars(int nChars, unsigned char *buffer);
552
    void streamSetPos(Goffset pos, int dir = 0);
553
    Dict *streamGetDict() const;
554
555
    // Output.
556
    const char *getTypeName() const;
557
    void print(FILE *f = stdout) const;
558
559
    double getNumWithDefaultValue(double defaultValue) const
560
2.78M
    {
561
2.78M
        if (unlikely(type != objInt && type != objInt64 && type != objReal)) {
562
253k
            return defaultValue;
563
253k
        }
564
2.52M
        return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real;
565
2.78M
    }
566
567
141k
    bool getBoolWithDefaultValue(bool defaultValue) const { return (type == objBool) ? booln : defaultValue; }
568
569
private:
570
2.55G
    explicit Object(ObjType typeA) { type = typeA; }
571
    // Free object contents.
572
    void free();
573
574
    ObjType type; // object type
575
    union { // value for each type:
576
        bool booln; //   boolean
577
        int intg; //   integer
578
        long long int64g; //   64-bit integer
579
        double real; //   real
580
        GooString *string; // [hex] string
581
        char *cString; //   name or command, depending on objType
582
        Array *array; //   array
583
        Dict *dict; //   dictionary
584
        Stream *stream; //   stream
585
        Ref ref; //   indirect reference
586
    };
587
};
588
589
//------------------------------------------------------------------------
590
// Array accessors.
591
//------------------------------------------------------------------------
592
593
#include "Array.h"
594
595
//------------------------------------------------------------------------
596
// Dict accessors.
597
//------------------------------------------------------------------------
598
599
#include "Dict.h"
600
601
//------------------------------------------------------------------------
602
// Stream accessors.
603
//------------------------------------------------------------------------
604
605
#include "Stream.h"
606
607
#endif