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>
39 return "position in set";
53 return "unsigned integer";
55 return "date time expression";
66 switch (tagTextEncoding) {
68 return make_pair(
"ISO-8859-1", 1.0f);
70 return make_pair(
"UTF-8", 1.0f);
72 return make_pair(
"UTF-16LE", 2.0f);
74 return make_pair(
"UTF-16BE", 2.0f);
76 return make_pair(
nullptr, 0.0f);
136 : m_size(other.m_size)
137 , m_desc(other.m_desc)
138 , m_mimeType(other.m_mimeType)
139 , m_locale(other.m_locale)
140 , m_type(other.m_type)
141 , m_encoding(other.m_encoding)
142 , m_descEncoding(other.m_descEncoding)
146 m_ptr = make_unique<char[]>(m_size);
147 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
179 assignText(text, textSize, textEncoding, convertTo);
193 assignText(text, std::strlen(text), textEncoding, convertTo);
248 , m_encoding(encoding)
256 m_ptr = std::make_unique<char[]>(m_size);
257 std::copy(
data,
data + m_size, m_ptr.get());
276 , m_encoding(encoding)
281 m_ptr = std::move(
data);
290 if (
this == &other) {
293 m_size = other.m_size;
294 m_type = other.m_type;
295 m_desc = other.m_desc;
296 m_mimeType = other.m_mimeType;
297 m_locale = other.m_locale;
298 m_flags = other.m_flags;
299 m_encoding = other.m_encoding;
300 m_descEncoding = other.m_descEncoding;
304 m_ptr = make_unique<char[]>(m_size);
305 std::copy(other.m_ptr.get(), other.m_ptr.get() + other.m_size, m_ptr.get());
359 if (m_mimeType != other.m_mimeType || m_locale != other.m_locale || m_flags != other.m_flags) {
370 const auto utfEncodingToUse = pickUtfEncoding(m_descEncoding, other.m_descEncoding);
371 StringData str1, str2;
372 const char *data1, *data2;
374 if (m_descEncoding != utfEncodingToUse) {
376 str1 = convertString(
377 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
378 data1 = str1.first.get();
381 data1 = m_desc.data();
382 size1 = m_desc.size();
384 if (other.m_descEncoding != utfEncodingToUse) {
386 str2 = convertString(inputParameter.first, outputParameter.first, other.m_desc.data(), other.m_desc.size(),
387 outputParameter.second / inputParameter.second);
388 data2 = str2.first.get();
391 data2 = other.m_desc.data();
392 size2 = other.m_desc.size();
402 if (m_type == other.m_type) {
406 if (m_size != other.m_size && m_encoding == other.m_encoding) {
415 const auto utfEncodingToUse = pickUtfEncoding(m_encoding, other.m_encoding);
417 const char *data1, *data2;
419 if (m_encoding != utfEncodingToUse) {
427 if (other.m_encoding != utfEncodingToUse) {
428 str2 = other.
toString(utfEncodingToUse);
432 data2 = other.m_ptr.get();
433 size2 = other.m_size;
458 for (
const auto dataType : { m_type, other.m_type }) {
479 return lhs.
rating == rhs.rating && lhs.playCounter == rhs.playCounter && lhs.scale == rhs.scale
489 }
catch (
const ConversionException &) {
529 }
catch (
const ConversionException &e) {
547 switch (m_encoding) {
550 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
552 return stringToNumber<std::int32_t>(u16str);
555 return bufferToNumber<std::int32_t>(m_ptr.get(), m_size);
559 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
561 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
564 if (m_size ==
sizeof(std::int32_t)) {
565 return *
reinterpret_cast<std::int32_t *
>(m_ptr.get());
567 throw ConversionException(
"Can not convert assigned data to integer because the data size is not appropriate.");
572 if (unsignedInteger > std::numeric_limits<std::int32_t>::max()) {
573 throw ConversionException(argsToString(
"Unsigned integer too big for conversion to integer."));
575 return static_cast<std::int32_t
>(unsignedInteger);
578 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to integer."));
589 switch (m_encoding) {
592 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
594 return stringToNumber<std::uint64_t>(u16str);
597 return bufferToNumber<std::uint64_t>(m_ptr.get(), m_size);
604 throw ConversionException(argsToString(
"Can not convert negative value to unsigned integer."));
606 return static_cast<std::uint64_t
>(integer);
611 if (m_size ==
sizeof(std::uint64_t)) {
612 return *
reinterpret_cast<std::uint64_t *
>(m_ptr.get());
614 throw ConversionException(
"Can not convert assigned data to unsigned integer because the data size is not appropriate.");
616 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to integer."));
635 }
catch (
const ConversionException &) {
648 if (m_size ==
sizeof(std::int32_t)) {
649 index =
static_cast<int>(*
reinterpret_cast<std::int32_t *
>(m_ptr.get()));
650 }
else if (m_size ==
sizeof(std::uint64_t)) {
651 const auto unsignedInt = *
reinterpret_cast<std::uint64_t *
>(m_ptr.get());
652 if (unsignedInt <= std::numeric_limits<int>::max()) {
653 index =
static_cast<int>(unsignedInt);
658 throw ConversionException(
"The assigned index/integer is of unappropriate size.");
662 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to genre index."));
665 throw ConversionException(
"The assigned number is not a valid standard genre index.");
682 switch (m_encoding) {
685 auto u16str = u16string(
reinterpret_cast<char16_t *
>(m_ptr.get()), m_size / 2);
695 case sizeof(std::int32_t):
696 return PositionInSet(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
697 case 2 *
sizeof(std::int32_t):
699 *(
reinterpret_cast<std::int32_t *
>(m_ptr.get())), *(
reinterpret_cast<std::int32_t *
>(m_ptr.get() +
sizeof(std::int32_t))));
701 throw ConversionException(
"The size of the assigned data is not appropriate.");
705 case sizeof(std::uint64_t): {
706 const auto track = *(
reinterpret_cast<std::uint64_t *
>(m_ptr.get()));
707 if (track < std::numeric_limits<std::int32_t>::max()) {
713 throw ConversionException(
"The size of the assigned data is not appropriate.");
715 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to position in set."));
735 case sizeof(std::int32_t):
736 return TimeSpan(*(
reinterpret_cast<std::int32_t *
>(m_ptr.get())));
737 case sizeof(std::int64_t):
738 return TimeSpan(*(
reinterpret_cast<std::int64_t *
>(m_ptr.get())));
740 throw ConversionException(
"The size of the assigned data is not appropriate for conversion to time span.");
744 case sizeof(std::uint64_t): {
745 const auto ticks = *(
reinterpret_cast<std::uint64_t *
>(m_ptr.get()));
746 if (ticks <
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max())) {
747 return TimeSpan(
static_cast<std::int64_t
>(ticks));
752 throw ConversionException(
"The size of the assigned data is not appropriate.");
754 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to time span."));
772 return DateTime::fromIsoStringGmt(str.data());
773 }
catch (
const ConversionException &) {
774 return DateTime::fromString(str);
780 if (m_size ==
sizeof(std::int32_t)) {
781 return DateTime(*(
reinterpret_cast<std::uint32_t *
>(m_ptr.get())));
782 }
else if (m_size ==
sizeof(std::uint64_t)) {
783 return DateTime(*(
reinterpret_cast<std::uint64_t *
>(m_ptr.get())));
785 throw ConversionException(
"The size of the assigned data is not appropriate for conversion to date time.");
790 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
808 return DateTimeExpression::fromIsoString(str.data());
809 }
catch (
const ConversionException &) {
810 return DateTimeExpression::fromString(str.data());
821 throw ConversionException(
"The size of the assigned data is not appropriate for conversion to date time expression.");
824 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
852 popularity.rating =
static_cast<double>(
toInteger());
855 auto s = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
856 auto reader = BinaryReader(&s);
858 s.exceptions(std::ios_base::failbit | std::ios_base::badbit);
859#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
860 s.rdbuf()->pubsetbuf(m_ptr.get(),
static_cast<std::streamsize
>(m_size));
862 s.write(m_ptr.get(),
static_cast<std::streamsize
>(m_size));
864 popularity.user = reader.readLengthPrefixedString();
865 popularity.rating = reader.readFloat64LE();
866 popularity.playCounter = reader.readUInt64LE();
867 popularity.scale =
static_cast<TagType>(reader.readUInt64LE());
868 }
catch (
const std::ios_base::failure &) {
869 throw ConversionException(argsToString(
"Assigned popularity is invalid"));
877 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to date time."));
905 popularity.scale = scale;
906 }
else if (!popularity.scaleTo(scale)) {
907 throw ConversionException(argsToString(
"Assigned popularity cannot be scaled accordingly"));
923 if (m_encoding == encoding) {
927 StringData encodedData;
933 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
936 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
939 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
949 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
953 m_ptr = make_unique<char[]>(m_size = encodedData.second);
954 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
956 m_encoding = encoding;
975 if (encoding == m_descEncoding) {
978 if (m_desc.empty()) {
979 m_descEncoding = encoding;
982 StringData encodedData;
988 encodedData = convertLatin1ToUtf8(m_desc.data(), m_desc.size());
991 encodedData = convertUtf16LEToUtf8(m_desc.data(), m_desc.size());
994 encodedData = convertUtf16BEToUtf8(m_desc.data(), m_desc.size());
1003 encodedData = convertString(
1004 inputParameter.first, outputParameter.first, m_desc.data(), m_desc.size(), outputParameter.second / inputParameter.second);
1007 m_desc.assign(encodedData.first.get(), encodedData.second);
1008 m_descEncoding = encoding;
1034 result.assign(m_ptr.get(), m_size);
1036 StringData encodedData;
1042 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
1045 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
1048 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
1058 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
1061 result.assign(encodedData.first.get(), encodedData.second);
1075 result.assign(genreName);
1077 throw ConversionException(
"No string representation for the assigned standard genre index available.");
1097 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
1101 : convertUtf8ToUtf16BE(result.data(), result.size());
1102 result.assign(encodedData.first.get(), encodedData.second);
1123 string regularStrRes;
1127 result.assign(
reinterpret_cast<const char16_t *
>(m_ptr.get()), m_size /
sizeof(
char16_t));
1129 StringData encodedData;
1135 encodedData = convertLatin1ToUtf8(m_ptr.get(), m_size);
1138 encodedData = convertUtf16LEToUtf8(m_ptr.get(), m_size);
1141 encodedData = convertUtf16BEToUtf8(m_ptr.get(), m_size);
1151 = convertString(inputParameter.first, outputParameter.first, m_ptr.get(), m_size, outputParameter.second / inputParameter.second);
1154 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(
char16_t));
1158 regularStrRes = numberToString(
toInteger());
1166 regularStrRes.clear();
1168 regularStrRes.assign(genreName);
1170 throw ConversionException(
"No string representation for the assigned standard genre index available.");
1178 regularStrRes =
toDateTime().toString(DateTimeOutputFormat::IsoOmittingDefaultComponents);
1190 throw ConversionException(argsToString(
"Can not convert ",
tagDataTypeString(m_type),
" to string."));
1194 : convertUtf8ToUtf16BE(regularStrRes.data(), result.size());
1195 result.assign(
reinterpret_cast<const char16_t *
>(encodedData.first.get()), encodedData.second /
sizeof(
const char16_t));
1214 stripBom(text, textSize, textEncoding);
1222 m_ptr = make_unique<char[]>(m_size = textSize);
1223 copy(text, text + textSize, m_ptr.get());
1227 StringData encodedData;
1228 switch (textEncoding) {
1231 switch (convertTo) {
1233 encodedData = convertUtf8ToLatin1(text, textSize);
1236 encodedData = convertUtf8ToUtf16LE(text, textSize);
1239 encodedData = convertUtf8ToUtf16BE(text, textSize);
1248 encodedData = convertString(inputParameter.first, outputParameter.first, text, textSize, outputParameter.second / inputParameter.second);
1252 m_ptr = make_unique<char[]>(m_size = encodedData.second);
1253 copy(encodedData.first.get(), encodedData.first.get() + encodedData.second, m_ptr.get());
1262 m_size =
sizeof(value);
1263 m_ptr = make_unique<char[]>(m_size);
1264 std::copy(
reinterpret_cast<const char *
>(&value),
reinterpret_cast<const char *
>(&value) + m_size, m_ptr.get());
1275 m_size =
sizeof(value);
1276 m_ptr = make_unique<char[]>(m_size);
1277 std::copy(
reinterpret_cast<const char *
>(&value),
reinterpret_cast<const char *
>(&value) + m_size, m_ptr.get());
1295 if (length > m_size) {
1296 m_ptr = make_unique<char[]>(length);
1299 std::copy(
data,
data + length, m_ptr.get());
1305 m_encoding = encoding;
1324 m_encoding = encoding;
1325 m_ptr = std::move(
data);
1333 auto s = std::stringstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary);
1334 auto writer = BinaryWriter(&s);
1336 s.exceptions(std::ios_base::failbit | std::ios_base::badbit);
1337 writer.writeLengthPrefixedString(value.
user);
1338 writer.writeFloat64LE(value.
rating);
1340 writer.writeUInt64LE(
static_cast<std::uint64_t
>(value.
scale));
1341 auto size =
static_cast<std::size_t
>(s.tellp());
1342 auto ptr = std::make_unique<char[]>(size);
1343 s.read(ptr.get(), s.tellp());
1345 }
catch (
const std::ios_base::failure &) {
1346 throw ConversionException(
"Unable to serialize specified Popularity");
1357 if ((length >= 3) && (BE::toUInt24(text) == 0x00EFBBBF)) {
1363 if ((length >= 2) && (LE::toInt<std::uint16_t>(text) == 0xFEFF)) {
1369 if ((length >= 2) && (BE::toInt<std::uint16_t>(text) == 0xFEFF)) {
1384 if (currentEncoding !=
1385#
if defined(CONVERSION_UTILITIES_BYTE_ORDER_LITTLE_ENDIAN)
1387#elif defined(CONVERSION_UTILITIES_BYTE_ORDER_BIG_ENDIAN)
1390#error
"Host byte order not supported"
1393 for (
auto &c : u16str) {
1394 c = swapOrder(
static_cast<std::uint16_t
>(c));
1404 if (size1 != size2) {
1411 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
1418 for (
auto i1 = data1, i2 = data2, end = data1 + size1; i1 != end; ++i1, ++i2) {
1435 return emptyTagValue;
1452 if (
scale == targetScale) {
1457 double genericRating;
1463 genericRating =
rating / (5.0 / 4.0) + 1.0;
1466 genericRating =
rating < 1.0 ? 0.0 : ((
rating - 1.0) / (254.0 / 4.0) + 1.0);
1470 genericRating =
rating / 20.0;
1477 switch (targetScale) {
1482 rating = (genericRating - 1.0) * (5.0 / 4.0);
1485 rating = genericRating < 1.0 ? 0.0 : ((genericRating - 1.0) * (254.0 / 4.0) + 1.0);
1489 rating = genericRating * 20.0;
1495 scale = targetScale;
1505 return isEmpty() ? std::string()
1528 const auto parts = splitStringSimple<std::vector<std::string_view>>(str,
"|");
1530 if (parts.empty()) {
1532 }
else if (parts.size() > 3) {
1533 throw ConversionException(
"Wrong format, expected \"rating\" or \"user|rating|play-counter\"");
1536 if (parts.size() == 1) {
1538 res.rating = stringToNumber<
decltype(res.rating)>(parts.front());
1540 }
catch (
const ConversionException &) {
1544 res.
user = parts.front();
1545 if (parts.size() > 1) {
1546 res.rating = stringToNumber<
decltype(res.rating)>(parts[1]);
1548 if (parts.size() > 2) {
1549 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 (using th...
std::string toDisplayString() const
CppUtilities::DateTimeExpression toDateTimeExpression() const
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.
~TagValue()
Destroys the TagValue.
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 bool canEncodingBeUsed(TagTextEncoding encoding) const
Returns an indication whether the specified encoding can be used to provide string values for the tag...
virtual TagTextEncoding proposedTextEncoding() const
Returns the proposed text encoding.
Contains all classes and functions of the TagInfo library.
TAG_PARSER_EXPORT 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)
The Popularity class contains a value for ID3v2's "Popularimeter" field.
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 ...
The TagValuePrivate struct contains private fields of the TagValue class.