encoder/Plugin: migrate from class Error to C++ exceptions

This commit is contained in:
Max Kellermann 2016-10-28 21:29:01 +02:00
parent aead221184
commit 0c343cb1c3
15 changed files with 145 additions and 257 deletions

View File

@ -28,6 +28,7 @@
struct AudioFormat; struct AudioFormat;
struct Tag; struct Tag;
class Error;
class Encoder { class Encoder {
const bool implements_tag; const bool implements_tag;

View File

@ -22,27 +22,27 @@
class PreparedEncoder; class PreparedEncoder;
struct ConfigBlock; struct ConfigBlock;
class Error;
struct EncoderPlugin { struct EncoderPlugin {
const char *name; const char *name;
PreparedEncoder *(*init)(const ConfigBlock &block, /**
Error &error); * Throws #std::runtime_error on error.
*/
PreparedEncoder *(*init)(const ConfigBlock &block);
}; };
/** /**
* Creates a new encoder object. * Creates a new encoder object.
* *
* Throws #std::runtime_error on error.
*
* @param plugin the encoder plugin * @param plugin the encoder plugin
* @param error location to store the error occurring, or nullptr to ignore errors.
* @return an encoder object on success, nullptr on failure
*/ */
static inline PreparedEncoder * static inline PreparedEncoder *
encoder_init(const EncoderPlugin &plugin, const ConfigBlock &block, encoder_init(const EncoderPlugin &plugin, const ConfigBlock &block)
Error &error)
{ {
return plugin.init(block, error); return plugin.init(block);
} }
#endif #endif

View File

@ -89,10 +89,10 @@ private:
}; };
class PreparedFlacEncoder final : public PreparedEncoder { class PreparedFlacEncoder final : public PreparedEncoder {
unsigned compression; const unsigned compression;
public: public:
bool Configure(const ConfigBlock &block, Error &error); PreparedFlacEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */ /* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override; Encoder *Open(AudioFormat &audio_format, Error &) override;
@ -104,26 +104,15 @@ public:
static constexpr Domain flac_encoder_domain("vorbis_encoder"); static constexpr Domain flac_encoder_domain("vorbis_encoder");
bool PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block)
PreparedFlacEncoder::Configure(const ConfigBlock &block, Error &) :compression(block.GetBlockValue("compression", 5u))
{ {
compression = block.GetBlockValue("compression", 5u);
return true;
} }
static PreparedEncoder * static PreparedEncoder *
flac_encoder_init(const ConfigBlock &block, Error &error) flac_encoder_init(const ConfigBlock &block)
{ {
auto *encoder = new PreparedFlacEncoder(); return new PreparedFlacEncoder(block);
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */
delete encoder;
return nullptr;
}
return encoder;
} }
static bool static bool

View File

@ -24,6 +24,7 @@
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "util/NumberParser.hxx" #include "util/NumberParser.hxx"
#include "util/ReusableArray.hxx" #include "util/ReusableArray.hxx"
#include "util/RuntimeError.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
@ -58,7 +59,7 @@ class PreparedLameEncoder final : public PreparedEncoder {
int bitrate; int bitrate;
public: public:
bool Configure(const ConfigBlock &block, Error &error); PreparedLameEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */ /* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override; Encoder *Open(AudioFormat &audio_format, Error &) override;
@ -70,8 +71,7 @@ public:
static constexpr Domain lame_encoder_domain("lame_encoder"); static constexpr Domain lame_encoder_domain("lame_encoder");
bool PreparedLameEncoder::PreparedLameEncoder(const ConfigBlock &block)
PreparedLameEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value; const char *value;
char *endptr; char *endptr;
@ -82,55 +82,32 @@ PreparedLameEncoder::Configure(const ConfigBlock &block, Error &error)
quality = ParseDouble(value, &endptr); quality = ParseDouble(value, &endptr);
if (*endptr != '\0' || quality < -1.0 || quality > 10.0) { if (*endptr != '\0' || quality < -1.0 || quality > 10.0)
error.Format(config_domain, throw FormatRuntimeError("quality \"%s\" is not a number in the "
"quality \"%s\" is not a number in the " "range -1 to 10",
"range -1 to 10", value);
value);
return false;
}
if (block.GetBlockValue("bitrate") != nullptr) { if (block.GetBlockValue("bitrate") != nullptr)
error.Set(config_domain, throw std::runtime_error("quality and bitrate are both defined");
"quality and bitrate are both defined");
return false;
}
} else { } else {
/* a bit rate was configured */ /* a bit rate was configured */
value = block.GetBlockValue("bitrate"); value = block.GetBlockValue("bitrate");
if (value == nullptr) { if (value == nullptr)
error.Set(config_domain, throw std::runtime_error("neither bitrate nor quality defined");
"neither bitrate nor quality defined");
return false;
}
quality = -2.0; quality = -2.0;
bitrate = ParseInt(value, &endptr); bitrate = ParseInt(value, &endptr);
if (*endptr != '\0' || bitrate <= 0) { if (*endptr != '\0' || bitrate <= 0)
error.Set(config_domain, throw std::runtime_error("bitrate should be a positive integer");
"bitrate should be a positive integer");
return false;
}
} }
return true;
} }
static PreparedEncoder * static PreparedEncoder *
lame_encoder_init(const ConfigBlock &block, Error &error) lame_encoder_init(const ConfigBlock &block)
{ {
auto *encoder = new PreparedLameEncoder(); return new PreparedLameEncoder(block);
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */
delete encoder;
return nullptr;
}
return encoder;
} }
static bool static bool

