Changeset: 43285126f7c8 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=43285126f7c8
Added Files:
common/stream/pump.c
Modified Files:
common/stream/Makefile.ag
common/stream/pump.h
common/stream/xz_stream.c
Branch: makelibstreamgreatagain
Log Message:
Create pump_stream and use it in xz_stream
diffs (truncated from 824 to 300 lines):
diff --git a/common/stream/Makefile.ag b/common/stream/Makefile.ag
--- a/common/stream/Makefile.ag
+++ b/common/stream/Makefile.ag
@@ -25,7 +25,8 @@ lib_stream = {
memio.c callback.c blackhole.c fwf.c \
iconv_stream.c \
misc.c text_stream.c \
- stream.h stream_internal.h stream_socket.h
+ pump.c \
+ stream.h stream_internal.h stream_socket.h pump.h
VERSION = $(STREAM_VERSION)
LIBS = WIN32?$(SOCKET_LIBS) \
WIN32?$(zlib_LIBS) \
diff --git a/common/stream/pump.c b/common/stream/pump.c
new file mode 100644
--- /dev/null
+++ b/common/stream/pump.c
@@ -0,0 +1,272 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * Copyright 1997 - July 2008 CWI, August 2008 - 2020 MonetDB B.V.
+ */
+
+/* streams working on a lzma/xz-compressed disk file */
+
+#include "monetdb_config.h"
+#include "stream.h"
+#include "stream_internal.h"
+#include "pump.h"
+
+#include <assert.h>
+
+static pump_result pump_in(stream *restrict s);
+static pump_result pump_out(stream *restrict s, pump_action action);
+
+static ssize_t pump_read(stream *restrict s, void *restrict buf, size_t
elmsize, size_t cnt);
+static ssize_t pump_write(stream *restrict s, const void *restrict buf, size_t
elmsize, size_t cnt);
+static int pump_flush(stream *s);
+static void pump_close(stream *s);
+static void pump_destroy(stream *s);
+
+
+stream *
+pump_stream(stream *inner, pump_state *state)
+{
+ assert(inner);
+ assert(state);
+ assert(state->set_src_win != NULL);
+ assert(state->get_dst_win != NULL);
+ assert(state->set_dst_win != NULL);
+ assert(state->get_buffer != NULL);
+ assert(state->worker != NULL);
+ assert(state->finalizer != NULL);
+
+ inner_state_t *inner_state = state->inner_state;
+
+ stream *s = create_wrapper_stream(NULL, inner);
+ if (s == NULL)
+ return NULL;
+
+ pump_buffer buf = state->get_buffer(inner_state);
+ if (s->readonly) {
+ // Read from inner stream to src buffer through pumper to
outbufs.
+ // This means the src window starts empty
+ buf.count = 0;
+ state->set_src_win(inner_state, buf);
+ } else {
+ // from inbufs through pumper to dst buffer to inner stream.
+ // This means the out window is our whole buffer.
+ state->set_dst_win(inner_state, buf);
+ }
+
+ s->stream_data.p = (void*) state;
+ s->read = pump_read;
+ s->write = pump_write;
+ s->flush = pump_flush;
+ s->close = pump_close;
+ s->destroy = pump_destroy;
+ return s;
+}
+
+
+static ssize_t
+pump_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt)
+{
+ pump_state *state = (pump_state*) s->stream_data.p;
+ inner_state_t *inner_state = state->inner_state;
+ size_t size = elmsize * cnt;
+
+ state->set_dst_win(inner_state, (pump_buffer){ .start = buf, .count =
size});
+ pump_result ret = pump_in(s);
+ if (ret == PUMP_ERROR) {
+ s->errnr = MNSTR_READ_ERROR;
+ return -1;
+ }
+
+ void *free_space = state->get_dst_win(inner_state).start;
+ ssize_t nread = (char*) free_space - (char*) buf;
+
+ return nread / (ssize_t) elmsize;
+}
+
+
+static ssize_t
+pump_write(stream *restrict s, const void *restrict buf, size_t elmsize,
size_t cnt)
+{
+ pump_state *state = (pump_state*) s->stream_data.p;
+ inner_state_t *inner_state = state->inner_state;
+ size_t size = elmsize * cnt;
+
+ if (size == 0)
+ return cnt;
+
+ state->set_src_win(inner_state, (pump_buffer){ .start = (void*)buf,
.count = size });
+ pump_result ret = pump_out(s, PUMP_NO_FLUSH);
+ if (ret == PUMP_ERROR)
+ return -1;
+ ssize_t nwritten = (char*)state->get_src_win(inner_state).start -
(char*)buf;
+ return nwritten / (ssize_t) elmsize;
+}
+
+
+static int pump_flush(stream *s)
+{
+ pump_state *state = (pump_state*) s->stream_data.p;
+ inner_state_t *inner_state = state->inner_state;
+
+ state->set_src_win(inner_state, (pump_buffer){ .start = NULL, .count =
0 });
+ ssize_t nwritten = pump_out(s, PUMP_FLUSH_DATA);
+ if (nwritten < 0)
+ return nwritten;
+ else
+ return mnstr_flush(s->inner);
+}
+
+
+static void
+pump_close(stream *s)
+{
+ pump_state *state = (pump_state*) s->stream_data.p;
+ inner_state_t *inner_state = state->inner_state;
+
+ if (!s->readonly) {
+ state->set_src_win(inner_state, (pump_buffer){ .start = NULL,
.count = 0 });
+ pump_out(s, PUMP_FINISH);
+ }
+ mnstr_close(s->inner);
+}
+
+
+static void
+pump_destroy(stream *s)
+{
+ pump_state *state = (pump_state*) s->stream_data.p;
+ inner_state_t *inner_state = state->inner_state;
+
+ mnstr_destroy(s->inner);
+ state->finalizer(inner_state);
+}
+
+static pump_result
+pump_in(stream *restrict s)
+{
+ pump_state *state = (pump_state *) s->stream_data.p;
+ inner_state_t *inner_state = state->inner_state;
+
+ void *before = state->get_dst_win(inner_state).start;
+ (void) before; // nice while in the debugger
+
+ pump_buffer buffer = state->get_buffer(inner_state);
+ while (1) {
+ pump_buffer dst = state->get_dst_win(inner_state);
+ pump_buffer src = state->get_src_win(inner_state);
+
+ if (dst.count == 0)
+ // Output buffer is full, we're done.
+ return PUMP_OK;
+
+ // Handle input, if possible and necessary
+ if (src.start != NULL && src.count == 0) {
+ // start != NULL means we haven't encountered EOF yet
+
+ ssize_t nread = mnstr_read(s->inner, buffer.start, 1,
buffer.count);
+
+ if (nread < 0)
+ // Error. Return directly, discarding any data
lingering
+ // in the internal state.
+ return PUMP_ERROR;
+ if (nread == 0)
+ // Set to NULL so we'll remember next time.
+ // Maybe there is some data in the internal
state we don't
+ // return immediately.
+ src = (pump_buffer){.start=NULL, .count=0};
+ else
+ // All good
+ src = (pump_buffer) { .start = buffer.start,
.count = nread};
+
+ state->set_src_win(inner_state, src);
+ }
+
+ pump_action action = (src.start != NULL) ? PUMP_NO_FLUSH :
PUMP_FINISH;
+
+ // Try to make some progress
+ assert(dst.count > 0);
+ assert(src.count > 0 || action == PUMP_FINISH);
+ pump_result ret = state->worker(inner_state, action);
+ if (ret == PUMP_ERROR)
+ return PUMP_ERROR;
+
+ if (ret == PUMP_END)
+ // If you say so
+ return PUMP_END;
+
+ // If we get here we made some progress so we're ready for a
new iteration.
+ }
+}
+
+
+static pump_result
+pump_out(stream *restrict s, pump_action action)
+{
+ pump_state *state = (pump_state *) s->stream_data.p;
+ inner_state_t *inner_state = state->inner_state;
+
+ void *before = state->get_src_win(inner_state).start;
+ (void) before; // nice while in the debugger
+
+ pump_buffer buffer = state->get_buffer(inner_state);
+
+ while (1) {
+ pump_buffer dst = state->get_dst_win(inner_state);
+ pump_buffer src = state->get_src_win(inner_state);
+
+ // Make sure there is room in the output buffer
+ if (dst.count == 0) {
+ size_t amount = (char*)dst.start - (char*)buffer.start;
+ ssize_t nwritten = mnstr_write(s->inner, buffer.start,
1, amount);
+ if (nwritten != (ssize_t)amount)
+ return PUMP_ERROR;
+ dst = buffer;
+ state->set_dst_win(inner_state, dst); // reset output
window
+ }
+
+ // Try to make progress
+ pump_result ret = state->worker(inner_state, action);
+ if (ret == PUMP_ERROR)
+ return PUMP_ERROR;
+
+ // src and dst have been invalidated by the call to worker
+ dst = state->get_dst_win(inner_state);
+ src = state->get_src_win(inner_state);
+
+ // There was no error but if input is still available, we
definitely
+ // need another round
+ if (src.count > 0)
+ continue;
+
+ // Though the input data has been consumed, some of it might
still
+ // linger in the internal state.
+ if (action == PUMP_NO_FLUSH) {
+ // Let it linger, we'll combine it with the next batch
+ assert(ret == PUMP_OK); // worker would never PUMP_END,
would it?
+ return PUMP_OK;
+ }
+
+ // We are flushing or finishing or whatever.
+ // We may need to do more iterations to fully flush the
internal state.
+ // Is there any internal state left?
+ if (ret == PUMP_OK)
+ // yes, there is
+ continue;
+
+ // All internal state has been drained.
+ // Now drain the output buffer
+ assert(ret == PUMP_END);
+ size_t amount = (char*)dst.start - (char*)buffer.start;
+ if (amount > 0) {
+ ssize_t nwritten = mnstr_write(s->inner, buffer.start,
1, amount);
+ if (nwritten != (ssize_t)amount)
+ return PUMP_ERROR;
+ }
+ state->set_dst_win(inner_state, buffer); // reset output window
+ return PUMP_END;
+ }
+
+
+}
\ No newline at end of file
diff --git a/common/stream/pump.h b/common/stream/pump.h
--- a/common/stream/pump.h
+++ b/common/stream/pump.h
@@ -26,11 +26,8 @@
*
* All compression libraries we use have such an API but of course the
* parameter types etc are always different.
- * This function encapsulates the logic of copying a whole buffer
- * out to a stream in a type-agnostic way.
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list