1 | // Copyright 2015 Google Inc. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | // |
15 | //////////////////////////////////////////////////////////////////////////////// |
16 | |
17 | #ifndef PIEX_BINARY_PARSE_RANGE_CHECKED_BYTE_PTR_H_ |
18 | #define PIEX_BINARY_PARSE_RANGE_CHECKED_BYTE_PTR_H_ |
19 | |
20 | #include <assert.h> |
21 | |
22 | #include <cstddef> |
23 | #include <memory> |
24 | #include <string> |
25 | #include <vector> |
26 | |
27 | namespace piex { |
28 | namespace binary_parse { |
29 | |
30 | // Since NaCl does not comply to C++11 we can not just use stdint.h. |
31 | typedef unsigned short uint16; // NOLINT |
32 | typedef short int16; // NOLINT |
33 | typedef unsigned int uint32; |
34 | typedef int int32; |
35 | |
36 | enum MemoryStatus { |
37 | RANGE_CHECKED_BYTE_SUCCESS = 0, |
38 | RANGE_CHECKED_BYTE_ERROR = 1, |
39 | RANGE_CHECKED_BYTE_ERROR_OVERFLOW = 2, |
40 | RANGE_CHECKED_BYTE_ERROR_UNDERFLOW = 3, |
41 | }; |
42 | |
43 | // Interface that RangeCheckedBytePtr uses to access the underlying array of |
44 | // bytes. This allows RangeCheckedBytePtr to be used to access data as if it |
45 | // were stored contiguously in memory, even if the data is in fact split up |
46 | // into non-contiguous chunks and / or does not reside in memory. |
47 | // |
48 | // The only requirement is that the data can be read in pages of a fixed (but |
49 | // configurable) size. Notionally, the byte array (which contains length() |
50 | // bytes) is split up into non-overlapping pages of pageSize() bytes each. |
51 | // (The last page may be shorter if length() is not a multiple of pageSize().) |
52 | // There are therefore (length() - 1) / pageSize() + 1 such pages, with indexes |
53 | // 0 through (length() - 1) / pageSize(). Page i contains the bytes from offset |
54 | // i * pageSize() in the array up to and including the byte at offset |
55 | // (i + 1) * pageSize() - 1 (or, in the case of the last page, length() - 1). |
56 | // |
57 | // In essence, RangeCheckedBytePtr and PagedByteArray together provide a poor |
58 | // man's virtual-memory-and-memory-mapped-file work-alike in situations where |
59 | // virtual memory cannot be used or would consume too much virtual address |
60 | // space. |
61 | // |
62 | // Thread safety: In general, subclasses implementing this interface should |
63 | // ensure that the member functions are thread-safe. It will then be safe to |
64 | // access the same array from multiple threads. (Note that RangeCheckedBytePtr |
65 | // itself is not thread-safe in the sense that a single instance of |
66 | // RangeCheckedBytePtr cannot be used concurrently from multiple threads; it |
67 | // is, however, safe to use different RangeCheckedBytePtr instances in |
68 | // different threads to access the same PagedByteArray concurrently, assuming |
69 | // that the PagedByteArray implementation is thread-safe.) |
70 | class PagedByteArray { |
71 | public: |
72 | // Base class for pages in the byte array. Implementations of PagedByteArray |
73 | // can create a subclass of the Page class to manage the lifetime of buffers |
74 | // associated with a page returned by getPage(). For example, a |
75 | // PagedByteArray backed by a file might define a Page subclass like this: |
76 | // |
77 | // class FilePage : public Page { |
78 | // std::vector<unsigned char> bytes; |
79 | // }; |
80 | // |
81 | // The corresponding getPage() implementation could then look like this: |
82 | // |
83 | // void getPage(size_t page_index, const unsigned char** begin, |
84 | // const unsigned char** end, std::shared_ptr<Page>* page) |
85 | // { |
86 | // // Create a new page. |
87 | // std::shared_ptr<FilePage> file_page(new FilePage()); |
88 | // |
89 | // // Read contents of page from file into file_page->bytes. |
90 | // [...] |
91 | // |
92 | // // Set *begin and *end to point to beginning and end of |
93 | // // file_page->bytes vector. |
94 | // *begin = &file_page->bytes[0]; |
95 | // *end = *begin + file_page->bytes.size(); |
96 | // |
97 | // // Return page to caller |
98 | // *page = file_page; |
99 | // } |
100 | // |
101 | // In this way, the storage associated with the page (the FilePage::bytes |
102 | // vector) will be kept alive until the RangeCheckedBytePtr releases the |
103 | // shared pointer. |
104 | class Page {}; |
105 | |
106 | typedef std::shared_ptr<Page> PagePtr; |
107 | |
108 | virtual ~PagedByteArray(); |
109 | |
110 | // Returns the length of the array in bytes. The value returned must remain |
111 | // the same on every call for the entire lifetime of the object. |
112 | virtual size_t length() const = 0; |
113 | |
114 | // Returns the length of each page in bytes. (The last page may be shorter |
115 | // than pageSize() if length() is not a multiple of pageSize() -- see also |
116 | // the class-wide comment above.) The value returned must remain the same on |
117 | // every call for the entire lifetime of the object. |
118 | virtual size_t pageSize() const = 0; |
119 | |
120 | // Returns a pointer to a memory buffer containing the data for the page |
121 | // with index "page_index". |
122 | // |
123 | // *begin is set to point to the first byte of the page; *end is set to point |
124 | // one byte beyond the last byte in the page. This implies that: |
125 | // - (*end - *begin) == pageSize() for every page except the last page |
126 | // - (*end - *begin) == length() - pageSize() * ((length() - 1) / pageSize()) |
127 | // for the last page |
128 | // |
129 | // *page will be set to a SharedPtr that the caller will hold on to until |
130 | // it no longer needs to access the memory buffer. The memory buffer will |
131 | // remain valid until the SharedPtr is released or the PagedByteArray object |
132 | // is destroyed. An implementation may choose to return a null SharedPtr; |
133 | // this indicates that the memory buffer will remain valid until the |
134 | // PagedByteArray object is destroyed, even if the caller does not hold on to |
135 | // the SharedPtr. (This is intended as an optimization that some |
136 | // implementations may choose to take advantage of, as a null SharedPtr is |
137 | // cheaper to copy.) |
138 | virtual void getPage(size_t page_index, const unsigned char **begin, |
139 | const unsigned char **end, PagePtr *page) const = 0; |
140 | }; |
141 | |
142 | typedef std::shared_ptr<PagedByteArray> PagedByteArrayPtr; |
143 | |
144 | // Smart pointer that has the same semantics as a "const unsigned char *" (plus |
145 | // some convenience functions) but provides range checking and the ability to |
146 | // access arrays that are not contiguous in memory or do not reside entirely in |
147 | // memory (through the PagedByteArray interface). |
148 | // |
149 | // In the following, we abbreviate RangeCheckedBytePtr as RCBP. |
150 | // |
151 | // The intent of this class is to allow easy security hardening of code that |
152 | // parses binary data structures using raw byte pointers. To do this, only the |
153 | // declarations of the pointers need to be changed; the code that uses the |
154 | // pointers can remain unchanged. |
155 | // |
156 | // If an illegal operation occurs on a pointer, an error flag is set, and all |
157 | // read operations from this point on return 0. This means that error checking |
158 | // need not be done after every access; it is sufficient to check the error flag |
159 | // (using errorOccurred()) once before the RCBP is destroyed. Again, this allows |
160 | // the majority of the parsing code to remain unchanged. (Note caveats below |
161 | // that apply if a copy of the pointer is created.) |
162 | // |
163 | // Legal operations are exactly the ones that would be legal on a raw C++ |
164 | // pointer. Read accesses are legal if they fall within the underlying array. A |
165 | // RCBP may point to any element in the underlying array or one element beyond |
166 | // the end of the array. |
167 | // |
168 | // For brevity, the documentation for individual member functions does not state |
169 | // explicitly that the error flag will be set on out-of-range operations. |
170 | // |
171 | // Note: |
172 | // |
173 | // - Just as for raw pointers, it is legal for a pointer to point one element |
174 | // beyond the end of the array, but it is illegal to use operator*() on such a |
175 | // pointer. |
176 | // |
177 | // - If a copy of an RCBP is created, then performing illegal operations on the |
178 | // copy affects the error flag of the copy, but not of the original pointer. |
179 | // Note that using operator+ and operator- also creates a copy of the pointer. |
180 | // For example: |
181 | // |
182 | // // Assume we have an RCBP called "p" and a size_t variable called |
183 | // // "offset". |
184 | // RangeCheckedBytePtr sub_data_structure = p + offset; |
185 | // |
186 | // If "offset" is large enough to cause an out-of-range access, then |
187 | // sub_data_structure.errorOccurred() will be true, but p.errorOccurred() will |
188 | // still be false. The error flag for sub_data_structure therefore needs to be |
189 | // checked before it is destroyed. |
190 | class RangeCheckedBytePtr { |
191 | private: |
192 | // This class maintains the following class invariants: |
193 | // - page_data_ always points to a buffer of at least current_page_len_ |
194 | // bytes. |
195 | // |
196 | // - The current position lies within the sub-array, i.e. |
197 | // sub_array_begin_ <= current_pos_ <= sub_array_end_ |
198 | // |
199 | // - The sub-array is entirely contained within the array, i.e. |
200 | // 0 <= sub_array_begin <= sub_array_end <= array_->length() |
201 | // |
202 | // - If the current page is non-empty, it lies completely within the |
203 | // sub-array, i.e. |
204 | // if _current_page_len_ >= 0, then |
205 | // sub_array_begin_ <= page_begin_offset_ |
206 | // and |
207 | // page_begin_offset_ + current_page_len_ <= sub_array_end_ |
208 | // (See also restrictPageToSubArray().) |
209 | // (If _current_page_len_ == 0, then page_begin_offset_ may lie outside |
210 | // the sub-array; this condition is harmless. Additional logic would be |
211 | // required to make page_begin_offset_ lie within the sub-array in this |
212 | // case, and it would serve no purpose other than to make the invariant |
213 | // slightly simpler.) |
214 | // |
215 | // Note that it is _not_ a class invariant that current_pos_ needs to lie |
216 | // within the current page. Making this an invariant would have two |
217 | // undesirable consequences: |
218 | // a) When operator[] is called with an index that lies beyond the end of |
219 | // the current page, it would need to temporarily load the page that |
220 | // contains this index, but it wouldn't be able to "retain" the page |
221 | // (i.e. make it the current page) because that would violate the |
222 | // proposed invariant. This would lead to inefficient behavior in the |
223 | // case where code accesses a large range of indices beyond the end of |
224 | // the page because a page would need to be loaded temporarily on each |
225 | // access. |
226 | // b) It would require more code: loadPageForOffset() would need to be |
227 | // called anywhere that current_pos_ changes (whereas, with the present |
228 | // approach, loadPageForOffset() is only called in operator[]). |
229 | |
230 | // PagedByteArray that is accessed by this pointer. |
231 | PagedByteArrayPtr array_; |
232 | |
233 | // Pointer to the current page. |
234 | mutable PagedByteArray::PagePtr page_; |
235 | |
236 | // Pointer to the current page's data buffer. |
237 | mutable const unsigned char *page_data_; |
238 | |
239 | // All of the following offsets are defined relative to the beginning of |
240 | // the array defined by the PagedByteArray array_. |
241 | |
242 | // Array offset that the pointer points to. |
243 | size_t current_pos_; |
244 | |
245 | // Start offset of the current sub-array. |
246 | size_t sub_array_begin_; |
247 | |
248 | // End offset of the current sub-array. |
249 | size_t sub_array_end_; |
250 | |
251 | // Array offset corresponding to the "page_data_" pointer. |
252 | mutable size_t page_begin_offset_; |
253 | |
254 | // Length of the current page. |
255 | mutable size_t current_page_len_; |
256 | |
257 | // Error flag. This is mutable because methods that don't affect the value |
258 | // of the pointer itself (such as operator[]) nevertheless need to be able to |
259 | // signal error conditions. |
260 | mutable MemoryStatus error_flag_; |
261 | |
262 | RangeCheckedBytePtr(); |
263 | |
264 | public: |
265 | // Creates a pointer that points to the first element of 'array', which has a |
266 | // length of 'len'. The caller must ensure that the array remains valid until |
267 | // this pointer and any pointers created from it have been destroyed. |
268 | // Note: 'len' may be zero, but 'array' must in this case still be a valid, |
269 | // non-null pointer. |
270 | explicit RangeCheckedBytePtr(const unsigned char *array, const size_t len); |
271 | |
272 | // Creates a pointer that points to the first element of the given |
273 | // PagedByteArray. The caller must ensure that this PagedByteArray remains |
274 | // valid until this pointer and any pointers created from it have been |
275 | // destroyed. |
276 | explicit RangeCheckedBytePtr(PagedByteArray *array); |
277 | |
278 | // Creates an invalid RangeCheckedBytePtr. Calling errorOccurred() on the |
279 | // result of invalidPointer() always returns true. |
280 | // Do not check a RangeCheckedBytePtr for validity by comparing against |
281 | // invalidPointer(); use errorOccurred() instead. |
282 | static RangeCheckedBytePtr invalidPointer(); |
283 | |
284 | // Returns a RangeCheckedBytePtr that points to a sub-array of this pointer's |
285 | // underlying array. The sub-array starts at position 'pos' relative to this |
286 | // pointer and is 'length' bytes long. The sub-array must lie within this |
287 | // pointer's array, i.e. pos + length <= remainingLength() must hold. If this |
288 | // condition is violated, an invalid pointer is returned. |
289 | RangeCheckedBytePtr pointerToSubArray(size_t pos, size_t length) const; |
290 | |
291 | // Returns the number of bytes remaining in the array from this pointer's |
292 | // present position. |
293 | inline size_t remainingLength() const; |
294 | |
295 | // Returns the offset (or index) in the underlying array that this pointer |
296 | // points to. If this pointer was created using pointerToSubArray(), the |
297 | // offset is relative to the beginning of the sub-array (and not relative to |
298 | // the beginning of the original array). |
299 | size_t offsetInArray() const; |
300 | |
301 | // Returns whether an out-of-bounds error has ever occurred on this pointer in |
302 | // the past. An error occurs if a caller attempts to read from a position |
303 | // outside the bounds of the array or to move the pointer outside the bounds |
304 | // of the array. |
305 | // |
306 | // The error flag is never reset. Once an error has occurred, |
307 | // all subsequent attempts to read from the pointer (even within the bounds of |
308 | // the array) return 0. |
309 | // |
310 | // Note that it is permissible for a pointer to point one element past the end |
311 | // of the array, but it is not permissible to read from this position. This is |
312 | // equivalent to the semantics of raw C++ pointers. |
313 | inline bool errorOccurred() const; |
314 | |
315 | // Returns the substring of length 'length' located at position 'pos' relative |
316 | // to this pointer. |
317 | std::string substr(size_t pos, size_t length) const; |
318 | |
319 | // Returns 'length' number of bytes from the array starting at position 'pos' |
320 | // relative to this pointer. |
321 | std::vector<unsigned char> (size_t pos, size_t length) const; |
322 | |
323 | // Equivalent to calling convert(0, output). |
324 | template <class T> |
325 | bool convert(T *output) const { |
326 | union { |
327 | T t; |
328 | unsigned char ch[sizeof(T)]; |
329 | } buffer; |
330 | for (size_t i = 0; i < sizeof(T); i++) { |
331 | buffer.ch[i] = (*this)[i]; |
332 | } |
333 | if (!errorOccurred()) { |
334 | *output = buffer.t; |
335 | } |
336 | return !errorOccurred(); |
337 | } |
338 | |
339 | // Reinterprets this pointer as a pointer to an array of T, then returns the |
340 | // element at position 'index' in this array of T. (Note that this position |
341 | // corresponds to position index * sizeof(T) in the underlying byte array.) |
342 | // |
343 | // Returns true if successful; false if an out-of-range error occurred or if |
344 | // the error flag was already set on the pointer when calling convert(). |
345 | // |
346 | // The conversion from a sequence of sizeof(T) bytes to a T is performed in an |
347 | // implementation-defined fashion. This conversion is equivalent to the one |
348 | // obtained using the following union by filling the array 'ch' and then |
349 | // reading the member 't': |
350 | // |
351 | // union { |
352 | // T t; |
353 | // unsigned char ch[sizeof(T)]; |
354 | // }; |
355 | // |
356 | // Callers should note that, among other things, the conversion is not |
357 | // endian-agnostic with respect to the endianness of T. |
358 | template <class T> |
359 | bool convert(size_t index, T *output) const { |
360 | RangeCheckedBytePtr p = (*this) + index * sizeof(T); |
361 | bool valid = p.convert(output); |
362 | if (!valid) { |
363 | error_flag_ = p.error_flag_; |
364 | } |
365 | return valid; |
366 | } |
367 | |
368 | // Operators. Unless otherwise noted, these operators have the same semantics |
369 | // as the same operators on an unsigned char pointer. |
370 | |
371 | // If an out-of-range access is attempted, returns 0 (and sets the error |
372 | // flag). |
373 | inline unsigned char operator[](size_t i) const; |
374 | |
375 | inline unsigned char operator*() const; |
376 | |
377 | inline RangeCheckedBytePtr &operator++(); |
378 | |
379 | inline RangeCheckedBytePtr operator++(int); |
380 | |
381 | inline RangeCheckedBytePtr &operator--(); |
382 | |
383 | inline RangeCheckedBytePtr operator--(int); |
384 | |
385 | inline RangeCheckedBytePtr &operator+=(size_t x); |
386 | |
387 | inline RangeCheckedBytePtr &operator-=(size_t x); |
388 | |
389 | inline friend RangeCheckedBytePtr operator+(const RangeCheckedBytePtr &p, |
390 | size_t x); |
391 | |
392 | inline friend RangeCheckedBytePtr operator-(const RangeCheckedBytePtr &p, |
393 | size_t x); |
394 | |
395 | // Tests whether x and y point at the same position in the underlying array. |
396 | // Two pointers that point at the same position but have different |
397 | // sub-arrays still compare equal. It is not legal to compare two pointers |
398 | // that point into different paged byte arrays. |
399 | friend bool operator==(const RangeCheckedBytePtr &x, |
400 | const RangeCheckedBytePtr &y); |
401 | |
402 | // Returns !(x == y). |
403 | friend bool operator!=(const RangeCheckedBytePtr &x, |
404 | const RangeCheckedBytePtr &y); |
405 | |
406 | private: |
407 | void loadPageForOffset(size_t offset) const; |
408 | void restrictPageToSubArray() const; |
409 | }; |
410 | |
411 | // Returns the result of calling std::memcmp() on the sequences of 'num' bytes |
412 | // pointed to by 'x' and 'y'. The result is undefined if either |
413 | // x.remainingLength() or y.remainingLength() is less than 'num'. |
414 | int memcmp(const RangeCheckedBytePtr &x, const RangeCheckedBytePtr &y, |
415 | size_t num); |
416 | |
417 | // Returns the result of calling std::memcmp() (note: _not_ strcmp()) on the |
418 | // y.length() number of bytes pointed to by 'x' and the string 'y'. The result |
419 | // is undefined if x.remainingLength() is less than y.length(). |
420 | int strcmp(const RangeCheckedBytePtr &x, const std::string &y); |
421 | |
422 | // Returns the length of the zero-terminated string starting at 'src' (not |
423 | // including the '\0' terminator). If no '\0' occurs before the end of the |
424 | // array, the result is undefined. |
425 | size_t strlen(const RangeCheckedBytePtr &src); |
426 | |
427 | // Integer decoding functions. |
428 | // |
429 | // These functions read signed (Get16s, Get32s) or unsigned (Get16u, Get32u) |
430 | // integers from 'input'. The integer read from the input can be specified to be |
431 | // either big-endian (big_endian == true) or little-endian |
432 | // (little_endian == false). Signed integers are read in two's-complement |
433 | // representation. The integer read in the specified format is then converted to |
434 | // the implementation's native integer representation and returned. In other |
435 | // words, the semantics of these functions are independent of the |
436 | // implementation's endianness and signed integer representation. |
437 | // |
438 | // If an out-of-range error occurs, these functions do _not_ set the error flag |
439 | // on 'input'. Instead, they set 'status' to RANGE_CHECKED_BYTE_ERROR and return |
440 | // 0. |
441 | // |
442 | // Note: |
443 | // - If an error occurs and 'status' is already set to an error value (i.e. a |
444 | // value different from RANGE_CHECKED_BYTE_SUCCESS), the value of 'status' is |
445 | // left unchanged. |
446 | // - If the operation is successful, 'status' is left unchanged (i.e. it is not |
447 | // actively set to RANGE_CHECKED_BYTE_SUCCESS). |
448 | // |
449 | // Together, these two properties mean that these functions can be used to read |
450 | // a number of integers in succession with only a single error check, like this: |
451 | // |
452 | // MemoryStatus status = RANGE_CHECKED_BYTE_SUCCESS; |
453 | // int16 val1 = Get16s(input, false, &status); |
454 | // int32 val2 = Get32s(input + 2, false, &status); |
455 | // uint32 val3 = Get32u(input + 6, false, &status); |
456 | // if (status != RANGE_CHECKED_BYTE_SUCCESS) { |
457 | // // error handling |
458 | // } |
459 | int16 Get16s(const RangeCheckedBytePtr &input, const bool big_endian, |
460 | MemoryStatus *status); |
461 | uint16 Get16u(const RangeCheckedBytePtr &input, const bool big_endian, |
462 | MemoryStatus *status); |
463 | int32 Get32s(const RangeCheckedBytePtr &input, const bool big_endian, |
464 | MemoryStatus *status); |
465 | uint32 Get32u(const RangeCheckedBytePtr &input, const bool big_endian, |
466 | MemoryStatus *status); |
467 | |
468 | size_t RangeCheckedBytePtr::remainingLength() const { |
469 | if (!errorOccurred()) { |
470 | // current_pos_ <= sub_array_end_ is a class invariant, but protect |
471 | // against violations of this invariant. |
472 | if (current_pos_ <= sub_array_end_) { |
473 | return sub_array_end_ - current_pos_; |
474 | } else { |
475 | assert(false); |
476 | return 0; |
477 | } |
478 | } else { |
479 | return 0; |
480 | } |
481 | } |
482 | |
483 | bool RangeCheckedBytePtr::errorOccurred() const { |
484 | return error_flag_ != RANGE_CHECKED_BYTE_SUCCESS; |
485 | } |
486 | |
487 | unsigned char RangeCheckedBytePtr::operator[](size_t i) const { |
488 | // Check that pointer doesn't have an error flag set. |
489 | if (!errorOccurred()) { |
490 | // Offset in array to read from. |
491 | const size_t read_offset = current_pos_ + i; |
492 | |
493 | // Check for the common case first: The byte we want to read lies in the |
494 | // current page. For performance reasons, we don't check for the case |
495 | // "read_offset < page_begin_offset_" explicitly; if it occurs, it will |
496 | // lead to wraparound (which is well-defined for unsigned quantities), and |
497 | // this will cause the test "pos_in_page < current_page_len_" to fail. |
498 | size_t pos_in_page = read_offset - page_begin_offset_; |
499 | if (pos_in_page < current_page_len_) { |
500 | return page_data_[pos_in_page]; |
501 | } |
502 | |
503 | // Check that the offset we're trying to read lies within the sub-array |
504 | // we're allowed to access. |
505 | if (read_offset >= sub_array_begin_ && read_offset < sub_array_end_) { |
506 | // Read the page that contains the offset "read_offset". |
507 | loadPageForOffset(read_offset); |
508 | |
509 | // Compute the position within the new page from which we need to read. |
510 | pos_in_page = read_offset - page_begin_offset_; |
511 | |
512 | // After the call to loadPageForOffset(), read_offset must lie within |
513 | // the current page, and therefore pos_in_page must be less than the |
514 | // length of the page. We nevertheless check for this to protect against |
515 | // potential bugs in loadPageForOffset(). |
516 | assert(pos_in_page < current_page_len_); |
517 | if (pos_in_page < current_page_len_) { |
518 | return page_data_[pos_in_page]; |
519 | } |
520 | } |
521 | } |
522 | |
523 | // All error cases fall through to here. |
524 | #ifdef BREAK_IF_DEBUGGING_AND_OUT_OF_RANGE |
525 | assert(false); |
526 | #endif |
527 | error_flag_ = RANGE_CHECKED_BYTE_ERROR_OVERFLOW; |
528 | // return 0, which represents the invalid value |
529 | return static_cast<unsigned char>(0); |
530 | } |
531 | |
532 | unsigned char RangeCheckedBytePtr::operator*() const { return (*this)[0]; } |
533 | |
534 | RangeCheckedBytePtr &RangeCheckedBytePtr::operator++() { |
535 | if (current_pos_ < sub_array_end_) { |
536 | current_pos_++; |
537 | } else { |
538 | #ifdef BREAK_IF_DEBUGGING_AND_OUT_OF_RANGE |
539 | assert(false); |
540 | #endif |
541 | error_flag_ = RANGE_CHECKED_BYTE_ERROR_OVERFLOW; |
542 | } |
543 | return *this; |
544 | } |
545 | |
546 | RangeCheckedBytePtr RangeCheckedBytePtr::operator++(int) { |
547 | RangeCheckedBytePtr result(*this); |
548 | ++(*this); |
549 | return result; |
550 | } |
551 | |
552 | RangeCheckedBytePtr &RangeCheckedBytePtr::operator--() { |
553 | if (current_pos_ > sub_array_begin_) { |
554 | current_pos_--; |
555 | } else { |
556 | #ifdef BREAK_IF_DEBUGGING_AND_OUT_OF_RANGE |
557 | assert(false); |
558 | #endif |
559 | error_flag_ = RANGE_CHECKED_BYTE_ERROR_UNDERFLOW; |
560 | } |
561 | return *this; |
562 | } |
563 | |
564 | RangeCheckedBytePtr RangeCheckedBytePtr::operator--(int) { |
565 | RangeCheckedBytePtr result(*this); |
566 | --(*this); |
567 | return result; |
568 | } |
569 | |
570 | RangeCheckedBytePtr &RangeCheckedBytePtr::operator+=(size_t x) { |
571 | if (remainingLength() >= x) { |
572 | current_pos_ += x; |
573 | } else { |
574 | #ifdef BREAK_IF_DEBUGGING_AND_OUT_OF_RANGE |
575 | assert(false); |
576 | #endif |
577 | error_flag_ = RANGE_CHECKED_BYTE_ERROR_OVERFLOW; |
578 | } |
579 | return *this; |
580 | } |
581 | |
582 | RangeCheckedBytePtr &RangeCheckedBytePtr::operator-=(size_t x) { |
583 | if (x <= current_pos_ - sub_array_begin_) { |
584 | current_pos_ -= x; |
585 | } else { |
586 | #ifdef BREAK_IF_DEBUGGING_AND_OUT_OF_RANGE |
587 | assert(false); |
588 | #endif |
589 | error_flag_ = RANGE_CHECKED_BYTE_ERROR_UNDERFLOW; |
590 | } |
591 | return *this; |
592 | } |
593 | |
594 | RangeCheckedBytePtr operator+(const RangeCheckedBytePtr &p, size_t x) { |
595 | RangeCheckedBytePtr result(p); |
596 | result += x; |
597 | return result; |
598 | } |
599 | |
600 | RangeCheckedBytePtr operator-(const RangeCheckedBytePtr &p, size_t x) { |
601 | RangeCheckedBytePtr result(p); |
602 | result -= x; |
603 | return result; |
604 | } |
605 | |
606 | } // namespace binary_parse |
607 | } // namespace piex |
608 | |
609 | #endif // PIEX_BINARY_PARSE_RANGE_CHECKED_BYTE_PTR_H_ |
610 | |