View File

@ -51,8 +51,7 @@ public:
}; };
static PreparedEncoder * static PreparedEncoder *
null_encoder_init(gcc_unused const ConfigBlock &block, null_encoder_init(gcc_unused const ConfigBlock &block)
gcc_unused Error &error)
{ {
return new PreparedNullEncoder(); return new PreparedNullEncoder();
} }

View File

@ -30,6 +30,8 @@
#include <opus.h> #include <opus.h>
#include <ogg/ogg.h> #include <ogg/ogg.h>
#include <stdexcept>
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -78,7 +80,7 @@ class PreparedOpusEncoder final : public PreparedEncoder {
int signal; int signal;
public: public:
bool Configure(const ConfigBlock &block, Error &error); PreparedOpusEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */ /* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override; Encoder *Open(AudioFormat &audio_format, Error &) override;
@ -90,8 +92,7 @@ public:
static constexpr Domain opus_encoder_domain("opus_encoder"); static constexpr Domain opus_encoder_domain("opus_encoder");
bool PreparedOpusEncoder::PreparedOpusEncoder(const ConfigBlock &block)
PreparedOpusEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value = block.GetBlockValue("bitrate", "auto"); const char *value = block.GetBlockValue("bitrate", "auto");
if (strcmp(value, "auto") == 0) if (strcmp(value, "auto") == 0)
@ -102,17 +103,13 @@ PreparedOpusEncoder::Configure(const ConfigBlock &block, Error &error)
char *endptr; char *endptr;
bitrate = strtoul(value, &endptr, 10); bitrate = strtoul(value, &endptr, 10);
if (endptr == value || *endptr != 0 || if (endptr == value || *endptr != 0 ||
bitrate < 500 || bitrate > 512000) { bitrate < 500 || bitrate > 512000)
error.Set(config_domain, "Invalid bit rate"); throw std::runtime_error("Invalid bit rate");
return false;
}
} }
complexity = block.GetBlockValue("complexity", 10u); complexity = block.GetBlockValue("complexity", 10u);
if (complexity > 10) { if (complexity > 10)
error.Format(config_domain, "Invalid complexity"); throw std::runtime_error("Invalid complexity");
return false;
}
value = block.GetBlockValue("signal", "auto"); value = block.GetBlockValue("signal", "auto");
if (strcmp(value, "auto") == 0) if (strcmp(value, "auto") == 0)
@ -121,27 +118,14 @@ PreparedOpusEncoder::Configure(const ConfigBlock &block, Error &error)
signal = OPUS_SIGNAL_VOICE; signal = OPUS_SIGNAL_VOICE;
else if (strcmp(value, "music") == 0) else if (strcmp(value, "music") == 0)
signal = OPUS_SIGNAL_MUSIC; signal = OPUS_SIGNAL_MUSIC;
else { else
error.Format(config_domain, "Invalid signal"); throw std::runtime_error("Invalid signal");
return false;
}
return true;
} }
static PreparedEncoder * static PreparedEncoder *
opus_encoder_init(const ConfigBlock &block, Error &error) opus_encoder_init(const ConfigBlock &block)
{ {
auto *encoder = new PreparedOpusEncoder(); return new PreparedOpusEncoder(block);
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */
delete encoder;
return nullptr;
}
return encoder;
} }
OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc) OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc)

View File

