Update PIEX

This commit is contained in:
yujieqin
2016-01-05 14:23:00 +01:00
parent b3023f4449
commit c6f2d0c8dd
5 changed files with 167 additions and 36 deletions

View File

@@ -19,12 +19,12 @@
'sources': [
'src/piex.cc',
'src/tiff_parser.cc',
'src/tiff_parser.h',
],
'variables': {
'headers': [
'src/piex.h',
'src/piex_types.h',
'src/tiff_parser.h',
],
},
'include_dirs': ['.'],

View File

@@ -81,10 +81,10 @@ class PagedByteArray {
// The corresponding getPage() implementation could then look like this:
//
// void getPage(size_t page_index, const unsigned char** begin,
// const unsigned char** end, photos::ncf::util::SharedPtr<Page>* page)
// const unsigned char** end, std::shared_ptr<Page>* page)
// {
// // Create a new page.
// photos::ncf::util::SharedPtr<FilePage> file_page(new FilePage());
// std::shared_ptr<FilePage> file_page(new FilePage());
//
// // Read contents of page from file into file_page->bytes.
// [...]
@@ -141,6 +141,52 @@ class PagedByteArray {
typedef std::shared_ptr<PagedByteArray> PagedByteArrayPtr;
// Smart pointer that has the same semantics as a "const unsigned char *" (plus
// some convenience functions) but provides range checking and the ability to
// access arrays that are not contiguous in memory or do not reside entirely in
// memory (through the PagedByteArray interface).
//
// In the following, we abbreviate RangeCheckedBytePtr as RCBP.
//
// The intent of this class is to allow easy security hardening of code that
// parses binary data structures using raw byte pointers. To do this, only the
// declarations of the pointers need to be changed; the code that uses the
// pointers can remain unchanged.
//
// If an illegal operation occurs on a pointer, an error flag is set, and all
// read operations from this point on return 0. This means that error checking
// need not be done after every access; it is sufficient to check the error flag
// (using errorOccurred()) once before the RCBP is destroyed. Again, this allows
// the majority of the parsing code to remain unchanged. (Note caveats below
// that apply if a copy of the pointer is created.)
//
// Legal operations are exactly the ones that would be legal on a raw C++
// pointer. Read accesses are legal if they fall within the underlying array. A
// RCBP may point to any element in the underlying array or one element beyond
// the end of the array.
//
// For brevity, the documentation for individual member functions does not state
// explicitly that the error flag will be set on out-of-range operations.
//
// Note:
//
// - Just as for raw pointers, it is legal for a pointer to point one element
// beyond the end of the array, but it is illegal to use operator*() on such a
// pointer.
//
// - If a copy of an RCBP is created, then performing illegal operations on the
// copy affects the error flag of the copy, but not of the original pointer.
// Note that using operator+ and operator- also creates a copy of the pointer.
// For example:
//
// // Assume we have an RCBP called "p" and a size_t variable called
// // "offset".
// RangeCheckedBytePtr sub_data_structure = p + offset;
//
// If "offset" is large enough to cause an out-of-range access, then
// sub_data_structure.errorOccurred() will be true, but p.errorOccurred() will
// still be false. The error flag for sub_data_structure therefore needs to be
// checked before it is destroyed.
class RangeCheckedBytePtr {
private:
// This class maintains the following class invariants:
@@ -209,13 +255,18 @@ class RangeCheckedBytePtr {
mutable size_t current_page_len_;
// Error flag. This is mutable because methods that don't affect the value
// of the pointer itself (such as operator+() and operator-())
// nevertheless need to be able to signal error conditions.
// of the pointer itself (such as operator[]) nevertheless need to be able to
// signal error conditions.
mutable MemoryStatus error_flag_;
RangeCheckedBytePtr();
public:
// Creates a pointer that points to the first element of 'array', which has a
// length of 'len'. The caller must ensure that the array remains valid until
// this pointer and any pointers created from it have been destroyed.
// Note: 'len' may be zero, but 'array' must in this case still be a valid,
// non-null pointer.
explicit RangeCheckedBytePtr(const unsigned char *array, const size_t len);
// Creates a pointer that points to the first element of the given
@@ -230,14 +281,21 @@ class RangeCheckedBytePtr {
// invalidPointer(); use errorOccurred() instead.
static RangeCheckedBytePtr invalidPointer();
// Returns a RangeCheckedBytePtr points to an array which start at the byte
// position "pos" and spans length bytes.
// If the desired range is is out of the RangeCheckedBytePtr's range returns
// an invalid pointer.
// Returns a RangeCheckedBytePtr that points to a sub-array of this pointer's
// underlying array. The sub-array starts at position 'pos' relative to this
// pointer and is 'length' bytes long. The sub-array must lie within this
// pointer's array, i.e. pos + length <= remainingLength() must hold. If this
// condition is violated, an invalid pointer is returned.
RangeCheckedBytePtr pointerToSubArray(size_t pos, size_t length) const;
// Returns the number of bytes remaining in the array from this pointer's
// present position.
inline size_t remainingLength() const;
// Returns the offset (or index) in the underlying array that this pointer
// points to. If this pointer was created using pointerToSubArray(), the
// offset is relative to the beginning of the sub-array (and not relative to
// the beginning of the original array).
size_t offsetInArray() const;
// Returns whether an out-of-bounds error has ever occurred on this pointer in
@@ -254,17 +312,15 @@ class RangeCheckedBytePtr {
// equivalent to the semantics of raw C++ pointers.
inline bool errorOccurred() const;
// DEPRECATED: Use "!errorOccurred()" instead (note negation), which returns
// the same result as isValid() in all cases.
inline bool isValid() const;
// Returns the substring of length 'length' located at position 'pos' relative
// to this pointer.
std::string substr(size_t pos, size_t length) const;
// Returns 'length' number of bytes from the array starting at position 'pos'
// relative to this pointer.
std::vector<unsigned char> extractBytes(size_t pos, size_t length) const;
// This function is not endian-agnostic. But we think it better than using
// reinterpret_cast or simply casting the unsigned char * pointer to T *
// which is also not endian-agnostic
// Equivalent to calling convert(0, output).
template <class T>
bool convert(T *output) const {
union {
@@ -280,6 +336,25 @@ class RangeCheckedBytePtr {
return !errorOccurred();
}
// Reinterprets this pointer as a pointer to an array of T, then returns the
// element at position 'index' in this array of T. (Note that this position
// corresponds to position index * sizeof(T) in the underlying byte array.)
//
// Returns true if successful; false if an out-of-range error occurred or if
// the error flag was already set on the pointer when calling convert().
//
// The conversion from a sequence of sizeof(T) bytes to a T is performed in an
// implementation-defined fashion. This conversion is equivalent to the one
// obtained using the following union by filling the array 'ch' and then
// reading the member 't':
//
// union {
// T t;
// unsigned char ch[sizeof(T)];
// };
//
// Callers should note that, among other things, the conversion is not
// endian-agnostic with respect to the endianness of T.
template <class T>
bool convert(size_t index, T *output) const {
RangeCheckedBytePtr p = (*this) + index * sizeof(T);
@@ -290,9 +365,11 @@ class RangeCheckedBytePtr {
return valid;
}
// operators
// Operators. Unless otherwise noted, these operators have the same semantics
// as the same operators on an unsigned char pointer.
// this returns a 0 (static_cast<unsigned char>(0)) if out of range
// If an out-of-range access is attempted, returns 0 (and sets the error
// flag).
inline unsigned char operator[](size_t i) const;
inline unsigned char operator*() const;
@@ -331,27 +408,60 @@ class RangeCheckedBytePtr {
void restrictPageToSubArray() const;
};
// util functions
// Returns the result of calling std::memcmp() on the sequences of 'num' bytes
// pointed to by 'x' and 'y'. The result is undefined if either
// x.remainingLength() or y.remainingLength() is less than 'num'.
int memcmp(const RangeCheckedBytePtr &x, const RangeCheckedBytePtr &y,
size_t num);
// Returns the result of calling std::memcmp() (note: _not_ strcmp()) on the
// y.length() number of bytes pointed to by 'x' and the string 'y'. The result
// is undefined if x.remainingLength() is less than y.length().
int strcmp(const RangeCheckedBytePtr &x, const std::string &y);
// Returns the length of the zero-terminated string starting at 'src' (not
// including the '\0' terminator). If no '\0' occurs before the end of the
// array, the result is undefined.
size_t strlen(const RangeCheckedBytePtr &src);
// Decode 16-bit signed integer from binary input.
// Integer decoding functions.
//
// These functions read signed (Get16s, Get32s) or unsigned (Get16u, Get32u)
// integers from 'input'. The integer read from the input can be specified to be
// either big-endian (big_endian == true) or little-endian
// (little_endian == false). Signed integers are read in two's-complement
// representation. The integer read in the specified format is then converted to
// the implementation's native integer representation and returned. In other
// words, the semantics of these functions are independent of the
// implementation's endianness and signed integer representation.
//
// If an out-of-range error occurs, these functions do _not_ set the error flag
// on 'input'. Instead, they set 'status' to RANGE_CHECKED_BYTE_ERROR and return
// 0.
//
// Note:
// - If an error occurs and 'status' is already set to an error value (i.e. a
// value different from RANGE_CHECKED_BYTE_SUCCESS), the value of 'status' is
// left unchanged.
// - If the operation is successful, 'status' is left unchanged (i.e. it is not
// actively set to RANGE_CHECKED_BYTE_SUCCESS).
//
// Together, these two properties mean that these functions can be used to read
// a number of integers in succession with only a single error check, like this:
//
// MemoryStatus status = RANGE_CHECKED_BYTE_SUCCESS;
// int16 val1 = Get16s(input, false, &status);
// int32 val2 = Get32s(input + 2, false, &status);
// uint32 val3 = Get32u(input + 6, false, &status);
// if (status != RANGE_CHECKED_BYTE_SUCCESS) {
// // error handling
// }
int16 Get16s(const RangeCheckedBytePtr &input, const bool big_endian,
MemoryStatus *status);
// Decode 16-bit unsigned integer from binary input.
uint16 Get16u(const RangeCheckedBytePtr &input, const bool big_endian,
MemoryStatus *status);
// Decode 32-bit signed integer from binary input.
int32 Get32s(const RangeCheckedBytePtr &input, const bool big_endian,
MemoryStatus *status);
// Decode 32-bit unsigned integer from binary input.
uint32 Get32u(const RangeCheckedBytePtr &input, const bool big_endian,
MemoryStatus *status);
@@ -374,10 +484,6 @@ bool RangeCheckedBytePtr::errorOccurred() const {
return error_flag_ != RANGE_CHECKED_BYTE_SUCCESS;
}
bool RangeCheckedBytePtr::isValid() const {
return error_flag_ == RANGE_CHECKED_BYTE_SUCCESS;
}
unsigned char RangeCheckedBytePtr::operator[](size_t i) const {
// Check that pointer doesn't have an error flag set.
if (!errorOccurred()) {

View File

@@ -51,6 +51,9 @@ struct PreviewImageData {
Rational longitude[3];
bool altitude_ref = false; // true is above, false below sea level
Rational altitude;
Rational time_stamp[3]; // Giving hour, minute and second.
std::string date_stamp; // Giving as "YYYY:MM:DD" format.
};
// Required data to find the preview image and to handle it correctly.

View File

@@ -105,25 +105,44 @@ void FillGpsPreviewImageData(const TiffDirectory& gps_directory,
if (gps_directory.Has(kGpsTagLatitudeRef) &&
gps_directory.Has(kGpsTagLatitude) &&
gps_directory.Has(kGpsTagLongitudeRef) &&
gps_directory.Has(kGpsTagLongitude)) {
gps_directory.Has(kGpsTagLongitude) &&
gps_directory.Has(kGpsTagTimeStamp) &&
gps_directory.Has(kGpsTagDateStamp)) {
preview_image_data->gps.is_valid = false;
std::string value;
if (!gps_directory.Get(kGpsTagLatitudeRef, &value) || value.empty() ||
(value[0] != 'N' && value[0] != 'S') ||
!GetRational(kGpsTagLatitude, gps_directory, 3,
&preview_image_data->gps.latitude[0])) {
!GetRational(kGpsTagLatitude, gps_directory, 3 /* data size */,
preview_image_data->gps.latitude)) {
return;
}
preview_image_data->gps.latitude_ref = value[0];
if (!gps_directory.Get(kGpsTagLongitudeRef, &value) || value.empty() ||
(value[0] != 'E' && value[0] != 'W') ||
!GetRational(kGpsTagLongitude, gps_directory, 3,
&preview_image_data->gps.longitude[0])) {
!GetRational(kGpsTagLongitude, gps_directory, 3 /* data size */,
preview_image_data->gps.longitude)) {
return;
}
preview_image_data->gps.longitude_ref = value[0];
if (!GetRational(kGpsTagTimeStamp, gps_directory, 3 /* data size */,
preview_image_data->gps.time_stamp)) {
return;
}
constexpr size_t kGpsDateStampSize = 11;
if (!gps_directory.Get(kGpsTagDateStamp,
&preview_image_data->gps.date_stamp)) {
return;
}
if (preview_image_data->gps.date_stamp.size() == kGpsDateStampSize) {
// Resize the date_stamp to remove the "NULL" at the end of string.
preview_image_data->gps.date_stamp.resize(kGpsDateStampSize - 1);
} else {
return;
}
if (gps_directory.Has(kGpsTagAltitudeRef) &&
gps_directory.Has(kGpsTagAltitude)) {
std::vector<std::uint8_t> bytes;
@@ -533,7 +552,8 @@ Error TiffParser::Parse(const TagSet& desired_tags,
tiff_content->gps_directory.reset(new TiffDirectory(endian_));
const TagSet gps_tags = {kGpsTagLatitudeRef, kGpsTagLatitude,
kGpsTagLongitudeRef, kGpsTagLongitude,
kGpsTagAltitudeRef, kGpsTagAltitude};
kGpsTagAltitudeRef, kGpsTagAltitude,
kGpsTagTimeStamp, kGpsTagDateStamp};
return ParseDirectory(
tiff_offset_, tiff_offset_ + offset, endian_, gps_tags, stream_,
tiff_content->gps_directory.get(), &next_ifd_offset);

View File

@@ -46,6 +46,8 @@ enum Tags {
kGpsTagLongitude = 4,
kGpsTagAltitudeRef = 5,
kGpsTagAltitude = 6,
kGpsTagTimeStamp = 7,
kGpsTagDateStamp = 29,
kOlymTagAspectFrame = 0x1113,
kOlymTagCameraSettings = 0x2020,
kOlymTagRawProcessing = 0x2040,