/* * -*- c++ -*- * * (C) Copyright IBM Corp. and others 2013 - All Rights Reserved * * Range checking * */ #ifndef __LETABLEREFERENCE_H #define __LETABLEREFERENCE_H #include "LETypes.h" #include "LEFontInstance.h" #define kQuestionmarkTableTag 0x3F3F3F3FUL #define kTildeTableTag 0x7e7e7e7eUL #ifdef __cplusplus // internal - interface for range checking U_NAMESPACE_BEGIN #if LE_ASSERT_BAD_FONT class LETableReference; // fwd /** * defined in OpenTypeUtilities.cpp * @internal */ extern void _debug_LETableReference(const char *f, int l, const char *msg, const LETableReference *what, const void *ptr, size_t len); #define LE_DEBUG_TR(x) _debug_LETableReference(__FILE__, __LINE__, x, this, NULL, 0); #define LE_DEBUG_TR3(x,y,z) _debug_LETableReference(__FILE__, __LINE__, x, this, (const void*)y, (size_t)z); #if 0 #define LE_TRACE_TR(x) _debug_LETableReference(__FILE__, __LINE__, x, this, NULL, 0); #else #define LE_TRACE_TR(x) #endif #else #define LE_DEBUG_TR(x) #define LE_DEBUG_TR3(x,y,z) #define LE_TRACE_TR(x) #endif /** * @internal */ class LETableReference { public: /** * @internal * Construct from a specific tag */ LETableReference(const LEFontInstance* font, LETag tableTag, LEErrorCode &success) : fFont(font), fTag(tableTag), fParent(NULL), fStart(NULL),fLength(LE_UINTPTR_MAX) { loadTable(success); LE_TRACE_TR("INFO: new table load") } LETableReference(const LETableReference &parent, LEErrorCode &success) : fFont(parent.fFont), fTag(parent.fTag), fParent(&parent), fStart(parent.fStart), fLength(parent.fLength) { if(LE_FAILURE(success)) { clear(); } LE_TRACE_TR("INFO: new clone") } LETableReference(const le_uint8* data, size_t length = LE_UINTPTR_MAX) : fFont(NULL), fTag(kQuestionmarkTableTag), fParent(NULL), fStart(data), fLength(length) { LE_TRACE_TR("INFO: new raw") } LETableReference() : fFont(NULL), fTag(kQuestionmarkTableTag), fParent(NULL), fStart(NULL), fLength(0) { LE_TRACE_TR("INFO: new empty") } ~LETableReference() { fTag=kTildeTableTag; LE_TRACE_TR("INFO: new dtor") } /** * @internal * @param length if LE_UINTPTR_MAX means "whole table" * subset */ LETableReference(const LETableReference &parent, size_t offset, size_t length, LEErrorCode &err) : fFont(parent.fFont), fTag(parent.fTag), fParent(&parent), fStart((parent.fStart)+offset), fLength(length) { if(LE_SUCCESS(err)) { if(isEmpty()) { //err = LE_MISSING_FONT_TABLE_ERROR; clear(); // it's just empty. Not an error. } else if(offset >= fParent->fLength) { LE_DEBUG_TR3("offset out of range: (%p) +%d", NULL, offset); err = LE_INDEX_OUT_OF_BOUNDS_ERROR; clear(); } else { if(fLength == LE_UINTPTR_MAX && fParent->fLength != LE_UINTPTR_MAX) { fLength = (fParent->fLength) - offset; // decrement length as base address is incremented } if(fLength != LE_UINTPTR_MAX) { // if we have bounds: if(offset+fLength > fParent->fLength) { LE_DEBUG_TR3("offset+fLength out of range: (%p) +%d", NULL, offset+fLength); err = LE_INDEX_OUT_OF_BOUNDS_ERROR; // exceeded clear(); } } } } else { clear(); } LE_TRACE_TR("INFO: new subset") } const void* getAlias() const { return (const void*)fStart; } const void* getAliasRAW() const { LE_DEBUG_TR("getAliasRAW()"); return (const void*)fStart; } le_bool isEmpty() const { return fStart==NULL || fLength==0; } le_bool isValid() const { return !isEmpty(); } le_bool hasBounds() const { return fLength!=LE_UINTPTR_MAX; } void clear() { fLength=0; fStart=NULL; } size_t getLength() const { return fLength; } const LEFontInstance* getFont() const { return fFont; } LETag getTag() const { return fTag; } const LETableReference* getParent() const { return fParent; } void addOffset(size_t offset, LEErrorCode &success) { if(hasBounds()) { if(offset > fLength) { LE_DEBUG_TR("addOffset off end"); success = LE_INDEX_OUT_OF_BOUNDS_ERROR; return; } else { fLength -= offset; } } fStart += offset; } size_t ptrToOffset(const void *atPtr, LEErrorCode &success) const { if(atPtr==NULL) return 0; if(LE_FAILURE(success)) return LE_UINTPTR_MAX; if((atPtr < fStart) || (hasBounds() && (atPtr > fStart+fLength))) { LE_DEBUG_TR3("ptrToOffset args out of range: %p", atPtr, 0); success = LE_INDEX_OUT_OF_BOUNDS_ERROR; return LE_UINTPTR_MAX; } return ((const le_uint8*)atPtr)-fStart; } /** * Clamp down the length, for range checking. */ size_t contractLength(size_t newLength) { if(fLength!=LE_UINTPTR_MAX&&newLength>0&&newLength<=fLength) { fLength = newLength; } return fLength; } /** * Throw an error if offset+length off end */ public: size_t verifyLength(size_t offset, size_t length, LEErrorCode &success) { if(isValid()&& LE_SUCCESS(success) && fLength!=LE_UINTPTR_MAX && length!=LE_UINTPTR_MAX && offset!=LE_UINTPTR_MAX && (offset+length)>fLength) { LE_DEBUG_TR3("verifyLength failed (%p) %d",NULL, offset+length); success = LE_INDEX_OUT_OF_BOUNDS_ERROR; #if LE_ASSERT_BAD_FONT fprintf(stderr, "offset=%lu, len=%lu, would be at %p, (%lu) off end. End at %p\n", offset,length, fStart+offset+length, (offset+length-fLength), (offset+length-fLength)+fStart); #endif } return fLength; } /** * Change parent link to another */ LETableReference &reparent(const LETableReference &base) { fParent = &base; return *this; } /** * remove parent link. Factory functions should do this. */ void orphan(void) { fParent=NULL; } protected: const LEFontInstance* fFont; LETag fTag; const LETableReference *fParent; const le_uint8 *fStart; // keep as 8 bit internally, for pointer math size_t fLength; void loadTable(LEErrorCode &success) { if(LE_SUCCESS(success)) { fStart = (const le_uint8*)(fFont->getFontTable(fTag, fLength)); // note - a null table is not an error. } } void setRaw(const void *data, size_t length = LE_UINTPTR_MAX) { fFont = NULL; fTag = kQuestionmarkTableTag; fParent = NULL; fStart = (const le_uint8*)data; fLength = length; } }; template class LETableVarSizer { public: inline static size_t getSize(); }; // base definition- could override for adjustments template inline size_t LETableVarSizer::getSize() { return sizeof(T); } /** * \def LE_VAR_ARRAY * @param x Type (T) * @param y some member that is of length ANY_NUMBER * Call this after defining a class, for example: * LE_VAR_ARRAY(FeatureListTable,featureRecordArray) * this is roughly equivalent to: * template<> inline size_t LETableVarSizer::getSize() { return sizeof(FeatureListTable) - (sizeof(le_uint16)*ANY_NUMBER); } * it's a specialization that informs the LETableReference subclasses to NOT include the variable array in the size. * dereferencing NULL is valid here because we never actually dereference it, just inside sizeof. */ #define LE_VAR_ARRAY(x,y) template<> inline size_t LETableVarSizer::getSize() { return sizeof(x) - (sizeof(((const x*)0)->y)); } /** * \def LE_CORRECT_SIZE * @param x type (T) * @param y fixed size for T */ #define LE_CORRECT_SIZE(x,y) template<> inline size_t LETableVarSizer::getSize() { return y; } /** * Open a new entry based on an existing table */ /** * \def LE_UNBOUNDED_ARRAY * define an array with no *known* bound. Will trim to available size. * @internal */ #define LE_UNBOUNDED_ARRAY LE_UINT32_MAX template class LEReferenceToArrayOf : public LETableReference { public: LEReferenceToArrayOf(const LETableReference &parent, LEErrorCode &success, size_t offset, le_uint32 count) : LETableReference(parent, offset, LE_UINTPTR_MAX, success), fCount(count) { LE_TRACE_TR("INFO: new RTAO by offset") if(LE_SUCCESS(success)) { if(count == LE_UNBOUNDED_ARRAY) { // not a known length count = getLength()/LETableVarSizer::getSize(); // fit to max size } LETableReference::verifyLength(0, LETableVarSizer::getSize()*count, success); } if(LE_FAILURE(success)) { fCount=0; clear(); } } LEReferenceToArrayOf(const LETableReference &parent, LEErrorCode &success, const T* array, le_uint32 count) : LETableReference(parent, parent.ptrToOffset(array, success), LE_UINTPTR_MAX, success), fCount(count) { LE_TRACE_TR("INFO: new RTAO") if(LE_SUCCESS(success)) { if(count == LE_UNBOUNDED_ARRAY) { // not a known length count = getLength()/LETableVarSizer::getSize(); // fit to max size } LETableReference::verifyLength(0, LETableVarSizer::getSize()*count, success); } if(LE_FAILURE(success)) clear(); } LEReferenceToArrayOf(const LETableReference &parent, LEErrorCode &success, const T* array, size_t offset, le_uint32 count) : LETableReference(parent, parent.ptrToOffset(array, success)+offset, LE_UINTPTR_MAX, success), fCount(count) { LE_TRACE_TR("INFO: new RTAO") if(LE_SUCCESS(success)) { if(count == LE_UNBOUNDED_ARRAY) { // not a known length count = getLength()/LETableVarSizer::getSize(); // fit to max size } LETableReference::verifyLength(0, LETableVarSizer::getSize()*count, success); } if(LE_FAILURE(success)) clear(); } LEReferenceToArrayOf() :LETableReference(), fCount(0) {} le_uint32 getCount() const { return fCount; } using LETableReference::getAlias; const T *getAlias(le_uint32 i, LEErrorCode &success) const { return ((const T*)(((const char*)getAlias())+getOffsetFor(i, success))); } const T *getAliasRAW() const { LE_DEBUG_TR("getAliasRAW<>"); return (const T*)fStart; } const T& getObject(le_uint32 i, LEErrorCode &success) const { return *getAlias(i,success); } const T& operator()(le_uint32 i, LEErrorCode &success) const { return *getAlias(i,success); } size_t getOffsetFor(le_uint32 i, LEErrorCode &success) const { if(LE_SUCCESS(success)&&i::getSize()*i; } else { success = LE_INDEX_OUT_OF_BOUNDS_ERROR; } return 0; } LEReferenceToArrayOf &reparent(const LETableReference &base) { fParent = &base; return *this; } LEReferenceToArrayOf(const LETableReference& parent, LEErrorCode & success) : LETableReference(parent,0, LE_UINTPTR_MAX, success), fCount(0) { LE_TRACE_TR("INFO: null RTAO") } private: le_uint32 fCount; }; template class LEReferenceTo : public LETableReference { public: /** * open a sub reference. * @param parent parent reference * @param success error status * @param atPtr location of reference - if NULL, will be at offset zero (i.e. downcast of parent). Otherwise must be a pointer within parent's bounds. */ LEReferenceTo(const LETableReference &parent, LEErrorCode &success, const void* atPtr) : LETableReference(parent, parent.ptrToOffset(atPtr, success), LE_UINTPTR_MAX, success) { verifyLength(0, LETableVarSizer::getSize(), success); if(LE_FAILURE(success)) clear(); } /** * ptr plus offset */ LEReferenceTo(const LETableReference &parent, LEErrorCode &success, const void* atPtr, size_t offset) : LETableReference(parent, parent.ptrToOffset(atPtr, success)+offset, LE_UINTPTR_MAX, success) { verifyLength(0, LETableVarSizer::getSize(), success); if(LE_FAILURE(success)) clear(); } LEReferenceTo(const LETableReference &parent, LEErrorCode &success, size_t offset) : LETableReference(parent, offset, LE_UINTPTR_MAX, success) { verifyLength(0, LETableVarSizer::getSize(), success); if(LE_FAILURE(success)) clear(); } LEReferenceTo(const LETableReference &parent, LEErrorCode &success) : LETableReference(parent, 0, LE_UINTPTR_MAX, success) { verifyLength(0, LETableVarSizer::getSize(), success); if(LE_FAILURE(success)) clear(); } LEReferenceTo(const LEFontInstance *font, LETag tableTag, LEErrorCode &success) : LETableReference(font, tableTag, success) { verifyLength(0, LETableVarSizer::getSize(), success); if(LE_FAILURE(success)) clear(); } LEReferenceTo(const le_uint8 *data, size_t length = LE_UINTPTR_MAX) : LETableReference(data, length) {} LEReferenceTo(const T *data, size_t length = LE_UINTPTR_MAX) : LETableReference((const le_uint8*)data, length) {} LEReferenceTo() : LETableReference(NULL) {} LEReferenceTo& operator=(const T* other) { setRaw(other); return *this; } LEReferenceTo &reparent(const LETableReference &base) { fParent = &base; return *this; } /** * roll forward by one size. * same as addOffset(LETableVarSizer::getSize(),success) */ void addObject(LEErrorCode &success) { addOffset(LETableVarSizer::getSize(), success); } void addObject(size_t count, LEErrorCode &success) { addOffset(LETableVarSizer::getSize()*count, success); } const T *operator->() const { return getAlias(); } const T *getAlias() const { return (const T*)fStart; } const T *getAliasRAW() const { LE_DEBUG_TR("getAliasRAW<>"); return (const T*)fStart; } }; U_NAMESPACE_END #endif #endif