@ -88,7 +88,7 @@ class PreparedShineEncoder final : public PreparedEncoder {
shine_config_t config; shine_config_t config;
public: public:
bool Configure(const ConfigBlock &block, Error &error); PreparedShineEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */ /* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override; Encoder *Open(AudioFormat &audio_format, Error &) override;
@ -98,28 +98,16 @@ public:
} }
}; };
inline bool PreparedShineEncoder::PreparedShineEncoder(const ConfigBlock &block)
PreparedShineEncoder::Configure(const ConfigBlock &block, Error &)
{ {
shine_set_config_mpeg_defaults(&config.mpeg); shine_set_config_mpeg_defaults(&config.mpeg);
config.mpeg.bitr = block.GetBlockValue("bitrate", 128); config.mpeg.bitr = block.GetBlockValue("bitrate", 128);
return true;
} }
static PreparedEncoder * static PreparedEncoder *
shine_encoder_init(const ConfigBlock &block, Error &error) shine_encoder_init(const ConfigBlock &block)
{ {
auto *encoder = new PreparedShineEncoder(); return new PreparedShineEncoder(block);
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */
delete encoder;
return nullptr;
}
return encoder;
} }
static shine_t static shine_t

View File

@ -23,6 +23,7 @@
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "util/NumberParser.hxx" #include "util/NumberParser.hxx"
#include "util/RuntimeError.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "Log.hxx" #include "Log.hxx"
@ -77,7 +78,7 @@ class PreparedTwolameEncoder final : public PreparedEncoder {
int bitrate; int bitrate;
public: public:
bool Configure(const ConfigBlock &block, Error &error); PreparedTwolameEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */ /* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override; Encoder *Open(AudioFormat &audio_format, Error &) override;
@ -89,8 +90,7 @@ public:
static constexpr Domain twolame_encoder_domain("twolame_encoder"); static constexpr Domain twolame_encoder_domain("twolame_encoder");
bool PreparedTwolameEncoder::PreparedTwolameEncoder(const ConfigBlock &block)
PreparedTwolameEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value; const char *value;
char *endptr; char *endptr;
@ -101,58 +101,35 @@ PreparedTwolameEncoder::Configure(const ConfigBlock &block, Error &error)
quality = ParseDouble(value, &endptr); quality = ParseDouble(value, &endptr);
if (*endptr != '\0' || quality < -1.0 || quality > 10.0) { if (*endptr != '\0' || quality < -1.0 || quality > 10.0)
error.Format(config_domain, throw FormatRuntimeError("quality \"%s\" is not a number in the "
"quality \"%s\" is not a number in the " "range -1 to 10",
"range -1 to 10", value);
value);
return false;
}
if (block.GetBlockValue("bitrate") != nullptr) { if (block.GetBlockValue("bitrate") != nullptr)
error.Set(config_domain, throw std::runtime_error("quality and bitrate are both defined");
"quality and bitrate are both defined");
return false;
}
} else { } else {
/* a bit rate was configured */ /* a bit rate was configured */
value = block.GetBlockValue("bitrate"); value = block.GetBlockValue("bitrate");
if (value == nullptr) { if (value == nullptr)
error.Set(config_domain, throw std::runtime_error("neither bitrate nor quality defined");
"neither bitrate nor quality defined");
return false;
}
quality = -2.0; quality = -2.0;
bitrate = ParseInt(value, &endptr); bitrate = ParseInt(value, &endptr);
if (*endptr != '\0' || bitrate <= 0) { if (*endptr != '\0' || bitrate <= 0)
error.Set(config_domain, throw std::runtime_error("bitrate should be a positive integer");
"bitrate should be a positive integer");
return false;
}
} }
return true;
} }
static PreparedEncoder * static PreparedEncoder *
twolame_encoder_init(const ConfigBlock &block, Error &error_r) twolame_encoder_init(const ConfigBlock &block)
{ {
FormatDebug(twolame_encoder_domain, FormatDebug(twolame_encoder_domain,
"libtwolame version %s", get_twolame_version()); "libtwolame version %s", get_twolame_version());
auto *encoder = new PreparedTwolameEncoder(); return new PreparedTwolameEncoder(block);
/* load configuration from "block" */
if (!encoder->Configure(block, error_r)) {
/* configuration has failed, roll back and return error */
delete encoder;
return nullptr;
}
return encoder;
} }
static bool static bool

View File

