| /* |
| * -*- c++ -*- |
| * |
| * (C) Copyright IBM Corp. and others 2015 - 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 T> |
| class LETableVarSizer { |
| public: |
| inline static size_t getSize(); |
| }; |
| |
| // base definition- could override for adjustments |
| template<class T> inline |
| size_t LETableVarSizer<T>::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<FeatureListTable>::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<x>::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<x>::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 T> |
| 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<T>::getSize(); // fit to max size |
| } |
| LETableReference::verifyLength(0, LETableVarSizer<T>::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<T>::getSize(); // fit to max size |
| } |
| LETableReference::verifyLength(0, LETableVarSizer<T>::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<T>::getSize(); // fit to max size |
| } |
| LETableReference::verifyLength(0, LETableVarSizer<T>::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 { |
| const T *ret = getAlias(i, success); |
| if (LE_FAILURE(success) || ret==NULL) { |
| return *(new T(0)); |
| } else { |
| return *ret; |
| } |
| } |
| |
| 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<getCount()) { |
| return LETableVarSizer<T>::getSize()*i; |
| } else { |
| success = LE_INDEX_OUT_OF_BOUNDS_ERROR; |
| } |
| return 0; |
| } |
| |
| LEReferenceToArrayOf<T> &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 T> |
| 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. |
| */ |
| inline LEReferenceTo(const LETableReference &parent, LEErrorCode &success, const void* atPtr) |
| : LETableReference(parent, parent.ptrToOffset(atPtr, success), LE_UINTPTR_MAX, success) { |
| verifyLength(0, LETableVarSizer<T>::getSize(), success); |
| if(LE_FAILURE(success)) clear(); |
| } |
| /** |
| * ptr plus offset |
| */ |
| inline 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<T>::getSize(), success); |
| if(LE_FAILURE(success)) clear(); |
| } |
| inline LEReferenceTo(const LETableReference &parent, LEErrorCode &success, size_t offset) |
| : LETableReference(parent, offset, LE_UINTPTR_MAX, success) { |
| verifyLength(0, LETableVarSizer<T>::getSize(), success); |
| if(LE_FAILURE(success)) clear(); |
| } |
| inline LEReferenceTo(const LETableReference &parent, LEErrorCode &success) |
| : LETableReference(parent, 0, LE_UINTPTR_MAX, success) { |
| verifyLength(0, LETableVarSizer<T>::getSize(), success); |
| if(LE_FAILURE(success)) clear(); |
| } |
| inline LEReferenceTo(const LEFontInstance *font, LETag tableTag, LEErrorCode &success) |
| : LETableReference(font, tableTag, success) { |
| verifyLength(0, LETableVarSizer<T>::getSize(), success); |
| if(LE_FAILURE(success)) clear(); |
| } |
| inline LEReferenceTo(const le_uint8 *data, size_t length = LE_UINTPTR_MAX) : LETableReference(data, length) {} |
| inline LEReferenceTo(const T *data, size_t length = LE_UINTPTR_MAX) : LETableReference((const le_uint8*)data, length) {} |
| inline LEReferenceTo() : LETableReference(NULL) {} |
| |
| inline LEReferenceTo<T>& operator=(const T* other) { |
| setRaw(other); |
| return *this; |
| } |
| |
| LEReferenceTo<T> &reparent(const LETableReference &base) { |
| fParent = &base; |
| return *this; |
| } |
| |
| /** |
| * roll forward by one <T> size. |
| * same as addOffset(LETableVarSizer<T>::getSize(),success) |
| */ |
| void addObject(LEErrorCode &success) { |
| addOffset(LETableVarSizer<T>::getSize(), success); |
| } |
| void addObject(size_t count, LEErrorCode &success) { |
| addOffset(LETableVarSizer<T>::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 |