fs/io/OutputStream: use C++ exceptions in Write()
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
bool
|
||||
BufferedOutputStream::AppendToBuffer(const void *data, size_t size)
|
||||
BufferedOutputStream::AppendToBuffer(const void *data, size_t size) noexcept
|
||||
{
|
||||
auto r = buffer.Write();
|
||||
if (r.size < size)
|
||||
@@ -37,46 +37,36 @@ BufferedOutputStream::AppendToBuffer(const void *data, size_t size)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
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 */
|
||||
if (AppendToBuffer(data, size))
|
||||
return true;
|
||||
return;
|
||||
|
||||
/* not enough room in the buffer - flush it */
|
||||
if (!Flush())
|
||||
return false;
|
||||
Flush();
|
||||
|
||||
/* see if there's now enough room */
|
||||
if (AppendToBuffer(data, size))
|
||||
return true;
|
||||
return;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
return Write(p, strlen(p));
|
||||
Write(p, strlen(p));
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BufferedOutputStream::Format(const char *fmt, ...)
|
||||
{
|
||||
if (gcc_unlikely(last_error.IsDefined()))
|
||||
return false;
|
||||
|
||||
auto r = buffer.Write();
|
||||
if (r.IsEmpty()) {
|
||||
if (!Flush())
|
||||
return false;
|
||||
|
||||
Flush();
|
||||
r = buffer.Write();
|
||||
}
|
||||
|
||||
@@ -90,8 +80,7 @@ BufferedOutputStream::Format(const char *fmt, ...)
|
||||
/* buffer was not large enough; flush it and try
|
||||
again */
|
||||
|
||||
if (!Flush())
|
||||
return false;
|
||||
Flush();
|
||||
|
||||
r = buffer.Write();
|
||||
|
||||
@@ -112,37 +101,15 @@ BufferedOutputStream::Format(const char *fmt, ...)
|
||||
}
|
||||
|
||||
buffer.Append(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BufferedOutputStream::Flush()
|
||||
{
|
||||
if (!Check())
|
||||
return false;
|
||||
|
||||
auto r = buffer.Read();
|
||||
if (r.IsEmpty())
|
||||
return true;
|
||||
return;
|
||||
|
||||
bool success = os.Write(r.data, r.size, last_error);
|
||||
if (gcc_likely(success))
|
||||
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;
|
||||
os.Write(r.data, r.size);
|
||||
buffer.Consume(r.size);
|
||||
}
|
||||
|
@@ -23,67 +23,37 @@
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
#include "util/DynamicFifoBuffer.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
class OutputStream;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* An #OutputStream wrapper that buffers its output to reduce the
|
||||
* 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 {
|
||||
OutputStream &os;
|
||||
|
||||
DynamicFifoBuffer<char> buffer;
|
||||
|
||||
Error last_error;
|
||||
|
||||
public:
|
||||
BufferedOutputStream(OutputStream &_os)
|
||||
:os(_os), buffer(32768) {}
|
||||
|
||||
bool Write(const void *data, size_t size);
|
||||
bool Write(const char *p);
|
||||
void Write(const void *data, size_t size);
|
||||
void Write(const char *p);
|
||||
|
||||
gcc_printf(2,3)
|
||||
bool 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;
|
||||
}
|
||||
void Format(const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Write buffer contents to the #OutputStream.
|
||||
*/
|
||||
bool Flush();
|
||||
|
||||
bool Flush(Error &error);
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
bool AppendToBuffer(const void *data, size_t size);
|
||||
bool AppendToBuffer(const void *data, size_t size) noexcept;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -21,9 +21,6 @@
|
||||
#include "FileOutputStream.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
@@ -50,26 +47,19 @@ BaseFileOutputStream::Tell() const
|
||||
return uint64_t(high) << 32 | uint64_t(low);
|
||||
}
|
||||
|
||||
bool
|
||||
BaseFileOutputStream::Write(const void *data, size_t size, Error &error)
|
||||
void
|
||||
BaseFileOutputStream::Write(const void *data, size_t size)
|
||||
{
|
||||
assert(IsDefined());
|
||||
|
||||
DWORD nbytes;
|
||||
if (!WriteFile(handle, data, size, &nbytes, nullptr)) {
|
||||
error.FormatLastError("Failed to write to %s",
|
||||
path.ToUTF8().c_str());
|
||||
return false;
|
||||
}
|
||||
if (!WriteFile(handle, data, size, &nbytes, nullptr))
|
||||
throw FormatLastError("Failed to write to %s",
|
||||
GetPath().c_str());
|
||||
|
||||
if (size_t(nbytes) != size) {
|
||||
error.FormatLastError(ERROR_DISK_FULL,
|
||||
"Failed to write to %s",
|
||||
path.ToUTF8().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (size_t(nbytes) != size)
|
||||
throw FormatLastError(ERROR_DISK_FULL, "Failed to write to %s",
|
||||
GetPath().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -143,22 +133,17 @@ BaseFileOutputStream::Tell() const
|
||||
return fd.Tell();
|
||||
}
|
||||
|
||||
bool
|
||||
BaseFileOutputStream::Write(const void *data, size_t size, Error &error)
|
||||
void
|
||||
BaseFileOutputStream::Write(const void *data, size_t size)
|
||||
{
|
||||
assert(IsDefined());
|
||||
|
||||
ssize_t nbytes = fd.Write(data, size);
|
||||
if (nbytes < 0) {
|
||||
error.FormatErrno("Failed to write to %s", GetPath().c_str());
|
||||
return false;
|
||||
} else if ((size_t)nbytes < size) {
|
||||
error.FormatErrno(ENOSPC,
|
||||
"Failed to write to %s", GetPath().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (nbytes < 0)
|
||||
throw FormatErrno("Failed to write to %s", GetPath().c_str());
|
||||
else if ((size_t)nbytes < size)
|
||||
throw FormatErrno(ENOSPC, "Failed to write to %s",
|
||||
GetPath().c_str());
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -119,7 +119,7 @@ public:
|
||||
uint64_t Tell() const;
|
||||
|
||||
/* 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 {
|
||||
|
@@ -21,7 +21,6 @@
|
||||
#include "GzipOutputStream.hxx"
|
||||
#include "lib/zlib/Domain.hxx"
|
||||
#include "lib/zlib/Error.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
GzipOutputStream::GzipOutputStream(OutputStream &_next) throw(ZlibError)
|
||||
:next(_next)
|
||||
@@ -47,8 +46,8 @@ GzipOutputStream::~GzipOutputStream()
|
||||
deflateEnd(&z);
|
||||
}
|
||||
|
||||
bool
|
||||
GzipOutputStream::Flush(Error &error)
|
||||
void
|
||||
GzipOutputStream::Flush()
|
||||
{
|
||||
/* no more input */
|
||||
z.next_in = nullptr;
|
||||
@@ -60,21 +59,18 @@ GzipOutputStream::Flush(Error &error)
|
||||
z.avail_out = sizeof(output);
|
||||
|
||||
int result = deflate(&z, Z_FINISH);
|
||||
if (z.next_out > output &&
|
||||
!next.Write(output, z.next_out - output, error))
|
||||
return false;
|
||||
if (z.next_out > output)
|
||||
next.Write(output, z.next_out - output);
|
||||
|
||||
if (result == Z_STREAM_END)
|
||||
return true;
|
||||
else if (result != Z_OK) {
|
||||
error.Set(zlib_domain, result, zError(result));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
else if (result != Z_OK)
|
||||
throw ZlibError(result);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GzipOutputStream::Write(const void *_data, size_t size, Error &error)
|
||||
void
|
||||
GzipOutputStream::Write(const void *_data, size_t size)
|
||||
{
|
||||
/* zlib's API requires non-const input pointer */
|
||||
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);
|
||||
|
||||
int result = deflate(&z, Z_NO_FLUSH);
|
||||
if (result != Z_OK) {
|
||||
error.Set(zlib_domain, result, zError(result));
|
||||
return false;
|
||||
}
|
||||
if (result != Z_OK)
|
||||
throw ZlibError(result);
|
||||
|
||||
if (z.next_out > output &&
|
||||
!next.Write(output, z.next_out - output, error))
|
||||
return false;
|
||||
if (z.next_out > output)
|
||||
next.Write(output, z.next_out - output);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -28,8 +28,6 @@
|
||||
#include <assert.h>
|
||||
#include <zlib.h>
|
||||
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* A filter that compresses data written to it using zlib, forwarding
|
||||
* compressed data in the "gzip" format.
|
||||
@@ -52,10 +50,10 @@ public:
|
||||
* Finish the file and write all data remaining in zlib's
|
||||
* output buffer.
|
||||
*/
|
||||
bool Flush(Error &error);
|
||||
void Flush();
|
||||
|
||||
/* 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
|
||||
|
@@ -21,18 +21,18 @@
|
||||
#define MPD_OUTPUT_STREAM_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
class Error;
|
||||
|
||||
class OutputStream {
|
||||
public:
|
||||
OutputStream() = default;
|
||||
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
|
||||
|
@@ -22,7 +22,6 @@
|
||||
|
||||
#include "check.h"
|
||||
#include "OutputStream.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -34,12 +33,10 @@ public:
|
||||
StdioOutputStream(FILE *_file):file(_file) {}
|
||||
|
||||
/* virtual methods from class OutputStream */
|
||||
bool Write(const void *data, size_t size,
|
||||
gcc_unused Error &error) override {
|
||||
void Write(const void *data, size_t size) override {
|
||||
fwrite(data, 1, size, file);
|
||||
|
||||
/* this class is debug-only and ignores errors */
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user