@ -25,6 +25,7 @@
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "util/StringUtil.hxx" #include "util/StringUtil.hxx"
#include "util/NumberParser.hxx" #include "util/NumberParser.hxx"
#include "util/RuntimeError.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
@ -73,7 +74,7 @@ class PreparedVorbisEncoder final : public PreparedEncoder {
int bitrate; int bitrate;
public: public:
bool Configure(const ConfigBlock &block, Error &error); PreparedVorbisEncoder(const ConfigBlock &block);
/* virtual methods from class PreparedEncoder */ /* virtual methods from class PreparedEncoder */
Encoder *Open(AudioFormat &audio_format, Error &) override; Encoder *Open(AudioFormat &audio_format, Error &) override;
@ -85,8 +86,7 @@ public:
static constexpr Domain vorbis_encoder_domain("vorbis_encoder"); static constexpr Domain vorbis_encoder_domain("vorbis_encoder");
bool PreparedVorbisEncoder::PreparedVorbisEncoder(const ConfigBlock &block)
PreparedVorbisEncoder::Configure(const ConfigBlock &block, Error &error)
{ {
const char *value = block.GetBlockValue("quality"); const char *value = block.GetBlockValue("quality");
if (value != nullptr) { if (value != nullptr) {
@ -95,56 +95,33 @@ PreparedVorbisEncoder::Configure(const ConfigBlock &block, Error &error)
char *endptr; char *endptr;
quality = ParseDouble(value, &endptr); quality = ParseDouble(value, &endptr);
if (*endptr != '\0' || quality < -1.0 || quality > 10.0) { if (*endptr != '\0' || quality < -1.0 || quality > 10.0)
error.Format(config_domain, throw FormatRuntimeError("quality \"%s\" is not a number in the "
"quality \"%s\" is not a number in the " "range -1 to 10",
"range -1 to 10", value);
value);
return false;
}
if (block.GetBlockValue("bitrate") != nullptr) { if (block.GetBlockValue("bitrate") != nullptr)
error.Set(config_domain, throw std::runtime_error("quality and bitrate are both defined");
"quality and bitrate are both defined");
return false;
}
} else { } else {
/* a bit rate was configured */ /* a bit rate was configured */
value = block.GetBlockValue("bitrate"); value = block.GetBlockValue("bitrate");
if (value == nullptr) { if (value == nullptr)
error.Set(config_domain, throw std::runtime_error("neither bitrate nor quality defined");
"neither bitrate nor quality defined");
return false;
}
quality = -2.0; quality = -2.0;
char *endptr; char *endptr;
bitrate = ParseInt(value, &endptr); bitrate = ParseInt(value, &endptr);
if (*endptr != '\0' || bitrate <= 0) { if (*endptr != '\0' || bitrate <= 0)
error.Set(config_domain, throw std::runtime_error("bitrate should be a positive integer");
"bitrate should be a positive integer");
return false;
}
} }
return true;
} }
static PreparedEncoder * static PreparedEncoder *
vorbis_encoder_init(const ConfigBlock &block, Error &error) vorbis_encoder_init(const ConfigBlock &block)
{ {
auto *encoder = new PreparedVorbisEncoder(); return new PreparedVorbisEncoder(block);
/* load configuration from "block" */
if (!encoder->Configure(block, error)) {
/* configuration has failed, roll back and return error */
delete encoder;
return nullptr;
}
return encoder;
} }
bool bool

View File

@ -98,8 +98,7 @@ fill_wave_header(WaveHeader *header, int channels, int bits,
} }
static PreparedEncoder * static PreparedEncoder *
wave_encoder_init(gcc_unused const ConfigBlock &block, wave_encoder_init(gcc_unused const ConfigBlock &block)
gcc_unused Error &error)
{ {
return new PreparedWaveEncoder(); return new PreparedWaveEncoder();
} }

View File

@ -148,9 +148,7 @@ RecorderOutput::Configure(const ConfigBlock &block, Error &error)
/* initialize encoder */ /* initialize encoder */
prepared_encoder = encoder_init(*encoder_plugin, block, error); prepared_encoder = encoder_init(*encoder_plugin, block);
if (prepared_encoder == nullptr)
return false;
return true; return true;
} }
@ -160,14 +158,19 @@ RecorderOutput::Create(const ConfigBlock &block, Error &error)
{ {
RecorderOutput *recorder = new RecorderOutput(); RecorderOutput *recorder = new RecorderOutput();
if (!recorder->Initialize(block, error)) { try {
delete recorder; if (!recorder->Initialize(block, error)) {
return nullptr; delete recorder;
} return nullptr;
}
if (!recorder->Configure(block, error)) { if (!recorder->Configure(block, error)) {
delete recorder;
return nullptr;
}
} catch (...) {
delete recorder; delete recorder;
return nullptr; throw;
} }
return recorder; return recorder;

View File

@ -194,9 +194,7 @@ ShoutOutput::Configure(const ConfigBlock &block, Error &error)
return false; return false;
} }
prepared_encoder = encoder_init(*encoder_plugin, block, error); prepared_encoder = encoder_init(*encoder_plugin, block);
if (prepared_encoder == nullptr)
return false;
unsigned shout_format; unsigned shout_format;
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0) if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
@ -304,9 +302,14 @@ ShoutOutput::Create(const ConfigBlock &block, Error &error)
return nullptr; return nullptr;
} }
if (!sd->Configure(block, error)) { try {
if (!sd->Configure(block, error)) {
delete sd;
return nullptr;
}
} catch (...) {
delete sd; delete sd;
return nullptr; throw;
} }
return sd; return sd;

