-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathabstractattachment.cpp
207 lines (185 loc) · 5.83 KB
/
abstractattachment.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#include "./abstractattachment.h"
#include "./exceptions.h"
#include "./mediafileinfo.h"
#include "./progressfeedback.h"
#include <c++utilities/io/copy.h>
#include <memory>
#include <sstream>
using namespace std;
using namespace CppUtilities;
namespace TagParser {
/// \brief The AbstractAttachmentPrivate struct contains private fields of the AbstractAttachment class.
struct AbstractAttachmentPrivate {};
/*!
* \class TagParser::StreamDataBlock
* \brief The StreamDataBlock class is a reference to a certain data block of a stream.
*/
/*!
* \brief Constructs a new StreamDataBlock.
*
* The derived is responsible for the prober initialization of the object.
*/
StreamDataBlock::StreamDataBlock()
: m_stream(nullptr)
, m_startOffset(0)
, m_endOffset(0)
{
}
/*!
* \brief Constructs a new StreamDataBlock with the specified \a stream and offsets.
*
* The \a stream must be provided as function returning a reference the associated stream. This way of passing the stream
* allows the caller to change the stream without the need to update all StreamDataBlock objects
* referring to the stream. This is required when rewriting a file because during rewriting the original file
* gets renamed and then reopend with another stream object.
*
* The object does NOT take ownership over the stream returned by the specified function.
*/
StreamDataBlock::StreamDataBlock(const std::function<std::istream &()> &stream, std::uint64_t startOffset, std::ios_base::seekdir startDir,
std::uint64_t endOffset, std::ios_base::seekdir endDir)
: m_stream(stream)
{
auto &s = stream();
auto currentPos = s.tellg();
s.seekg(static_cast<std::istream::off_type>(startOffset), startDir);
m_startOffset = static_cast<std::uint64_t>(s.tellg());
s.seekg(static_cast<std::istream::off_type>(endOffset), endDir);
m_endOffset = static_cast<std::uint64_t>(s.tellg());
s.seekg(currentPos);
if (m_endOffset < m_startOffset) {
throw std::ios_base::failure("End offset is less than start offset.");
}
}
/*!
* \brief Discards buffered data.
*/
StreamDataBlock::~StreamDataBlock()
{
}
/*!
* \brief Buffers the data block. Buffered data can be accessed via buffer().
*/
void StreamDataBlock::makeBuffer() const
{
m_buffer = make_unique<char[]>(size());
stream().seekg(static_cast<std::istream::off_type>(startOffset()));
stream().read(m_buffer.get(), static_cast<std::streamsize>(size()));
}
/*!
* \brief Copies the data to the specified \a stream.
* \remarks Makes use of the buffer allocated with makeBuffer() if this method has been called before.
*/
void StreamDataBlock::copyTo(ostream &stream) const
{
if (buffer()) {
stream.write(buffer().get(), static_cast<std::streamsize>(size()));
} else {
CopyHelper<0x2000> copyHelper;
m_stream().seekg(static_cast<std::streamsize>(startOffset()));
copyHelper.copy(m_stream(), stream, size());
}
}
/*!
* \class TagParser::FileDataBlock
* \brief The FileDataBlock class is a reference to a certain data block of a file stream.
*/
/*!
* \brief Constructs a new FileDataBlock with the specified \a path.
*
* Opens a file stream with the specified \a path.
*
* \throws Throws ios_base::failure when an IO error occurs.
*/
FileDataBlock::FileDataBlock(std::string_view path, Diagnostics &diag, AbortableProgressFeedback &progress)
: m_fileInfo(make_unique<MediaFileInfo>())
{
m_fileInfo->setPath(path);
m_fileInfo->open(true);
m_fileInfo->parseContainerFormat(diag, progress);
m_startOffset = 0;
m_endOffset = m_fileInfo->size();
m_stream = [this]() -> std::istream & { return this->m_fileInfo->stream(); };
}
/*!
* \brief Destroys the FileDataBlock.
* \remarks This method is needed although it is empty. Otherwise the default d'tor would be
* inlined where FileDataBlock is used creating a dependency to MediaFileInfo which
* therefore couldn't be opaque anymore.
*/
FileDataBlock::~FileDataBlock()
{
}
/*!
* \class TagParser::AbstractAttachment
* \brief The AbstractAttachment class parses and stores attachment information.
*/
/*!
* \brief Constructs a new attachment.
*/
AbstractAttachment::AbstractAttachment()
: m_id(0)
, m_isDataFromFile(false)
, m_ignored(false)
{
}
/*!
* \brief Destroys the attachment.
*/
AbstractAttachment::~AbstractAttachment()
{
}
/*!
* \brief Returns a label for the track.
*/
string AbstractAttachment::label() const
{
stringstream ss;
ss << "ID: " << id();
if (!name().empty()) {
ss << ", name: \"" << name() << "\"";
}
if (!mimeType().empty()) {
ss << ", mime-type: \"" << mimeType() << "\"";
}
return ss.str();
}
/*!
* \brief Resets the object to its initial state.
*/
void AbstractAttachment::clear()
{
m_description.clear();
m_name.clear();
m_mimeType.clear();
m_id = 0;
m_data.reset();
}
/*!
* \brief Sets the data, name and MIME-type for the specified \a path.
*
* A stream for the file with the specified \a path is opened (read-only).
* This stream will be freed by the attachment if the other data is assigned
* or the attachment gets destroyed.
*
* \throws Throws std::ios_base::failure when an IO error occurs.
* \throws Throws TagParser::Failure or a derived class when a parsing
* error occurs.
*
* When such an exception is thrown, the attachment remains unchanged.
*/
void AbstractAttachment::setFile(string_view path, Diagnostics &diag, AbortableProgressFeedback &progress)
{
m_data.reset();
auto file = make_unique<FileDataBlock>(path, diag, progress);
const auto fileName = file->fileInfo()->fileName();
if (!fileName.empty()) {
m_name = fileName;
}
const auto mimeType = file->fileInfo()->mimeType();
if (!mimeType.empty()) {
m_mimeType = mimeType;
}
m_data = std::move(file);
m_isDataFromFile = true;
}
} // namespace TagParser