Stream should always write all bytes

This commit is contained in:
topjohnwu 2021-11-21 04:38:22 -08:00
parent d8b9265484
commit 5787aa1078
3 changed files with 218 additions and 231 deletions

View File

@ -29,19 +29,15 @@ constexpr size_t ZOPFLI_CHUNK = ZOPFLI_MASTER_BLOCK_SIZE;
constexpr size_t ZOPFLI_CHUNK = CHUNK; constexpr size_t ZOPFLI_CHUNK = CHUNK;
#endif #endif
class cpr_stream : public filter_stream { class out_stream : public filter_stream {
public:
using filter_stream::filter_stream; using filter_stream::filter_stream;
using stream::read; using stream::read;
ssize_t writeFully(void *buf, size_t len) override {
return write(buf, len);
}
}; };
class gz_strm : public cpr_stream { class gz_strm : public out_stream {
public: public:
ssize_t write(const void *buf, size_t len) override { bool write(const void *buf, size_t len) override {
return len ? write(buf, len, Z_NO_FLUSH) : 0; return len == 0 || write(buf, len, Z_NO_FLUSH);
} }
~gz_strm() override { ~gz_strm() override {
@ -63,7 +59,7 @@ protected:
} mode; } mode;
gz_strm(mode_t mode, stream_ptr &&base) : gz_strm(mode_t mode, stream_ptr &&base) :
cpr_stream(std::move(base)), mode(mode), strm{}, outbuf{0} { out_stream(std::move(base)), mode(mode), strm{}, outbuf{0} {
switch(mode) { switch(mode) {
case DECODE: case DECODE:
inflateInit2(&strm, 15 | 16); inflateInit2(&strm, 15 | 16);
@ -78,8 +74,7 @@ private:
z_stream strm; z_stream strm;
uint8_t outbuf[CHUNK]; uint8_t outbuf[CHUNK];
ssize_t write(const void *buf, size_t len, int flush) { bool write(const void *buf, size_t len, int flush) {
size_t ret = 0;
strm.next_in = (Bytef *) buf; strm.next_in = (Bytef *) buf;
strm.avail_in = len; strm.avail_in = len;
do { do {
@ -96,11 +91,12 @@ private:
} }
if (code == Z_STREAM_ERROR) { if (code == Z_STREAM_ERROR) {
LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code); LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code);
return -1; return false;
} }
ret += bwrite(outbuf, sizeof(outbuf) - strm.avail_out); if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
return false;
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return ret; return true;
} }
}; };
@ -114,14 +110,40 @@ public:
explicit gz_encoder(stream_ptr &&base) : gz_strm(ENCODE, std::move(base)) {}; explicit gz_encoder(stream_ptr &&base) : gz_strm(ENCODE, std::move(base)) {};
}; };
class zopfli_encoder : public cpr_stream { class zopfli_encoder : public out_stream {
public: public:
ssize_t write(const void *buf, size_t len) override { bool write(const void *buf, size_t len) override {
return len ? write(static_cast<const unsigned char *>(buf), len) : 0; if (len == 0)
return true;
auto in = static_cast<const unsigned char *>(buf);
in_size += len;
crcvalue = crc32_z(crcvalue, in, len);
for (size_t offset = 0; offset < len; offset += ZOPFLI_CHUNK) {
size_t end_offset = std::min(len, offset + ZOPFLI_CHUNK);
ZopfliDeflatePart(&zo, 2, 0, in, offset, end_offset, &bp, &out, &outsize);
if (bp) {
// The last byte is not complete
if (!bwrite(out, outsize - 1))
return false;
uint8_t b = out[outsize - 1];
free_out();
ZOPFLI_APPEND_DATA(b, &out, &outsize);
} else {
if (!bwrite(out, outsize))
return false;
free_out();
}
} }
explicit zopfli_encoder(stream_ptr &&base) : cpr_stream(std::move(base)), return true;
zo({}), out(nullptr), outsize(0), bp(0), crcvalue(crc32_z(0L, Z_NULL, 0)), in_size(0) { }
explicit zopfli_encoder(stream_ptr &&base) :
out_stream(std::move(base)), zo({}), out(nullptr), outsize(0), bp(0),
crcvalue(crc32_z(0L, Z_NULL, 0)), in_size(0) {
ZopfliInitOptions(&zo); ZopfliInitOptions(&zo);
// Speed things up a bit, this still leads to better compression than zlib // Speed things up a bit, this still leads to better compression than zlib
@ -174,36 +196,12 @@ private:
out = nullptr; out = nullptr;
outsize = 0; outsize = 0;
} }
ssize_t write(const unsigned char *buf, size_t len) {
ssize_t ret = 0;
in_size += len;
crcvalue = crc32_z(crcvalue, buf, len);
for (size_t offset = 0; offset < len; offset += ZOPFLI_CHUNK) {
size_t end_offset = std::min(len, offset + ZOPFLI_CHUNK);
ZopfliDeflatePart(&zo, 2, 0, buf, offset, end_offset, &bp, &out, &outsize);
if (bp) {
// The last byte is not complete
ret += bwrite(out, outsize - 1);
uint8_t b = out[outsize - 1];
free_out();
ZOPFLI_APPEND_DATA(b, &out, &outsize);
} else {
ret += bwrite(out, outsize);
free_out();
}
}
return ret;
}
}; };
class bz_strm : public cpr_stream { class bz_strm : public out_stream {
public: public:
ssize_t write(const void *buf, size_t len) override { bool write(const void *buf, size_t len) override {
return len ? write(buf, len, BZ_RUN) : 0; return len == 0 || write(buf, len, BZ_RUN);
} }
~bz_strm() override { ~bz_strm() override {
@ -225,7 +223,7 @@ protected:
} mode; } mode;
bz_strm(mode_t mode, stream_ptr &&base) : bz_strm(mode_t mode, stream_ptr &&base) :
cpr_stream(std::move(base)), mode(mode), strm{}, outbuf{0} { out_stream(std::move(base)), mode(mode), strm{}, outbuf{0} {
switch(mode) { switch(mode) {
case DECODE: case DECODE:
BZ2_bzDecompressInit(&strm, 0, 0); BZ2_bzDecompressInit(&strm, 0, 0);
@ -240,8 +238,7 @@ private:
bz_stream strm; bz_stream strm;
char outbuf[CHUNK]; char outbuf[CHUNK];
ssize_t write(const void *buf, size_t len, int flush) { bool write(const void *buf, size_t len, int flush) {
size_t ret = 0;
strm.next_in = (char *) buf; strm.next_in = (char *) buf;
strm.avail_in = len; strm.avail_in = len;
do { do {
@ -258,11 +255,12 @@ private:
} }
if (code < 0) { if (code < 0) {
LOGW("bzip2 %s failed (%d)\n", mode ? "encode" : "decode", code); LOGW("bzip2 %s failed (%d)\n", mode ? "encode" : "decode", code);
return -1; return false;
} }
ret += bwrite(outbuf, sizeof(outbuf) - strm.avail_out); if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
return false;
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return ret; return true;
} }
}; };
@ -276,10 +274,10 @@ public:
explicit bz_encoder(stream_ptr &&base) : bz_strm(ENCODE, std::move(base)) {}; explicit bz_encoder(stream_ptr &&base) : bz_strm(ENCODE, std::move(base)) {};
}; };
class lzma_strm : public cpr_stream { class lzma_strm : public out_stream {
public: public:
ssize_t write(const void *buf, size_t len) override { bool write(const void *buf, size_t len) override {
return len ? write(buf, len, LZMA_RUN) : 0; return len == 0 || write(buf, len, LZMA_RUN);
} }
~lzma_strm() override { ~lzma_strm() override {
@ -295,7 +293,7 @@ protected:
} mode; } mode;
lzma_strm(mode_t mode, stream_ptr &&base) : lzma_strm(mode_t mode, stream_ptr &&base) :
cpr_stream(std::move(base)), mode(mode), strm(LZMA_STREAM_INIT), outbuf{0} { out_stream(std::move(base)), mode(mode), strm(LZMA_STREAM_INIT), outbuf{0} {
lzma_options_lzma opt; lzma_options_lzma opt;
// Initialize preset // Initialize preset
@ -326,8 +324,7 @@ private:
lzma_stream strm; lzma_stream strm;
uint8_t outbuf[CHUNK]; uint8_t outbuf[CHUNK];
ssize_t write(const void *buf, size_t len, lzma_action flush) { bool write(const void *buf, size_t len, lzma_action flush) {
size_t ret = 0;
strm.next_in = (uint8_t *) buf; strm.next_in = (uint8_t *) buf;
strm.avail_in = len; strm.avail_in = len;
do { do {
@ -336,11 +333,12 @@ private:
int code = lzma_code(&strm, flush); int code = lzma_code(&strm, flush);
if (code != LZMA_OK && code != LZMA_STREAM_END) { if (code != LZMA_OK && code != LZMA_STREAM_END) {
LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", code); LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", code);
return -1; return false;
} }
ret += bwrite(outbuf, sizeof(outbuf) - strm.avail_out); if (!bwrite(outbuf, sizeof(outbuf) - strm.avail_out))
return false;
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
return ret; return true;
} }
}; };
@ -359,10 +357,10 @@ public:
explicit lzma_encoder(stream_ptr &&base) : lzma_strm(ENCODE_LZMA, std::move(base)) {} explicit lzma_encoder(stream_ptr &&base) : lzma_strm(ENCODE_LZMA, std::move(base)) {}
}; };
class LZ4F_decoder : public cpr_stream { class LZ4F_decoder : public out_stream {
public: public:
explicit LZ4F_decoder(stream_ptr &&base) : explicit LZ4F_decoder(stream_ptr &&base) :
cpr_stream(std::move(base)), ctx(nullptr), outbuf(nullptr), outCapacity(0) { out_stream(std::move(base)), ctx(nullptr), outbuf(nullptr), outCapacity(0) {
LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
} }
@ -371,35 +369,10 @@ public:
delete[] outbuf; delete[] outbuf;
} }
ssize_t write(const void *buf, size_t len) override { bool write(const void *buf, size_t len) override {
size_t ret = 0; auto in = reinterpret_cast<const uint8_t *>(buf);
auto inbuf = reinterpret_cast<const uint8_t *>(buf); if (!outbuf) {
if (!outbuf) size_t read = len;
read_header(inbuf, len);
size_t read, write;
LZ4F_errorCode_t code;
do {
read = len;
write = outCapacity;
code = LZ4F_decompress(ctx, outbuf, &write, inbuf, &read, nullptr);
if (LZ4F_isError(code)) {
LOGW("LZ4F decode error: %s\n", LZ4F_getErrorName(code));
return -1;
}
len -= read;
inbuf += read;
ret += bwrite(outbuf, write);
} while (len != 0 || write != 0);
return ret;
}
private:
LZ4F_decompressionContext_t ctx;
uint8_t *outbuf;
size_t outCapacity;
void read_header(const uint8_t *&in, size_t &size) {
size_t read = size;
LZ4F_frameInfo_t info; LZ4F_frameInfo_t info;
LZ4F_getFrameInfo(ctx, &info, in, &read); LZ4F_getFrameInfo(ctx, &info, in, &read);
switch (info.blockSizeID) { switch (info.blockSizeID) {
@ -411,54 +384,41 @@ private:
} }
outbuf = new uint8_t[outCapacity]; outbuf = new uint8_t[outCapacity];
in += read; in += read;
size -= read; len -= read;
} }
};
class LZ4F_encoder : public cpr_stream {
public:
explicit LZ4F_encoder(stream_ptr &&base) :
cpr_stream(std::move(base)), ctx(nullptr), outbuf(nullptr), outCapacity(0) {
LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
}
ssize_t write(const void *buf, size_t len) override {
size_t ret = 0;
if (!outbuf)
ret += write_header();
if (len == 0)
return 0;
auto inbuf = reinterpret_cast<const uint8_t *>(buf);
size_t read, write; size_t read, write;
LZ4F_errorCode_t code;
do { do {
read = len > BLOCK_SZ ? BLOCK_SZ : len; read = len;
write = LZ4F_compressUpdate(ctx, outbuf, outCapacity, inbuf, read, nullptr); write = outCapacity;
if (LZ4F_isError(write)) { code = LZ4F_decompress(ctx, outbuf, &write, in, &read, nullptr);
LOGW("LZ4F encode error: %s\n", LZ4F_getErrorName(write)); if (LZ4F_isError(code)) {
return -1; LOGW("LZ4F decode error: %s\n", LZ4F_getErrorName(code));
return false;
} }
len -= read; len -= read;
inbuf += read; in += read;
ret += bwrite(outbuf, write); if (!bwrite(outbuf, write))
} while (len != 0); return false;
return ret; } while (len != 0 || write != 0);
} return true;
~LZ4F_encoder() override {
size_t len = LZ4F_compressEnd(ctx, outbuf, outCapacity, nullptr);
bwrite(outbuf, len);
LZ4F_freeCompressionContext(ctx);
delete[] outbuf;
} }
private: private:
LZ4F_compressionContext_t ctx; LZ4F_decompressionContext_t ctx;
uint8_t *outbuf; uint8_t *outbuf;
size_t outCapacity; size_t outCapacity;
};
static constexpr size_t BLOCK_SZ = 1 << 22; class LZ4F_encoder : public out_stream {
public:
explicit LZ4F_encoder(stream_ptr &&base) :
out_stream(std::move(base)), ctx(nullptr), out_buf(nullptr), outCapacity(0) {
LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
}
int write_header() { bool write(const void *buf, size_t len) override {
if (!out_buf) {
LZ4F_preferences_t prefs { LZ4F_preferences_t prefs {
.frameInfo = { .frameInfo = {
.blockSizeID = LZ4F_max4MB, .blockSizeID = LZ4F_max4MB,
@ -470,24 +430,50 @@ private:
.autoFlush = 1, .autoFlush = 1,
}; };
outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs); outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs);
outbuf = new uint8_t[outCapacity]; out_buf = new uint8_t[outCapacity];
size_t write = LZ4F_compressBegin(ctx, outbuf, outCapacity, &prefs); size_t write = LZ4F_compressBegin(ctx, out_buf, outCapacity, &prefs);
return bwrite(outbuf, write); if (!bwrite(out_buf, write))
return false;
} }
if (len == 0)
return true;
auto in = reinterpret_cast<const uint8_t *>(buf);
size_t read, write;
do {
read = len > BLOCK_SZ ? BLOCK_SZ : len;
write = LZ4F_compressUpdate(ctx, out_buf, outCapacity, in, read, nullptr);
if (LZ4F_isError(write)) {
LOGW("LZ4F encode error: %s\n", LZ4F_getErrorName(write));
return false;
}
len -= read;
in += read;
if (!bwrite(out_buf, write))
return false;
} while (len != 0);
return true;
}
~LZ4F_encoder() override {
size_t len = LZ4F_compressEnd(ctx, out_buf, outCapacity, nullptr);
bwrite(out_buf, len);
LZ4F_freeCompressionContext(ctx);
delete[] out_buf;
}
private:
LZ4F_compressionContext_t ctx;
uint8_t *out_buf;
size_t outCapacity;
static constexpr size_t BLOCK_SZ = 1 << 22;
}; };
class buf_cpr_stream : public chunk_out_stream { class LZ4_decoder : public chunk_out_stream {
public:
using chunk_out_stream::chunk_out_stream;
ssize_t writeFully(void *buf, size_t len) override {
return write(buf, len);
}
};
class LZ4_decoder : public buf_cpr_stream {
public: public:
explicit LZ4_decoder(stream_ptr &&base) : explicit LZ4_decoder(stream_ptr &&base) :
buf_cpr_stream(std::move(base), LZ4_COMPRESSED, sizeof(block_sz) + 4), chunk_out_stream(std::move(base), LZ4_COMPRESSED, sizeof(block_sz) + 4),
out_buf(new char[LZ4_UNCOMPRESSED]), block_sz(0) {} out_buf(new char[LZ4_UNCOMPRESSED]), block_sz(0) {}
~LZ4_decoder() override { ~LZ4_decoder() override {
@ -496,10 +482,10 @@ public:
} }
protected: protected:
ssize_t write_chunk(const void *buf, size_t len) override { bool write_chunk(const void *buf, size_t len) override {
// This is an error // This is an error
if (len != chunk_sz) if (len != chunk_sz)
return -1; return false;
auto in = reinterpret_cast<const char *>(buf); auto in = reinterpret_cast<const char *>(buf);
@ -511,14 +497,14 @@ protected:
memcpy(&block_sz, in, sizeof(block_sz)); memcpy(&block_sz, in, sizeof(block_sz));
} }
chunk_sz = block_sz; chunk_sz = block_sz;
return 0; return true;
} else { } else {
int r = LZ4_decompress_safe(in, out_buf, block_sz, LZ4_UNCOMPRESSED); int r = LZ4_decompress_safe(in, out_buf, block_sz, LZ4_UNCOMPRESSED);
chunk_sz = sizeof(block_sz); chunk_sz = sizeof(block_sz);
block_sz = 0; block_sz = 0;
if (r < 0) { if (r < 0) {
LOGW("LZ4HC decompression failure (%d)\n", r); LOGW("LZ4HC decompression failure (%d)\n", r);
return -1; return false;
} }
return bwrite(out_buf, r); return bwrite(out_buf, r);
} }
@ -529,10 +515,10 @@ private:
uint32_t block_sz; uint32_t block_sz;
}; };
class LZ4_encoder : public buf_cpr_stream { class LZ4_encoder : public chunk_out_stream {
public: public:
explicit LZ4_encoder(stream_ptr &&base, bool lg) : explicit LZ4_encoder(stream_ptr &&base, bool lg) :
buf_cpr_stream(std::move(base), LZ4_UNCOMPRESSED), chunk_out_stream(std::move(base), LZ4_UNCOMPRESSED),
out_buf(new char[LZ4_COMPRESSED]), lg(lg), in_total(0) { out_buf(new char[LZ4_COMPRESSED]), lg(lg), in_total(0) {
bwrite("\x02\x21\x4c\x18", 4); bwrite("\x02\x21\x4c\x18", 4);
} }
@ -545,15 +531,13 @@ public:
} }
protected: protected:
ssize_t write_chunk(const void *buf, size_t len) override { bool write_chunk(const void *buf, size_t len) override {
int r = LZ4_compress_HC((const char *) buf, out_buf, len, LZ4_COMPRESSED, LZ4HC_CLEVEL_MAX); int r = LZ4_compress_HC((const char *) buf, out_buf, len, LZ4_COMPRESSED, LZ4HC_CLEVEL_MAX);
if (r == 0) { if (r == 0) {
LOGW("LZ4HC compression failure\n"); LOGW("LZ4HC compression failure\n");
return -1; return false;
} }
bwrite(&r, sizeof(r)); return bwrite(&r, sizeof(r)) && bwrite(out_buf, r);
bwrite(out_buf, r);
return r + sizeof(r);
} }
private: private:
@ -644,7 +628,7 @@ void decompress(char *infile, const char *outfile) {
strm = get_decoder(type, make_unique<fp_stream>(out_fp)); strm = get_decoder(type, make_unique<fp_stream>(out_fp));
if (ext) *ext = '.'; if (ext) *ext = '.';
} }
if (strm->write(buf, len) < 0) if (!strm->write(buf, len))
LOGE("Decompression error!\n"); LOGE("Decompression error!\n");
} }
@ -687,9 +671,9 @@ void compress(const char *method, const char *infile, const char *outfile) {
char buf[4096]; char buf[4096];
size_t len; size_t len;
while ((len = fread(buf, 1, sizeof(buf), in_fp))) { while ((len = fread(buf, 1, sizeof(buf), in_fp))) {
if (strm->write(buf, len) < 0) if (!strm->write(buf, len))
LOGE("Compression error!\n"); LOGE("Compression error!\n");
}; }
strm.reset(nullptr); strm.reset(nullptr);
fclose(in_fp); fclose(in_fp);

View File

@ -11,8 +11,7 @@ public:
virtual ssize_t read(void *buf, size_t len); virtual ssize_t read(void *buf, size_t len);
virtual ssize_t readFully(void *buf, size_t len); virtual ssize_t readFully(void *buf, size_t len);
virtual ssize_t readv(const iovec *iov, int iovcnt); virtual ssize_t readv(const iovec *iov, int iovcnt);
virtual ssize_t write(const void *buf, size_t len); virtual bool write(const void *buf, size_t len);
virtual ssize_t writeFully(void *buf, size_t len);
virtual ssize_t writev(const iovec *iov, int iovcnt); virtual ssize_t writev(const iovec *iov, int iovcnt);
virtual off_t seek(off_t off, int whence); virtual off_t seek(off_t off, int whence);
virtual ~stream() = default; virtual ~stream() = default;
@ -26,7 +25,7 @@ public:
filter_stream(stream_ptr &&base) : base(std::move(base)) {} filter_stream(stream_ptr &&base) : base(std::move(base)) {}
ssize_t read(void *buf, size_t len) override; ssize_t read(void *buf, size_t len) override;
ssize_t write(const void *buf, size_t len) override; bool write(const void *buf, size_t len) override;
// Seeking while filtering does not make sense // Seeking while filtering does not make sense
off_t seek(off_t off, int whence) final { return stream::seek(off, whence); } off_t seek(off_t off, int whence) final { return stream::seek(off, whence); }
@ -44,16 +43,16 @@ public:
chunk_out_stream(stream_ptr &&base, size_t buf_sz = 4096) chunk_out_stream(stream_ptr &&base, size_t buf_sz = 4096)
: chunk_out_stream(std::move(base), buf_sz, buf_sz) {} : chunk_out_stream(std::move(base), buf_sz, buf_sz) {}
~chunk_out_stream() { delete[] _buf; } ~chunk_out_stream() override { delete[] _buf; }
// Reading does not make sense // Reading does not make sense
ssize_t read(void *buf, size_t len) final { return stream::read(buf, len); } ssize_t read(void *buf, size_t len) final { return stream::read(buf, len); }
ssize_t write(const void *buf, size_t len) final; bool write(const void *buf, size_t len) final;
protected: protected:
// Classes inheriting this class has to call close() in the destructor // Classes inheriting this class has to call close() in its destructor
void close(); void close();
virtual ssize_t write_chunk(const void *buf, size_t len) = 0; virtual bool write_chunk(const void *buf, size_t len) = 0;
size_t chunk_sz; size_t chunk_sz;
@ -71,7 +70,7 @@ public:
byte_stream(Byte *&buf, size_t &len) : byte_stream(reinterpret_cast<uint8_t *&>(buf), len) {} byte_stream(Byte *&buf, size_t &len) : byte_stream(reinterpret_cast<uint8_t *&>(buf), len) {}
ssize_t read(void *buf, size_t len) override; ssize_t read(void *buf, size_t len) override;
ssize_t write(const void *buf, size_t len) override; bool write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override; off_t seek(off_t off, int whence) override;
private: private:
@ -83,16 +82,23 @@ private:
void resize(size_t new_pos, bool zero = false); void resize(size_t new_pos, bool zero = false);
}; };
class file_stream : public stream {
public:
bool write(const void *buf, size_t len) final;
protected:
virtual ssize_t do_write(const void *buf, size_t len) = 0;
};
// File stream but does not close the file descriptor at any time // File stream but does not close the file descriptor at any time
class fd_stream : public stream { class fd_stream : public file_stream {
public: public:
fd_stream(int fd) : fd(fd) {} fd_stream(int fd) : fd(fd) {}
ssize_t read(void *buf, size_t len) override; ssize_t read(void *buf, size_t len) override;
ssize_t readv(const iovec *iov, int iovcnt) override; ssize_t readv(const iovec *iov, int iovcnt) override;
ssize_t write(const void *buf, size_t len) override;
ssize_t writev(const iovec *iov, int iovcnt) override; ssize_t writev(const iovec *iov, int iovcnt) override;
off_t seek(off_t off, int whence) override; off_t seek(off_t off, int whence) override;
protected:
ssize_t do_write(const void *buf, size_t len) override;
private: private:
int fd; int fd;
}; };
@ -102,15 +108,14 @@ private:
* ****************************************/ * ****************************************/
// sFILE -> stream_ptr // sFILE -> stream_ptr
class fp_stream final : public stream { class fp_stream final : public file_stream {
public: public:
fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {} fp_stream(FILE *fp = nullptr) : fp(fp, fclose) {}
fp_stream(sFILE &&fp) : fp(std::move(fp)) {} fp_stream(sFILE &&fp) : fp(std::move(fp)) {}
ssize_t read(void *buf, size_t len) override; ssize_t read(void *buf, size_t len) override;
ssize_t write(const void *buf, size_t len) override;
off_t seek(off_t off, int whence) override; off_t seek(off_t off, int whence) override;
protected:
ssize_t do_write(const void *buf, size_t len) override;
private: private:
sFILE fp; sFILE fp;
}; };

