8 #include <c++utilities/conversion/binaryconversion.h>
9 #include <c++utilities/conversion/conversionexception.h>
10 #include <c++utilities/conversion/stringbuilder.h>
11 #include <c++utilities/conversion/stringconversion.h>
28 case TagDataType::Text:
30 case TagDataType::Integer:
32 case TagDataType::PositionInSet:
33 return "position in set";
34 case TagDataType::StandardGenreIndex:
36 case TagDataType::TimeSpan:
40 case TagDataType::Picture:
42 case TagDataType::Binary:
54 switch (tagTextEncoding) {
55 case TagTextEncoding::Latin1:
56 return make_pair(
"ISO-8859-1", 1.0f);
58 return make_pair(
"UTF-8", 1.0f);
59 case TagTextEncoding::Utf16LittleEndian:
60 return make_pair(
"UTF-16LE", 2.0f);
61 case TagTextEncoding::Utf16BigEndian:
62 return make_pair(
"UTF-16BE", 2.0f);
64 return make_pair(
nullptr, 0.0f);
97 : m_size(other.m_size)
98 , m_desc(other.m_desc)
99 , m_mimeType(other.m_mimeType)
100 , m_language(other.m_language)
101 , m_type(other.m_type)
102 , m_encoding(other.m_encoding)
103 , m_descEncoding(other.m_descEncoding)
104 , m_labeledAsReadonly(other.m_labeledAsReadonly)
107 m_ptr = make_unique<char[]>(m_size);
108 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
117 if (
this == &other) {
120 m_size = other.m_size;
121 m_type = other.m_type;
122 m_desc = other.m_desc;
123 m_mimeType = other.m_mimeType;
124 m_language = other.m_language;
125 m_labeledAsReadonly = other.m_labeledAsReadonly;
126 m_encoding = other.m_encoding;
127 m_descEncoding = other.m_descEncoding;
131 m_ptr = make_unique<char[]>(m_size);
132 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
142 case TagTextEncoding::Utf16LittleEndian:
143 case TagTextEncoding::Utf16BigEndian:
148 case TagTextEncoding::Utf16LittleEndian:
149 case TagTextEncoding::Utf16BigEndian:
186 if (m_mimeType != other.m_mimeType || m_language != other.m_language || m_labeledAsReadonly != other.m_labeledAsReadonly) {
193 if (!
compareData(m_desc, other.m_desc, options & TagValueComparisionFlags::CaseInsensitive)) {
197 const auto utfEncodingToUse = pickUtfEncoding(m_descEncoding, other.m_descEncoding);
198 StringData str1, str2;
199 const char *data1, *data2;
201 if (m_descEncoding != utfEncodingToUse) {
203 str1 = convertString(
204 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
205 data1 = str1.first.get();
208 data1 = m_desc.data();
209 size1 = m_desc.size();
211 if (other.m_descEncoding != utfEncodingToUse) {
213 str2 = convertString(inputParameter.first, outputParameter.first, other.m_desc.data(), other.m_desc.size(),
214 outputParameter.second / inputParameter.second);
215 data2 = str2.first.get();
218 data2 = other.m_desc.data();
219 size2 = other.m_desc.size();
221 if (!
compareData(data1, size1, data2, size2, options & TagValueComparisionFlags::CaseInsensitive)) {
228 if (m_type == other.m_type) {
230 case TagDataType::Text: {
232 if (m_size != other.m_size && m_encoding == other.m_encoding) {
236 return compareData(other, options & TagValueComparisionFlags::CaseInsensitive);
240 const auto utfEncodingToUse = pickUtfEncoding(m_encoding, other.m_encoding);
242 const char *data1, *data2;
244 if (m_encoding != utfEncodingToUse) {
252 if (other.m_encoding != utfEncodingToUse) {
253 str2 = other.
toString(utfEncodingToUse);
257 data2 = other.m_ptr.get();
258 size2 = other.m_size;
260 return compareData(data1, size1, data2, size2, options & TagValueComparisionFlags::CaseInsensitive);
264 case TagDataType::Integer:
266 case TagDataType::StandardGenreIndex:
268 case TagDataType::TimeSpan:
272 case TagDataType::Picture:
273 case TagDataType::Binary:
281 for (
const auto dataType : { m_type, other.m_type }) {
283 case TagDataType::TimeSpan:
285 case TagDataType::Picture:
286 case TagDataType::Binary:
295 }
catch (
const ConversionException &) {
312 m_labeledAsReadonly =
false;
313 m_encoding = TagTextEncoding::Latin1;
314 m_descEncoding = TagTextEncoding::Latin1;
329 case TagDataType::Text:
330 switch (m_encoding) {
332 case TagTextEncoding::Latin1:
334 return bufferToNumber<std::int32_t>(m_ptr.get(), m_size);
335 case TagTextEncoding::Utf16LittleEndian:
336 case TagTextEncoding::Utf16BigEndian:
337 u16string u16str(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
339 return stringToNumber<std::int32_t>(u16str);
343 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
345 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
346 case TagDataType::Integer:
347 case TagDataType::StandardGenreIndex:
348 if (m_size ==
sizeof(std::int32_t)) {
349 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
351 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
353 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to integer."));
369 case TagDataType::Text: {
372 }
catch (
const ConversionException &) {
374 if (m_encoding == TagTextEncoding::Latin1) {
382 case TagDataType::StandardGenreIndex:
383 case TagDataType::Integer:
384 if (m_size !=
sizeof(std::int32_t)) {
385 throw ConversionException(
"The assigned index/integer is of unappropriate size.");
387 index =
static_cast<int>(*
reinterpret_cast<std::int32_t *
>(m_ptr.get()));
390 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to genre index."));
393 throw ConversionException(
"The assigned number is not a valid standard genre index.");
409 case TagDataType::Text:
410 switch (m_encoding) {
412 case TagTextEncoding::Latin1:
415 case TagTextEncoding::Utf16LittleEndian:
416 case TagTextEncoding::Utf16BigEndian:
417 u16string u16str(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
421 case TagDataType::Integer:
424 case sizeof(std::int32_t):
425 return PositionInSet(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
426 case 2 *
sizeof(std::int32_t):
428 *(
reinterpret_cast<std::int32_t *
>(m_ptr.get())), *(
reinterpret_cast<std::int32_t *
>(m_ptr.get() +
sizeof(std::int32_t))));
430 throw ConversionException(
"The size of the assigned data is not appropriate.");
433 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to position in set."));
448 case TagDataType::Text:
450 case TagDataType::Integer:
451 case TagDataType::TimeSpan:
453 case sizeof(std::int32_t):
454 return TimeSpan(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
455 case sizeof(std::int64_t):
456 return TimeSpan(*(
reinterpret_cast<std::int64_t *
>(m_ptr.get())));
458 throw ConversionException(
"The size of the assigned integer is not appropriate for conversion to time span.");
461 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to time span."));
476 case TagDataType::Text: {
479 return DateTime::fromIsoStringGmt(str.data());
480 }
catch (
const ConversionException &) {
481 return DateTime::fromString(str);
484 case TagDataType::Integer:
486 if (m_size ==
sizeof(std::int32_t)) {
487 return DateTime(*(
reinterpret_cast<std::uint32_t *
>(m_ptr.get())));
488 }
else if (m_size ==
sizeof(std::int64_t)) {
489 return DateTime(*(
reinterpret_cast<std::uint64_t *
>(m_ptr.get())));
491 throw ConversionException(
"The size of the assigned integer is not appropriate for conversion to date time.");
494 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
509 if (m_encoding == encoding) {
512 if (
type() == TagDataType::Text) {
513 StringData encodedData;
518 case TagTextEncoding::Latin1:
519 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
521 case TagTextEncoding::Utf16LittleEndian:
522 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
524 case TagTextEncoding::Utf16BigEndian:
525 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
535 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
539 m_ptr = make_unique<char[]>(m_size = encodedData.second);
540 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
542 m_encoding = encoding;
561 if (encoding == m_descEncoding) {
564 if (m_desc.empty()) {
565 m_descEncoding = encoding;
568 StringData encodedData;
573 case TagTextEncoding::Latin1:
574 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
576 case TagTextEncoding::Utf16LittleEndian:
577 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
579 case TagTextEncoding::Utf16BigEndian:
580 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
589 encodedData = convertString(
590 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
593 m_desc.assign(encodedData.first.get(), encodedData.second);
594 m_descEncoding = encoding;
617 case TagDataType::Text:
619 result.assign(m_ptr.get(), m_size);
621 StringData encodedData;
626 case TagTextEncoding::Latin1:
627 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
629 case TagTextEncoding::Utf16LittleEndian:
630 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
632 case TagTextEncoding::Utf16BigEndian:
633 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
643 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
646 result.assign(encodedData.first.get(), encodedData.second);
649 case TagDataType::Integer:
655 case TagDataType::StandardGenreIndex: {
660 result.assign(genreName);
662 throw ConversionException(
"No string representation for the assigned standard genre index available.");
666 case TagDataType::TimeSpan:
670 result =
toDateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents);
673 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
675 if (encoding == TagTextEncoding::Utf16LittleEndian || encoding == TagTextEncoding::Utf16BigEndian) {
676 auto encodedData = encoding == TagTextEncoding::Utf16LittleEndian ? convertUtf8ToUtf16LE(result.data(), result.size())
677 : convertUtf8ToUtf16BE(result.data(), result.size());
678 result.assign(encodedData.first.get(), encodedData.second);
699 string regularStrRes;
701 case TagDataType::Text:
703 result.assign(
reinterpret_cast<const char16_t *
>(m_ptr.get()), m_size /
sizeof(char16_t));
705 StringData encodedData;
710 case TagTextEncoding::Latin1:
711 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
713 case TagTextEncoding::Utf16LittleEndian:
714 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
716 case TagTextEncoding::Utf16BigEndian:
717 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
727 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
730 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(char16_t));
733 case TagDataType::Integer:
734 regularStrRes = numberToString(
toInteger());
739 case TagDataType::StandardGenreIndex: {
742 regularStrRes.clear();
744 regularStrRes.assign(genreName);
746 throw ConversionException(
"No string representation for the assigned standard genre index available.");
750 case TagDataType::TimeSpan:
754 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
756 if (encoding == TagTextEncoding::Utf16LittleEndian || encoding == TagTextEncoding::Utf16BigEndian) {
757 auto encodedData = encoding == TagTextEncoding::Utf16LittleEndian ? convertUtf8ToUtf16LE(regularStrRes.data(), result.size())
758 : convertUtf8ToUtf16BE(regularStrRes.data(), result.size());
759 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(
const char16_t));
775 m_type = TagDataType::Text;
778 stripBom(text, textSize, textEncoding);
786 m_ptr = make_unique<char[]>(m_size = textSize);
787 copy(text, text + textSize, m_ptr.get());
791 StringData encodedData;
792 switch (textEncoding) {
796 case TagTextEncoding::Latin1:
797 encodedData = convertUtf8ToLatin1(text, textSize);
799 case TagTextEncoding::Utf16LittleEndian:
800 encodedData = convertUtf8ToUtf16LE(text, textSize);
802 case TagTextEncoding::Utf16BigEndian:
803 encodedData = convertUtf8ToUtf16BE(text, textSize);
812 encodedData = convertString(inputParameter.first, outputParameter.first, text, textSize, outputParameter.second / inputParameter.second);
816 m_ptr = make_unique<char[]>(m_size = encodedData.second);
817 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
826 m_size =
sizeof(value);
827 m_ptr = make_unique<char[]>(m_size);
828 std::copy(
reinterpret_cast<const char *
>(&value),
reinterpret_cast<const char *
>(&value) + m_size, m_ptr.get());
829 m_type = TagDataType::Integer;
830 m_encoding = TagTextEncoding::Latin1;
843 if (
type == TagDataType::Text) {
846 if (length > m_size) {
847 m_ptr = make_unique<char[]>(length);
850 std::copy(data, data + length, m_ptr.get());
856 m_encoding = encoding;
875 m_encoding = encoding;
886 if ((length >= 3) && (BE::toUInt24(text) == 0x00EFBBBF)) {
891 case TagTextEncoding::Utf16LittleEndian:
892 if ((length >= 2) && (LE::toUInt16(text) == 0xFEFF)) {
897 case TagTextEncoding::Utf16BigEndian:
898 if ((length >= 2) && (BE::toUInt16(text) == 0xFEFF)) {
913 if (currentEncoding !=
914 #
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
915 TagTextEncoding::Utf16LittleEndian
916 #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
917 TagTextEncoding::Utf16BigEndian
919 #error
"Host byte order not supported"
922 for (
auto &c : u16str) {
923 c = swapOrder(
static_cast<std::uint16_t
>(c));
931 bool TagValue::compareData(
const char *data1, std::size_t size1,
const char *data2, std::size_t size2,
bool ignoreCase)
933 if (size1 != size2) {
940 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
947 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
964 return emptyTagValue;