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#include <c++utilities/io/binaryreader.h>
13#include <c++utilities/io/binarywriter.h>
36 return "position in set";
50 return "unsigned integer";
61 switch (tagTextEncoding) {
63 return make_pair(
"ISO-8859-1", 1.0f);
65 return make_pair(
"UTF-8", 1.0f);
67 return make_pair(
"UTF-16LE", 2.0f);
69 return make_pair(
"UTF-16BE", 2.0f);
71 return make_pair(
nullptr, 0.0f);
104 : m_size(other.m_size)
105 , m_desc(other.m_desc)
106 , m_mimeType(other.m_mimeType)
107 , m_locale(other.m_locale)
108 , m_type(other.m_type)
109 , m_encoding(other.m_encoding)
110 , m_descEncoding(other.m_descEncoding)
114 m_ptr = make_unique<char[]>(m_size);
115 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
124 if (
this == &other) {
127 m_size = other.m_size;
128 m_type = other.m_type;
129 m_desc = other.m_desc;
130 m_mimeType = other.m_mimeType;
131 m_locale = other.m_locale;
132 m_flags = other.m_flags;
133 m_encoding = other.m_encoding;
134 m_descEncoding = other.m_descEncoding;
138 m_ptr = make_unique<char[]>(m_size);
139 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
193 if (m_mimeType != other.m_mimeType || m_locale != other.m_locale || m_flags != other.m_flags) {
204 const auto utfEncodingToUse = pickUtfEncoding(m_descEncoding, other.m_descEncoding);
205 StringData str1, str2;
206 const char *data1, *data2;
208 if (m_descEncoding != utfEncodingToUse) {
210 str1 = convertString(
211 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
212 data1 = str1.first.get();
215 data1 = m_desc.data();
216 size1 = m_desc.size();
218 if (other.m_descEncoding != utfEncodingToUse) {
220 str2 = convertString(inputParameter.first, outputParameter.first, other.m_desc.data(), other.m_desc.size(),
221 outputParameter.second / inputParameter.second);
222 data2 = str2.first.get();
225 data2 = other.m_desc.data();
226 size2 = other.m_desc.size();
236 if (m_type == other.m_type) {
240 if (m_size != other.m_size && m_encoding == other.m_encoding) {
249 const auto utfEncodingToUse = pickUtfEncoding(m_encoding, other.m_encoding);
251 const char *data1, *data2;
253 if (m_encoding != utfEncodingToUse) {
261 if (other.m_encoding != utfEncodingToUse) {
262 str2 = other.
toString(utfEncodingToUse);
266 data2 = other.m_ptr.get();
267 size2 = other.m_size;
290 for (
const auto dataType : { m_type, other.m_type }) {
310 return lhs.
rating == rhs.rating && lhs.playCounter == rhs.playCounter && lhs.scale == rhs.scale
320 }
catch (
const ConversionException &) {
360 }
catch (
const ConversionException &e) {
378 switch (m_encoding) {
381 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
383 return stringToNumber<std::int32_t>(u16str);
386 return bufferToNumber<std::int32_t>(m_ptr.get(), m_size);
390 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
392 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
395 if (m_size ==
sizeof(std::int32_t)) {
396 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
398 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
403 if (unsignedInteger > std::numeric_limits<std::int32_t>::max()) {
404 throw ConversionException(argsToString(
"Unsigned integer too big for conversion to integer."));
406 return static_cast<std::int32_t
>(unsignedInteger);
409 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to integer."));
420 switch (m_encoding) {
423 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
425 return stringToNumber<std::uint64_t>(u16str);
428 return bufferToNumber<std::uint64_t>(m_ptr.get(), m_size);
435 throw ConversionException(argsToString(
"Can not convert negative value to unsigned integer."));
437 return static_cast<std::uint64_t
>(integer);
442 if (m_size ==
sizeof(std::uint64_t)) {
443 return *
reinterpret_cast<std::uint64_t *
>(m_ptr.get());
445 throw ConversionException(
"Can not convert assigned data to unsigned integer because the data size is not appropriate.");
447 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to integer."));
466 }
catch (
const ConversionException &) {
479 if (m_size ==
sizeof(std::int32_t)) {
480 index =
static_cast<int>(*
reinterpret_cast<std::int32_t *
>(m_ptr.get()));
481 }
else if (m_size ==
sizeof(std::uint64_t)) {
482 const auto unsignedInt = *
reinterpret_cast<std::uint64_t *
>(m_ptr.get());
483 if (unsignedInt <= std::numeric_limits<int>::max()) {
484 index =
static_cast<int>(unsignedInt);
489 throw ConversionException(
"The assigned index/integer is of unappropriate size.");
493 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to genre index."));
496 throw ConversionException(
"The assigned number is not a valid standard genre index.");
513 switch (m_encoding) {
516 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
526 case sizeof(std::int32_t):
527 return PositionInSet(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
528 case 2 *
sizeof(std::int32_t):
530 *(
reinterpret_cast<std::int32_t *
>(m_ptr.get())), *(
reinterpret_cast<std::int32_t *
>(m_ptr.get() +
sizeof(std::int32_t))));
532 throw ConversionException(
"The size of the assigned data is not appropriate.");
536 case sizeof(std::uint64_t): {
537 const auto track = *(
reinterpret_cast<std::uint64_t *
>(m_ptr.get()));
538 if (track < std::numeric_limits<std::int32_t>::max()) {
544 throw ConversionException(
"The size of the assigned data is not appropriate.");
546 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to position in set."));
566 case sizeof(std::int32_t):
567 return TimeSpan(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
568 case sizeof(std::int64_t):
569 return TimeSpan(*(
reinterpret_cast<std::int64_t *
>(m_ptr.get())));
571 throw ConversionException(
"The size of the assigned integer is not appropriate for conversion to time span.");
575 case sizeof(std::uint64_t): {
576 const auto ticks = *(
reinterpret_cast<std::uint64_t *
>(m_ptr.get()));
577 if (ticks < std::numeric_limits<std::int64_t>::max()) {
578 return TimeSpan(
static_cast<std::int64_t
>(ticks));
583 throw ConversionException(
"The size of the assigned data is not appropriate.");
585 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to time span."));
603 return DateTime::fromIsoStringGmt(str.data());
604 }
catch (
const ConversionException &) {
605 return DateTime::fromString(str);
611 if (m_size ==
sizeof(std::int32_t)) {
612 return DateTime(*(
reinterpret_cast<std::uint32_t *
>(m_ptr.get())));
613 }
else if (m_size ==
sizeof(std::uint64_t)) {
614 return DateTime(*(
reinterpret_cast<std::uint64_t *
>(m_ptr.get())));
616 throw ConversionException(
"The size of the assigned integer is not appropriate for conversion to date time.");
619 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
639 popularity.rating =
static_cast<double>(
toInteger());
642 auto s = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
643 auto reader = BinaryReader(&s);
645 s.exceptions(std::ios_base::failbit | std::ios_base::badbit);
646 s.rdbuf()->pubsetbuf(m_ptr.get(),
static_cast<std::streamsize
>(m_size));
647 popularity.user = reader.readLengthPrefixedString();
648 popularity.rating = reader.readFloat64LE();
649 popularity.playCounter = reader.readUInt64LE();
650 popularity.scale =
static_cast<TagType>(reader.readUInt64LE());
651 }
catch (
const std::ios_base::failure &) {
652 throw ConversionException(argsToString(
"Assigned popularity is invalid"));
660 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
676 if (m_encoding == encoding) {
680 StringData encodedData;
686 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
689 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
692 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
702 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
706 m_ptr = make_unique<char[]>(m_size = encodedData.second);
707 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
709 m_encoding = encoding;
728 if (encoding == m_descEncoding) {
731 if (m_desc.empty()) {
732 m_descEncoding = encoding;
735 StringData encodedData;
741 encodedData = convertLatin1ToUtf8(m_desc.data(), m_desc.size());
744 encodedData = convertUtf16LEToUtf8(m_desc.data(), m_desc.size());
747 encodedData = convertUtf16BEToUtf8(m_desc.data(), m_desc.size());
756 encodedData = convertString(
757 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
760 m_desc.assign(encodedData.first.get(), encodedData.second);
761 m_descEncoding = encoding;
786 result.assign(m_ptr.get(), m_size);
788 StringData encodedData;
794 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
797 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
800 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
810 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
813 result.assign(encodedData.first.get(), encodedData.second);
827 result.assign(genreName);
829 throw ConversionException(
"No string representation for the assigned standard genre index available.");
837 result =
toDateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents);
846 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
850 : convertUtf8ToUtf16BE(result.data(), result.size());
851 result.assign(encodedData.first.get(), encodedData.second);
872 string regularStrRes;
876 result.assign(
reinterpret_cast<const char16_t *
>(m_ptr.get()), m_size /
sizeof(
char16_t));
878 StringData encodedData;
884 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
887 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
890 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
900 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
903 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(
char16_t));
907 regularStrRes = numberToString(
toInteger());
915 regularStrRes.clear();
917 regularStrRes.assign(genreName);
919 throw ConversionException(
"No string representation for the assigned standard genre index available.");
927 regularStrRes =
toDateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents);
936 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
940 : convertUtf8ToUtf16BE(regularStrRes.data(), result.size());
941 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(
const char16_t));
960 stripBom(text, textSize, textEncoding);
968 m_ptr = make_unique<char[]>(m_size = textSize);
969 copy(text, text + textSize, m_ptr.get());
973 StringData encodedData;
974 switch (textEncoding) {
979 encodedData = convertUtf8ToLatin1(text, textSize);
982 encodedData = convertUtf8ToUtf16LE(text, textSize);
985 encodedData = convertUtf8ToUtf16BE(text, textSize);
994 encodedData = convertString(inputParameter.first, outputParameter.first, text, textSize, outputParameter.second / inputParameter.second);
998 m_ptr = make_unique<char[]>(m_size = encodedData.second);
999 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
1008 m_size =
sizeof(value);
1009 m_ptr = make_unique<char[]>(m_size);
1010 std::copy(
reinterpret_cast<const char *
>(&value),
reinterpret_cast<const char *
>(&value) + m_size, m_ptr.get());
1021 m_size =
sizeof(value);
1022 m_ptr = make_unique<char[]>(m_size);
1023 std::copy(
reinterpret_cast<const char *
>(&value),
reinterpret_cast<const char *
>(&value) + m_size, m_ptr.get());
1041 if (length > m_size) {
1042 m_ptr = make_unique<char[]>(length);
1045 std::copy(
data,
data + length, m_ptr.get());
1051 m_encoding = encoding;
1070 m_encoding = encoding;
1079 auto s = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
1080 auto writer = BinaryWriter(&s);
1082 s.exceptions(std::ios_base::failbit | std::ios_base::badbit);
1083 writer.writeLengthPrefixedString(value.
user);
1084 writer.writeFloat64LE(value.
rating);
1086 writer.writeUInt64LE(
static_cast<std::uint64_t
>(value.
scale));
1087 auto size =
static_cast<std::size_t
>(s.tellp());
1088 auto ptr = std::make_unique<char[]>(size);
1089 s.read(ptr.get(), s.tellp());
1091 }
catch (
const std::ios_base::failure &) {
1092 throw ConversionException(
"Unable to serialize specified Popularity");
1103 if ((length >= 3) && (BE::toUInt24(text) == 0x00EFBBBF)) {
1109 if ((length >= 2) && (LE::toUInt16(text) == 0xFEFF)) {
1115 if ((length >= 2) && (BE::toUInt16(text) == 0xFEFF)) {
1130 if (currentEncoding !=
1131#
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
1133#elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
1136#error
"Host byte order not supported"
1139 for (
auto &c : u16str) {
1140 c = swapOrder(
static_cast<std::uint16_t
>(c));
1150 if (size1 != size2) {
1157 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
1164 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
1181 return emptyTagValue;
1190 return isEmpty() ? std::string()
1200 const auto parts = splitStringSimple<std::vector<std::string_view>>(str,
"|");
1202 if (parts.empty()) {
1204 }
else if (parts.size() > 3) {
1205 throw ConversionException(
"Wrong format, expected \"rating\" or \"user|rating|play-counter\"");
1208 if (parts.size() == 1) {
1210 res.rating = stringToNumber<
decltype(res.rating)>(parts.front());
1212 }
catch (
const ConversionException &) {
1216 res.user = parts.front();
1217 if (parts.size() > 1) {
1218 res.rating = stringToNumber<
decltype(res.rating)>(parts[1]);
1220 if (parts.size() > 2) {
1221 res.playCounter = stringToNumber<
decltype(res.playCounter)>(parts[2]);
static constexpr bool isEmptyGenre(int index)
Returns whether the genre index indicates the genre field is not set at all.
static constexpr bool isIndexSupported(int index)
Returns an indication whether the specified numerical denotation is supported by this class.
static constexpr int emptyGenreIndex()
Returns the preferred genre index to indicate that no genre is set at all.
static constexpr int genreCount()
Returns the number of supported genres.
static int indexFromString(std::string_view genre)
Returns the numerical denotation of the specified genre or -1 if genre is unknown.
static std::string_view stringFromIndex(int index)
Returns the genre name for the specified numerical denotation as C-style string.
The PositionInSet class describes the position of an element in a set which consists of a certain num...
StringType toString() const
Returns the string representation of the current PositionInSet.
The TagValue class wraps values of different types.
bool compareData(const TagValue &other, bool ignoreCase=false) const
Returns whether the raw data of the current instance equals the raw data of other.
void clearMetadata()
Wipes assigned meta data.
void assignText(const char *text, std::size_t textSize, TagTextEncoding textEncoding=TagTextEncoding::Latin1, TagTextEncoding convertTo=TagTextEncoding::Unspecified)
Assigns a copy of the given text.
void assignInteger(int value)
Assigns the given integer value.
bool compareTo(const TagValue &other, TagValueComparisionFlags options=TagValueComparisionFlags::None) const
Returns whether both instances are equal.
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.
CppUtilities::DateTime toDateTime() const
Converts the value of the current TagValue object to its equivalent DateTime representation.
std::string toDisplayString() const
Returns a "display string" for the specified value.
TagTextEncoding dataEncoding() const
Returns the data encoding.
void assignData(const char *data, std::size_t length, TagDataType type=TagDataType::Binary, TagTextEncoding encoding=TagTextEncoding::Latin1)
std::uint64_t toUnsignedInteger() const
std::int32_t toInteger() const
Converts the value of the current TagValue object to its equivalent integer representation.
PositionInSet toPositionInSet() const
Converts the value of the current TagValue object to its equivalent PositionInSet representation.
TagDataType type() const
Returns the type of the assigned value.
void convertDataEncodingForTag(const Tag *tag)
Ensures the encoding of the currently assigned text value is supported by the specified tag.
void assignPopularity(const Popularity &value)
Assigns the specified popularity value.
void assignUnsignedInteger(std::uint64_t value)
Assigns the given unsigned integer value.
Popularity toPopularity() const
Converts the value of the current TagValue object to its equivalent Popularity representation.
std::string_view data() const
Returns the currently assigned raw data.
std::u16string toWString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::wstring representation.
void convertDataEncoding(TagTextEncoding encoding)
Converts the currently assigned text value to the specified encoding.
TagTextEncoding descriptionEncoding() const
Returns the description encoding.
TagValue & operator=(const TagValue &other)
Assigns the value of another TagValue to the current instance.
std::string toString(TagTextEncoding encoding=TagTextEncoding::Unspecified) const
Converts the value of the current TagValue object to its equivalent std::string representation.
static const TagValue & empty()
Returns a default-constructed TagValue where TagValue::isNull() and TagValue::isEmpty() both return t...
static void stripBom(const char *&text, std::size_t &length, TagTextEncoding encoding)
Strips the byte order mask from the specified text.
bool isEmpty() const
Returns whether no or an empty value is assigned.
void convertDescriptionEncoding(TagTextEncoding encoding)
Converts the assigned description to use the specified encoding.
int toStandardGenreIndex() const
Converts the value of the current TagValue object to its equivalent standard genre index.
CppUtilities::TimeSpan toTimeSpan() const
Converts the value of the current TagValue object to its equivalent TimeSpan representation.
TagValue()
Constructs an empty TagValue.
The Tag class is used to store, read and write tag information.
virtual TagTextEncoding proposedTextEncoding() const
Returns the proposed text encoding.
virtual bool canEncodingBeUsed(TagTextEncoding encoding) const
Returns an indication whether the specified encoding can be used to provide string values for the tag...
Contains all classes and functions of the TagInfo library.
std::string_view tagDataTypeString(TagDataType dataType)
Returns the string representation of the specified dataType.
TagTextEncoding
Specifies the text encoding.
TagType
Specifies the tag type.
pair< const char *, float > encodingParameter(TagTextEncoding tagTextEncoding)
Returns the encoding parameter (name of the character set and bytes per character) for the specified ...
TagValueComparisionFlags
The TagValueComparisionOption enum specifies options for TagValue::compareTo().
TagValueFlags
Specifies additional flags about the tag value.
TagDataType
Specifies the data type.
static constexpr unsigned char toLower(const unsigned char c)
std::string user
The user who gave the rating / played the file, e.g. identified by e-mail address.
bool isEmpty() const
Returns whether the Popularity is empty. The scale and zero-values don't count.
static Popularity fromString(std::string_view str)
Parses the popularity from str assuming the same format as toString() produces.
std::uint64_t playCounter
Play counter specific to the user.
TagType scale
Specifies the scale used for rating by the tag defining that scale.
double rating
The rating on a tag type specific scale.
std::string toString() const
Returns the popularity as string in the format "rating" if only a rating is present or in the format ...