fs/io/OutputStream: use C++ exceptions in Write()
This commit is contained in:
parent
36d6ead65c
commit
e4a06da14e
@ -239,8 +239,7 @@ SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path,
|
|||||||
for (const auto &uri_utf8 : contents)
|
for (const auto &uri_utf8 : contents)
|
||||||
playlist_print_uri(bos, uri_utf8.c_str());
|
playlist_print_uri(bos, uri_utf8.c_str());
|
||||||
|
|
||||||
if (!bos.Flush(error))
|
bos.Flush();
|
||||||
return false;
|
|
||||||
|
|
||||||
fos.Commit();
|
fos.Commit();
|
||||||
return true;
|
return true;
|
||||||
@ -415,9 +414,7 @@ spl_append_song(const char *utf8path, const DetachedSong &song, Error &error)
|
|||||||
|
|
||||||
playlist_print_song(bos, song);
|
playlist_print_song(bos, song);
|
||||||
|
|
||||||
if (!bos.Flush(error))
|
bos.Flush();
|
||||||
return false;
|
|
||||||
|
|
||||||
fos.Commit();
|
fos.Commit();
|
||||||
|
|
||||||
idle_add(IDLE_STORED_PLAYLIST);
|
idle_add(IDLE_STORED_PLAYLIST);
|
||||||
|
@ -86,9 +86,7 @@ spl_save_queue(const char *name_utf8, const Queue &queue, Error &error)
|
|||||||
for (unsigned i = 0; i < queue.GetLength(); i++)
|
for (unsigned i = 0; i < queue.GetLength(); i++)
|
||||||
playlist_print_song(bos, queue.Get(i));
|
playlist_print_song(bos, queue.Get(i));
|
||||||
|
|
||||||
if (!bos.Flush(error))
|
bos.Flush();
|
||||||
return false;
|
|
||||||
|
|
||||||
fos.Commit();
|
fos.Commit();
|
||||||
|
|
||||||
idle_add(IDLE_STORED_PLAYLIST);
|
idle_add(IDLE_STORED_PLAYLIST);
|
||||||
|
@ -75,12 +75,12 @@ StateFile::Write(BufferedOutputStream &os)
|
|||||||
playlist_state_save(os, partition.playlist, partition.pc);
|
playlist_state_save(os, partition.playlist, partition.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline void
|
||||||
StateFile::Write(OutputStream &os, Error &error)
|
StateFile::Write(OutputStream &os)
|
||||||
{
|
{
|
||||||
BufferedOutputStream bos(os);
|
BufferedOutputStream bos(os);
|
||||||
Write(bos);
|
Write(bos);
|
||||||
return bos.Flush(error);
|
bos.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -90,13 +90,8 @@ StateFile::Write()
|
|||||||
"Saving state file %s", path_utf8.c_str());
|
"Saving state file %s", path_utf8.c_str());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Error error;
|
|
||||||
FileOutputStream fos(path);
|
FileOutputStream fos(path);
|
||||||
if (!Write(fos, error)) {
|
Write(fos);
|
||||||
LogError(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fos.Commit();
|
fos.Commit();
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
LogError(e);
|
LogError(e);
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
void CheckModified();
|
void CheckModified();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool Write(OutputStream &os, Error &error);
|
void Write(OutputStream &os);
|
||||||
void Write(BufferedOutputStream &os);
|
void Write(BufferedOutputStream &os);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,9 +90,6 @@ directory_save(BufferedOutputStream &os, const Directory &directory)
|
|||||||
|
|
||||||
if (!child.IsMount())
|
if (!child.IsMount())
|
||||||
directory_save(os, child);
|
directory_save(os, child);
|
||||||
|
|
||||||
if (!os.Check())
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &song : directory.songs)
|
for (const auto &song : directory.songs)
|
||||||
|
@ -364,8 +364,8 @@ SimpleDatabase::GetStats(const DatabaseSelection &selection,
|
|||||||
return ::GetStats(*this, selection, stats, error);
|
return ::GetStats(*this, selection, stats, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
SimpleDatabase::Save(Error &error)
|
SimpleDatabase::Save()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
const ScopeDatabaseLock protect;
|
const ScopeDatabaseLock protect;
|
||||||
@ -395,16 +395,12 @@ SimpleDatabase::Save(Error &error)
|
|||||||
|
|
||||||
db_save_internal(bos, *root);
|
db_save_internal(bos, *root);
|
||||||
|
|
||||||
if (!bos.Flush(error)) {
|
bos.Flush();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_ZLIB
|
#ifdef ENABLE_ZLIB
|
||||||
if (gzip != nullptr) {
|
if (gzip != nullptr) {
|
||||||
bool success = gzip->Flush(error);
|
gzip->Flush();
|
||||||
gzip.reset();
|
gzip.reset();
|
||||||
if (!success)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -413,8 +409,6 @@ SimpleDatabase::Save(Error &error)
|
|||||||
FileInfo fi;
|
FileInfo fi;
|
||||||
if (GetFileInfo(path, fi))
|
if (GetFileInfo(path, fi))
|
||||||
mtime = fi.GetModificationTime();
|
mtime = fi.GetModificationTime();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -83,7 +83,7 @@ public:
|
|||||||
return *root;
|
return *root;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Save(Error &error);
|
void Save();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there is a valid database file on the disk.
|
* Returns true if there is a valid database file on the disk.
|
||||||
|
@ -130,9 +130,7 @@ UpdateService::Task()
|
|||||||
|
|
||||||
if (modified || !next.db->FileExists()) {
|
if (modified || !next.db->FileExists()) {
|
||||||
try {
|
try {
|
||||||
Error error;
|
next.db->Save();
|
||||||
if (!next.db->Save(error))
|
|
||||||
LogError(error, "Failed to save database");
|
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
LogError(e, "Failed to save database");
|
LogError(e, "Failed to save database");
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
#include "EncoderInterface.hxx"
|
#include "EncoderInterface.hxx"
|
||||||
#include "fs/io/OutputStream.hxx"
|
#include "fs/io/OutputStream.hxx"
|
||||||
|
|
||||||
bool
|
void
|
||||||
EncoderToOutputStream(OutputStream &os, Encoder &encoder, Error &error)
|
EncoderToOutputStream(OutputStream &os, Encoder &encoder)
|
||||||
{
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
/* read from the encoder */
|
/* read from the encoder */
|
||||||
@ -31,11 +31,10 @@ EncoderToOutputStream(OutputStream &os, Encoder &encoder, Error &error)
|
|||||||
char buffer[32768];
|
char buffer[32768];
|
||||||
size_t nbytes = encoder_read(&encoder, buffer, sizeof(buffer));
|
size_t nbytes = encoder_read(&encoder, buffer, sizeof(buffer));
|
||||||
if (nbytes == 0)
|
if (nbytes == 0)
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
/* write everything to the stream */
|
/* write everything to the stream */
|
||||||
|
|
||||||
if (!os.Write(buffer, nbytes, error))
|
os.Write(buffer, nbytes);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,8 @@
|
|||||||
|
|
||||||
struct Encoder;
|
struct Encoder;
|
||||||
class OutputStream;
|
class OutputStream;
|
||||||
class Error;
|
|
||||||
|
|
||||||
bool
|
void
|
||||||
EncoderToOutputStream(OutputStream &os, Encoder &encoder, Error &error);
|
EncoderToOutputStream(OutputStream &os, Encoder &encoder);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BufferedOutputStream::AppendToBuffer(const void *data, size_t size)
|
BufferedOutputStream::AppendToBuffer(const void *data, size_t size) noexcept
|
||||||
{
|
{
|
||||||
auto r = buffer.Write();
|
auto r = buffer.Write();
|
||||||
if (r.size < size)
|
if (r.size < size)
|
||||||
@ -37,46 +37,36 @@ BufferedOutputStream::AppendToBuffer(const void *data, size_t size)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
BufferedOutputStream::Write(const void *data, size_t size)
|
BufferedOutputStream::Write(const void *data, size_t size)
|
||||||
{
|
{
|
||||||
if (gcc_unlikely(last_error.IsDefined()))
|
|
||||||
/* the stream has already failed */
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* try to append to the current buffer */
|
/* try to append to the current buffer */
|
||||||
if (AppendToBuffer(data, size))
|
if (AppendToBuffer(data, size))
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
/* not enough room in the buffer - flush it */
|
/* not enough room in the buffer - flush it */
|
||||||
if (!Flush())
|
Flush();
|
||||||
return false;
|
|
||||||
|
|
||||||
/* see if there's now enough room */
|
/* see if there's now enough room */
|
||||||
if (AppendToBuffer(data, size))
|
if (AppendToBuffer(data, size))
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
/* too large for the buffer: direct write */
|
/* too large for the buffer: direct write */
|
||||||
return os.Write(data, size, last_error);
|
os.Write(data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
BufferedOutputStream::Write(const char *p)
|
BufferedOutputStream::Write(const char *p)
|
||||||
{
|
{
|
||||||
return Write(p, strlen(p));
|
Write(p, strlen(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
BufferedOutputStream::Format(const char *fmt, ...)
|
BufferedOutputStream::Format(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
if (gcc_unlikely(last_error.IsDefined()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto r = buffer.Write();
|
auto r = buffer.Write();
|
||||||
if (r.IsEmpty()) {
|
if (r.IsEmpty()) {
|
||||||
if (!Flush())
|
Flush();
|
||||||
return false;
|
|
||||||
|
|
||||||
r = buffer.Write();
|
r = buffer.Write();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,8 +80,7 @@ BufferedOutputStream::Format(const char *fmt, ...)
|
|||||||
/* buffer was not large enough; flush it and try
|
/* buffer was not large enough; flush it and try
|
||||||
again */
|
again */
|
||||||
|
|
||||||
if (!Flush())
|
Flush();
|
||||||
return false;
|
|
||||||
|
|
||||||
r = buffer.Write();
|
r = buffer.Write();
|
||||||
|
|
||||||
@ -112,37 +101,15 @@ BufferedOutputStream::Format(const char *fmt, ...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffer.Append(size);
|
buffer.Append(size);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
BufferedOutputStream::Flush()
|
BufferedOutputStream::Flush()
|
||||||
{
|
{
|
||||||
if (!Check())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto r = buffer.Read();
|
auto r = buffer.Read();
|
||||||
if (r.IsEmpty())
|
if (r.IsEmpty())
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
bool success = os.Write(r.data, r.size, last_error);
|
os.Write(r.data, r.size);
|
||||||
if (gcc_likely(success))
|
buffer.Consume(r.size);
|
||||||
buffer.Consume(r.size);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
BufferedOutputStream::Flush(Error &error)
|
|
||||||
{
|
|
||||||
if (!Check(error))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto r = buffer.Read();
|
|
||||||
if (r.IsEmpty())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
bool success = os.Write(r.data, r.size, error);
|
|
||||||
if (gcc_likely(success))
|
|
||||||
buffer.Consume(r.size);
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
@ -23,67 +23,37 @@
|
|||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
#include "util/DynamicFifoBuffer.hxx"
|
#include "util/DynamicFifoBuffer.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
class OutputStream;
|
class OutputStream;
|
||||||
class Error;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An #OutputStream wrapper that buffers its output to reduce the
|
* An #OutputStream wrapper that buffers its output to reduce the
|
||||||
* number of OutputStream::Write() calls.
|
* number of OutputStream::Write() calls.
|
||||||
*
|
|
||||||
* It simplifies error handling by managing an #Error attribute.
|
|
||||||
* Invoke any number of writes, and check for errors in the end using
|
|
||||||
* Check().
|
|
||||||
*/
|
*/
|
||||||
class BufferedOutputStream {
|
class BufferedOutputStream {
|
||||||
OutputStream &os;
|
OutputStream &os;
|
||||||
|
|
||||||
DynamicFifoBuffer<char> buffer;
|
DynamicFifoBuffer<char> buffer;
|
||||||
|
|
||||||
Error last_error;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BufferedOutputStream(OutputStream &_os)
|
BufferedOutputStream(OutputStream &_os)
|
||||||
:os(_os), buffer(32768) {}
|
:os(_os), buffer(32768) {}
|
||||||
|
|
||||||
bool Write(const void *data, size_t size);
|
void Write(const void *data, size_t size);
|
||||||
bool Write(const char *p);
|
void Write(const char *p);
|
||||||
|
|
||||||
gcc_printf(2,3)
|
gcc_printf(2,3)
|
||||||
bool Format(const char *fmt, ...);
|
void Format(const char *fmt, ...);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns false if an error has occurred.
|
|
||||||
*/
|
|
||||||
gcc_pure
|
|
||||||
bool Check() const {
|
|
||||||
return !last_error.IsDefined();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns false if an error has occurred. In that case, a
|
|
||||||
* copy of the #Error is returned.
|
|
||||||
*/
|
|
||||||
bool Check(Error &error) const {
|
|
||||||
if (last_error.IsDefined()) {
|
|
||||||
error.Set(last_error);
|
|
||||||
return false;
|
|
||||||
} else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write buffer contents to the #OutputStream.
|
* Write buffer contents to the #OutputStream.
|
||||||
*/
|
*/
|
||||||
bool Flush();
|
void Flush();
|
||||||
|
|
||||||
bool Flush(Error &error);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool AppendToBuffer(const void *data, size_t size);
|
bool AppendToBuffer(const void *data, size_t size) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,9 +21,6 @@
|
|||||||
#include "FileOutputStream.hxx"
|
#include "FileOutputStream.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
#include "system/Error.hxx"
|
#include "system/Error.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
|
|
||||||
#include <system_error>
|
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
|
||||||
@ -50,26 +47,19 @@ BaseFileOutputStream::Tell() const
|
|||||||
return uint64_t(high) << 32 | uint64_t(low);
|
return uint64_t(high) << 32 | uint64_t(low);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
BaseFileOutputStream::Write(const void *data, size_t size, Error &error)
|
BaseFileOutputStream::Write(const void *data, size_t size)
|
||||||
{
|
{
|
||||||
assert(IsDefined());
|
assert(IsDefined());
|
||||||
|
|
||||||
DWORD nbytes;
|
DWORD nbytes;
|
||||||
if (!WriteFile(handle, data, size, &nbytes, nullptr)) {
|
if (!WriteFile(handle, data, size, &nbytes, nullptr))
|
||||||
error.FormatLastError("Failed to write to %s",
|
throw FormatLastError("Failed to write to %s",
|
||||||
path.ToUTF8().c_str());
|
GetPath().c_str());
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size_t(nbytes) != size) {
|
if (size_t(nbytes) != size)
|
||||||
error.FormatLastError(ERROR_DISK_FULL,
|
throw FormatLastError(ERROR_DISK_FULL, "Failed to write to %s",
|
||||||
"Failed to write to %s",
|
GetPath().c_str());
|
||||||
path.ToUTF8().c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -143,22 +133,17 @@ BaseFileOutputStream::Tell() const
|
|||||||
return fd.Tell();
|
return fd.Tell();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
BaseFileOutputStream::Write(const void *data, size_t size, Error &error)
|
BaseFileOutputStream::Write(const void *data, size_t size)
|
||||||
{
|
{
|
||||||
assert(IsDefined());
|
assert(IsDefined());
|
||||||
|
|
||||||
ssize_t nbytes = fd.Write(data, size);
|
ssize_t nbytes = fd.Write(data, size);
|
||||||
if (nbytes < 0) {
|
if (nbytes < 0)
|
||||||
error.FormatErrno("Failed to write to %s", GetPath().c_str());
|
throw FormatErrno("Failed to write to %s", GetPath().c_str());
|
||||||
return false;
|
else if ((size_t)nbytes < size)
|
||||||
} else if ((size_t)nbytes < size) {
|
throw FormatErrno(ENOSPC, "Failed to write to %s",
|
||||||
error.FormatErrno(ENOSPC,
|
GetPath().c_str());
|
||||||
"Failed to write to %s", GetPath().c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -119,7 +119,7 @@ public:
|
|||||||
uint64_t Tell() const;
|
uint64_t Tell() const;
|
||||||
|
|
||||||
/* virtual methods from class OutputStream */
|
/* virtual methods from class OutputStream */
|
||||||
bool Write(const void *data, size_t size, Error &error) override;
|
void Write(const void *data, size_t size) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileOutputStream final : public BaseFileOutputStream {
|
class FileOutputStream final : public BaseFileOutputStream {
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
#include "GzipOutputStream.hxx"
|
#include "GzipOutputStream.hxx"
|
||||||
#include "lib/zlib/Domain.hxx"
|
#include "lib/zlib/Domain.hxx"
|
||||||
#include "lib/zlib/Error.hxx"
|
#include "lib/zlib/Error.hxx"
|
||||||
#include "util/Error.hxx"
|
|
||||||
|
|
||||||
GzipOutputStream::GzipOutputStream(OutputStream &_next) throw(ZlibError)
|
GzipOutputStream::GzipOutputStream(OutputStream &_next) throw(ZlibError)
|
||||||
:next(_next)
|
:next(_next)
|
||||||
@ -47,8 +46,8 @@ GzipOutputStream::~GzipOutputStream()
|
|||||||
deflateEnd(&z);
|
deflateEnd(&z);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
GzipOutputStream::Flush(Error &error)
|
GzipOutputStream::Flush()
|
||||||
{
|
{
|
||||||
/* no more input */
|
/* no more input */
|
||||||
z.next_in = nullptr;
|
z.next_in = nullptr;
|
||||||
@ -60,21 +59,18 @@ GzipOutputStream::Flush(Error &error)
|
|||||||
z.avail_out = sizeof(output);
|
z.avail_out = sizeof(output);
|
||||||
|
|
||||||
int result = deflate(&z, Z_FINISH);
|
int result = deflate(&z, Z_FINISH);
|
||||||
if (z.next_out > output &&
|
if (z.next_out > output)
|
||||||
!next.Write(output, z.next_out - output, error))
|
next.Write(output, z.next_out - output);
|
||||||
return false;
|
|
||||||
|
|
||||||
if (result == Z_STREAM_END)
|
if (result == Z_STREAM_END)
|
||||||
return true;
|
break;
|
||||||
else if (result != Z_OK) {
|
else if (result != Z_OK)
|
||||||
error.Set(zlib_domain, result, zError(result));
|
throw ZlibError(result);
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
GzipOutputStream::Write(const void *_data, size_t size, Error &error)
|
GzipOutputStream::Write(const void *_data, size_t size)
|
||||||
{
|
{
|
||||||
/* zlib's API requires non-const input pointer */
|
/* zlib's API requires non-const input pointer */
|
||||||
void *data = const_cast<void *>(_data);
|
void *data = const_cast<void *>(_data);
|
||||||
@ -88,15 +84,10 @@ GzipOutputStream::Write(const void *_data, size_t size, Error &error)
|
|||||||
z.avail_out = sizeof(output);
|
z.avail_out = sizeof(output);
|
||||||
|
|
||||||
int result = deflate(&z, Z_NO_FLUSH);
|
int result = deflate(&z, Z_NO_FLUSH);
|
||||||
if (result != Z_OK) {
|
if (result != Z_OK)
|
||||||
error.Set(zlib_domain, result, zError(result));
|
throw ZlibError(result);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (z.next_out > output &&
|
if (z.next_out > output)
|
||||||
!next.Write(output, z.next_out - output, error))
|
next.Write(output, z.next_out - output);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
class Error;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A filter that compresses data written to it using zlib, forwarding
|
* A filter that compresses data written to it using zlib, forwarding
|
||||||
* compressed data in the "gzip" format.
|
* compressed data in the "gzip" format.
|
||||||
@ -52,10 +50,10 @@ public:
|
|||||||
* Finish the file and write all data remaining in zlib's
|
* Finish the file and write all data remaining in zlib's
|
||||||
* output buffer.
|
* output buffer.
|
||||||
*/
|
*/
|
||||||
bool Flush(Error &error);
|
void Flush();
|
||||||
|
|
||||||
/* virtual methods from class OutputStream */
|
/* virtual methods from class OutputStream */
|
||||||
bool Write(const void *data, size_t size, Error &error) override;
|
void Write(const void *data, size_t size) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,18 +21,18 @@
|
|||||||
#define MPD_OUTPUT_STREAM_HXX
|
#define MPD_OUTPUT_STREAM_HXX
|
||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "Compiler.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
class Error;
|
|
||||||
|
|
||||||
class OutputStream {
|
class OutputStream {
|
||||||
public:
|
public:
|
||||||
OutputStream() = default;
|
OutputStream() = default;
|
||||||
OutputStream(const OutputStream &) = delete;
|
OutputStream(const OutputStream &) = delete;
|
||||||
|
|
||||||
virtual bool Write(const void *data, size_t size, Error &error) = 0;
|
/**
|
||||||
|
* Throws std::exception on error.
|
||||||
|
*/
|
||||||
|
virtual void Write(const void *data, size_t size) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "OutputStream.hxx"
|
#include "OutputStream.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -34,12 +33,10 @@ public:
|
|||||||
StdioOutputStream(FILE *_file):file(_file) {}
|
StdioOutputStream(FILE *_file):file(_file) {}
|
||||||
|
|
||||||
/* virtual methods from class OutputStream */
|
/* virtual methods from class OutputStream */
|
||||||
bool Write(const void *data, size_t size,
|
void Write(const void *data, size_t size) override {
|
||||||
gcc_unused Error &error) override {
|
|
||||||
fwrite(data, 1, size, file);
|
fwrite(data, 1, size, file);
|
||||||
|
|
||||||
/* this class is debug-only and ignores errors */
|
/* this class is debug-only and ignores errors */
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ class RecorderOutput {
|
|||||||
/**
|
/**
|
||||||
* Writes pending data from the encoder to the output file.
|
* Writes pending data from the encoder to the output file.
|
||||||
*/
|
*/
|
||||||
bool EncoderToFile(Error &error);
|
void EncoderToFile();
|
||||||
|
|
||||||
void SendTag(const Tag &tag);
|
void SendTag(const Tag &tag);
|
||||||
|
|
||||||
@ -175,12 +175,12 @@ RecorderOutput::Create(const ConfigBlock &block, Error &error)
|
|||||||
return recorder;
|
return recorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline void
|
||||||
RecorderOutput::EncoderToFile(Error &error)
|
RecorderOutput::EncoderToFile()
|
||||||
{
|
{
|
||||||
assert(file != nullptr);
|
assert(file != nullptr);
|
||||||
|
|
||||||
return EncoderToOutputStream(*file, *encoder, error);
|
EncoderToOutputStream(*file, *encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
@ -213,9 +213,11 @@ RecorderOutput::Open(AudioFormat &audio_format, Error &error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!HasDynamicPath()) {
|
if (!HasDynamicPath()) {
|
||||||
if (!EncoderToFile(error)) {
|
try {
|
||||||
|
EncoderToFile();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
encoder->Close();
|
encoder->Close();
|
||||||
delete file;
|
error.Set(recorder_domain, e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -237,8 +239,15 @@ RecorderOutput::Commit(Error &error)
|
|||||||
|
|
||||||
/* flush the encoder and write the rest to the file */
|
/* flush the encoder and write the rest to the file */
|
||||||
|
|
||||||
bool success = encoder_end(encoder, error) &&
|
bool success = encoder_end(encoder, error);
|
||||||
EncoderToFile(error);
|
if (success) {
|
||||||
|
try {
|
||||||
|
EncoderToFile();
|
||||||
|
} catch (...) {
|
||||||
|
encoder->Close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* now really close everything */
|
/* now really close everything */
|
||||||
|
|
||||||
@ -328,9 +337,12 @@ RecorderOutput::ReopenFormat(AllocatedPath &&new_path, Error &error)
|
|||||||
AudioFormat as before */
|
AudioFormat as before */
|
||||||
assert(new_audio_format == effective_audio_format);
|
assert(new_audio_format == effective_audio_format);
|
||||||
|
|
||||||
if (!EncoderToOutputStream(*new_file, *encoder, error)) {
|
try {
|
||||||
|
EncoderToOutputStream(*new_file, *encoder);
|
||||||
|
} catch (const std::exception &e) {
|
||||||
encoder->Close();
|
encoder->Close();
|
||||||
delete new_file;
|
delete new_file;
|
||||||
|
error.Set(recorder_domain, e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,9 +388,19 @@ RecorderOutput::SendTag(const Tag &tag)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!encoder_pre_tag(encoder, error) ||
|
if (!encoder_pre_tag(encoder, error)) {
|
||||||
!EncoderToFile(error) ||
|
LogError(error);
|
||||||
!encoder_tag(encoder, tag, error))
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
EncoderToFile();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LogError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encoder_tag(encoder, tag, error))
|
||||||
LogError(error);
|
LogError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,9 +415,17 @@ RecorderOutput::Play(const void *chunk, size_t size, Error &error)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return encoder_write(encoder, chunk, size, error) &&
|
if (!encoder_write(encoder, chunk, size, error))
|
||||||
EncoderToFile(error)
|
return 0;
|
||||||
? size : 0;
|
|
||||||
|
try {
|
||||||
|
EncoderToFile();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
error.Set(recorder_domain, e.what());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef AudioOutputWrapper<RecorderOutput> Wrapper;
|
typedef AudioOutputWrapper<RecorderOutput> Wrapper;
|
||||||
|
@ -43,10 +43,7 @@ Copy(OutputStream &dest, int src)
|
|||||||
if (nbytes == 0)
|
if (nbytes == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!dest.Write(buffer, nbytes, error)) {
|
dest.Write(buffer, nbytes);
|
||||||
fprintf(stderr, "%s\n", error.GetMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,60 +63,58 @@ int main(int argc, char **argv)
|
|||||||
ConfigBlock block;
|
ConfigBlock block;
|
||||||
block.AddBlockParam("quality", "5.0", -1);
|
block.AddBlockParam("quality", "5.0", -1);
|
||||||
|
|
||||||
Error error;
|
try {
|
||||||
const auto encoder = encoder_init(*plugin, block, error);
|
Error error;
|
||||||
if (encoder == NULL) {
|
const auto encoder = encoder_init(*plugin, block, error);
|
||||||
LogError(error, "Failed to initialize encoder");
|
if (encoder == NULL) {
|
||||||
return EXIT_FAILURE;
|
LogError(error, "Failed to initialize encoder");
|
||||||
}
|
|
||||||
|
|
||||||
/* open the encoder */
|
|
||||||
|
|
||||||
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
|
||||||
if (argc > 2) {
|
|
||||||
if (!audio_format_parse(audio_format, argv[2], false, error)) {
|
|
||||||
LogError(error, "Failed to parse audio format");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!encoder->Open(audio_format, error)) {
|
|
||||||
LogError(error, "Failed to open encoder");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
StdioOutputStream os(stdout);
|
|
||||||
|
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
|
||||||
LogError(error);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do it */
|
|
||||||
|
|
||||||
ssize_t nbytes;
|
|
||||||
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
|
|
||||||
if (!encoder_write(encoder, buffer, nbytes, error)) {
|
|
||||||
LogError(error, "encoder_write() failed");
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
/* open the encoder */
|
||||||
LogError(error);
|
|
||||||
|
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
||||||
|
if (argc > 2) {
|
||||||
|
if (!audio_format_parse(audio_format, argv[2], false, error)) {
|
||||||
|
LogError(error, "Failed to parse audio format");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encoder->Open(audio_format, error)) {
|
||||||
|
LogError(error, "Failed to open encoder");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!encoder_end(encoder, error)) {
|
StdioOutputStream os(stdout);
|
||||||
LogError(error, "encoder_flush() failed");
|
|
||||||
|
EncoderToOutputStream(os, *encoder);
|
||||||
|
|
||||||
|
/* do it */
|
||||||
|
|
||||||
|
ssize_t nbytes;
|
||||||
|
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
|
||||||
|
if (!encoder_write(encoder, buffer, nbytes, error)) {
|
||||||
|
LogError(error, "encoder_write() failed");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncoderToOutputStream(os, *encoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encoder_end(encoder, error)) {
|
||||||
|
LogError(error, "encoder_flush() failed");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncoderToOutputStream(os, *encoder);
|
||||||
|
|
||||||
|
encoder->Close();
|
||||||
|
encoder->Dispose();
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LogError(e);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
|
||||||
LogError(error);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder->Close();
|
|
||||||
encoder->Dispose();
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "fs/io/GunzipReader.hxx"
|
#include "fs/io/GunzipReader.hxx"
|
||||||
#include "fs/io/FileReader.hxx"
|
#include "fs/io/FileReader.hxx"
|
||||||
#include "fs/io/StdioOutputStream.hxx"
|
#include "fs/io/StdioOutputStream.hxx"
|
||||||
|
#include "Log.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -36,8 +37,7 @@ Copy(OutputStream &dest, Reader &src, Error &error)
|
|||||||
if (nbytes == 0)
|
if (nbytes == 0)
|
||||||
return !error.IsDefined();
|
return !error.IsDefined();
|
||||||
|
|
||||||
if (!dest.Write(buffer, nbytes, error))
|
dest.Write(buffer, nbytes);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,11 +66,16 @@ main(int argc, gcc_unused char **argv)
|
|||||||
|
|
||||||
Path path = Path::FromFS(argv[1]);
|
Path path = Path::FromFS(argv[1]);
|
||||||
|
|
||||||
Error error;
|
try {
|
||||||
if (!CopyGunzip(stdout, path, error)) {
|
Error error;
|
||||||
fprintf(stderr, "%s\n", error.GetMessage());
|
if (!CopyGunzip(stdout, path, error)) {
|
||||||
|
fprintf(stderr, "%s\n", error.GetMessage());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LogError(e);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,7 @@ Copy(OutputStream &dest, int src, Error &error)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dest.Write(buffer, nbytes, error))
|
dest.Write(buffer, nbytes);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +49,11 @@ static bool
|
|||||||
CopyGzip(OutputStream &_dest, int src, Error &error)
|
CopyGzip(OutputStream &_dest, int src, Error &error)
|
||||||
{
|
{
|
||||||
GzipOutputStream dest(_dest);
|
GzipOutputStream dest(_dest);
|
||||||
return Copy(dest, src, error) &&
|
if (!Copy(dest, src, error))
|
||||||
dest.Flush(error);
|
return false;
|
||||||
|
|
||||||
|
dest.Flush();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
@ -51,72 +51,63 @@ main(gcc_unused int argc, gcc_unused char **argv)
|
|||||||
const auto encoder = encoder_init(*plugin, block, IgnoreError());
|
const auto encoder = encoder_init(*plugin, block, IgnoreError());
|
||||||
assert(encoder != NULL);
|
assert(encoder != NULL);
|
||||||
|
|
||||||
/* open the encoder */
|
try {
|
||||||
|
/* open the encoder */
|
||||||
|
|
||||||
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
AudioFormat audio_format(44100, SampleFormat::S16, 2);
|
||||||
success = encoder->Open(audio_format, IgnoreError());
|
success = encoder->Open(audio_format, IgnoreError());
|
||||||
assert(success);
|
assert(success);
|
||||||
|
|
||||||
StdioOutputStream os(stdout);
|
StdioOutputStream os(stdout);
|
||||||
|
|
||||||
Error error;
|
EncoderToOutputStream(os, *encoder);
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
|
||||||
LogError(error);
|
/* write a block of data */
|
||||||
|
|
||||||
|
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError());
|
||||||
|
assert(success);
|
||||||
|
|
||||||
|
EncoderToOutputStream(os, *encoder);
|
||||||
|
|
||||||
|
/* write a tag */
|
||||||
|
|
||||||
|
success = encoder_pre_tag(encoder, IgnoreError());
|
||||||
|
assert(success);
|
||||||
|
|
||||||
|
EncoderToOutputStream(os, *encoder);
|
||||||
|
|
||||||
|
Tag tag;
|
||||||
|
|
||||||
|
{
|
||||||
|
TagBuilder tag_builder;
|
||||||
|
tag_builder.AddItem(TAG_ARTIST, "Foo");
|
||||||
|
tag_builder.AddItem(TAG_TITLE, "Bar");
|
||||||
|
tag_builder.Commit(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
success = encoder_tag(encoder, tag, IgnoreError());
|
||||||
|
assert(success);
|
||||||
|
|
||||||
|
EncoderToOutputStream(os, *encoder);
|
||||||
|
|
||||||
|
/* write another block of data */
|
||||||
|
|
||||||
|
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError());
|
||||||
|
assert(success);
|
||||||
|
|
||||||
|
/* finish */
|
||||||
|
|
||||||
|
success = encoder_end(encoder, IgnoreError());
|
||||||
|
assert(success);
|
||||||
|
|
||||||
|
EncoderToOutputStream(os, *encoder);
|
||||||
|
|
||||||
|
encoder->Close();
|
||||||
|
encoder->Dispose();
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
LogError(e);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write a block of data */
|
|
||||||
|
|
||||||
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError());
|
|
||||||
assert(success);
|
|
||||||
|
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
|
||||||
LogError(error);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write a tag */
|
|
||||||
|
|
||||||
success = encoder_pre_tag(encoder, IgnoreError());
|
|
||||||
assert(success);
|
|
||||||
|
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
|
||||||
LogError(error);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tag tag;
|
|
||||||
|
|
||||||
{
|
|
||||||
TagBuilder tag_builder;
|
|
||||||
tag_builder.AddItem(TAG_ARTIST, "Foo");
|
|
||||||
tag_builder.AddItem(TAG_TITLE, "Bar");
|
|
||||||
tag_builder.Commit(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
success = encoder_tag(encoder, tag, IgnoreError());
|
|
||||||
assert(success);
|
|
||||||
|
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
|
||||||
LogError(error);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write another block of data */
|
|
||||||
|
|
||||||
success = encoder_write(encoder, zero, sizeof(zero), IgnoreError());
|
|
||||||
assert(success);
|
|
||||||
|
|
||||||
/* finish */
|
|
||||||
|
|
||||||
success = encoder_end(encoder, IgnoreError());
|
|
||||||
assert(success);
|
|
||||||
|
|
||||||
if (!EncoderToOutputStream(os, *encoder, error)) {
|
|
||||||
LogError(error);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder->Close();
|
|
||||||
encoder->Dispose();
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user