Added CMake checks about IEEE754 compliance.
cmake_policy(SET CMP0022 OLD)
endif()
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
+
option(ENABLE_STATIC "Make static version of libtag" OFF)
if(ENABLE_STATIC)
add_definitions(-DTAGLIB_STATIC)
include(CheckTypeSize)
include(CheckCXXSourceCompiles)
include(TestBigEndian)
+include(TestFloatFormat)
-# Check if the size of integral types are suitable.
+# Check if the size of numeric types are suitable.
check_type_size("short" SIZEOF_SHORT)
if(NOT ${SIZEOF_SHORT} EQUAL 2)
MESSAGE(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
endif()
+check_type_size("float" SIZEOF_FLOAT)
+if(NOT ${SIZEOF_FLOAT} EQUAL 4)
+ MESSAGE(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
+endif()
+
+check_type_size("double" SIZEOF_DOUBLE)
+if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
+ MESSAGE(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
+endif()
+
# Determine the CPU byte order.
test_big_endian(IS_BIG_ENDIAN)
set(SYSTEM_BYTEORDER 2)
endif()
+# Check if the format of floating point types are suitable.
+
+test_float_format(FP_IEEE754)
+if(${FP_IEEE754} EQUAL 1)
+ set(FLOAT_BYTEORDER 1)
+elseif(${FP_IEEE754} EQUAL 2)
+ set(FLOAT_BYTEORDER 2)
+else()
+ MESSAGE(FATAL_ERROR "TagLib requires that floating point types are IEEE754 compliant.")
+endif()
+
+
# Determine which kind of atomic operations your compiler supports.
check_cxx_source_compiles("
--- /dev/null
+int main()
+{
+ double bin1[] = {
+ // "*TAGLIB*" encoded as a little-endian floating-point number
+ (double)3.9865557444897601e-105, (double)0.0
+ };
+ float bin2[] = {
+ // "*TL*" encoded as a little-endian floating-point number
+ (float)1.81480400e-013, (float)0.0
+ };
+
+ return 0;
+}
--- /dev/null
+# Returns 1 if IEEE754 little-endian, 2 if IEEE754 big-endian, otherwise 0.
+
+MACRO(TEST_FLOAT_FORMAT FP_IEEE754)
+ IF(NOT FP_IEEE754)
+ TRY_COMPILE(HAVE_${FP_IEEE754} "${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}/cmake/TestFloatFormat.c"
+ COPY_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin")
+
+ SET(FP_IEEE754 0)
+
+ IF(HAVE_${FP_IEEE754})
+
+ # dont match first/last letter because of string rounding errors :-)
+ FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
+ DOUBLE_IEEE754_LE LIMIT_COUNT 1 REGEX "TAGLIB")
+ FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
+ DOUBLE_IEEE754_BE LIMIT_COUNT 1 REGEX "BILGAT")
+ FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
+ FLOAT_IEEE754_LE LIMIT_COUNT 1 REGEX "TL")
+ FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
+ FLOAT_IEEE754_BE LIMIT_COUNT 1 REGEX "LT")
+
+ IF(DOUBLE_IEEE754_LE AND FLOAT_IEEE754_LE)
+ SET(FP_IEEE754_LE 1)
+ ENDIF()
+
+ IF(DOUBLE_IEEE754_BE AND FLOAT_IEEE754_BE)
+ SET(FP_IEEE754_BE 1)
+ ENDIF()
+
+ # OS X Universal binaries will contain both strings, set it to the host
+ IF(FP_IEEE754_LE AND FP_IEEE754_BE)
+ IF(CMAKE_SYSTEM_PROCESSOR MATCHES powerpc)
+ SET(FP_IEEE754_LE FALSE)
+ SET(FP_IEEE754_BE TRUE)
+ ELSE()
+ SET(FP_IEEE754_LE TRUE)
+ SET(FP_IEEE754_BE FALSE)
+ ENDIF()
+ ENDIF()
+
+ IF(FP_IEEE754_LE)
+ SET(FP_IEEE754 1)
+ ELSEIF(FP_IEEE754_BE)
+ SET(FP_IEEE754 2)
+ ENDIF()
+ ENDIF()
+
+ # just some informational output for the user
+ IF(FP_IEEE754_LE)
+ MESSAGE(STATUS "Checking the floating point format - IEEE754 (LittleEndian)")
+ ELSEIF(FP_IEEE754_BE)
+ MESSAGE(STATUS "Checking the floating point format - IEEE754 (BigEndian)")
+ ELSE()
+ MESSAGE(STATUS "Checking the floating point format - Not IEEE754 or failed to detect.")
+ ENDIF()
+
+ SET(FP_IEEE754 "${${FP_IEEE754}}" CACHE INTERNAL "Result of TEST_FLOAT_FORMAT" FORCE)
+ ENDIF()
+ENDMACRO(TEST_FLOAT_FORMAT FP_IEEE754)
+
/* config.h. Generated by cmake from config.h.cmake */
-/* Indicates the byte order of your target system */
+/* Integer byte order of your target system */
/* 1 if little-endian, 2 if big-endian. */
#cmakedefine SYSTEM_BYTEORDER ${SYSTEM_BYTEORDER}
+/* IEEE754 byte order of your target system. */
+/* 1 if little-endian, 2 if big-endian. */
+#cmakedefine FLOAT_BYTEORDER ${FLOAT_BYTEORDER}
+
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP_16 1
#cmakedefine HAVE_GCC_BYTESWAP_32 1
#include <tstring.h>
#include <tdebug.h>
-#include <cmath>
-// ldexp is a c99 function, which might not be defined in <cmath>
-// so we pull in math.h too and hope it does the right (wrong) thing
-// wrt. c99 functions in C++
-#include <math.h>
-
#include "aiffproperties.h"
-////////////////////////////////////////////////////////////////////////////////
-// nasty 80-bit float helpers
-////////////////////////////////////////////////////////////////////////////////
-
-#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)
-
-static double ConvertFromIeeeExtended(const TagLib::uchar *bytes)
-{
- double f;
- int expon;
- unsigned long hiMant, loMant;
-
- expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
-
- hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) |
- ((unsigned long)(bytes[3] & 0xFF) << 16) |
- ((unsigned long)(bytes[4] & 0xFF) << 8) |
- ((unsigned long)(bytes[5] & 0xFF));
-
- loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) |
- ((unsigned long)(bytes[7] & 0xFF) << 16) |
- ((unsigned long)(bytes[8] & 0xFF) << 8) |
- ((unsigned long)(bytes[9] & 0xFF));
-
- if (expon == 0 && hiMant == 0 && loMant == 0)
- f = 0;
- else {
- if(expon == 0x7FFF) /* Infinity or NaN */
- f = HUGE_VAL;
- else {
- expon -= 16383;
- f = ldexp(UnsignedToFloat(hiMant), expon -= 31);
- f += ldexp(UnsignedToFloat(loMant), expon -= 32);
- }
- }
-
- if(bytes[0] & 0x80)
- return -f;
- else
- return f;
-}
-
using namespace TagLib;
class RIFF::AIFF::Properties::PropertiesPrivate
d->channels = data.toShort(0U);
d->sampleFrames = data.toUInt(2U);
d->sampleWidth = data.toShort(6U);
- double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<const uchar *>(data.data() + 8));
+ const long double sampleRate = data.toFloat80BE(8);
d->sampleRate = (int)sampleRate;
d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0);
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
#include <algorithm>
#include <iostream>
+#include <limits>
+#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstddef>
return ByteVector(reinterpret_cast<const char *>(&value), sizeof(T));
}
+template <typename TFloat, typename TInt, Utils::ByteOrder ENDIAN>
+TFloat toFloat(const ByteVector &v, size_t offset)
+{
+ if (offset > v.size() - sizeof(TInt)) {
+ debug("toFloat() - offset is out of range. Returning 0.");
+ return 0.0;
+ }
+
+ union {
+ TInt i;
+ TFloat f;
+ } tmp;
+ ::memcpy(&tmp, v.data() + offset, sizeof(TInt));
+
+ if(ENDIAN != Utils::FloatByteOrder)
+ tmp.i = Utils::byteSwap(tmp.i);
+
+ return tmp.f;
+}
+
+template <typename TFloat, typename TInt, Utils::ByteOrder ENDIAN>
+ByteVector fromFloat(TFloat value)
+{
+ union {
+ TInt i;
+ TFloat f;
+ } tmp;
+ tmp.f = value;
+
+ if(ENDIAN != Utils::FloatByteOrder)
+ tmp.i = Utils::byteSwap(tmp.i);
+
+ return ByteVector(reinterpret_cast<char *>(&tmp), sizeof(TInt));
+}
+
+template <Utils::ByteOrder ENDIAN>
+long double toFloat80(const ByteVector &v, size_t offset)
+{
+ if(offset > v.size() - 10) {
+ debug("toFloat80() - offset is out of range. Returning 0.");
+ return 0.0;
+ }
+
+ uchar bytes[10];
+ ::memcpy(bytes, v.data() + offset, 10);
+
+ if(ENDIAN == Utils::LittleEndian) {
+ std::swap(bytes[0], bytes[9]);
+ std::swap(bytes[1], bytes[8]);
+ std::swap(bytes[2], bytes[7]);
+ std::swap(bytes[3], bytes[6]);
+ std::swap(bytes[4], bytes[5]);
+ }
+
+ // 1-bit sign
+ const bool negative = ((bytes[0] & 0x80) != 0);
+
+ // 15-bit exponent
+ const int exponent = ((bytes[0] & 0x7F) << 8) | bytes[1];
+
+ // 64-bit fraction. Leading 1 is explicit.
+ const ulonglong fraction
+ = (static_cast<ulonglong>(bytes[2]) << 56)
+ | (static_cast<ulonglong>(bytes[3]) << 48)
+ | (static_cast<ulonglong>(bytes[4]) << 40)
+ | (static_cast<ulonglong>(bytes[5]) << 32)
+ | (static_cast<ulonglong>(bytes[6]) << 24)
+ | (static_cast<ulonglong>(bytes[7]) << 16)
+ | (static_cast<ulonglong>(bytes[8]) << 8)
+ | (static_cast<ulonglong>(bytes[9]));
+
+ long double val;
+ if(exponent == 0 && fraction == 0)
+ val = 0;
+ else {
+ if(exponent == 0x7FFF) {
+ debug("toFloat80() - can't handle the infinity or NaN. Returning 0.");
+ return 0.0;
+ }
+ else
+ val = ::ldexp(static_cast<long double>(fraction), exponent - 16383 - 63);
+ }
+
+ if(negative)
+ return -val;
+ else
+ return val;
+}
+
class DataPrivate : public RefCounter
{
public:
return fromNumber<unsigned long long>(value, mostSignificantByteFirst);
}
+ByteVector ByteVector::fromFloat32LE(float value)
+{
+ return fromFloat<float, uint, Utils::LittleEndian>(value);
+}
+
+ByteVector ByteVector::fromFloat32BE(float value)
+{
+ return fromFloat<float, uint, Utils::BigEndian>(value);
+}
+
+ByteVector ByteVector::fromFloat64LE(double value)
+{
+ return fromFloat<double, ulonglong, Utils::LittleEndian>(value);
+}
+
+ByteVector ByteVector::fromFloat64BE(double value)
+{
+ return fromFloat<double, ulonglong, Utils::BigEndian>(value);
+}
+
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
return toNumber<unsigned long long>(*this, offset, mostSignificantByteFirst);
}
+float ByteVector::toFloat32LE(size_t offset) const
+{
+ return toFloat<float, uint, Utils::LittleEndian>(*this, offset);
+}
+
+float ByteVector::toFloat32BE(size_t offset) const
+{
+ return toFloat<float, uint, Utils::BigEndian>(*this, offset);
+}
+
+double ByteVector::toFloat64LE(size_t offset) const
+{
+ return toFloat<double, ulonglong, Utils::LittleEndian>(*this, offset);
+}
+
+double ByteVector::toFloat64BE(size_t offset) const
+{
+ return toFloat<double, ulonglong, Utils::BigEndian>(*this, offset);
+}
+
+long double ByteVector::toFloat80LE(size_t offset) const
+{
+ return toFloat80<Utils::LittleEndian>(*this, offset);
+}
+
+long double ByteVector::toFloat80BE(size_t offset) const
+{
+ return toFloat80<Utils::BigEndian>(*this, offset);
+}
+
const char &ByteVector::operator[](int index) const
{
return d->data->data[d->offset + index];
uint toUInt(bool mostSignificantByteFirst = true) const;
/*!
- * Converts the 4 bytes at \a offset of the vector to an unsigned integer.
+ * Converts the 4 bytes at \a offset of the vector to an unsigned integer.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
uint toUInt(uint offset, bool mostSignificantByteFirst = true) const;
/*!
- * Converts the \a length bytes at \a offset of the vector to an unsigned
- * integer. If \a length is larger than 4, the excess is ignored.
+ * Converts the \a length bytes at \a offset of the vector to an unsigned
+ * integer. If \a length is larger than 4, the excess is ignored.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
*/
long long toLongLong(uint offset, bool mostSignificantByteFirst = true) const;
+ /*
+ * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754
+ * 32-bit little-endian floating point number.
+ */
+ float toFloat32LE(size_t offset) const;
+
+ /*
+ * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754
+ * 32-bit big-endian floating point number.
+ */
+ float toFloat32BE(size_t offset) const;
+
+ /*
+ * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754
+ * 64-bit little-endian floating point number.
+ */
+ double toFloat64LE(size_t offset) const;
+
+ /*
+ * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754
+ * 64-bit big-endian floating point number.
+ */
+ double toFloat64BE(size_t offset) const;
+
+ /*
+ * Converts the 10 bytes at \a offset of the vector to a long double as an
+ * IEEE754 80-bit little-endian floating point number.
+ *
+ * \note This may compromise the precision depends on the size of long double.
+ */
+ long double toFloat80LE(size_t offset) const;
+
+ /*
+ * Converts the 10 bytes at \a offset of the vector to a long double as an
+ * IEEE754 80-bit big-endian floating point number.
+ *
+ * \note This may compromise the precision depends on the size of long double.
+ */
+ long double toFloat80BE(size_t offset) const;
+
/*!
* Creates a 4 byte ByteVector based on \a value. If
* \a mostSignificantByteFirst is true, then this will operate left to right
*/
static ByteVector fromLongLong(long long value, bool mostSignificantByteFirst = true);
+ /*!
+ * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit
+ * little-endian floating point number.
+ *
+ * \see fromFloat32BE()
+ */
+ static ByteVector fromFloat32LE(float value);
+
+ /*!
+ * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit
+ * big-endian floating point number.
+ *
+ * \see fromFloat32LE()
+ */
+ static ByteVector fromFloat32BE(float value);
+
+ /*!
+ * Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit
+ * little-endian floating point number.
+ *
+ * \see fromFloat64BE()
+ */
+ static ByteVector fromFloat64LE(double value);
+
+ /*!
+ * Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit
+ * big-endian floating point number.
+ *
+ * \see fromFloat64LE()
+ */
+ static ByteVector fromFloat64BE(double value);
+
/*!
* Returns a ByteVector based on the CString \a s.
*/
# if SYSTEM_BYTEORDER == 1
- const ByteOrder SystemByteOrder = LittleEndian;
+ const ByteOrder SystemByteOrder = LittleEndian;
# else
- const ByteOrder SystemByteOrder = BigEndian;
+ const ByteOrder SystemByteOrder = BigEndian;
# endif
else
return BigEndian;
}
-
- const ByteOrder SystemByteOrder = systemByteOrder();
+
+ const ByteOrder SystemByteOrder = systemByteOrder();
+
+#endif
+
+#ifdef FLOAT_BYTEORDER
+
+# if FLOAT_BYTEORDER == 1
+
+ const ByteOrder FloatByteOrder = LittleEndian;
+
+# else
+
+ const ByteOrder FloatByteOrder = BigEndian;
+
+# endif
+
+#else
+
+ inline ByteOrder floatByteOrder()
+ {
+ double bin[] = {
+ // "*TAGLIB*" encoded as a little-endian floating-point number
+ (double) 3.9865557444897601e-105, (double) 0.0
+ };
+
+ char *str = (char*)&bin[0];
+ if(strncmp(&str[1], "TAGLIB", 6) == 0)
+ return LittleEndian;
+ else
+ return BigEndian;
+ }
+
+ const ByteOrder FloatByteOrder = floatByteOrder();
#endif
}
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <cstring>
#include <tbytevector.h>
#include <tbytevectorlist.h>
#include <cppunit/extensions/HelperMacros.h>
CPPUNIT_TEST(testRfind1);
CPPUNIT_TEST(testRfind2);
CPPUNIT_TEST(testToHex);
- CPPUNIT_TEST(testToUShort);
+ CPPUNIT_TEST(testNumericCoversion);
CPPUNIT_TEST(testReplace);
CPPUNIT_TEST_SUITE_END();
CPPUNIT_ASSERT_EQUAL(ByteVector("f0e1d2c3b4a5968778695a4b3c2d1e0f"), v.toHex());
}
- void testToUShort()
+ void testNumericCoversion()
{
CPPUNIT_ASSERT_EQUAL((unsigned short)0xFFFF, ByteVector("\xff\xff", 2).toUShort());
CPPUNIT_ASSERT_EQUAL((unsigned short)0x0001, ByteVector("\x00\x01", 2).toUShort());
CPPUNIT_ASSERT_EQUAL((unsigned short)0x0100, ByteVector("\x00\x01", 2).toUShort(false));
CPPUNIT_ASSERT_EQUAL((unsigned short)0xFF01, ByteVector("\xFF\x01", 2).toUShort());
CPPUNIT_ASSERT_EQUAL((unsigned short)0x01FF, ByteVector("\xFF\x01", 2).toUShort(false));
+
+ const uchar PI32LE[] = { 0x00, 0xdb, 0x0f, 0x49, 0x40 };
+ const uchar PI32BE[] = { 0x00, 0x40, 0x49, 0x0f, 0xdb };
+ const uchar PI64LE[] = { 0x00, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40 };
+ const uchar PI64BE[] = { 0x00, 0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18 };
+ const uchar PI80LE[] = { 0x00, 0x00, 0xc0, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0x00, 0x40 };
+ const uchar PI80BE[] = { 0x00, 0x40, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc0, 0x00 };
+
+ ByteVector pi32le(reinterpret_cast<const char*>(PI32LE), 5);
+ CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi32le.toFloat32LE(1) * 10000));
+
+ ByteVector pi32be(reinterpret_cast<const char*>(PI32BE), 5);
+ CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi32be.toFloat32BE(1) * 10000));
+
+ ByteVector pi64le(reinterpret_cast<const char*>(PI64LE), 9);
+ CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi64le.toFloat64LE(1) * 10000));
+
+ ByteVector pi64be(reinterpret_cast<const char*>(PI64BE), 9);
+ CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi64be.toFloat64BE(1) * 10000));
+
+ ByteVector pi80le(reinterpret_cast<const char*>(PI80LE), 11);
+ CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi80le.toFloat80LE(1) * 10000));
+
+ ByteVector pi80be(reinterpret_cast<const char*>(PI80BE), 11);
+ CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi80be.toFloat80BE(1) * 10000));
+
+ ByteVector pi32le2 = ByteVector::fromFloat32LE(pi32le.toFloat32LE(1));
+ CPPUNIT_ASSERT(memcmp(pi32le.data() + 1, pi32le2.data(), 4) == 0);
+
+ ByteVector pi32be2 = ByteVector::fromFloat32BE(pi32be.toFloat32BE(1));
+ CPPUNIT_ASSERT(memcmp(pi32be.data() + 1, pi32be2.data(), 4) == 0);
+
+ ByteVector pi64le2 = ByteVector::fromFloat64LE(pi64le.toFloat64LE(1));
+ CPPUNIT_ASSERT(memcmp(pi64le.data() + 1, pi64le2.data(), 8) == 0);
+
+ ByteVector pi64be2 = ByteVector::fromFloat64BE(pi64be.toFloat64BE(1));
+ CPPUNIT_ASSERT(memcmp(pi64be.data() + 1, pi64be2.data(), 8) == 0);
}
void testReplace()