View File

@ -117,9 +117,7 @@ HttpdOutput::Configure(const ConfigBlock &block, Error &error)
/* initialize encoder */ /* initialize encoder */
prepared_encoder = encoder_init(*encoder_plugin, block, error); prepared_encoder = encoder_init(*encoder_plugin, block);
if (prepared_encoder == nullptr)
return false;
/* determine content type */ /* determine content type */
content_type = prepared_encoder->GetMimeType(); content_type = prepared_encoder->GetMimeType();

View File

@ -67,11 +67,7 @@ int main(int argc, char **argv)
try { try {
Error error; Error error;
std::unique_ptr<PreparedEncoder> p_encoder(encoder_init(*plugin, block, error)); std::unique_ptr<PreparedEncoder> p_encoder(encoder_init(*plugin, block));
if (p_encoder == nullptr) {
LogError(error, "Failed to initialize encoder");
return EXIT_FAILURE;
}
/* open the encoder */ /* open the encoder */

View File

@ -39,7 +39,7 @@ static uint8_t zero[256];
int int
main(gcc_unused int argc, gcc_unused char **argv) main(gcc_unused int argc, gcc_unused char **argv)
{ try {
gcc_unused bool success; gcc_unused bool success;
/* create the encoder */ /* create the encoder */
@ -50,65 +50,62 @@ main(gcc_unused int argc, gcc_unused char **argv)
ConfigBlock block; ConfigBlock block;
block.AddBlockParam("quality", "5.0", -1); block.AddBlockParam("quality", "5.0", -1);
std::unique_ptr<PreparedEncoder> p_encoder(encoder_init(*plugin, block, std::unique_ptr<PreparedEncoder> p_encoder(encoder_init(*plugin, block));
IgnoreError()));
assert(p_encoder != nullptr); assert(p_encoder != nullptr);
try { /* open the encoder */
/* open the encoder */
AudioFormat audio_format(44100, SampleFormat::S16, 2); AudioFormat audio_format(44100, SampleFormat::S16, 2);
std::unique_ptr<Encoder> encoder(p_encoder->Open(audio_format, std::unique_ptr<Encoder> encoder(p_encoder->Open(audio_format,
IgnoreError())); IgnoreError()));
assert(encoder != nullptr); assert(encoder != nullptr);
StdioOutputStream os(stdout); StdioOutputStream os(stdout);
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
/* write a block of data */ /* write a block of data */
success = encoder->Write(zero, sizeof(zero), IgnoreError()); success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success); assert(success);
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
/* write a tag */ /* write a tag */
success = encoder->PreTag(IgnoreError()); success = encoder->PreTag(IgnoreError());
assert(success); assert(success);
EncoderToOutputStream(os, *encoder); EncoderToOutputStream(os, *encoder);
Tag tag; Tag tag;
{ {
TagBuilder tag_builder; TagBuilder tag_builder;
tag_builder.AddItem(TAG_ARTIST, "Foo"); tag_builder.AddItem(TAG_ARTIST, "Foo");
tag_builder.AddItem(TAG_TITLE, "Bar"); tag_builder.AddItem(TAG_TITLE, "Bar");
tag_builder.Commit(tag); tag_builder.Commit(tag);
}
success = encoder->SendTag(tag, IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
/* write another block of data */
success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success);
/* finish */
success = encoder->End(IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
return EXIT_SUCCESS;
} catch (const std::exception &e) {
LogError(e);
return EXIT_FAILURE;
} }
success = encoder->SendTag(tag, IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
/* write another block of data */
success = encoder->Write(zero, sizeof(zero), IgnoreError());
assert(success);
/* finish */
success = encoder->End(IgnoreError());
assert(success);
EncoderToOutputStream(os, *encoder);
return EXIT_SUCCESS;
} catch (const std::exception &e) {
LogError(e);
return EXIT_FAILURE;
} }