Improve warnings when parsing Vorbis comments
This commit is contained in:
parent
18d4a5e8de
commit
932687f93d
|
@ -135,7 +135,7 @@ void OggIterator::read(char *buffer, std::size_t count)
|
||||||
{
|
{
|
||||||
std::size_t bytesRead = 0;
|
std::size_t bytesRead = 0;
|
||||||
while (*this && count) {
|
while (*this && count) {
|
||||||
const auto available = currentSegmentSize() - m_bytesRead;
|
const auto available = remainingBytesInCurrentSegment();
|
||||||
stream().seekg(static_cast<std::streamoff>(currentCharacterOffset()));
|
stream().seekg(static_cast<std::streamoff>(currentCharacterOffset()));
|
||||||
if (count <= available) {
|
if (count <= available) {
|
||||||
stream().read(buffer + bytesRead, static_cast<std::streamsize>(count));
|
stream().read(buffer + bytesRead, static_cast<std::streamsize>(count));
|
||||||
|
@ -169,7 +169,7 @@ std::size_t OggIterator::readAll(char *buffer, std::size_t max)
|
||||||
{
|
{
|
||||||
auto bytesRead = std::size_t(0);
|
auto bytesRead = std::size_t(0);
|
||||||
while (*this && max) {
|
while (*this && max) {
|
||||||
const auto available = currentSegmentSize() - m_bytesRead;
|
const auto available = remainingBytesInCurrentSegment();
|
||||||
stream().seekg(static_cast<std::streamoff>(currentCharacterOffset()), std::ios_base::beg);
|
stream().seekg(static_cast<std::streamoff>(currentCharacterOffset()), std::ios_base::beg);
|
||||||
if (max <= available) {
|
if (max <= available) {
|
||||||
stream().read(buffer + bytesRead, static_cast<std::streamsize>(max));
|
stream().read(buffer + bytesRead, static_cast<std::streamsize>(max));
|
||||||
|
|
|
@ -34,6 +34,8 @@ public:
|
||||||
std::uint64_t currentCharacterOffset() const;
|
std::uint64_t currentCharacterOffset() const;
|
||||||
std::uint64_t tellg() const;
|
std::uint64_t tellg() const;
|
||||||
std::uint32_t currentSegmentSize() const;
|
std::uint32_t currentSegmentSize() const;
|
||||||
|
std::uint64_t remainingBytesInCurrentSegment() const;
|
||||||
|
std::uint64_t bytesReadFromCurrentSegment() const;
|
||||||
void setFilter(std::uint32_t streamSerialId);
|
void setFilter(std::uint32_t streamSerialId);
|
||||||
void removeFilter();
|
void removeFilter();
|
||||||
bool isLastPageFetched() const;
|
bool isLastPageFetched() const;
|
||||||
|
@ -240,6 +242,22 @@ inline std::uint32_t OggIterator::currentSegmentSize() const
|
||||||
return m_pages[m_page].segmentSizes()[m_segment];
|
return m_pages[m_page].segmentSizes()[m_segment];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the number of bytes left to read in the current segment.
|
||||||
|
*/
|
||||||
|
inline std::uint64_t OggIterator::remainingBytesInCurrentSegment() const
|
||||||
|
{
|
||||||
|
return currentSegmentSize() - m_bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns the number of bytes read from the current segment.
|
||||||
|
*/
|
||||||
|
inline uint64_t OggIterator::bytesReadFromCurrentSegment() const
|
||||||
|
{
|
||||||
|
return m_bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Allows to filter pages by the specified \a streamSerialId.
|
* \brief Allows to filter pages by the specified \a streamSerialId.
|
||||||
*
|
*
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include <c++utilities/conversion/binaryconversion.h>
|
#include <c++utilities/conversion/binaryconversion.h>
|
||||||
#include <c++utilities/io/binaryreader.h>
|
#include <c++utilities/io/binaryreader.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace CppUtilities;
|
using namespace CppUtilities;
|
||||||
|
|
||||||
|
@ -51,13 +53,15 @@ void OggPage::parseHeader(istream &stream, std::uint64_t startOffset, std::int32
|
||||||
maxSize -= m_segmentCount;
|
maxSize -= m_segmentCount;
|
||||||
}
|
}
|
||||||
// read segment size table
|
// read segment size table
|
||||||
m_segmentSizes.push_back(0);
|
m_segmentSizes.emplace_back(0);
|
||||||
for (std::uint8_t i = 0; i < m_segmentCount;) {
|
for (std::uint8_t i = 0; i < m_segmentCount;) {
|
||||||
std::uint8_t entry = reader.readByte();
|
std::uint8_t entry = reader.readByte();
|
||||||
maxSize -= entry;
|
maxSize -= entry;
|
||||||
m_segmentSizes.back() += entry;
|
m_segmentSizes.back() += entry;
|
||||||
if (++i < m_segmentCount && entry < 0xff) {
|
if (++i < m_segmentCount && entry < 0xFF) {
|
||||||
m_segmentSizes.push_back(0);
|
m_segmentSizes.emplace_back(0);
|
||||||
|
} else if (i == m_segmentCount && entry == 0xFF) {
|
||||||
|
m_headerTypeFlag |= 0x80; // FIXME v11: don't abuse header type flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check whether the maximum size is exceeded
|
// check whether the maximum size is exceeded
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
bool isContinued() const;
|
bool isContinued() const;
|
||||||
bool isFirstpage() const;
|
bool isFirstpage() const;
|
||||||
bool isLastPage() const;
|
bool isLastPage() const;
|
||||||
|
bool isLastSegmentUnconcluded() const;
|
||||||
std::uint64_t absoluteGranulePosition() const;
|
std::uint64_t absoluteGranulePosition() const;
|
||||||
std::uint32_t streamSerialNumber() const;
|
std::uint32_t streamSerialNumber() const;
|
||||||
bool matchesStreamSerialNumber(std::uint32_t streamSerialNumber) const;
|
bool matchesStreamSerialNumber(std::uint32_t streamSerialNumber) const;
|
||||||
|
@ -101,7 +102,7 @@ inline std::uint8_t OggPage::streamStructureVersion() const
|
||||||
*/
|
*/
|
||||||
inline std::uint8_t OggPage::headerTypeFlag() const
|
inline std::uint8_t OggPage::headerTypeFlag() const
|
||||||
{
|
{
|
||||||
return m_headerTypeFlag;
|
return m_headerTypeFlag & 0xF; // last 4 bits are used internally
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -128,6 +129,14 @@ inline bool OggPage::isLastPage() const
|
||||||
return m_headerTypeFlag & 0x04;
|
return m_headerTypeFlag & 0x04;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns whether the last segment is unconcluded (the last lacing value of the last segment is 0xFF).
|
||||||
|
*/
|
||||||
|
inline bool OggPage::isLastSegmentUnconcluded() const
|
||||||
|
{
|
||||||
|
return m_headerTypeFlag & 0x80;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Returns the absolute granule position.
|
* \brief Returns the absolute granule position.
|
||||||
*
|
*
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "../diagnostics.h"
|
#include "../diagnostics.h"
|
||||||
#include "../exceptions.h"
|
#include "../exceptions.h"
|
||||||
|
|
||||||
|
#include <c++utilities/conversion/stringbuilder.h>
|
||||||
#include <c++utilities/io/binaryreader.h>
|
#include <c++utilities/io/binaryreader.h>
|
||||||
#include <c++utilities/io/binarywriter.h>
|
#include <c++utilities/io/binarywriter.h>
|
||||||
#include <c++utilities/io/copy.h>
|
#include <c++utilities/io/copy.h>
|
||||||
|
@ -201,6 +202,23 @@ template <class StreamType> void VorbisComment::internalParse(StreamType &stream
|
||||||
diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
|
diag.emplace_back(DiagLevel::Critical, "Vorbis comment is truncated.", context);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// warn if there are bytes left in the last segment of the Ogg packet containing the comment
|
||||||
|
if constexpr (std::is_same_v<std::decay_t<StreamType>, OggIterator>) {
|
||||||
|
auto bytesRemaining = std::uint64_t();
|
||||||
|
if (stream) {
|
||||||
|
bytesRemaining = stream.remainingBytesInCurrentSegment();
|
||||||
|
if (stream.currentPage().isLastSegmentUnconcluded()) {
|
||||||
|
stream.nextSegment();
|
||||||
|
if (stream) {
|
||||||
|
bytesRemaining += stream.remainingBytesInCurrentSegment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bytesRemaining) {
|
||||||
|
diag.emplace_back(DiagLevel::Warning, argsToString(bytesRemaining, " bytes left in last segment."), context);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -52,7 +52,7 @@ template <class StreamType> void VorbisCommentField::internalParse(StreamType &s
|
||||||
static const string context("parsing Vorbis comment field");
|
static const string context("parsing Vorbis comment field");
|
||||||
char buff[4];
|
char buff[4];
|
||||||
if (maxSize < 4) {
|
if (maxSize < 4) {
|
||||||
diag.emplace_back(DiagLevel::Critical, "Field expected.", context);
|
diag.emplace_back(DiagLevel::Critical, argsToString("Field expected at ", static_cast<std::streamoff>(stream.tellg()), '.'), context);
|
||||||
throw TruncatedDataException();
|
throw TruncatedDataException();
|
||||||
} else {
|
} else {
|
||||||
maxSize -= 4;
|
maxSize -= 4;
|
||||||
|
@ -71,7 +71,8 @@ template <class StreamType> void VorbisCommentField::internalParse(StreamType &s
|
||||||
setId(string(data.get(), idSize));
|
setId(string(data.get(), idSize));
|
||||||
if (!idSize) {
|
if (!idSize) {
|
||||||
// empty field ID
|
// empty field ID
|
||||||
diag.emplace_back(DiagLevel::Critical, "The field ID is empty.", context);
|
diag.emplace_back(
|
||||||
|
DiagLevel::Critical, argsToString("The field ID at ", static_cast<std::streamoff>(stream.tellg()), " is empty."), context);
|
||||||
throw InvalidDataException();
|
throw InvalidDataException();
|
||||||
} else if (id() == VorbisCommentIds::cover()) {
|
} else if (id() == VorbisCommentIds::cover()) {
|
||||||
// extract cover value
|
// extract cover value
|
||||||
|
@ -99,7 +100,7 @@ template <class StreamType> void VorbisCommentField::internalParse(StreamType &s
|
||||||
setValue(TagValue(string(data.get() + idSize + 1, size - idSize - 1), TagTextEncoding::Utf8));
|
setValue(TagValue(string(data.get() + idSize + 1, size - idSize - 1), TagTextEncoding::Utf8));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
diag.emplace_back(DiagLevel::Critical, "Field is truncated.", context);
|
diag.emplace_back(DiagLevel::Critical, argsToString("Field at ", static_cast<std::streamoff>(stream.tellg()), " is truncated."), context);
|
||||||
throw TruncatedDataException();
|
throw TruncatedDataException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue