diff --git a/src/ifd.cpp b/src/ifd.cpp index e13cebe0..94a03f78 100644 --- a/src/ifd.cpp +++ b/src/ifd.cpp @@ -20,14 +20,14 @@ */ /* File: ifd.cpp - Version: $Name: $ $Revision: 1.10 $ + Version: $Name: $ $Revision: 1.11 $ Author(s): Andreas Huggel (ahu) History: 26-Jan-04, ahu: created 11-Feb-04, ahu: isolated as a component */ // ***************************************************************************** #include "rcsid.hpp" -EXIV2_RCSID("@(#) $Name: $ $Revision: 1.10 $ $RCSfile: ifd.cpp,v $") +EXIV2_RCSID("@(#) $Name: $ $Revision: 1.11 $ $RCSfile: ifd.cpp,v $") // ***************************************************************************** // included header files @@ -104,51 +104,56 @@ namespace Exif { void Entry::setValue(uint32 data, ByteOrder byteOrder) { - if (data_ == 0) { + if (data_ == 0 || size_ < 4) { if (!alloc_) { - throw Error("Invariant alloc violated in Entry::setValue"); + throw Error("cannot allocate memory"); } - data_ = new char[4]; + size_ = 4; + delete[] data_; + data_ = new char[size_]; } - // No need to resize previously allocated memory ul2Data(data_, data, byteOrder); - size_ = 4; + // do not change size_ type_ = unsignedLong; count_ = 1; } void Entry::setValue(uint16 type, uint32 count, const char* data, long size) { - // Make sure size is always at least four bytes - long newSize = std::max(long(4), size); + long dataSize = count * TypeInfo::typeSize(TypeId(type_)); + // No minimum size requirement, but make sure the buffer can hold the data + if (size < dataSize) { + throw Error("Size too small"); + } if (alloc_) { delete[] data_; - data_ = new char[newSize]; - memset(data_, 0x0, 4); - memcpy(data_, data, size); + data_ = new char[size]; + memset(data_, 0x0, size); + memcpy(data_, data, dataSize); + size_ = size; } else { if (size_ == 0) { // Set the data pointer of a virgin entry - if (size < 4) throw Error("Size too small"); data_ = const_cast(data); + size_ = size; } else { // Overwrite existing data if it fits into the buffer - if (newSize > size_) throw Error("Size too large"); - memset(data_, 0x0, std::max(long(4), size_)); - memcpy(data_, data, size); + if (dataSize > size_) throw Error("Value too large"); + memset(data_, 0x0, size_); + memcpy(data_, data, dataSize); + // do not change size_ } } type_ = type; count_ = count; - size_ = newSize; } // Entry::setValue const char* Entry::component(uint32 n) const { if (n >= count()) return 0; - return data_ + n * typeSize(); + return data() + n * typeSize(); } // Entry::component Ifd::Ifd(IfdId ifdId) @@ -173,7 +178,7 @@ namespace Exif { Ifd::PreEntries preEntries; int n = getUShort(buf, byteOrder); - long o = 2; + long o = 2; for (int i = 0; i < n; ++i) { Ifd::PreEntry pe; @@ -217,6 +222,7 @@ namespace Exif { e.setTag(i->tag_); // Set the offset to the data, relative to start of IFD e.setOffset(i->size_ > 4 ? i->offset_ - offset_ : i->offsetLoc_); + // Set the size to at least for bytes to accomodate offset-data e.setValue(i->type_, i->count_, buf + e.offset(), std::max(long(4), i->size_)); this->add(e); @@ -281,24 +287,25 @@ namespace Exif { const iterator e = entries_.end(); iterator i = b; for (; i != e; ++i) { - us2Data(buf+o, i->tag(), byteOrder); - us2Data(buf+o+2, i->type(), byteOrder); - ul2Data(buf+o+4, i->count(), byteOrder); + us2Data(buf + o, i->tag(), byteOrder); + us2Data(buf + o + 2, i->type(), byteOrder); + ul2Data(buf + o + 4, i->count(), byteOrder); if (i->size() > 4) { // Set the offset of the entry, data immediately follows the IFD i->setOffset(size() + dataSize); - ul2Data(buf+o+8, offset_ + i->offset(), byteOrder); + ul2Data(buf + o + 8, offset_ + i->offset(), byteOrder); dataSize += i->size(); } else { // Copy data into the offset field - memcpy(buf+o+8, i->data(), 4); + memset(buf + o + 8, 0x0, 4); + memcpy(buf + o + 8, i->data(), i->size()); } o += 12; } // Add the offset to the next IFD to the data buffer - o += ul2Data(buf+o, next_, byteOrder); + o += ul2Data(buf + o, next_, byteOrder); // Add the data of all IFD entries to the data buffer for (i = b; i != e; ++i) { @@ -322,10 +329,10 @@ namespace Exif { { // Todo: Implement Assert (Stroustup 24.3.7.2) if (alloc_ != entry.alloc()) { - throw Error("Invariant alloc violated in Ifd::add"); + throw Error("Ifd::add : alloc mismatch"); } if (ifdId_ != entry.ifdId()) { - throw Error("Invariant ifdId violated in Ifd::add"); + throw Error("Ifd::add : ifdId mismatch"); } // allow duplicates entries_.push_back(entry); @@ -387,14 +394,10 @@ namespace Exif { } else { unsigned char* data = (unsigned char*)i->data(); - offset << std::setw(2) << std::setfill('0') << std::hex - << (int)data[0] << " " - << std::setw(2) << std::setfill('0') << std::hex - << (int)data[1] << " " - << std::setw(2) << std::setfill('0') << std::hex - << (int)data[2] << " " - << std::setw(2) << std::setfill('0') << std::hex - << (int)data[3] << " "; + for (int k = 0; k < i->size(); ++k) { + offset << std::setw(2) << std::setfill('0') << std::hex + << (int)data[k] << " "; + } } os << prefix << std::setw(5) << std::setfill(' ') << std::dec << std::right << i - b diff --git a/src/ifd.hpp b/src/ifd.hpp index 9d264e41..883abce9 100644 --- a/src/ifd.hpp +++ b/src/ifd.hpp @@ -21,7 +21,7 @@ /*! @file ifd.hpp @brief Encoding and decoding of IFD (Image File Directory) data - @version $Name: $ $Revision: 1.10 $ + @version $Name: $ $Revision: 1.11 $ @author Andreas Huggel (ahu) ahuggel@gmx.net @date 09-Jan-04, ahu: created @@ -89,37 +89,42 @@ namespace Exif { /*! @brief Set the value of the entry to a single unsigned long component, i.e., set the type of the entry to unsigned long, number of - components to one, size to four bytes and the value according - to the data provided. This method can be used to set the value - of a tag which contains a pointer (offset) to a location in the - %Exif data (like e.g., ExifTag, 0x8769 in IFD0, which contains a - pointer to the Exif IFD). This method cannot be used to set the - value of a newly created %Entry in non-alloc mode. + components to one and the value according to the data provided. + + The size of the data buffer is set to at least four bytes, but is left + unchanged if it can accomodate the pointer. This method can be used + to set the value of a tag which contains a pointer (offset) to a + location in the %Exif data (like e.g., ExifTag, 0x8769 in IFD0, which + contains a pointer to the %Exif IFD). +
This method cannot be used to set the value of a newly created + %Entry in non-alloc mode. */ void setValue(uint32 data, ByteOrder byteOrder); /*! - @brief Set type, count, size and the data of the entry. - - Copies the provided buffer when called in memory allocation mode. In - non-alloc mode, use this method to set the data of a newly created - %Entry. The data buffer provided must be at least four bytes to - initialise an %Entry in non-alloc mode. In this case, only the pointer - to the buffer is copied, i.e., the buffer must remain valid throughout - the life of the %Entry. Subsequent calls in non-alloc mode overwrite - the data pointed to by this pointer with the data provided, i.e., the - buffer provided in subsequent calls can be deleted after the call. -
Todo: This sounds too complicated: should I isolate the init - functionality into a separate method? + @brief Set type, count, the data buffer and its size. + Copies the provided buffer when called in memory allocation mode. +
In non-alloc mode, use this method to initialise the data of a + newly created %Entry. In this case, only the pointer to the buffer is + copied, i.e., the buffer must remain valid throughout the life of the + %Entry. Subsequent calls in non-alloc mode will overwrite the data + pointed to by this pointer with the data provided, i.e., the buffer + provided in subsequent calls can be deleted after the call. +
In either memory allocation mode, the data buffer provided must be + large enough to hold count components of type. The size of the buffer + will be as indicated in the size argument. I.e., it is possible to + allocate (set) a data buffer larger than required to hold count + components of the given type. + @param type The type of the data. @param count Number of components in the buffer. - @param data Pointer to the data buffer. - @param size Size of the data buffer in bytes. - @throw Error ("Size too large") if no memory allocation is allowed and - the size of the data in buf is greater than the existing size - of the data of the entry.
- @throw Error ("Size too small") if an attempt is made to initialise an - %Entry in non-alloc mode with a buffer with size less than four. + @param data Pointer to the data buffer. + @param size Size of the desired data buffer in bytes. + @throw Error ("Value too large") if no memory allocation is allowed + and the size of the data buffer is larger than the existing + data buffer of the entry.
+ @throw Error ("Size too small") if size is not large enough to hold + count components of the given type. */ void setValue(uint16 type, uint32 count, const char* data, long size); //@} @@ -145,8 +150,9 @@ namespace Exif { //! Return the number of components in the value uint32 count() const { return count_; } /*! - @brief Return the size of the value in bytes, it is at least four - bytes unless it is 0. + @brief Return the size of the data buffer in bytes. + @note There is no minimum size for the data buffer, except that it + must be large enough to hold the data. */ long size() const { return size_; } //! Return the offset from the start of the IFD to the data of the entry @@ -167,20 +173,32 @@ namespace Exif { private: /*! - @brief True: Requires memory allocation and deallocation,
- False: No memory management needed. + True: Requires memory allocation and deallocation,
+ False: No memory management needed. */ bool alloc_; - IfdId ifdId_; // Redundant IFD id (it is also at the IFD) - int idx_; // Unique id of an entry within an IFD (0 if not set) - MakerNote* makerNote_; // Pointer to the associated MakerNote - uint16 tag_; // Tag - uint16 type_; // Type - uint32 count_; // Number of components - uint32 offset_; // Offset from the start of the IFD to the data - long size_; // Size of the data in bytes, at least four bytes - char* data_; // Pointer to the data buffer, which is always at - // least four bytes big (or 0, if not allocated) + //! Redundant IFD id (it is also at the IFD) + IfdId ifdId_; + //! Unique id of an entry within an IFD (0 if not set) + int idx_; + //! Pointer to the associated MakerNote + MakerNote* makerNote_; + //! Tag + uint16 tag_; + //! Type + uint16 type_; + //! Number of components + uint32 count_; + //! Offset from the start of the IFD to the data + uint32 offset_; + /*! + Size of the data buffer holding the value in bytes, there is + no minimum size. + */ + long size_; + //! Pointer to the data buffer + char* data_; + }; // class Entry //! Container type to hold all IFD directory entries