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//
30// To see a description of the changes please see the Changelog file that
31// came with your tarball or type make ChangeLog if you are building from git
32//
33//========================================================================
34
35#ifndef OBJECT_H
36#define OBJECT_H
37
38#include <cassert>
39#include <set>
40#include <cstdio>
41#include <cstring>
42#include <climits>
43#include "goo/gmem.h"
44#include "goo/GooString.h"
45#include "goo/GooLikely.h"
46#include "Error.h"
47#include "poppler_private_export.h"
48
49#define OBJECT_TYPE_CHECK(wanted_type) \
50 if (unlikely(type != wanted_type)) { \
51 error(errInternal, 0, \
52 "Call to Object where the object was type {0:d}, " \
53 "not the expected type {1:d}", \
54 type, wanted_type); \
55 abort(); \
56 }
57
58#define OBJECT_2TYPES_CHECK(wanted_type1, wanted_type2) \
59 if (unlikely(type != wanted_type1) && unlikely(type != wanted_type2)) { \
60 error(errInternal, 0, \
61 "Call to Object where the object was type {0:d}, " \
62 "not the expected type {1:d} or {2:d}", \
63 type, wanted_type1, wanted_type2); \
64 abort(); \
65 }
66
67#define OBJECT_3TYPES_CHECK(wanted_type1, wanted_type2, wanted_type3) \
68 if (unlikely(type != wanted_type1) && unlikely(type != wanted_type2) && unlikely(type != wanted_type3)) { \
69 error(errInternal, 0, \
70 "Call to Object where the object was type {0:d}, " \
71 "not the expected type {1:d}, {2:d} or {3:d}", \
72 type, wanted_type1, wanted_type2, wanted_type3); \
73 abort(); \
74 }
75
76#define CHECK_NOT_DEAD \
77 if (unlikely(type == objDead)) { \
78 error(errInternal, 0, "Call to dead object"); \
79 abort(); \
80 }
81
82class XRef;
83class Array;
84class Dict;
85class Stream;
86
87//------------------------------------------------------------------------
88// Ref
89//------------------------------------------------------------------------
90
91struct Ref
92{
93 int num; // object number
94 int gen; // generation number
95
96 static constexpr Ref INVALID() { return { .num: -1, .gen: -1 }; };
97};
98
99inline bool operator==(const Ref lhs, const Ref rhs) noexcept
100{
101 return lhs.num == rhs.num && lhs.gen == rhs.gen;
102}
103
104inline bool operator!=(const Ref lhs, const Ref rhs) noexcept
105{
106 return lhs.num != rhs.num || lhs.gen != rhs.gen;
107}
108
109inline bool operator<(const Ref lhs, const Ref rhs) noexcept
110{
111 if (lhs.num != rhs.num) {
112 return lhs.num < rhs.num;
113 }
114 return lhs.gen < rhs.gen;
115}
116
117struct RefRecursionChecker
118{
119 RefRecursionChecker() { }
120
121 RefRecursionChecker(const RefRecursionChecker &) = delete;
122 RefRecursionChecker &operator=(const RefRecursionChecker &) = delete;
123
124 bool insert(Ref ref)
125 {
126 if (ref == Ref::INVALID()) {
127 return true;
128 }
129
130 // insert returns std::pair<iterator,bool>
131 // where the bool is whether the insert succeeded
132 return alreadySeenRefs.insert(x: ref.num).second;
133 }
134
135 void remove(Ref ref) { alreadySeenRefs.erase(x: ref.num); }
136
137private:
138 std::set<int> alreadySeenRefs;
139};
140
141struct RefRecursionCheckerRemover
142{
143 // Removes ref from c when this object is removed
144 RefRecursionCheckerRemover(RefRecursionChecker &c, Ref r) : checker(c), ref(r) { }
145 ~RefRecursionCheckerRemover() { checker.remove(ref); }
146
147 RefRecursionCheckerRemover(const RefRecursionCheckerRemover &) = delete;
148 RefRecursionCheckerRemover &operator=(const RefRecursionCheckerRemover &) = delete;
149
150private:
151 RefRecursionChecker &checker;
152 Ref ref;
153};
154
155namespace std {
156
157template<>
158struct hash<Ref>
159{
160 using argument_type = Ref;
161 using result_type = size_t;
162
163 result_type operator()(const argument_type ref) const noexcept { return std::hash<int> {}(ref.num) ^ (std::hash<int> {}(ref.gen) << 1); }
164};
165
166}
167
168//------------------------------------------------------------------------
169// object types
170//------------------------------------------------------------------------
171
172enum ObjType
173{
174 // simple objects
175 objBool, // boolean
176 objInt, // integer
177 objReal, // real
178 objString, // string
179 objName, // name
180 objNull, // null
181
182 // complex objects
183 objArray, // array
184 objDict, // dictionary
185 objStream, // stream
186 objRef, // indirect reference
187
188 // special objects
189 objCmd, // command name
190 objError, // error return from Lexer
191 objEOF, // end of file return from Lexer
192 objNone, // uninitialized object
193
194 // poppler-only objects
195 objInt64, // integer with at least 64-bits
196 objHexString, // hex string
197 objDead // and object after shallowCopy
198};
199
200constexpr int numObjTypes = 17; // total number of object types
201
202//------------------------------------------------------------------------
203// Object
204//------------------------------------------------------------------------
205
206class POPPLER_PRIVATE_EXPORT Object
207{
208public:
209 Object() : type(objNone) { }
210 ~Object() { free(); }
211
212 explicit Object(bool boolnA)
213 {
214 type = objBool;
215 booln = boolnA;
216 }
217 explicit Object(int intgA)
218 {
219 type = objInt;
220 intg = intgA;
221 }
222 explicit Object(ObjType typeA) { type = typeA; }
223 explicit Object(double realA)
224 {
225 type = objReal;
226 real = realA;
227 }
228 explicit Object(GooString *stringA)
229 {
230 assert(stringA);
231 type = objString;
232 string = stringA;
233 }
234 explicit Object(std::string &&stringA)
235 {
236 type = objString;
237 string = new GooString(stringA);
238 }
239 Object(ObjType typeA, GooString *stringA)
240 {
241 assert(typeA == objHexString);
242 assert(stringA);
243 type = typeA;
244 string = stringA;
245 }
246 Object(ObjType typeA, const char *stringA)
247 {
248 assert(typeA == objName || typeA == objCmd);
249 assert(stringA);
250 type = typeA;
251 cString = copyString(s: stringA);
252 }
253 explicit Object(long long int64gA)
254 {
255 type = objInt64;
256 int64g = int64gA;
257 }
258 explicit Object(Array *arrayA)
259 {
260 assert(arrayA);
261 type = objArray;
262 array = arrayA;
263 }
264 explicit Object(Dict *dictA)
265 {
266 assert(dictA);
267 type = objDict;
268 dict = dictA;
269 }
270 explicit Object(Stream *streamA)
271 {
272 assert(streamA);
273 type = objStream;
274 stream = streamA;
275 }
276 explicit Object(const Ref r)
277 {
278 type = objRef;
279 ref = r;
280 }
281
282 template<typename T>
283 Object(T) = delete;
284
285 Object(Object &&other) noexcept
286 {
287 std::memcpy(dest: reinterpret_cast<void *>(this), src: &other, n: sizeof(Object));
288 other.type = objDead;
289 }
290
291 Object &operator=(Object &&other) noexcept
292 {
293 free();
294
295 std::memcpy(dest: reinterpret_cast<void *>(this), src: &other, n: sizeof(Object));
296 other.type = objDead;
297
298 return *this;
299 }
300
301 Object &operator=(const Object &other) = delete;
302 Object(const Object &other) = delete;
303
304 // Set object to null.
305 void setToNull()
306 {
307 free();
308 type = objNull;
309 }
310
311 // Copies all object types except
312 // objArray, objDict, objStream whose refcount is increased by 1
313 Object copy() const;
314
315 // Deep copies all object types (recursively)
316 // except objStream whose refcount is increased by 1
317 Object deepCopy() const;
318
319 // If object is a Ref, fetch and return the referenced object.
320 // Otherwise, return a copy of the object.
321 Object fetch(XRef *xref, int recursion = 0) const;
322
323 // Type checking.
324 ObjType getType() const
325 {
326 CHECK_NOT_DEAD;
327 return type;
328 }
329 bool isBool() const
330 {
331 CHECK_NOT_DEAD;
332 return type == objBool;
333 }
334 bool isInt() const
335 {
336 CHECK_NOT_DEAD;
337 return type == objInt;
338 }
339 bool isReal() const
340 {
341 CHECK_NOT_DEAD;
342 return type == objReal;
343 }
344 bool isNum() const
345 {
346 CHECK_NOT_DEAD;
347 return type == objInt || type == objReal || type == objInt64;
348 }
349 bool isString() const
350 {
351 CHECK_NOT_DEAD;
352 return type == objString;
353 }
354 bool isHexString() const
355 {
356 CHECK_NOT_DEAD;
357 return type == objHexString;
358 }
359 bool isName() const
360 {
361 CHECK_NOT_DEAD;
362 return type == objName;
363 }
364 bool isNull() const
365 {
366 CHECK_NOT_DEAD;
367 return type == objNull;
368 }
369 bool isArray() const
370 {
371 CHECK_NOT_DEAD;
372 return type == objArray;
373 }
374 bool isDict() const
375 {
376 CHECK_NOT_DEAD;
377 return type == objDict;
378 }
379 bool isStream() const
380 {
381 CHECK_NOT_DEAD;
382 return type == objStream;
383 }
384 bool isRef() const
385 {
386 CHECK_NOT_DEAD;
387 return type == objRef;
388 }
389 bool isCmd() const
390 {
391 CHECK_NOT_DEAD;
392 return type == objCmd;
393 }
394 bool isError() const
395 {
396 CHECK_NOT_DEAD;
397 return type == objError;
398 }
399 bool isEOF() const
400 {
401 CHECK_NOT_DEAD;
402 return type == objEOF;
403 }
404 bool isNone() const
405 {
406 CHECK_NOT_DEAD;
407 return type == objNone;
408 }
409 bool isInt64() const
410 {
411 CHECK_NOT_DEAD;
412 return type == objInt64;
413 }
414 bool isIntOrInt64() const
415 {
416 CHECK_NOT_DEAD;
417 return type == objInt || type == objInt64;
418 }
419
420 // Special type checking.
421 bool isName(const char *nameA) const { return type == objName && !strcmp(s1: cString, s2: nameA); }
422 bool isDict(const char *dictType) const;
423 bool isCmd(const char *cmdA) const { return type == objCmd && !strcmp(s1: cString, s2: cmdA); }
424
425 // Accessors.
426 bool getBool() const
427 {
428 OBJECT_TYPE_CHECK(objBool);
429 return booln;
430 }
431 int getInt() const
432 {
433 OBJECT_TYPE_CHECK(objInt);
434 return intg;
435 }
436 double getReal() const
437 {
438 OBJECT_TYPE_CHECK(objReal);
439 return real;
440 }
441
442 // Note: integers larger than 2^53 can not be exactly represented by a double.
443 // Where the exact value of integers up to 2^63 is required, use isInt64()/getInt64().
444 double getNum() const
445 {
446 OBJECT_3TYPES_CHECK(objInt, objInt64, objReal);
447 return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real;
448 }
449 double getNum(bool *ok) const
450 {
451 if (unlikely(type != objInt && type != objInt64 && type != objReal)) {
452 *ok = false;
453 return 0.;
454 }
455 return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real;
456 }
457 const GooString *getString() const
458 {
459 OBJECT_TYPE_CHECK(objString);
460 return string;
461 }
462 const GooString *getHexString() const
463 {
464 OBJECT_TYPE_CHECK(objHexString);
465 return string;
466 }
467 const char *getName() const
468 {
469 OBJECT_TYPE_CHECK(objName);
470 return cString;
471 }
472 Array *getArray() const
473 {
474 OBJECT_TYPE_CHECK(objArray);
475 return array;
476 }
477 Dict *getDict() const
478 {
479 OBJECT_TYPE_CHECK(objDict);
480 return dict;
481 }
482 Stream *getStream() const
483 {
484 OBJECT_TYPE_CHECK(objStream);
485 return stream;
486 }
487 Ref getRef() const
488 {
489 OBJECT_TYPE_CHECK(objRef);
490 return ref;
491 }
492 int getRefNum() const
493 {
494 OBJECT_TYPE_CHECK(objRef);
495 return ref.num;
496 }
497 int getRefGen() const
498 {
499 OBJECT_TYPE_CHECK(objRef);
500 return ref.gen;
501 }
502 const char *getCmd() const
503 {
504 OBJECT_TYPE_CHECK(objCmd);
505 return cString;
506 }
507 long long getInt64() const
508 {
509 OBJECT_TYPE_CHECK(objInt64);
510 return int64g;
511 }
512 long long getIntOrInt64() const
513 {
514 OBJECT_2TYPES_CHECK(objInt, objInt64);
515 return type == objInt ? intg : int64g;
516 }
517
518 // Array accessors.
519 int arrayGetLength() const;
520 void arrayAdd(Object &&elem);
521 void arrayRemove(int i);
522 Object arrayGet(int i, int recursion) const;
523 const Object &arrayGetNF(int i) const;
524
525 // Dict accessors.
526 int dictGetLength() const;
527 void dictAdd(char *key, Object &&val) = delete;
528 void dictAdd(const char *key, Object &&val);
529 void dictSet(const char *key, Object &&val);
530 void dictRemove(const char *key);
531 bool dictIs(const char *dictType) const;
532 Object dictLookup(const char *key, int recursion = 0) const;
533 const Object &dictLookupNF(const char *key) const;
534 const char *dictGetKey(int i) const;
535 Object dictGetVal(int i) const;
536 const Object &dictGetValNF(int i) const;
537
538 // Stream accessors.
539 void streamReset();
540 void streamClose();
541 int streamGetChar();
542 int streamGetChars(int nChars, unsigned char *buffer);
543 void streamSetPos(Goffset pos, int dir = 0);
544 Dict *streamGetDict() const;
545
546 // Output.
547 const char *getTypeName() const;
548 void print(FILE *f = stdout) const;
549
550 double getNumWithDefaultValue(double defaultValue) const
551 {
552 if (unlikely(type != objInt && type != objInt64 && type != objReal)) {
553 return defaultValue;
554 }
555 return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real;
556 }
557
558 bool getBoolWithDefaultValue(bool defaultValue) const { return (type == objBool) ? booln : defaultValue; }
559
560private:
561 // Free object contents.
562 void free();
563
564 ObjType type; // object type
565 union { // value for each type:
566 bool booln; // boolean
567 int intg; // integer
568 long long int64g; // 64-bit integer
569 double real; // real
570 GooString *string; // [hex] string
571 char *cString; // name or command, depending on objType
572 Array *array; // array
573 Dict *dict; // dictionary
574 Stream *stream; // stream
575 Ref ref; // indirect reference
576 };
577};
578
579//------------------------------------------------------------------------
580// Array accessors.
581//------------------------------------------------------------------------
582
583#include "Array.h"
584
585inline int Object::arrayGetLength() const
586{
587 OBJECT_TYPE_CHECK(objArray);
588 return array->getLength();
589}
590
591inline void Object::arrayAdd(Object &&elem)
592{
593 OBJECT_TYPE_CHECK(objArray);
594 array->add(elem: std::move(elem));
595}
596
597inline void Object::arrayRemove(int i)
598{
599 OBJECT_TYPE_CHECK(objArray);
600 array->remove(i);
601}
602
603inline Object Object::arrayGet(int i, int recursion = 0) const
604{
605 OBJECT_TYPE_CHECK(objArray);
606 return array->get(i, recursion);
607}
608
609inline const Object &Object::arrayGetNF(int i) const
610{
611 OBJECT_TYPE_CHECK(objArray);
612 return array->getNF(i);
613}
614
615//------------------------------------------------------------------------
616// Dict accessors.
617//------------------------------------------------------------------------
618
619#include "Dict.h"
620
621inline int Object::dictGetLength() const
622{
623 OBJECT_TYPE_CHECK(objDict);
624 return dict->getLength();
625}
626
627inline void Object::dictAdd(const char *key, Object &&val)
628{
629 OBJECT_TYPE_CHECK(objDict);
630 dict->add(key, val: std::move(val));
631}
632
633inline void Object::dictSet(const char *key, Object &&val)
634{
635 OBJECT_TYPE_CHECK(objDict);
636 dict->set(key, val: std::move(val));
637}
638
639inline void Object::dictRemove(const char *key)
640{
641 OBJECT_TYPE_CHECK(objDict);
642 dict->remove(key);
643}
644
645inline bool Object::dictIs(const char *dictType) const
646{
647 OBJECT_TYPE_CHECK(objDict);
648 return dict->is(type: dictType);
649}
650
651inline bool Object::isDict(const char *dictType) const
652{
653 return type == objDict && dictIs(dictType);
654}
655
656inline Object Object::dictLookup(const char *key, int recursion) const
657{
658 OBJECT_TYPE_CHECK(objDict);
659 return dict->lookup(key, recursion);
660}
661
662inline const Object &Object::dictLookupNF(const char *key) const
663{
664 OBJECT_TYPE_CHECK(objDict);
665 return dict->lookupNF(key);
666}
667
668inline const char *Object::dictGetKey(int i) const
669{
670 OBJECT_TYPE_CHECK(objDict);
671 return dict->getKey(i);
672}
673
674inline Object Object::dictGetVal(int i) const
675{
676 OBJECT_TYPE_CHECK(objDict);
677 return dict->getVal(i);
678}
679
680inline const Object &Object::dictGetValNF(int i) const
681{
682 OBJECT_TYPE_CHECK(objDict);
683 return dict->getValNF(i);
684}
685
686//------------------------------------------------------------------------
687// Stream accessors.
688//------------------------------------------------------------------------
689
690#include "Stream.h"
691
692inline void Object::streamReset()
693{
694 OBJECT_TYPE_CHECK(objStream);
695 stream->reset();
696}
697
698inline void Object::streamClose()
699{
700 OBJECT_TYPE_CHECK(objStream);
701 stream->close();
702}
703
704inline int Object::streamGetChar()
705{
706 OBJECT_TYPE_CHECK(objStream);
707 return stream->getChar();
708}
709
710inline int Object::streamGetChars(int nChars, unsigned char *buffer)
711{
712 OBJECT_TYPE_CHECK(objStream);
713 return stream->doGetChars(nChars, buffer);
714}
715
716inline Dict *Object::streamGetDict() const
717{
718 OBJECT_TYPE_CHECK(objStream);
719 return stream->getDict();
720}
721
722#endif
723

source code of poppler/poppler/Object.h