summaryrefslogtreecommitdiff
path: root/src/os_common/os_fstream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/os_common/os_fstream.c')
-rw-r--r--src/os_common/os_fstream.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/os_common/os_fstream.c b/src/os_common/os_fstream.c
new file mode 100644
index 00000000000..fc0daf1c211
--- /dev/null
+++ b/src/os_common/os_fstream.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 2014-2016 MongoDB, Inc.
+ * Copyright (c) 2008-2014 WiredTiger, Inc.
+ * All rights reserved.
+ *
+ * See the file LICENSE for redistribution information.
+ */
+
+#include "wt_internal.h"
+
+/* Buffer size for streamed reads/writes. */
+#define WT_STREAM_BUFSIZE 8192
+
+/*
+ * __fstream_close --
+ * Close a stream handle.
+ */
+static int
+__fstream_close(WT_SESSION_IMPL *session, WT_FSTREAM *fs)
+{
+ WT_DECL_RET;
+
+ if (!F_ISSET(fs, WT_STREAM_READ))
+ WT_TRET(fs->flush(session, fs));
+
+ WT_TRET(__wt_close(session, &fs->fh));
+ __wt_buf_free(session, &fs->buf);
+ __wt_free(session, fs);
+ return (ret);
+}
+
+/*
+ * __fstream_flush --
+ * Flush the data from a stream.
+ */
+static int
+__fstream_flush(WT_SESSION_IMPL *session, WT_FSTREAM *fs)
+{
+ if (fs->buf.size > 0) {
+ WT_RET(__wt_write(
+ session, fs->fh, fs->off, fs->buf.size, fs->buf.data));
+ fs->off += (wt_off_t)fs->buf.size;
+ fs->buf.size = 0;
+ }
+
+ return (0);
+}
+
+/*
+ * __fstream_flush_notsup --
+ * Stream flush unsupported.
+ */
+static int
+__fstream_flush_notsup(WT_SESSION_IMPL *session, WT_FSTREAM *fs)
+{
+ WT_RET_MSG(session, ENOTSUP, "%s: flush", fs->name);
+}
+
+/*
+ * __fstream_getline --
+ * Get a line from a stream.
+ *
+ * Implementation of the POSIX getline or BSD fgetln functions (finding the
+ * function in a portable way is hard, it's simple enough to write it instead).
+ *
+ * Note: Unlike the standard getline calls, this function doesn't include the
+ * trailing newline character in the returned buffer and discards empty lines
+ * (so the caller's EOF marker is a returned line length of 0).
+ */
+static int
+__fstream_getline(WT_SESSION_IMPL *session, WT_FSTREAM *fs, WT_ITEM *buf)
+{
+ const char *p;
+ size_t len;
+ char c;
+
+ /*
+ * We always NUL-terminate the returned string (even if it's empty),
+ * make sure there's buffer space for a trailing NUL in all cases.
+ */
+ WT_RET(__wt_buf_init(session, buf, 100));
+
+ for (;;) {
+ /* Check if we need to refill the buffer. */
+ if (WT_PTRDIFF(fs->buf.data, fs->buf.mem) >= fs->buf.size) {
+ len = WT_MIN(WT_STREAM_BUFSIZE,
+ (size_t)(fs->size - fs->off));
+ if (len == 0)
+ break; /* EOF */
+ WT_RET(__wt_buf_initsize(session, &fs->buf, len));
+ WT_RET(__wt_read(
+ session, fs->fh, fs->off, len, fs->buf.mem));
+ fs->off += (wt_off_t)len;
+ }
+
+ c = *(p = fs->buf.data);
+ fs->buf.data = ++p;
+
+ /* Leave space for a trailing NUL. */
+ WT_RET(__wt_buf_extend(session, buf, buf->size + 2));
+ if (c == '\n') {
+ if (buf->size == 0)
+ continue;
+ break;
+ }
+ ((char *)buf->mem)[buf->size++] = c;
+ }
+
+ ((char *)buf->mem)[buf->size] = '\0';
+
+ return (0);
+}
+
+/*
+ * __fstream_getline_notsup --
+ * Stream getline unsupported.
+ */
+static int
+__fstream_getline_notsup(WT_SESSION_IMPL *session, WT_FSTREAM *fs, WT_ITEM *buf)
+{
+ WT_UNUSED(buf);
+ WT_RET_MSG(session, ENOTSUP, "%s: getline", fs->name);
+}
+
+/*
+ * __fstream_printf --
+ * ANSI C vfprintf.
+ */
+static int
+__fstream_printf(
+ WT_SESSION_IMPL *session, WT_FSTREAM *fs, const char *fmt, va_list ap)
+{
+ WT_ITEM *buf;
+ va_list ap_copy;
+ size_t len, space;
+ char *p;
+
+ buf = &fs->buf;
+
+ for (;;) {
+ va_copy(ap_copy, ap);
+ p = (char *)((uint8_t *)buf->mem + buf->size);
+ WT_ASSERT(session, buf->memsize >= buf->size);
+ space = buf->memsize - buf->size;
+ len = (size_t)vsnprintf(p, space, fmt, ap_copy);
+ va_end(ap_copy);
+
+ if (len < space) {
+ buf->size += len;
+
+ return (buf->size >= WT_STREAM_BUFSIZE ?
+ __wt_fflush(session, fs) : 0);
+ }
+ WT_RET(__wt_buf_extend(session, buf, buf->size + len + 1));
+ }
+}
+
+/*
+ * __fstream_printf_notsup --
+ * ANSI C vfprintf unsupported.
+ */
+static int
+__fstream_printf_notsup(
+ WT_SESSION_IMPL *session, WT_FSTREAM *fs, const char *fmt, va_list ap)
+{
+ WT_UNUSED(fmt);
+ WT_UNUSED(ap);
+ WT_RET_MSG(session, ENOTSUP, "%s: printf", fs->name);
+}
+
+/*
+ * __wt_fopen --
+ * Open a stream handle.
+ */
+int
+__wt_fopen(WT_SESSION_IMPL *session,
+ const char *name, uint32_t open_flags, uint32_t flags, WT_FSTREAM **fsp)
+{
+ WT_DECL_RET;
+ WT_FH *fh;
+ WT_FSTREAM *fs;
+
+ fs = NULL;
+
+ WT_RET(__wt_open(
+ session, name, WT_OPEN_FILE_TYPE_REGULAR, open_flags, &fh));
+
+ WT_ERR(__wt_calloc_one(session, &fs));
+ fs->fh = fh;
+ fs->name = fh->name;
+ fs->flags = flags;
+
+ fs->close = __fstream_close;
+ WT_ERR(__wt_filesize(session, fh, &fs->size));
+ if (LF_ISSET(WT_STREAM_APPEND))
+ fs->off = fs->size;
+ if (LF_ISSET(WT_STREAM_APPEND | WT_STREAM_WRITE)) {
+ fs->flush = __fstream_flush;
+ fs->getline = __fstream_getline_notsup;
+ fs->printf = __fstream_printf;
+ } else {
+ WT_ASSERT(session, LF_ISSET(WT_STREAM_READ));
+ fs->flush = __fstream_flush_notsup;
+ fs->getline = __fstream_getline;
+ fs->printf = __fstream_printf_notsup;
+ }
+ *fsp = fs;
+ return (0);
+
+err: WT_TRET(__wt_close(session, &fh));
+ __wt_free(session, *fsp);
+ return (ret);
+}