View File

@ -13,7 +13,9 @@ static int strm_read(void *v, char *buf, int len) {
static int strm_write(void *v, const char *buf, int len) { static int strm_write(void *v, const char *buf, int len) {
auto strm = static_cast<stream *>(v); auto strm = static_cast<stream *>(v);
return strm->write(buf, len); if (!strm->write(buf, len))
return -1;
return len;
} }
static fpos_t strm_seek(void *v, fpos_t off, int whence) { static fpos_t strm_seek(void *v, fpos_t off, int whence) {
@ -64,33 +66,17 @@ ssize_t stream::readv(const iovec *iov, int iovcnt) {
return read_sz; return read_sz;
} }
ssize_t stream::write(const void *buf, size_t len) { bool stream::write(const void *buf, size_t len) {
LOGE("This stream does not implement write\n"); LOGE("This stream does not implement write\n");
return -1; return false;
}
ssize_t stream::writeFully(void *buf, size_t len) {
size_t write_sz = 0;
ssize_t ret;
do {
ret = write((byte *) buf + write_sz, len - write_sz);
if (ret < 0) {
if (errno == EINTR)
continue;
return ret;
}
write_sz += ret;
} while (write_sz != len && ret != 0);
return write_sz;
} }
ssize_t stream::writev(const iovec *iov, int iovcnt) { ssize_t stream::writev(const iovec *iov, int iovcnt) {
size_t write_sz = 0; size_t write_sz = 0;
for (int i = 0; i < iovcnt; ++i) { for (int i = 0; i < iovcnt; ++i) {
auto ret = writeFully(iov[i].iov_base, iov[i].iov_len); if (!write(iov[i].iov_base, iov[i].iov_len))
if (ret < 0) return write_sz;
return ret; write_sz += iov[i].iov_len;
write_sz += ret;
} }
return write_sz; return write_sz;
} }
@ -105,7 +91,7 @@ ssize_t fp_stream::read(void *buf, size_t len) {
return ret ? ret : (ferror(fp.get()) ? -1 : 0); return ret ? ret : (ferror(fp.get()) ? -1 : 0);
} }
ssize_t fp_stream::write(const void *buf, size_t len) { ssize_t fp_stream::do_write(const void *buf, size_t len) {
return fwrite(buf, 1, len, fp.get()); return fwrite(buf, 1, len, fp.get());
} }
@ -117,12 +103,11 @@ ssize_t filter_stream::read(void *buf, size_t len) {
return base->read(buf, len); return base->read(buf, len);
} }
ssize_t filter_stream::write(const void *buf, size_t len) { bool filter_stream::write(const void *buf, size_t len) {
return base->write(buf, len); return base->write(buf, len);
} }
ssize_t chunk_out_stream::write(const void *_in, size_t len) { bool chunk_out_stream::write(const void *_in, size_t len) {
ssize_t ret = 0;
auto in = static_cast<const uint8_t *>(_in); auto in = static_cast<const uint8_t *>(_in);
while (len) { while (len) {
if (buf_off + len >= chunk_sz) { if (buf_off + len >= chunk_sz) {
@ -140,10 +125,8 @@ ssize_t chunk_out_stream::write(const void *_in, size_t len) {
in += chunk_sz; in += chunk_sz;
len -= chunk_sz; len -= chunk_sz;
} }
auto r = write_chunk(src, chunk_sz); if (!write_chunk(src, chunk_sz))
if (r < 0) return false;
return ret;
ret += r;
} else { } else {
// Buffer internally // Buffer internally
if (!_buf) { if (!_buf) {
@ -154,7 +137,7 @@ ssize_t chunk_out_stream::write(const void *_in, size_t len) {
break; break;
} }
} }
return ret; return true;
} }
void chunk_out_stream::close() { void chunk_out_stream::close() {
@ -177,12 +160,12 @@ ssize_t byte_stream::read(void *buf, size_t len) {
return len; return len;
} }
ssize_t byte_stream::write(const void *buf, size_t len) { bool byte_stream::write(const void *buf, size_t len) {
resize(_pos + len); resize(_pos + len);
memcpy(_buf + _pos, buf, len); memcpy(_buf + _pos, buf, len);
_pos += len; _pos += len;
_len = std::max(_len, _pos); _len = std::max(_len, _pos);
return len; return true;
} }
off_t byte_stream::seek(off_t off, int whence) { off_t byte_stream::seek(off_t off, int whence) {
@ -227,7 +210,7 @@ ssize_t fd_stream::readv(const iovec *iov, int iovcnt) {
return ::readv(fd, iov, iovcnt); return ::readv(fd, iov, iovcnt);
} }
ssize_t fd_stream::write(const void *buf, size_t len) { ssize_t fd_stream::do_write(const void *buf, size_t len) {
return ::write(fd, buf, len); return ::write(fd, buf, len);
} }
@ -238,3 +221,18 @@ ssize_t fd_stream::writev(const iovec *iov, int iovcnt) {
off_t fd_stream::seek(off_t off, int whence) { off_t fd_stream::seek(off_t off, int whence) {
return lseek(fd, off, whence); return lseek(fd, off, whence);
} }
bool file_stream::write(const void *buf, size_t len) {
size_t write_sz = 0;
ssize_t ret;
do {
ret = do_write((byte *) buf + write_sz, len - write_sz);
if (ret < 0) {
if (errno == EINTR)
continue;
return false;
}
write_sz += ret;
} while (write_sz != len && ret != 0);
return true;
}