Tag Parser  10.0.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
tagvalue.cpp
Go to the documentation of this file.
1 #include "./tagvalue.h"
2 
4 #include "./tag.h"
5 
6 #include "./id3/id3genres.h"
7 
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>
12 
13 #include <algorithm>
14 #include <cstring>
15 #include <utility>
16 
17 using namespace std;
18 using namespace CppUtilities;
19 
20 namespace TagParser {
21 
25 std::string_view tagDataTypeString(TagDataType dataType)
26 {
27  switch (dataType) {
28  case TagDataType::Text:
29  return "text";
30  case TagDataType::Integer:
31  return "integer";
32  case TagDataType::PositionInSet:
33  return "position in set";
34  case TagDataType::StandardGenreIndex:
35  return "genre index";
36  case TagDataType::TimeSpan:
37  return "time span";
39  return "date time";
40  case TagDataType::Picture:
41  return "picture";
42  case TagDataType::Binary:
43  return "binary";
44  default:
45  return "undefined";
46  }
47 }
48 
52 pair<const char *, float> encodingParameter(TagTextEncoding tagTextEncoding)
53 {
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);
63  default:
64  return make_pair(nullptr, 0.0f);
65  }
66 }
67 
96 TagValue::TagValue(const TagValue &other)
97  : m_size(other.m_size)
98  , m_desc(other.m_desc)
99  , m_mimeType(other.m_mimeType)
100  , m_locale(other.m_locale)
101  , m_type(other.m_type)
102  , m_encoding(other.m_encoding)
103  , m_descEncoding(other.m_descEncoding)
104  , m_flags(TagValueFlags::None)
105 {
106  if (!other.isEmpty()) {
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());
109  }
110 }
111 
116 {
117  if (this == &other) {
118  return *this;
119  }
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_locale = other.m_locale;
125  m_flags = other.m_flags;
126  m_encoding = other.m_encoding;
127  m_descEncoding = other.m_descEncoding;
128  if (other.isEmpty()) {
129  m_ptr.reset();
130  } else {
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());
133  }
134  return *this;
135 }
136 
138 TagTextEncoding pickUtfEncoding(TagTextEncoding encoding1, TagTextEncoding encoding2)
139 {
140  switch (encoding1) {
144  return encoding1;
145  default:
146  switch (encoding2) {
150  return encoding2;
151  default:;
152  }
153  }
154  return TagTextEncoding::Utf8;
155 }
157 
181 bool TagValue::compareTo(const TagValue &other, TagValueComparisionFlags options) const
182 {
183  // check whether meta-data is equal (except description)
184  if (!(options & TagValueComparisionFlags::IgnoreMetaData)) {
185  // check meta-data which always uses UTF-8 (everything but description)
186  if (m_mimeType != other.m_mimeType || m_locale != other.m_locale || m_flags != other.m_flags) {
187  return false;
188  }
189 
190  // check description which might use different encodings
191  if (m_descEncoding == other.m_descEncoding || m_descEncoding == TagTextEncoding::Unspecified
192  || other.m_descEncoding == TagTextEncoding::Unspecified || m_desc.empty() || other.m_desc.empty()) {
193  if (!compareData(m_desc, other.m_desc, options & TagValueComparisionFlags::CaseInsensitive)) {
194  return false;
195  }
196  } else {
197  const auto utfEncodingToUse = pickUtfEncoding(m_descEncoding, other.m_descEncoding);
198  StringData str1, str2;
199  const char *data1, *data2;
200  size_t size1, size2;
201  if (m_descEncoding != utfEncodingToUse) {
202  const auto inputParameter = encodingParameter(m_descEncoding), outputParameter = encodingParameter(utfEncodingToUse);
203  str1 = convertString(
204  inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
205  data1 = str1.first.get();
206  size1 = str1.second;
207  } else {
208  data1 = m_desc.data();
209  size1 = m_desc.size();
210  }
211  if (other.m_descEncoding != utfEncodingToUse) {
212  const auto inputParameter = encodingParameter(other.m_descEncoding), outputParameter = encodingParameter(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();
216  size2 = str2.second;
217  } else {
218  data2 = other.m_desc.data();
219  size2 = other.m_desc.size();
220  }
221  if (!compareData(data1, size1, data2, size2, options & TagValueComparisionFlags::CaseInsensitive)) {
222  return false;
223  }
224  }
225  }
226 
227  // check for equality if both types are identical
228  if (m_type == other.m_type) {
229  switch (m_type) {
230  case TagDataType::Text: {
231  // compare raw data directly if the encoding is the same
232  if (m_size != other.m_size && m_encoding == other.m_encoding) {
233  return false;
234  }
235  if (m_encoding == other.m_encoding || m_encoding == TagTextEncoding::Unspecified || other.m_encoding == TagTextEncoding::Unspecified) {
237  }
238 
239  // compare UTF-8 or UTF-16 representation of strings avoiding unnecessary conversions
240  const auto utfEncodingToUse = pickUtfEncoding(m_encoding, other.m_encoding);
241  string str1, str2;
242  const char *data1, *data2;
243  size_t size1, size2;
244  if (m_encoding != utfEncodingToUse) {
245  str1 = toString(utfEncodingToUse);
246  data1 = str1.data();
247  size1 = str1.size();
248  } else {
249  data1 = m_ptr.get();
250  size1 = m_size;
251  }
252  if (other.m_encoding != utfEncodingToUse) {
253  str2 = other.toString(utfEncodingToUse);
254  data2 = str2.data();
255  size2 = str2.size();
256  } else {
257  data2 = other.m_ptr.get();
258  size2 = other.m_size;
259  }
260  return compareData(data1, size1, data2, size2, options & TagValueComparisionFlags::CaseInsensitive);
261  }
263  return toPositionInSet() == other.toPositionInSet();
265  return toInteger() == other.toInteger();
267  return toStandardGenreIndex() == other.toStandardGenreIndex();
269  return toTimeSpan() == other.toTimeSpan();
271  return toDateTime() == other.toDateTime();
273  case TagDataType::Binary:
275  return compareData(other);
276  }
277  return false;
278  }
279 
280  // check for equality if types are different by comparing the string representation (if that makes sense)
281  for (const auto dataType : { m_type, other.m_type }) {
282  switch (dataType) {
286  case TagDataType::Binary:
288  // do not attempt to convert these types to string because it will always fail anyways
289  return false;
290  default:;
291  }
292  }
293  try {
294  return compareData(toString(), other.toString(m_encoding), options & TagValueComparisionFlags::CaseInsensitive);
295  } catch (const ConversionException &) {
296  return false;
297  }
298 }
299 
307 {
308  m_desc.clear();
309  m_mimeType.clear();
310  m_locale.clear();
311  m_flags = TagValueFlags::None;
312  m_encoding = TagTextEncoding::Latin1;
313  m_descEncoding = TagTextEncoding::Latin1;
314  m_type = TagDataType::Undefined;
315 }
316 
322 std::int32_t TagValue::toInteger() const
323 {
324  if (isEmpty()) {
325  return 0;
326  }
327  switch (m_type) {
328  case TagDataType::Text:
329  switch (m_encoding) {
332  auto u16str = u16string(reinterpret_cast<char16_t *>(m_ptr.get()), m_size / 2);
333  ensureHostByteOrder(u16str, m_encoding);
334  return stringToNumber<std::int32_t>(u16str);
335  }
336  default:
337  return bufferToNumber<std::int32_t>(m_ptr.get(), m_size);
338  }
340  if (m_size == sizeof(PositionInSet)) {
341  return *reinterpret_cast<std::int32_t *>(m_ptr.get());
342  }
343  throw ConversionException("Can not convert assigned data to integer because the data size is not appropriate.");
346  if (m_size == sizeof(std::int32_t)) {
347  return *reinterpret_cast<std::int32_t *>(m_ptr.get());
348  }
349  throw ConversionException("Can not convert assigned data to integer because the data size is not appropriate.");
350  default:
351  throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to integer."));
352  }
353 }
354 
361 {
362  if (isEmpty()) {
364  }
365  int index = 0;
366  switch (m_type) {
367  case TagDataType::Text: {
368  try {
369  index = toInteger();
370  } catch (const ConversionException &) {
372  if (m_encoding == TagTextEncoding::Latin1) {
373  // no need to convert Latin-1 to UTF-8 (makes no difference in case of genre strings)
374  encoding = TagTextEncoding::Unspecified;
375  }
376  index = Id3Genres::indexFromString(toString(encoding));
377  }
378  break;
379  }
382  if (m_size != sizeof(std::int32_t)) {
383  throw ConversionException("The assigned index/integer is of unappropriate size.");
384  }
385  index = static_cast<int>(*reinterpret_cast<std::int32_t *>(m_ptr.get()));
386  break;
387  default:
388  throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to genre index."));
389  }
390  if (!Id3Genres::isEmptyGenre(index) && !Id3Genres::isIndexSupported(index)) {
391  throw ConversionException("The assigned number is not a valid standard genre index.");
392  }
393  return index;
394 }
395 
402 {
403  if (isEmpty()) {
404  return PositionInSet();
405  }
406  switch (m_type) {
407  case TagDataType::Text:
408  switch (m_encoding) {
411  auto u16str = u16string(reinterpret_cast<char16_t *>(m_ptr.get()), m_size / 2);
412  ensureHostByteOrder(u16str, m_encoding);
413  return PositionInSet(u16str);
414  }
415  default:
416  return PositionInSet(string(m_ptr.get(), m_size));
417  }
420  switch (m_size) {
421  case sizeof(std::int32_t):
422  return PositionInSet(*(reinterpret_cast<std::int32_t *>(m_ptr.get())));
423  case 2 * sizeof(std::int32_t):
424  return PositionInSet(
425  *(reinterpret_cast<std::int32_t *>(m_ptr.get())), *(reinterpret_cast<std::int32_t *>(m_ptr.get() + sizeof(std::int32_t))));
426  default:
427  throw ConversionException("The size of the assigned data is not appropriate.");
428  }
429  default:
430  throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to position in set."));
431  }
432 }
433 
440 {
441  if (isEmpty()) {
442  return TimeSpan();
443  }
444  switch (m_type) {
445  case TagDataType::Text:
446  return TimeSpan::fromString(toString(m_encoding == TagTextEncoding::Utf8 ? TagTextEncoding::Utf8 : TagTextEncoding::Latin1));
449  switch (m_size) {
450  case sizeof(std::int32_t):
451  return TimeSpan(*(reinterpret_cast<std::int32_t *>(m_ptr.get())));
452  case sizeof(std::int64_t):
453  return TimeSpan(*(reinterpret_cast<std::int64_t *>(m_ptr.get())));
454  default:
455  throw ConversionException("The size of the assigned integer is not appropriate for conversion to time span.");
456  }
457  default:
458  throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to time span."));
459  }
460 }
461 
467 DateTime TagValue::toDateTime() const
468 {
469  if (isEmpty()) {
470  return DateTime();
471  }
472  switch (m_type) {
473  case TagDataType::Text: {
475  try {
476  return DateTime::fromIsoStringGmt(str.data());
477  } catch (const ConversionException &) {
478  return DateTime::fromString(str);
479  }
480  }
483  if (m_size == sizeof(std::int32_t)) {
484  return DateTime(*(reinterpret_cast<std::uint32_t *>(m_ptr.get())));
485  } else if (m_size == sizeof(std::int64_t)) {
486  return DateTime(*(reinterpret_cast<std::uint64_t *>(m_ptr.get())));
487  } else {
488  throw ConversionException("The size of the assigned integer is not appropriate for conversion to date time.");
489  }
490  default:
491  throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to date time."));
492  }
493 }
494 
505 {
506  if (m_encoding == encoding) {
507  return;
508  }
509  if (type() == TagDataType::Text) {
510  StringData encodedData;
511  switch (encoding) {
513  // use pre-defined methods when encoding to UTF-8
514  switch (dataEncoding()) {
516  encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
517  break;
519  encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
520  break;
522  encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
523  break;
524  default:;
525  }
526  break;
527  default: {
528  // otherwise, determine input and output parameter to use general covertString method
529  const auto inputParameter = encodingParameter(dataEncoding());
530  const auto outputParameter = encodingParameter(encoding);
531  encodedData
532  = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
533  }
534  }
535  // can't just move the encoded data because it needs to be deleted with free
536  m_ptr = make_unique<char[]>(m_size = encodedData.second);
537  copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
538  }
539  m_encoding = encoding;
540 }
541 
547 {
548  if (type() == TagDataType::Text && !tag->canEncodingBeUsed(dataEncoding())) {
550  }
551 }
552 
557 {
558  if (encoding == m_descEncoding) {
559  return;
560  }
561  if (m_desc.empty()) {
562  m_descEncoding = encoding;
563  return;
564  }
565  StringData encodedData;
566  switch (encoding) {
568  // use pre-defined methods when encoding to UTF-8
569  switch (descriptionEncoding()) {
571  encodedData = convertLatin1ToUtf8(m_desc.data(), m_desc.size());
572  break;
574  encodedData = convertUtf16LEToUtf8(m_desc.data(), m_desc.size());
575  break;
577  encodedData = convertUtf16BEToUtf8(m_desc.data(), m_desc.size());
578  break;
579  default:;
580  }
581  break;
582  default: {
583  // otherwise, determine input and output parameter to use general covertString method
584  const auto inputParameter = encodingParameter(m_descEncoding);
585  const auto outputParameter = encodingParameter(encoding);
586  encodedData = convertString(
587  inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
588  }
589  }
590  m_desc.assign(encodedData.first.get(), encodedData.second);
591  m_descEncoding = encoding;
592 }
593 
606 void TagValue::toString(string &result, TagTextEncoding encoding) const
607 {
608  if (isEmpty()) {
609  result.clear();
610  return;
611  }
612 
613  switch (m_type) {
614  case TagDataType::Text:
615  if (encoding == TagTextEncoding::Unspecified || dataEncoding() == TagTextEncoding::Unspecified || encoding == dataEncoding()) {
616  result.assign(m_ptr.get(), m_size);
617  } else {
618  StringData encodedData;
619  switch (encoding) {
621  // use pre-defined methods when encoding to UTF-8
622  switch (dataEncoding()) {
624  encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
625  break;
627  encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
628  break;
630  encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
631  break;
632  default:;
633  }
634  break;
635  default: {
636  // otherwise, determine input and output parameter to use general covertString method
637  const auto inputParameter = encodingParameter(dataEncoding());
638  const auto outputParameter = encodingParameter(encoding);
639  encodedData
640  = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
641  }
642  }
643  result.assign(encodedData.first.get(), encodedData.second);
644  }
645  return;
647  result = numberToString(toInteger());
648  break;
650  result = toPositionInSet().toString();
651  break;
653  const auto genreIndex = toInteger();
654  if (Id3Genres::isEmptyGenre(genreIndex)) {
655  result.clear();
656  } else if (const auto genreName = Id3Genres::stringFromIndex(genreIndex); !genreName.empty()) {
657  result.assign(genreName);
658  } else {
659  throw ConversionException("No string representation for the assigned standard genre index available.");
660  }
661  break;
662  }
664  result = toTimeSpan().toString();
665  break;
667  result = toDateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents);
668  break;
669  default:
670  throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to string."));
671  }
673  auto encodedData = encoding == TagTextEncoding::Utf16LittleEndian ? convertUtf8ToUtf16LE(result.data(), result.size())
674  : convertUtf8ToUtf16BE(result.data(), result.size());
675  result.assign(encodedData.first.get(), encodedData.second);
676  }
677 }
678 
689 void TagValue::toWString(std::u16string &result, TagTextEncoding encoding) const
690 {
691  if (isEmpty()) {
692  result.clear();
693  return;
694  }
695 
696  string regularStrRes;
697  switch (m_type) {
698  case TagDataType::Text:
699  if (encoding == TagTextEncoding::Unspecified || encoding == dataEncoding()) {
700  result.assign(reinterpret_cast<const char16_t *>(m_ptr.get()), m_size / sizeof(char16_t));
701  } else {
702  StringData encodedData;
703  switch (encoding) {
705  // use pre-defined methods when encoding to UTF-8
706  switch (dataEncoding()) {
708  encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
709  break;
711  encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
712  break;
714  encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
715  break;
716  default:;
717  }
718  break;
719  default: {
720  // otherwise, determine input and output parameter to use general covertString method
721  const auto inputParameter = encodingParameter(dataEncoding());
722  const auto outputParameter = encodingParameter(encoding);
723  encodedData
724  = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
725  }
726  }
727  result.assign(reinterpret_cast<const char16_t *>(encodedData.first.get()), encodedData.second / sizeof(char16_t));
728  }
729  return;
731  regularStrRes = numberToString(toInteger());
732  break;
734  regularStrRes = toPositionInSet().toString();
735  break;
737  const auto genreIndex = toInteger();
738  if (Id3Genres::isEmptyGenre(genreIndex)) {
739  regularStrRes.clear();
740  } else if (const auto genreName = Id3Genres::stringFromIndex(genreIndex); !genreName.empty()) {
741  regularStrRes.assign(genreName);
742  } else {
743  throw ConversionException("No string representation for the assigned standard genre index available.");
744  }
745  break;
746  }
748  regularStrRes = toTimeSpan().toString();
749  break;
750  default:
751  throw ConversionException(argsToString("Can not convert ", tagDataTypeString(m_type), " to string."));
752  }
754  auto encodedData = encoding == TagTextEncoding::Utf16LittleEndian ? convertUtf8ToUtf16LE(regularStrRes.data(), result.size())
755  : convertUtf8ToUtf16BE(regularStrRes.data(), result.size());
756  result.assign(reinterpret_cast<const char16_t *>(encodedData.first.get()), encodedData.second / sizeof(const char16_t));
757  }
758 }
759 
770 void TagValue::assignText(const char *text, std::size_t textSize, TagTextEncoding textEncoding, TagTextEncoding convertTo)
771 {
772  m_type = TagDataType::Text;
773  m_encoding = convertTo == TagTextEncoding::Unspecified ? textEncoding : convertTo;
774 
775  stripBom(text, textSize, textEncoding);
776  if (!textSize) {
777  m_size = 0;
778  m_ptr.reset();
779  return;
780  }
781 
782  if (convertTo == TagTextEncoding::Unspecified || textEncoding == convertTo) {
783  m_ptr = make_unique<char[]>(m_size = textSize);
784  copy(text, text + textSize, m_ptr.get());
785  return;
786  }
787 
788  StringData encodedData;
789  switch (textEncoding) {
791  // use pre-defined methods when encoding to UTF-8
792  switch (convertTo) {
794  encodedData = convertUtf8ToLatin1(text, textSize);
795  break;
797  encodedData = convertUtf8ToUtf16LE(text, textSize);
798  break;
800  encodedData = convertUtf8ToUtf16BE(text, textSize);
801  break;
802  default:;
803  }
804  break;
805  default: {
806  // otherwise, determine input and output parameter to use general covertString method
807  const auto inputParameter = encodingParameter(textEncoding);
808  const auto outputParameter = encodingParameter(convertTo);
809  encodedData = convertString(inputParameter.first, outputParameter.first, text, textSize, outputParameter.second / inputParameter.second);
810  }
811  }
812  // can't just move the encoded data because it needs to be deleted with free
813  m_ptr = make_unique<char[]>(m_size = encodedData.second);
814  copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
815 }
816 
821 void TagValue::assignInteger(int value)
822 {
823  m_size = sizeof(value);
824  m_ptr = make_unique<char[]>(m_size);
825  std::copy(reinterpret_cast<const char *>(&value), reinterpret_cast<const char *>(&value) + m_size, m_ptr.get());
826  m_type = TagDataType::Integer;
827  m_encoding = TagTextEncoding::Latin1;
828 }
829 
838 void TagValue::assignData(const char *data, size_t length, TagDataType type, TagTextEncoding encoding)
839 {
840  if (type == TagDataType::Text) {
841  stripBom(data, length, encoding);
842  }
843  if (length > m_size) {
844  m_ptr = make_unique<char[]>(length);
845  }
846  if (length) {
847  std::copy(data, data + length, m_ptr.get());
848  } else {
849  m_ptr.reset();
850  }
851  m_size = length;
852  m_type = type;
853  m_encoding = encoding;
854 }
855 
868 void TagValue::assignData(unique_ptr<char[]> &&data, size_t length, TagDataType type, TagTextEncoding encoding)
869 {
870  m_size = length;
871  m_type = type;
872  m_encoding = encoding;
873  m_ptr = move(data);
874 }
875 
879 void TagValue::stripBom(const char *&text, size_t &length, TagTextEncoding encoding)
880 {
881  switch (encoding) {
883  if ((length >= 3) && (BE::toUInt24(text) == 0x00EFBBBF)) {
884  text += 3;
885  length -= 3;
886  }
887  break;
889  if ((length >= 2) && (LE::toUInt16(text) == 0xFEFF)) {
890  text += 2;
891  length -= 2;
892  }
893  break;
895  if ((length >= 2) && (BE::toUInt16(text) == 0xFEFF)) {
896  text += 2;
897  length -= 2;
898  }
899  break;
900  default:;
901  }
902 }
903 
908 void TagValue::ensureHostByteOrder(u16string &u16str, TagTextEncoding currentEncoding)
909 {
910  if (currentEncoding !=
911 #if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
913 #elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
915 #else
916 #error "Host byte order not supported"
917 #endif
918  ) {
919  for (auto &c : u16str) {
920  c = swapOrder(static_cast<std::uint16_t>(c));
921  }
922  }
923 }
924 
928 bool TagValue::compareData(const char *data1, std::size_t size1, const char *data2, std::size_t size2, bool ignoreCase)
929 {
930  if (size1 != size2) {
931  return false;
932  }
933  if (!size1) {
934  return true;
935  }
936  if (ignoreCase) {
937  for (auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
938  if (CaseInsensitiveCharComparer::toLower(static_cast<unsigned char>(*i1))
939  != CaseInsensitiveCharComparer::toLower(static_cast<unsigned char>(*i2))) {
940  return false;
941  }
942  }
943  } else {
944  for (auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
945  if (*i1 != *i2) {
946  return false;
947  }
948  }
949  }
950  return true;
951 }
952 
959 {
960  static TagValue emptyTagValue;
961  return emptyTagValue;
962 }
963 
964 } // namespace TagParser
static constexpr bool isEmptyGenre(int index)
Returns whether the genre index indicates the genre field is not set at all.
Definition: id3genres.h:53
static constexpr bool isIndexSupported(int index)
Returns an indication whether the specified numerical denotation is supported by this class.
Definition: id3genres.h:62
static constexpr int emptyGenreIndex()
Returns the preferred genre index to indicate that no genre is set at all.
Definition: id3genres.h:45
static int indexFromString(std::string_view genre)
Returns the numerical denotation of the specified genre or -1 if genre is unknown.
Definition: id3genres.cpp:42
static std::string_view stringFromIndex(int index)
Returns the genre name for the specified numerical denotation as C-style string.
Definition: id3genres.h:27
The PositionInSet class describes the position of an element in a set which consists of a certain num...
Definition: positioninset.h:21
StringType toString() const
Returns the string representation of the current PositionInSet.
The TagValue class wraps values of different types.
Definition: tagvalue.h:95
bool compareData(const TagValue &other, bool ignoreCase=false) const
Returns whether the raw data of the current instance equals the raw data of other.
Definition: tagvalue.h:791
void clearMetadata()
Wipes assigned meta data.
Definition: tagvalue.cpp:306
void assignText(const char *text, std::size_t textSize, TagTextEncoding textEncoding=TagTextEncoding::Latin1, TagTextEncoding convertTo=TagTextEncoding::Unspecified)
Assigns a copy of the given text.
Definition: tagvalue.cpp:770
void assignInteger(int value)
Assigns the given integer value.
Definition: tagvalue.cpp:821
bool compareTo(const TagValue &other, TagValueComparisionFlags options=TagValueComparisionFlags::None) const
Returns whether both instances are equal.
Definition: tagvalue.cpp:181
static void ensureHostByteOrder(std::u16string &u16str, TagTextEncoding currentEncoding)
Ensures the byte-order of the specified UTF-16 string matches the byte-order of the machine.
Definition: tagvalue.cpp:908
CppUtilities::DateTime toDateTime() const
Converts the value of the current TagValue object to its equivalent DateTime representation.
Definition: tagvalue.cpp:467
TagTextEncoding dataEncoding() const
Returns the data encoding.
Definition: tagvalue.h:755
void assignData(const char *data, std::size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
std::int32_t toInteger() const
Converts the value of the current TagValue object to its equivalent integer representation.
Definition: tagvalue.cpp:322
PositionInSet toPositionInSet() const
Converts the value of the current TagValue object to its equivalent PositionInSet representation.
Definition: tagvalue.cpp:401
TagDataType type() const
Returns the type of the assigned value.
Definition: tagvalue.h:468
void convertDataEncodingForTag(const Tag *tag)
Ensures the encoding of the currently assigned text value is supported by the specified tag.
Definition: tagvalue.cpp:546
std::string_view data() const
Returns the currently assigned raw data.
Definition: tagvalue.h:583
std::u16string toWString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::wstring representation.
Definition: tagvalue.h:498
void convertDataEncoding(TagTextEncoding encoding)
Converts the currently assigned text value to the specified encoding.
Definition: tagvalue.cpp:504
TagTextEncoding descriptionEncoding() const
Returns the description encoding.
Definition: tagvalue.h:765
TagValue & operator=(const TagValue &other)
Assigns the value of another TagValue to the current instance.
Definition: tagvalue.cpp:115
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
Definition: tagvalue.h:485
static const TagValue & empty()
Returns a default-constructed TagValue where TagValue::isNull() and TagValue::isEmpty() both return t...
Definition: tagvalue.cpp:958
static void stripBom(const char *&text, std::size_t &length, TagTextEncoding encoding)
Strips the byte order mask from the specified text.
Definition: tagvalue.cpp:879
bool isEmpty() const
Returns whether no or an empty value is assigned.
Definition: tagvalue.h:527
void convertDescriptionEncoding(TagTextEncoding encoding)
Converts the assigned description to use the specified encoding.
Definition: tagvalue.cpp:556
int toStandardGenreIndex() const
Converts the value of the current TagValue object to its equivalent standard genre index.
Definition: tagvalue.cpp:360
CppUtilities::TimeSpan toTimeSpan() const
Converts the value of the current TagValue object to its equivalent TimeSpan representation.
Definition: tagvalue.cpp:439
The Tag class is used to store, read and write tag information.
Definition: tag.h:108
virtual TagTextEncoding proposedTextEncoding() const
Returns the proposed text encoding.
Definition: tag.h:159
virtual bool canEncodingBeUsed(TagTextEncoding encoding) const
Returns an indication whether the specified encoding can be used to provide string values for the tag...
Definition: tag.h:164
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
std::string_view tagDataTypeString(TagDataType dataType)
Returns the string representation of the specified dataType.
Definition: tagvalue.cpp:25
TagTextEncoding
Specifies the text encoding.
Definition: tagvalue.h:28
TagValueComparisionFlags
The TagValueComparisionOption enum specifies options for TagValue::compareTo().
Definition: tagvalue.h:89
TagValueFlags
Specifies additional flags about the tag value.
Definition: tagvalue.h:42
TagDataType
Specifies the data type.
Definition: tagvalue.h:74
pair< const char *, float > encodingParameter(TagTextEncoding tagTextEncoding)
Returns the encoding parameter (name of the character set and bytes per character) for the specified ...
Definition: tagvalue.cpp:52
static constexpr unsigned char toLower(const unsigned char c)