]>
Commit | Line | Data |
---|---|---|
57a6839d A |
1 | /* |
2 | * -*- c++ -*- | |
3 | * | |
b331163b | 4 | * (C) Copyright IBM Corp. and others 2015 - All Rights Reserved |
57a6839d A |
5 | * |
6 | * Range checking | |
7 | * | |
8 | */ | |
9 | ||
10 | #ifndef __LETABLEREFERENCE_H | |
11 | #define __LETABLEREFERENCE_H | |
12 | ||
13 | #include "LETypes.h" | |
14 | #include "LEFontInstance.h" | |
15 | ||
16 | ||
17 | #define kQuestionmarkTableTag 0x3F3F3F3FUL | |
18 | #define kTildeTableTag 0x7e7e7e7eUL | |
19 | #ifdef __cplusplus | |
20 | ||
21 | // internal - interface for range checking | |
22 | U_NAMESPACE_BEGIN | |
23 | ||
24 | #if LE_ASSERT_BAD_FONT | |
25 | class LETableReference; // fwd | |
26 | /** | |
27 | * defined in OpenTypeUtilities.cpp | |
28 | * @internal | |
29 | */ | |
30 | extern void _debug_LETableReference(const char *f, int l, const char *msg, const LETableReference *what, const void *ptr, size_t len); | |
31 | ||
32 | #define LE_DEBUG_TR(x) _debug_LETableReference(__FILE__, __LINE__, x, this, NULL, 0); | |
33 | #define LE_DEBUG_TR3(x,y,z) _debug_LETableReference(__FILE__, __LINE__, x, this, (const void*)y, (size_t)z); | |
34 | #if 0 | |
35 | #define LE_TRACE_TR(x) _debug_LETableReference(__FILE__, __LINE__, x, this, NULL, 0); | |
36 | #else | |
37 | #define LE_TRACE_TR(x) | |
38 | #endif | |
39 | ||
40 | #else | |
41 | #define LE_DEBUG_TR(x) | |
42 | #define LE_DEBUG_TR3(x,y,z) | |
43 | #define LE_TRACE_TR(x) | |
44 | #endif | |
45 | ||
46 | /** | |
47 | * @internal | |
48 | */ | |
49 | class LETableReference { | |
50 | public: | |
51 | /** | |
52 | * @internal | |
53 | * Construct from a specific tag | |
54 | */ | |
55 | LETableReference(const LEFontInstance* font, LETag tableTag, LEErrorCode &success) : | |
56 | fFont(font), fTag(tableTag), fParent(NULL), fStart(NULL),fLength(LE_UINTPTR_MAX) { | |
57 | loadTable(success); | |
58 | LE_TRACE_TR("INFO: new table load") | |
59 | } | |
60 | ||
61 | LETableReference(const LETableReference &parent, LEErrorCode &success) : fFont(parent.fFont), fTag(parent.fTag), fParent(&parent), fStart(parent.fStart), fLength(parent.fLength) { | |
62 | if(LE_FAILURE(success)) { | |
63 | clear(); | |
64 | } | |
65 | LE_TRACE_TR("INFO: new clone") | |
66 | } | |
67 | ||
68 | LETableReference(const le_uint8* data, size_t length = LE_UINTPTR_MAX) : | |
69 | fFont(NULL), fTag(kQuestionmarkTableTag), fParent(NULL), fStart(data), fLength(length) { | |
70 | LE_TRACE_TR("INFO: new raw") | |
71 | } | |
72 | LETableReference() : | |
73 | fFont(NULL), fTag(kQuestionmarkTableTag), fParent(NULL), fStart(NULL), fLength(0) { | |
74 | LE_TRACE_TR("INFO: new empty") | |
75 | } | |
76 | ||
77 | ~LETableReference() { | |
78 | fTag=kTildeTableTag; | |
79 | LE_TRACE_TR("INFO: new dtor") | |
80 | } | |
81 | ||
82 | /** | |
83 | * @internal | |
84 | * @param length if LE_UINTPTR_MAX means "whole table" | |
85 | * subset | |
86 | */ | |
87 | LETableReference(const LETableReference &parent, size_t offset, size_t length, | |
88 | LEErrorCode &err) : | |
89 | fFont(parent.fFont), fTag(parent.fTag), fParent(&parent), | |
90 | fStart((parent.fStart)+offset), fLength(length) { | |
91 | if(LE_SUCCESS(err)) { | |
92 | if(isEmpty()) { | |
93 | //err = LE_MISSING_FONT_TABLE_ERROR; | |
94 | clear(); // it's just empty. Not an error. | |
95 | } else if(offset >= fParent->fLength) { | |
96 | LE_DEBUG_TR3("offset out of range: (%p) +%d", NULL, offset); | |
97 | err = LE_INDEX_OUT_OF_BOUNDS_ERROR; | |
98 | clear(); | |
99 | } else { | |
100 | if(fLength == LE_UINTPTR_MAX && | |
101 | fParent->fLength != LE_UINTPTR_MAX) { | |
102 | fLength = (fParent->fLength) - offset; // decrement length as base address is incremented | |
103 | } | |
104 | if(fLength != LE_UINTPTR_MAX) { // if we have bounds: | |
105 | if(offset+fLength > fParent->fLength) { | |
106 | LE_DEBUG_TR3("offset+fLength out of range: (%p) +%d", NULL, offset+fLength); | |
107 | err = LE_INDEX_OUT_OF_BOUNDS_ERROR; // exceeded | |
108 | clear(); | |
109 | } | |
110 | } | |
111 | } | |
112 | } else { | |
113 | clear(); | |
114 | } | |
115 | LE_TRACE_TR("INFO: new subset") | |
116 | } | |
117 | ||
118 | const void* getAlias() const { return (const void*)fStart; } | |
119 | const void* getAliasRAW() const { LE_DEBUG_TR("getAliasRAW()"); return (const void*)fStart; } | |
120 | le_bool isEmpty() const { return fStart==NULL || fLength==0; } | |
121 | le_bool isValid() const { return !isEmpty(); } | |
122 | le_bool hasBounds() const { return fLength!=LE_UINTPTR_MAX; } | |
123 | void clear() { fLength=0; fStart=NULL; } | |
124 | size_t getLength() const { return fLength; } | |
125 | const LEFontInstance* getFont() const { return fFont; } | |
126 | LETag getTag() const { return fTag; } | |
127 | const LETableReference* getParent() const { return fParent; } | |
128 | ||
129 | void addOffset(size_t offset, LEErrorCode &success) { | |
130 | if(hasBounds()) { | |
131 | if(offset > fLength) { | |
132 | LE_DEBUG_TR("addOffset off end"); | |
133 | success = LE_INDEX_OUT_OF_BOUNDS_ERROR; | |
134 | return; | |
135 | } else { | |
136 | fLength -= offset; | |
137 | } | |
138 | } | |
139 | fStart += offset; | |
140 | } | |
141 | ||
142 | size_t ptrToOffset(const void *atPtr, LEErrorCode &success) const { | |
143 | if(atPtr==NULL) return 0; | |
144 | if(LE_FAILURE(success)) return LE_UINTPTR_MAX; | |
145 | if((atPtr < fStart) || | |
146 | (hasBounds() && (atPtr > fStart+fLength))) { | |
147 | LE_DEBUG_TR3("ptrToOffset args out of range: %p", atPtr, 0); | |
148 | success = LE_INDEX_OUT_OF_BOUNDS_ERROR; | |
149 | return LE_UINTPTR_MAX; | |
150 | } | |
151 | return ((const le_uint8*)atPtr)-fStart; | |
152 | } | |
153 | ||
154 | /** | |
155 | * Clamp down the length, for range checking. | |
156 | */ | |
157 | size_t contractLength(size_t newLength) { | |
158 | if(fLength!=LE_UINTPTR_MAX&&newLength>0&&newLength<=fLength) { | |
159 | fLength = newLength; | |
160 | } | |
161 | return fLength; | |
162 | } | |
163 | ||
164 | /** | |
165 | * Throw an error if offset+length off end | |
166 | */ | |
167 | public: | |
168 | size_t verifyLength(size_t offset, size_t length, LEErrorCode &success) { | |
169 | if(isValid()&& | |
170 | LE_SUCCESS(success) && | |
171 | fLength!=LE_UINTPTR_MAX && length!=LE_UINTPTR_MAX && offset!=LE_UINTPTR_MAX && | |
172 | (offset+length)>fLength) { | |
173 | LE_DEBUG_TR3("verifyLength failed (%p) %d",NULL, offset+length); | |
174 | success = LE_INDEX_OUT_OF_BOUNDS_ERROR; | |
175 | #if LE_ASSERT_BAD_FONT | |
176 | 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); | |
177 | #endif | |
178 | } | |
179 | return fLength; | |
180 | } | |
181 | ||
2ca993e8 A |
182 | /** |
183 | * Throw an error if size*count overflows | |
184 | */ | |
185 | size_t verifyLength(size_t offset, size_t size, le_uint32 count, LEErrorCode &success) { | |
186 | if(count!=0 && size>LE_UINT32_MAX/count) { | |
187 | LE_DEBUG_TR3("verifyLength failed size=%u, count=%u", size, count); | |
188 | success = LE_INDEX_OUT_OF_BOUNDS_ERROR; | |
189 | return 0; | |
190 | } | |
191 | return verifyLength(offset, size*count, success); | |
192 | } | |
193 | ||
57a6839d A |
194 | /** |
195 | * Change parent link to another | |
196 | */ | |
197 | LETableReference &reparent(const LETableReference &base) { | |
198 | fParent = &base; | |
199 | return *this; | |
200 | } | |
201 | ||
202 | /** | |
203 | * remove parent link. Factory functions should do this. | |
204 | */ | |
205 | void orphan(void) { | |
206 | fParent=NULL; | |
207 | } | |
208 | ||
209 | protected: | |
210 | const LEFontInstance* fFont; | |
211 | LETag fTag; | |
212 | const LETableReference *fParent; | |
213 | const le_uint8 *fStart; // keep as 8 bit internally, for pointer math | |
214 | size_t fLength; | |
215 | ||
216 | void loadTable(LEErrorCode &success) { | |
217 | if(LE_SUCCESS(success)) { | |
218 | fStart = (const le_uint8*)(fFont->getFontTable(fTag, fLength)); // note - a null table is not an error. | |
219 | } | |
220 | } | |
221 | ||
222 | void setRaw(const void *data, size_t length = LE_UINTPTR_MAX) { | |
223 | fFont = NULL; | |
224 | fTag = kQuestionmarkTableTag; | |
225 | fParent = NULL; | |
226 | fStart = (const le_uint8*)data; | |
227 | fLength = length; | |
228 | } | |
229 | }; | |
230 | ||
231 | ||
232 | template<class T> | |
233 | class LETableVarSizer { | |
234 | public: | |
235 | inline static size_t getSize(); | |
236 | }; | |
237 | ||
238 | // base definition- could override for adjustments | |
239 | template<class T> inline | |
240 | size_t LETableVarSizer<T>::getSize() { | |
241 | return sizeof(T); | |
242 | } | |
243 | ||
244 | /** | |
245 | * \def LE_VAR_ARRAY | |
246 | * @param x Type (T) | |
247 | * @param y some member that is of length ANY_NUMBER | |
248 | * Call this after defining a class, for example: | |
249 | * LE_VAR_ARRAY(FeatureListTable,featureRecordArray) | |
250 | * this is roughly equivalent to: | |
251 | * template<> inline size_t LETableVarSizer<FeatureListTable>::getSize() { return sizeof(FeatureListTable) - (sizeof(le_uint16)*ANY_NUMBER); } | |
252 | * it's a specialization that informs the LETableReference subclasses to NOT include the variable array in the size. | |
253 | * dereferencing NULL is valid here because we never actually dereference it, just inside sizeof. | |
254 | */ | |
255 | #define LE_VAR_ARRAY(x,y) template<> inline size_t LETableVarSizer<x>::getSize() { return sizeof(x) - (sizeof(((const x*)0)->y)); } | |
256 | /** | |
257 | * \def LE_CORRECT_SIZE | |
258 | * @param x type (T) | |
259 | * @param y fixed size for T | |
260 | */ | |
261 | #define LE_CORRECT_SIZE(x,y) template<> inline size_t LETableVarSizer<x>::getSize() { return y; } | |
262 | ||
263 | /** | |
264 | * Open a new entry based on an existing table | |
265 | */ | |
266 | ||
267 | /** | |
268 | * \def LE_UNBOUNDED_ARRAY | |
269 | * define an array with no *known* bound. Will trim to available size. | |
270 | * @internal | |
271 | */ | |
272 | #define LE_UNBOUNDED_ARRAY LE_UINT32_MAX | |
273 | ||
274 | template<class T> | |
275 | class LEReferenceToArrayOf : public LETableReference { | |
276 | public: | |
277 | LEReferenceToArrayOf(const LETableReference &parent, LEErrorCode &success, size_t offset, le_uint32 count) | |
278 | : LETableReference(parent, offset, LE_UINTPTR_MAX, success), fCount(count) { | |
279 | LE_TRACE_TR("INFO: new RTAO by offset") | |
280 | if(LE_SUCCESS(success)) { | |
281 | if(count == LE_UNBOUNDED_ARRAY) { // not a known length | |
282 | count = getLength()/LETableVarSizer<T>::getSize(); // fit to max size | |
283 | } | |
2ca993e8 | 284 | LETableReference::verifyLength(0, LETableVarSizer<T>::getSize(), fCount, success); |
57a6839d A |
285 | } |
286 | if(LE_FAILURE(success)) { | |
287 | fCount=0; | |
288 | clear(); | |
289 | } | |
290 | } | |
291 | ||
292 | LEReferenceToArrayOf(const LETableReference &parent, LEErrorCode &success, const T* array, le_uint32 count) | |
293 | : LETableReference(parent, parent.ptrToOffset(array, success), LE_UINTPTR_MAX, success), fCount(count) { | |
294 | LE_TRACE_TR("INFO: new RTAO") | |
295 | if(LE_SUCCESS(success)) { | |
296 | if(count == LE_UNBOUNDED_ARRAY) { // not a known length | |
297 | count = getLength()/LETableVarSizer<T>::getSize(); // fit to max size | |
298 | } | |
2ca993e8 | 299 | LETableReference::verifyLength(0, LETableVarSizer<T>::getSize(), fCount, success); |
57a6839d A |
300 | } |
301 | if(LE_FAILURE(success)) clear(); | |
302 | } | |
303 | LEReferenceToArrayOf(const LETableReference &parent, LEErrorCode &success, const T* array, size_t offset, le_uint32 count) | |
304 | : LETableReference(parent, parent.ptrToOffset(array, success)+offset, LE_UINTPTR_MAX, success), fCount(count) { | |
305 | LE_TRACE_TR("INFO: new RTAO") | |
306 | if(LE_SUCCESS(success)) { | |
307 | if(count == LE_UNBOUNDED_ARRAY) { // not a known length | |
308 | count = getLength()/LETableVarSizer<T>::getSize(); // fit to max size | |
309 | } | |
2ca993e8 | 310 | LETableReference::verifyLength(0, LETableVarSizer<T>::getSize(), fCount, success); |
57a6839d A |
311 | } |
312 | if(LE_FAILURE(success)) clear(); | |
313 | } | |
314 | ||
315 | LEReferenceToArrayOf() :LETableReference(), fCount(0) {} | |
316 | ||
317 | le_uint32 getCount() const { return fCount; } | |
318 | ||
319 | using LETableReference::getAlias; | |
320 | ||
321 | const T *getAlias(le_uint32 i, LEErrorCode &success) const { | |
322 | return ((const T*)(((const char*)getAlias())+getOffsetFor(i, success))); | |
323 | } | |
324 | ||
325 | const T *getAliasRAW() const { LE_DEBUG_TR("getAliasRAW<>"); return (const T*)fStart; } | |
326 | ||
327 | const T& getObject(le_uint32 i, LEErrorCode &success) const { | |
b331163b A |
328 | const T *ret = getAlias(i, success); |
329 | if (LE_FAILURE(success) || ret==NULL) { | |
330 | return *(new T(0)); | |
331 | } else { | |
332 | return *ret; | |
333 | } | |
57a6839d A |
334 | } |
335 | ||
336 | const T& operator()(le_uint32 i, LEErrorCode &success) const { | |
337 | return *getAlias(i,success); | |
338 | } | |
339 | ||
340 | size_t getOffsetFor(le_uint32 i, LEErrorCode &success) const { | |
341 | if(LE_SUCCESS(success)&&i<getCount()) { | |
342 | return LETableVarSizer<T>::getSize()*i; | |
343 | } else { | |
344 | success = LE_INDEX_OUT_OF_BOUNDS_ERROR; | |
345 | } | |
346 | return 0; | |
347 | } | |
348 | ||
349 | LEReferenceToArrayOf<T> &reparent(const LETableReference &base) { | |
350 | fParent = &base; | |
351 | return *this; | |
352 | } | |
353 | ||
354 | LEReferenceToArrayOf(const LETableReference& parent, LEErrorCode & success) : LETableReference(parent,0, LE_UINTPTR_MAX, success), fCount(0) { | |
355 | LE_TRACE_TR("INFO: null RTAO") | |
356 | } | |
357 | ||
358 | private: | |
359 | le_uint32 fCount; | |
360 | }; | |
361 | ||
362 | ||
363 | template<class T> | |
364 | class LEReferenceTo : public LETableReference { | |
365 | public: | |
366 | /** | |
367 | * open a sub reference. | |
368 | * @param parent parent reference | |
369 | * @param success error status | |
370 | * @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. | |
371 | */ | |
372 | inline LEReferenceTo(const LETableReference &parent, LEErrorCode &success, const void* atPtr) | |
373 | : LETableReference(parent, parent.ptrToOffset(atPtr, success), LE_UINTPTR_MAX, success) { | |
374 | verifyLength(0, LETableVarSizer<T>::getSize(), success); | |
375 | if(LE_FAILURE(success)) clear(); | |
376 | } | |
377 | /** | |
378 | * ptr plus offset | |
379 | */ | |
380 | inline LEReferenceTo(const LETableReference &parent, LEErrorCode &success, const void* atPtr, size_t offset) | |
381 | : LETableReference(parent, parent.ptrToOffset(atPtr, success)+offset, LE_UINTPTR_MAX, success) { | |
382 | verifyLength(0, LETableVarSizer<T>::getSize(), success); | |
383 | if(LE_FAILURE(success)) clear(); | |
384 | } | |
385 | inline LEReferenceTo(const LETableReference &parent, LEErrorCode &success, size_t offset) | |
386 | : LETableReference(parent, offset, LE_UINTPTR_MAX, success) { | |
387 | verifyLength(0, LETableVarSizer<T>::getSize(), success); | |
388 | if(LE_FAILURE(success)) clear(); | |
389 | } | |
390 | inline LEReferenceTo(const LETableReference &parent, LEErrorCode &success) | |
391 | : LETableReference(parent, 0, LE_UINTPTR_MAX, success) { | |
392 | verifyLength(0, LETableVarSizer<T>::getSize(), success); | |
393 | if(LE_FAILURE(success)) clear(); | |
394 | } | |
395 | inline LEReferenceTo(const LEFontInstance *font, LETag tableTag, LEErrorCode &success) | |
396 | : LETableReference(font, tableTag, success) { | |
397 | verifyLength(0, LETableVarSizer<T>::getSize(), success); | |
398 | if(LE_FAILURE(success)) clear(); | |
399 | } | |
400 | inline LEReferenceTo(const le_uint8 *data, size_t length = LE_UINTPTR_MAX) : LETableReference(data, length) {} | |
401 | inline LEReferenceTo(const T *data, size_t length = LE_UINTPTR_MAX) : LETableReference((const le_uint8*)data, length) {} | |
402 | inline LEReferenceTo() : LETableReference(NULL) {} | |
403 | ||
404 | inline LEReferenceTo<T>& operator=(const T* other) { | |
405 | setRaw(other); | |
406 | return *this; | |
407 | } | |
408 | ||
409 | LEReferenceTo<T> &reparent(const LETableReference &base) { | |
410 | fParent = &base; | |
411 | return *this; | |
412 | } | |
413 | ||
414 | /** | |
415 | * roll forward by one <T> size. | |
416 | * same as addOffset(LETableVarSizer<T>::getSize(),success) | |
417 | */ | |
418 | void addObject(LEErrorCode &success) { | |
419 | addOffset(LETableVarSizer<T>::getSize(), success); | |
420 | } | |
421 | void addObject(size_t count, LEErrorCode &success) { | |
422 | addOffset(LETableVarSizer<T>::getSize()*count, success); | |
423 | } | |
424 | ||
425 | const T *operator->() const { return getAlias(); } | |
426 | const T *getAlias() const { return (const T*)fStart; } | |
427 | const T *getAliasRAW() const { LE_DEBUG_TR("getAliasRAW<>"); return (const T*)fStart; } | |
428 | }; | |
429 | ||
430 | ||
431 | U_NAMESPACE_END | |
432 | ||
433 | #endif | |
434 | ||
435 | #endif |