blob: ec06a023450a57d3b1f10d031c96a96ce7a9eb7b [file]
//========================================================================
//
// Object.h
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
// Copyright (C) 2008 Kees Cook <kees@outflux.net>
// Copyright (C) 2008, 2010, 2017-2021, 2023-2026 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2009 Jakub Wilk <jwilk@jwilk.net>
// Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
// Copyright (C) 2013, 2017, 2018 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2013 Adrian Perez de Castro <aperez@igalia.com>
// Copyright (C) 2016, 2020 Jakub Alba <jakubalba@gmail.com>
// 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
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
// Copyright (C) 2023 Oliver Sander <oliver.sander@tu-dresden.de>
// Copyright (C) 2024-2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
// Copyright (C) 2025 Jonathan Hähne <jonathan.haehne@hotmail.com>
// Copyright (C) 2025 Arnav V <arnav0872@gmail.com>
// Copyright (C) 2026 Adam Sampson <ats@offog.org>
// Copyright (C) 2026 Stefan Brüns <stefan.bruens@rwth-aachen.de>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
#ifndef OBJECT_H
#define OBJECT_H
#include <cassert>
#include <set>
#include <cstdio>
#include <cstdlib>
#include <variant>
#include <cstring>
#include "goo/GooString.h"
#include "goo/GooLikely.h"
#include "Error.h"
#include "poppler_private_export.h"
#define OBJECT_TYPE_CHECK(wanted_type) \
if (unlikely(type != (wanted_type))) { \
::error(errInternal, 0, \
"Call to Object where the object was type {0:d}, " \
"not the expected type {1:d}", \
type, wanted_type); \
abort(); \
}
#define OBJECT_2TYPES_CHECK(wanted_type1, wanted_type2) \
if (unlikely(type != (wanted_type1)) && unlikely(type != (wanted_type2))) { \
::error(errInternal, 0, \
"Call to Object where the object was type {0:d}, " \
"not the expected type {1:d} or {2:d}", \
type, wanted_type1, wanted_type2); \
abort(); \
}
#define OBJECT_3TYPES_CHECK(wanted_type1, wanted_type2, wanted_type3) \
if (unlikely(type != (wanted_type1)) && unlikely(type != (wanted_type2)) && unlikely(type != (wanted_type3))) { \
::error(errInternal, 0, \
"Call to Object where the object was type {0:d}, " \
"not the expected type {1:d}, {2:d} or {3:d}", \
type, wanted_type1, wanted_type2, wanted_type3); \
abort(); \
}
#define CHECK_NOT_DEAD \
if (unlikely(type == objDead)) { \
::error(errInternal, 0, "Call to dead object"); \
abort(); \
}
class XRef;
class Array;
class Dict;
class Stream;
//------------------------------------------------------------------------
// Ref
//------------------------------------------------------------------------
struct Ref
{
int num; // object number
int gen; // generation number
static constexpr Ref INVALID() { return { .num = -1, .gen = -1 }; };
};
inline bool operator==(const Ref lhs, const Ref rhs) noexcept
{
return lhs.num == rhs.num && lhs.gen == rhs.gen;
}
inline bool operator!=(const Ref lhs, const Ref rhs) noexcept
{
return lhs.num != rhs.num || lhs.gen != rhs.gen;
}
inline bool operator<(const Ref lhs, const Ref rhs) noexcept
{
if (lhs.num != rhs.num) {
return lhs.num < rhs.num;
}
return lhs.gen < rhs.gen;
}
struct RefRecursionChecker
{
RefRecursionChecker() = default;
RefRecursionChecker(const RefRecursionChecker &) = delete;
RefRecursionChecker &operator=(const RefRecursionChecker &) = delete;
bool insert(Ref ref)
{
if (ref == Ref::INVALID()) {
return true;
}
// insert returns std::pair<iterator,bool>
// where the bool is whether the insert succeeded
return alreadySeenRefs.insert(ref.num).second;
}
void remove(Ref ref) { alreadySeenRefs.erase(ref.num); }
private:
std::set<int> alreadySeenRefs;
};
struct RefRecursionCheckerRemover
{
// Removes ref from c when this object is removed
RefRecursionCheckerRemover(RefRecursionChecker &c, Ref r) : checker(c), ref(r) { }
~RefRecursionCheckerRemover() { checker.remove(ref); }
RefRecursionCheckerRemover(const RefRecursionCheckerRemover &) = delete;
RefRecursionCheckerRemover &operator=(const RefRecursionCheckerRemover &) = delete;
private:
RefRecursionChecker &checker;
Ref ref;
};
namespace std {
template<>
struct hash<Ref>
{
using argument_type = Ref;
using result_type = size_t;
result_type operator()(const argument_type ref) const noexcept { return std::hash<int> {}(ref.num) ^ (std::hash<int> {}(ref.gen) << 1); }
};
}
//------------------------------------------------------------------------
// object types
//------------------------------------------------------------------------
enum ObjType
{
// simple objects
objBool, // boolean
objInt, // integer
objReal, // real
objString, // string
objName, // name
objNull, // null
// complex objects
objArray, // array
objDict, // dictionary
objStream, // stream
objRef, // indirect reference
// special objects
objCmd, // command name
objError, // error return from Lexer
objEOF, // end of file return from Lexer
objNone, // uninitialized object
// poppler-only objects
objInt64, // integer with at least 64-bits
objHexString, // hex string
objDead // and object after shallowCopy
};
constexpr int numObjTypes = 17; // total number of object types
//------------------------------------------------------------------------
// Object
//------------------------------------------------------------------------
class POPPLER_PRIVATE_EXPORT Object
{
public:
static Object null() { return Object(objNull); }
static Object eof() { return Object(objEOF); }
static Object error() { return Object(objError); }
Object() : type(objNone) { }
~Object() = default;
explicit Object(bool boolnA) : type { objBool }, data { boolnA } { }
explicit Object(int intgA) : type { objInt }, data { intgA } { }
explicit Object(double realA) : type { objReal }, data { realA } { }
explicit Object(std::string &&stringA) : type { objString }, data { std::move(stringA) } { }
Object(ObjType typeA, std::string &&stringA) : type { typeA }, data { std::move(stringA) } { assert(typeA == objHexString); }
Object(ObjType typeA, const char *v) : Object(typeA, std::string_view(v)) { }
Object(ObjType typeA, std::string_view v) : type { typeA }, data { std::string { v } } { assert(typeA == objName || typeA == objCmd); }
explicit Object(long long int64gA) : type { objInt64 }, data { int64gA } { }
explicit Object(std::unique_ptr<Array> arrayA) : type { objArray }, data { std::shared_ptr<Array>(std::move(arrayA)) } { }
explicit Object(std::unique_ptr<Dict> &&dictA) : type { objDict }, data { std::shared_ptr<Dict>(std::move(dictA)) } { }
template<typename StreamType>
requires(std::is_base_of_v<Stream, StreamType>)
explicit Object(std::unique_ptr<StreamType> &&streamA) : type { objStream }, data { std::shared_ptr<Stream>(std::move(streamA)) }
{
}
explicit Object(Ref r) : type { objRef }, data { r } { }
template<typename T>
Object(T) = delete;
Object(Object &&other) noexcept : type { other.type }, data { std::move(other.data) } { other.type = objDead; }
Object &operator=(Object &&other) noexcept
{
data = std::move(other.data);
type = other.type;
other.type = objDead;
return *this;
}
Object &operator=(const Object &other) = delete;
Object(const Object &other) = delete;
// Set object to null.
void setToNull()
{
data = std::monostate {};
type = objNull;
}
// Copies all object types except
// objArray, objDict, objStream whose refcount is increased by 1
Object copy() const;
// Deep copies all object types (recursively)
// except objStream whose refcount is increased by 1
Object deepCopy() const;
// If object is a Ref, fetch and return the referenced object.
// Otherwise, return a copy of the object.
Object fetch(XRef *xref, int recursion = 0) const;
// Type checking.
ObjType getType() const
{
CHECK_NOT_DEAD;
return type;
}
bool isBool() const
{
CHECK_NOT_DEAD;
return type == objBool;
}
bool isInt() const
{
CHECK_NOT_DEAD;
return type == objInt;
}
bool isReal() const
{
CHECK_NOT_DEAD;
return type == objReal;
}
bool isNum() const
{
CHECK_NOT_DEAD;
return type == objInt || type == objReal || type == objInt64;
}
bool isString() const
{
CHECK_NOT_DEAD;
return type == objString;
}
bool isHexString() const
{
CHECK_NOT_DEAD;
return type == objHexString;
}
bool isName() const
{
CHECK_NOT_DEAD;
return type == objName;
}
bool isNull() const
{
CHECK_NOT_DEAD;
return type == objNull;
}
bool isArray() const
{
CHECK_NOT_DEAD;
return type == objArray;
}
bool isDict() const
{
CHECK_NOT_DEAD;
return type == objDict;
}
bool isStream() const
{
CHECK_NOT_DEAD;
return type == objStream;
}
bool isRef() const
{
CHECK_NOT_DEAD;
return type == objRef;
}
bool isCmd() const
{
CHECK_NOT_DEAD;
return type == objCmd;
}
bool isError() const
{
CHECK_NOT_DEAD;
return type == objError;
}
bool isEOF() const
{
CHECK_NOT_DEAD;
return type == objEOF;
}
bool isNone() const
{
CHECK_NOT_DEAD;
return type == objNone;
}
bool isInt64() const
{
CHECK_NOT_DEAD;
return type == objInt64;
}
bool isIntOrInt64() const
{
CHECK_NOT_DEAD;
return type == objInt || type == objInt64;
}
// Special type checking.
bool isName(std::string_view nameA) const { return type == objName && getName() == nameA; }
bool isArrayOfLength(int length) const;
bool isArrayOfLengthAtLeast(int length) const;
bool isDict(std::string_view dictType) const;
bool isCmd(std::string_view cmdA) const { return type == objCmd && std::get<std::string>(data) == cmdA; }
// Accessors.
bool getBool() const
{
OBJECT_TYPE_CHECK(objBool);
return std::get<bool>(data);
}
int getInt() const
{
OBJECT_TYPE_CHECK(objInt);
return std::get<int>(data);
}
double getReal() const
{
OBJECT_TYPE_CHECK(objReal);
return std::get<double>(data);
}
// Note: integers larger than 2^53 can not be exactly represented by a double.
// Where the exact value of integers up to 2^63 is required, use isInt64()/getInt64().
double getNum() const
{
OBJECT_3TYPES_CHECK(objInt, objInt64, objReal);
return type == objInt ? static_cast<double>(std::get<int>(data)) : type == objInt64 ? static_cast<double>(std::get<long long>(data)) : std::get<double>(data);
}
double getNum(bool *ok) const
{
if (unlikely(type != objInt && type != objInt64 && type != objReal)) {
*ok = false;
return 0.;
}
return type == objInt ? static_cast<double>(std::get<int>(data)) : type == objInt64 ? static_cast<double>(std::get<long long>(data)) : std::get<double>(data);
}
const std::string &getString() const
{
OBJECT_TYPE_CHECK(objString);
return std::get<std::string>(data);
}
std::unique_ptr<GooString> takeString()
{
OBJECT_TYPE_CHECK(objString);
std::unique_ptr<GooString> str = std::make_unique<GooString>(std::get<std::string>(std::move(data)));
data = std::monostate {};
type = objNull;
return str;
}
const std::string &getHexString() const
{
OBJECT_TYPE_CHECK(objHexString);
return std::get<std::string>(data);
}
const char *getName() const
{
OBJECT_TYPE_CHECK(objName);
return std::get<std::string>(data).c_str();
}
const std::string &getNameString() const
{
OBJECT_TYPE_CHECK(objName);
return std::get<std::string>(data);
}
Array *getArray() const
{
OBJECT_TYPE_CHECK(objArray);
return std::get<std::shared_ptr<Array>>(data).get();
}
Dict *getDict() const
{
OBJECT_TYPE_CHECK(objDict);
return std::get<std::shared_ptr<Dict>>(data).get();
}
Stream *getStream() const
{
OBJECT_TYPE_CHECK(objStream);
return std::get<std::shared_ptr<Stream>>(data).get();
}
Ref getRef() const
{
OBJECT_TYPE_CHECK(objRef);
return std::get<Ref>(data);
}
int getRefNum() const
{
OBJECT_TYPE_CHECK(objRef);
return std::get<Ref>(data).num;
}
int getRefGen() const
{
OBJECT_TYPE_CHECK(objRef);
return std::get<Ref>(data).gen;
}
const char *getCmd() const
{
OBJECT_TYPE_CHECK(objCmd);
return std::get<std::string>(data).c_str();
}
long long getInt64() const
{
OBJECT_TYPE_CHECK(objInt64);
return std::get<long long>(data);
}
long long getIntOrInt64() const
{
OBJECT_2TYPES_CHECK(objInt, objInt64);
return type == objInt ? std::get<int>(data) : std::get<long long>(data);
}
// Array accessors.
int arrayGetLength() const;
void arrayAdd(Object &&elem);
void arrayRemove(int i);
Object arrayGet(int i, int recursion) const;
const Object &arrayGetNF(int i) const;
// Dict accessors.
int dictGetLength() const;
void dictAdd(std::string_view key, Object &&val);
void dictSet(std::string_view key, Object &&val);
void dictRemove(std::string_view key);
Object dictLookup(std::string_view key, int recursion = 0) const;
const Object &dictLookupNF(std::string_view key) const;
Object dictGetVal(int i) const;
const Object &dictGetValNF(int i) const;
// Stream accessors.
[[nodiscard]] bool streamRewind();
int streamGetChar();
Dict *streamGetDict() const;
// Output.
const char *getTypeName() const;
void print(FILE *f = stdout) const;
double getNumWithDefaultValue(double defaultValue) const
{
if (unlikely(type != objInt && type != objInt64 && type != objReal)) {
return defaultValue;
}
return type == objInt ? static_cast<double>(std::get<int>(data)) : type == objInt64 ? static_cast<double>(std::get<long long>(data)) : std::get<double>(data);
}
bool getBoolWithDefaultValue(bool defaultValue) const { return (type == objBool) ? std::get<bool>(data) : defaultValue; }
private:
class PrivateTag
{
};
explicit Object(ObjType typeA) { type = typeA; }
explicit Object(ObjType typeA, std::variant<std::monostate, bool, int, long long, double, std::string, std::shared_ptr<Array>, std::shared_ptr<Dict>, std::shared_ptr<Stream>, Ref> dataA, PrivateTag /*unnamed*/)
: type { typeA }, data { std::move(dataA) }
{
}
ObjType type; // object type
std::variant<std::monostate, bool, int, long long, double, std::string, std::shared_ptr<Array>, std::shared_ptr<Dict>, std::shared_ptr<Stream>, Ref> data;
};
//------------------------------------------------------------------------
// Array accessors.
//------------------------------------------------------------------------
#include "Array.h"
//------------------------------------------------------------------------
// Dict accessors.
//------------------------------------------------------------------------
#include "Dict.h"
//------------------------------------------------------------------------
// Stream accessors.
//------------------------------------------------------------------------
#include "Stream.h"
#endif