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);
124 : m_size(other.m_size)
125 , m_desc(other.m_desc)
126 , m_mimeType(other.m_mimeType)
127 , m_locale(other.m_locale)
128 , m_type(other.m_type)
129 , m_encoding(other.m_encoding)
130 , m_descEncoding(other.m_descEncoding)
134 m_ptr = make_unique<char[]>(m_size);
135 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
144 if (
this == &other) {
147 m_size = other.m_size;
148 m_type = other.m_type;
149 m_desc = other.m_desc;
150 m_mimeType = other.m_mimeType;
151 m_locale = other.m_locale;
152 m_flags = other.m_flags;
153 m_encoding = other.m_encoding;
154 m_descEncoding = other.m_descEncoding;
158 m_ptr = make_unique<char[]>(m_size);
159 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
213 if (m_mimeType != other.m_mimeType || m_locale != other.m_locale || m_flags != other.m_flags) {
224 const auto utfEncodingToUse = pickUtfEncoding(m_descEncoding, other.m_descEncoding);
225 StringData str1, str2;
226 const char *data1, *data2;
228 if (m_descEncoding != utfEncodingToUse) {
230 str1 = convertString(
231 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
232 data1 = str1.first.get();
235 data1 = m_desc.data();
236 size1 = m_desc.size();
238 if (other.m_descEncoding != utfEncodingToUse) {
240 str2 = convertString(inputParameter.first, outputParameter.first, other.m_desc.data(), other.m_desc.size(),
241 outputParameter.second / inputParameter.second);
242 data2 = str2.first.get();
245 data2 = other.m_desc.data();
246 size2 = other.m_desc.size();
256 if (m_type == other.m_type) {
260 if (m_size != other.m_size && m_encoding == other.m_encoding) {
269 const auto utfEncodingToUse = pickUtfEncoding(m_encoding, other.m_encoding);
271 const char *data1, *data2;
273 if (m_encoding != utfEncodingToUse) {
281 if (other.m_encoding != utfEncodingToUse) {
282 str2 = other.
toString(utfEncodingToUse);
286 data2 = other.m_ptr.get();
287 size2 = other.m_size;
310 for (
const auto dataType : { m_type, other.m_type }) {
330 return lhs.
rating == rhs.rating && lhs.playCounter == rhs.playCounter && lhs.scale == rhs.scale
340 }
catch (
const ConversionException &) {
380 }
catch (
const ConversionException &e) {
398 switch (m_encoding) {
401 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
403 return stringToNumber<std::int32_t>(u16str);
406 return bufferToNumber<std::int32_t>(m_ptr.get(), m_size);
410 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
412 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
415 if (m_size ==
sizeof(std::int32_t)) {
416 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
418 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
423 if (unsignedInteger > std::numeric_limits<std::int32_t>::max()) {
424 throw ConversionException(argsToString(
"Unsigned integer too big for conversion to integer."));
426 return static_cast<std::int32_t
>(unsignedInteger);
429 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to integer."));
440 switch (m_encoding) {
443 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
445 return stringToNumber<std::uint64_t>(u16str);
448 return bufferToNumber<std::uint64_t>(m_ptr.get(), m_size);
455 throw ConversionException(argsToString(
"Can not convert negative value to unsigned integer."));
457 return static_cast<std::uint64_t
>(integer);
462 if (m_size ==
sizeof(std::uint64_t)) {
463 return *
reinterpret_cast<std::uint64_t *
>(m_ptr.get());
465 throw ConversionException(
"Can not convert assigned data to unsigned integer because the data size is not appropriate.");
467 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to integer."));
486 }
catch (
const ConversionException &) {
499 if (m_size ==
sizeof(std::int32_t)) {
500 index =
static_cast<int>(*
reinterpret_cast<std::int32_t *
>(m_ptr.get()));
501 }
else if (m_size ==
sizeof(std::uint64_t)) {
502 const auto unsignedInt = *
reinterpret_cast<std::uint64_t *
>(m_ptr.get());
503 if (unsignedInt <= std::numeric_limits<int>::max()) {
504 index =
static_cast<int>(unsignedInt);
509 throw ConversionException(
"The assigned index/integer is of unappropriate size.");
513 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to genre index."));
516 throw ConversionException(
"The assigned number is not a valid standard genre index.");
533 switch (m_encoding) {
536 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
546 case sizeof(std::int32_t):
547 return PositionInSet(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
548 case 2 *
sizeof(std::int32_t):
550 *(
reinterpret_cast<std::int32_t *
>(m_ptr.get())), *(
reinterpret_cast<std::int32_t *
>(m_ptr.get() +
sizeof(std::int32_t))));
552 throw ConversionException(
"The size of the assigned data is not appropriate.");
556 case sizeof(std::uint64_t): {
557 const auto track = *(
reinterpret_cast<std::uint64_t *
>(m_ptr.get()));
558 if (track < std::numeric_limits<std::int32_t>::max()) {
564 throw ConversionException(
"The size of the assigned data is not appropriate.");
566 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to position in set."));
586 case sizeof(std::int32_t):
587 return TimeSpan(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
588 case sizeof(std::int64_t):
589 return TimeSpan(*(
reinterpret_cast<std::int64_t *
>(m_ptr.get())));
591 throw ConversionException(
"The size of the assigned integer is not appropriate for conversion to time span.");
595 case sizeof(std::uint64_t): {
596 const auto ticks = *(
reinterpret_cast<std::uint64_t *
>(m_ptr.get()));
597 if (ticks < std::numeric_limits<std::int64_t>::max()) {
598 return TimeSpan(
static_cast<std::int64_t
>(ticks));
603 throw ConversionException(
"The size of the assigned data is not appropriate.");
605 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to time span."));
623 return DateTime::fromIsoStringGmt(str.data());
624 }
catch (
const ConversionException &) {
625 return DateTime::fromString(str);
631 if (m_size ==
sizeof(std::int32_t)) {
632 return DateTime(*(
reinterpret_cast<std::uint32_t *
>(m_ptr.get())));
633 }
else if (m_size ==
sizeof(std::uint64_t)) {
634 return DateTime(*(
reinterpret_cast<std::uint64_t *
>(m_ptr.get())));
636 throw ConversionException(
"The size of the assigned integer is not appropriate for conversion to date time.");
639 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
667 popularity.rating =
static_cast<double>(
toInteger());
670 auto s = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
671 auto reader = BinaryReader(&s);
673 s.exceptions(std::ios_base::failbit | std::ios_base::badbit);
674 s.rdbuf()->pubsetbuf(m_ptr.get(),
static_cast<std::streamsize
>(m_size));
675 popularity.user = reader.readLengthPrefixedString();
676 popularity.rating = reader.readFloat64LE();
677 popularity.playCounter = reader.readUInt64LE();
678 popularity.scale =
static_cast<TagType>(reader.readUInt64LE());
679 }
catch (
const std::ios_base::failure &) {
680 throw ConversionException(argsToString(
"Assigned popularity is invalid"));
688 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
716 popularity.scale = scale;
717 }
else if (!popularity.scaleTo(scale)) {
718 throw ConversionException(argsToString(
"Assigned popularity cannot be scaled accordingly"));
734 if (m_encoding == encoding) {
738 StringData encodedData;
744 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
747 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
750 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
760 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
764 m_ptr = make_unique<char[]>(m_size = encodedData.second);
765 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
767 m_encoding = encoding;
786 if (encoding == m_descEncoding) {
789 if (m_desc.empty()) {
790 m_descEncoding = encoding;
793 StringData encodedData;
799 encodedData = convertLatin1ToUtf8(m_desc.data(), m_desc.size());
802 encodedData = convertUtf16LEToUtf8(m_desc.data(), m_desc.size());
805 encodedData = convertUtf16BEToUtf8(m_desc.data(), m_desc.size());
814 encodedData = convertString(
815 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
818 m_desc.assign(encodedData.first.get(), encodedData.second);
819 m_descEncoding = encoding;
845 result.assign(m_ptr.get(), m_size);
847 StringData encodedData;
853 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
856 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
859 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
869 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
872 result.assign(encodedData.first.get(), encodedData.second);
886 result.assign(genreName);
888 throw ConversionException(
"No string representation for the assigned standard genre index available.");
896 result =
toDateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents);
905 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
909 : convertUtf8ToUtf16BE(result.data(), result.size());
910 result.assign(encodedData.first.get(), encodedData.second);
931 string regularStrRes;
935 result.assign(
reinterpret_cast<const char16_t *
>(m_ptr.get()), m_size /
sizeof(
char16_t));
937 StringData encodedData;
943 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
946 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
949 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
959 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
962 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(
char16_t));
966 regularStrRes = numberToString(
toInteger());
974 regularStrRes.clear();
976 regularStrRes.assign(genreName);
978 throw ConversionException(
"No string representation for the assigned standard genre index available.");
986 regularStrRes =
toDateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents);
995 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
999 : convertUtf8ToUtf16BE(regularStrRes.data(), result.size());
1000 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(
const char16_t));
1019 stripBom(text, textSize, textEncoding);
1027 m_ptr = make_unique<char[]>(m_size = textSize);
1028 copy(text, text + textSize, m_ptr.get());
1032 StringData encodedData;
1033 switch (textEncoding) {
1036 switch (convertTo) {
1038 encodedData = convertUtf8ToLatin1(text, textSize);
1041 encodedData = convertUtf8ToUtf16LE(text, textSize);
1044 encodedData = convertUtf8ToUtf16BE(text, textSize);
1053 encodedData = convertString(inputParameter.first, outputParameter.first, text, textSize, outputParameter.second / inputParameter.second);
1057 m_ptr = make_unique<char[]>(m_size = encodedData.second);
1058 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
1067 m_size =
sizeof(value);
1068 m_ptr = make_unique<char[]>(m_size);
1069 std::copy(
reinterpret_cast<const char *
>(&value),
reinterpret_cast<const char *
>(&value) + m_size, m_ptr.get());
1080 m_size =
sizeof(value);
1081 m_ptr = make_unique<char[]>(m_size);
1082 std::copy(
reinterpret_cast<const char *
>(&value),
reinterpret_cast<const char *
>(&value) + m_size, m_ptr.get());
1100 if (length > m_size) {
1101 m_ptr = make_unique<char[]>(length);
1104 std::copy(
data,
data + length, m_ptr.get());
1110 m_encoding = encoding;
1129 m_encoding = encoding;
1138 auto s = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
1139 auto writer = BinaryWriter(&s);
1141 s.exceptions(std::ios_base::failbit | std::ios_base::badbit);
1142 writer.writeLengthPrefixedString(value.
user);
1143 writer.writeFloat64LE(value.
rating);
1145 writer.writeUInt64LE(
static_cast<std::uint64_t
>(value.
scale));
1146 auto size =
static_cast<std::size_t
>(s.tellp());
1147 auto ptr = std::make_unique<char[]>(size);
1148 s.read(ptr.get(), s.tellp());
1150 }
catch (
const std::ios_base::failure &) {
1151 throw ConversionException(
"Unable to serialize specified Popularity");
1162 if ((length >= 3) && (BE::toUInt24(text) == 0x00EFBBBF)) {
1168 if ((length >= 2) && (LE::toUInt16(text) == 0xFEFF)) {
1174 if ((length >= 2) && (BE::toUInt16(text) == 0xFEFF)) {
1189 if (currentEncoding !=
1190#
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
1192#elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
1195#error
"Host byte order not supported"
1198 for (
auto &c : u16str) {
1199 c = swapOrder(
static_cast<std::uint16_t
>(c));
1209 if (size1 != size2) {
1216 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
1223 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
1240 return emptyTagValue;
1257 if (
scale == targetScale) {
1262 double genericRating;
1268 genericRating =
rating / (5.0 / 4.0) + 1.0;
1271 genericRating =
rating < 1.0 ? 0.0 : ((
rating - 1.0) / (254.0 / 4.0) + 1.0);
1275 genericRating =
rating / 20.0;
1282 switch (targetScale) {
1287 rating = (genericRating - 1.0) * (5.0 / 4.0);
1290 rating = genericRating < 1.0 ? 0.0 : ((genericRating - 1.0) * (254.0 / 4.0) + 1.0);
1294 rating = genericRating * 20.0;
1300 scale = targetScale;
1310 return isEmpty() ? std::string()
1333 const auto parts = splitStringSimple<std::vector<std::string_view>>(str,
"|");
1335 if (parts.empty()) {
1337 }
else if (parts.size() > 3) {
1338 throw ConversionException(
"Wrong format, expected \"rating\" or \"user|rating|play-counter\"");
1341 if (parts.size() == 1) {
1343 res.rating = stringToNumber<
decltype(res.rating)>(parts.front());
1345 }
catch (
const ConversionException &) {
1349 res.user = parts.front();
1350 if (parts.size() > 1) {
1351 res.rating = stringToNumber<
decltype(res.rating)>(parts[1]);
1353 if (parts.size() > 2) {
1354 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.
Popularity toScaledPopularity(TagType scale=TagType::Unspecified) const
Converts the value of the current TagValue object to its equivalent Popularity representation using t...
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 and sets TagType::Unsp...
std::uint64_t playCounter
Play counter specific to the user.
bool scaleTo(TagType targetScale)
Scales the rating from the current scale to targetScale.
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 ...