blob: 2dbc9e961cb90c91079b03905479da34816df6ba [file] [log] [blame]
//========================================================================
//
// 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-2019 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 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>
//
// 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 <stdio.h>
#include <string.h>
#include <limits.h>
#include "goo/gmem.h"
#include "goo/GooString.h"
#include "goo/GooLikely.h"
#include "Error.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 {-1, -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;
}
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
objDead // and object after shallowCopy
};
constexpr int numObjTypes = 16; // total number of object types
//------------------------------------------------------------------------
// Object
//------------------------------------------------------------------------
class Object {
public:
Object() : type(objNone) {}
~Object() { free(); }
explicit Object(bool boolnA)
{ type = objBool; booln = boolnA; }
explicit Object(int intgA)
{ type = objInt; intg = intgA; }
explicit Object(ObjType typeA)
{ type = typeA; }
explicit Object(double realA)
{ type = objReal; real = realA; }
explicit Object(GooString *stringA)
{ assert(stringA); type = objString; string = stringA; }
Object(ObjType typeA, const char *stringA)
{ assert(typeA == objName || typeA == objCmd); assert(stringA); type = typeA; cString = copyString(stringA); }
explicit Object(long long int64gA)
{ type = objInt64; int64g = int64gA; }
explicit Object(Array *arrayA)
{ assert(arrayA); type = objArray; array = arrayA; }
explicit Object(Dict *dictA)
{ assert(dictA); type = objDict; dict = dictA; }
explicit Object(Stream *streamA)
{ assert(streamA); type = objStream; stream = streamA; }
explicit Object(const Ref r)
{ type = objRef; ref = r; }
template<typename T> Object(T) = delete;
Object(Object&& other)
{
std::memcpy(reinterpret_cast<void*>(this), &other, sizeof(Object));
other.type = objDead;
}
Object& operator=(Object&& other)
{
free();
std::memcpy(reinterpret_cast<void*>(this), &other, sizeof(Object));
other.type = objDead;
return *this;
}
Object &operator=(const Object &other) = delete;
Object(const Object &other) = delete;
// Set object to null.
void setToNull() { free(); type = objNull; }
// Copy this to obj
Object copy() 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 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(const char *nameA) const
{ return type == objName && !strcmp(cString, nameA); }
bool isDict(const char *dictType) const;
bool isStream(const char *dictType) const;
bool isCmd(const char *cmdA) const
{ return type == objCmd && !strcmp(cString, cmdA); }
// Accessors.
bool getBool() const { OBJECT_TYPE_CHECK(objBool); return booln; }
int getInt() const { OBJECT_TYPE_CHECK(objInt); return intg; }
double getReal() const { OBJECT_TYPE_CHECK(objReal); return real; }
// 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 ? (double)intg : type == objInt64 ? (double)int64g : real; }
double getNum(bool *ok) const {
if (unlikely(type != objInt && type != objInt64 && type != objReal)) {
*ok = false;
return 0.;
}
return type == objInt ? (double)intg : type == objInt64 ? (double)int64g : real;
}
const GooString *getString() const { OBJECT_TYPE_CHECK(objString); return string; }
// After takeString() the only method that should be called for the object is free().
GooString *takeString() { OBJECT_TYPE_CHECK(objString); type = objDead; return string; }
const char *getName() const { OBJECT_TYPE_CHECK(objName); return cString; }
Array *getArray() const { OBJECT_TYPE_CHECK(objArray); return array; }
Dict *getDict() const { OBJECT_TYPE_CHECK(objDict); return dict; }
Stream *getStream() const { OBJECT_TYPE_CHECK(objStream); return stream; }
Ref getRef() const { OBJECT_TYPE_CHECK(objRef); return ref; }
int getRefNum() const { OBJECT_TYPE_CHECK(objRef); return ref.num; }
int getRefGen() const { OBJECT_TYPE_CHECK(objRef); return ref.gen; }
const char *getCmd() const { OBJECT_TYPE_CHECK(objCmd); return cString; }
long long getInt64() const { OBJECT_TYPE_CHECK(objInt64); return int64g; }
long long getIntOrInt64() const { OBJECT_2TYPES_CHECK(objInt, objInt64);
return type == objInt ? intg : int64g; }
// 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(char *key, Object &&val) = delete;
void dictAdd(const char *key, Object &&val);
void dictSet(const char *key, Object &&val);
void dictRemove(const char *key);
bool dictIs(const char *dictType) const;
Object dictLookup(const char *key, int recursion = 0) const;
const Object &dictLookupNF(const char *key) const;
const char *dictGetKey(int i) const;
Object dictGetVal(int i) const;
const Object &dictGetValNF(int i) const;
// Stream accessors.
bool streamIs(const char *dictType) const;
void streamReset();
void streamClose();
int streamGetChar() const;
int streamGetChars(int nChars, unsigned char *buffer) const;
int streamLookChar() const;
char *streamGetLine(char *buf, int size) const;
Goffset streamGetPos() const;
void streamSetPos(Goffset pos, int dir = 0);
Dict *streamGetDict() const;
// Output.
const char *getTypeName() const;
void print(FILE *f = stdout) const;
private:
// Free object contents.
void free();
ObjType type; // object type
union { // value for each type:
bool booln; // boolean
int intg; // integer
long long int64g; // 64-bit integer
double real; // real
GooString *string; // string
char *cString; // name or command, depending on objType
Array *array; // array
Dict *dict; // dictionary
Stream *stream; // stream
Ref ref; // indirect reference
};
};
//------------------------------------------------------------------------
// Array accessors.
//------------------------------------------------------------------------
#include "Array.h"
inline int Object::arrayGetLength() const
{ OBJECT_TYPE_CHECK(objArray); return array->getLength(); }
inline void Object::arrayAdd(Object &&elem)
{ OBJECT_TYPE_CHECK(objArray); array->add(std::move(elem)); }
inline void Object::arrayRemove(int i)
{ OBJECT_TYPE_CHECK(objArray); array->remove(i); }
inline Object Object::arrayGet(int i, int recursion = 0) const
{ OBJECT_TYPE_CHECK(objArray); return array->get(i, recursion); }
inline const Object &Object::arrayGetNF(int i) const
{ OBJECT_TYPE_CHECK(objArray); return array->getNF(i); }
//------------------------------------------------------------------------
// Dict accessors.
//------------------------------------------------------------------------
#include "Dict.h"
inline int Object::dictGetLength() const
{ OBJECT_TYPE_CHECK(objDict); return dict->getLength(); }
inline void Object::dictAdd(const char *key, Object &&val)
{ OBJECT_TYPE_CHECK(objDict); dict->add(key, std::move(val)); }
inline void Object::dictSet(const char *key, Object &&val)
{ OBJECT_TYPE_CHECK(objDict); dict->set(key, std::move(val)); }
inline void Object::dictRemove(const char *key)
{ OBJECT_TYPE_CHECK(objDict); dict->remove(key); }
inline bool Object::dictIs(const char *dictType) const
{ OBJECT_TYPE_CHECK(objDict); return dict->is(dictType); }
inline bool Object::isDict(const char *dictType) const
{ return type == objDict && dictIs(dictType); }
inline Object Object::dictLookup(const char *key, int recursion) const
{ OBJECT_TYPE_CHECK(objDict); return dict->lookup(key, recursion); }
inline const Object &Object::dictLookupNF(const char *key) const
{ OBJECT_TYPE_CHECK(objDict); return dict->lookupNF(key); }
inline const char *Object::dictGetKey(int i) const
{ OBJECT_TYPE_CHECK(objDict); return dict->getKey(i); }
inline Object Object::dictGetVal(int i) const
{ OBJECT_TYPE_CHECK(objDict); return dict->getVal(i); }
inline const Object &Object::dictGetValNF(int i) const
{ OBJECT_TYPE_CHECK(objDict); return dict->getValNF(i); }
//------------------------------------------------------------------------
// Stream accessors.
//------------------------------------------------------------------------
#include "Stream.h"
inline bool Object::streamIs(const char *dictType) const
{ OBJECT_TYPE_CHECK(objStream); return stream->getDict()->is(dictType); }
inline bool Object::isStream(const char *dictType) const
{ return type == objStream && streamIs(dictType); }
inline void Object::streamReset()
{ OBJECT_TYPE_CHECK(objStream); stream->reset(); }
inline void Object::streamClose()
{ OBJECT_TYPE_CHECK(objStream); stream->close(); }
inline int Object::streamGetChar() const
{ OBJECT_TYPE_CHECK(objStream); return stream->getChar(); }
inline int Object::streamGetChars(int nChars, unsigned char *buffer) const
{ OBJECT_TYPE_CHECK(objStream); return stream->doGetChars(nChars, buffer); }
inline int Object::streamLookChar() const
{ OBJECT_TYPE_CHECK(objStream); return stream->lookChar(); }
inline char *Object::streamGetLine(char *buf, int size) const
{ OBJECT_TYPE_CHECK(objStream); return stream->getLine(buf, size); }
inline Goffset Object::streamGetPos() const
{ OBJECT_TYPE_CHECK(objStream); return stream->getPos(); }
inline void Object::streamSetPos(Goffset pos, int dir)
{ OBJECT_TYPE_CHECK(objStream); stream->setPos(pos, dir); }
inline Dict *Object::streamGetDict() const
{ OBJECT_TYPE_CHECK(objStream); return stream->getDict(); }
#endif