You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
420 lines
15 KiB
420 lines
15 KiB
7 years ago
|
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
||
|
// Licensed under the MIT License:
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
// THE SOFTWARE.
|
||
|
|
||
|
#ifndef KJ_IO_H_
|
||
|
#define KJ_IO_H_
|
||
|
|
||
|
#if defined(__GNUC__) && !KJ_HEADER_WARNINGS
|
||
|
#pragma GCC system_header
|
||
|
#endif
|
||
|
|
||
|
#include <stddef.h>
|
||
|
#include "common.h"
|
||
|
#include "array.h"
|
||
|
#include "exception.h"
|
||
|
|
||
|
namespace kj {
|
||
|
|
||
|
// =======================================================================================
|
||
|
// Abstract interfaces
|
||
|
|
||
|
class InputStream {
|
||
|
public:
|
||
|
virtual ~InputStream() noexcept(false);
|
||
|
|
||
|
size_t read(void* buffer, size_t minBytes, size_t maxBytes);
|
||
|
// Reads at least minBytes and at most maxBytes, copying them into the given buffer. Returns
|
||
|
// the size read. Throws an exception on errors. Implemented in terms of tryRead().
|
||
|
//
|
||
|
// maxBytes is the number of bytes the caller really wants, but minBytes is the minimum amount
|
||
|
// needed by the caller before it can start doing useful processing. If the stream returns less
|
||
|
// than maxBytes, the caller will usually call read() again later to get the rest. Returning
|
||
|
// less than maxBytes is useful when it makes sense for the caller to parallelize processing
|
||
|
// with I/O.
|
||
|
//
|
||
|
// Never blocks if minBytes is zero. If minBytes is zero and maxBytes is non-zero, this may
|
||
|
// attempt a non-blocking read or may just return zero. To force a read, use a non-zero minBytes.
|
||
|
// To detect EOF without throwing an exception, use tryRead().
|
||
|
//
|
||
|
// If the InputStream can't produce minBytes, it MUST throw an exception, as the caller is not
|
||
|
// expected to understand how to deal with partial reads.
|
||
|
|
||
|
virtual size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) = 0;
|
||
|
// Like read(), but may return fewer than minBytes on EOF.
|
||
|
|
||
|
inline void read(void* buffer, size_t bytes) { read(buffer, bytes, bytes); }
|
||
|
// Convenience method for reading an exact number of bytes.
|
||
|
|
||
|
virtual void skip(size_t bytes);
|
||
|
// Skips past the given number of bytes, discarding them. The default implementation read()s
|
||
|
// into a scratch buffer.
|
||
|
};
|
||
|
|
||
|
class OutputStream {
|
||
|
public:
|
||
|
virtual ~OutputStream() noexcept(false);
|
||
|
|
||
|
virtual void write(const void* buffer, size_t size) = 0;
|
||
|
// Always writes the full size. Throws exception on error.
|
||
|
|
||
|
virtual void write(ArrayPtr<const ArrayPtr<const byte>> pieces);
|
||
|
// Equivalent to write()ing each byte array in sequence, which is what the default implementation
|
||
|
// does. Override if you can do something better, e.g. use writev() to do the write in a single
|
||
|
// syscall.
|
||
|
};
|
||
|
|
||
|
class BufferedInputStream: public InputStream {
|
||
|
// An input stream which buffers some bytes in memory to reduce system call overhead.
|
||
|
// - OR -
|
||
|
// An input stream that actually reads from some in-memory data structure and wants to give its
|
||
|
// caller a direct pointer to that memory to potentially avoid a copy.
|
||
|
|
||
|
public:
|
||
|
virtual ~BufferedInputStream() noexcept(false);
|
||
|
|
||
|
ArrayPtr<const byte> getReadBuffer();
|
||
|
// Get a direct pointer into the read buffer, which contains the next bytes in the input. If the
|
||
|
// caller consumes any bytes, it should then call skip() to indicate this. This always returns a
|
||
|
// non-empty buffer or throws an exception. Implemented in terms of tryGetReadBuffer().
|
||
|
|
||
|
virtual ArrayPtr<const byte> tryGetReadBuffer() = 0;
|
||
|
// Like getReadBuffer() but may return an empty buffer on EOF.
|
||
|
};
|
||
|
|
||
|
class BufferedOutputStream: public OutputStream {
|
||
|
// An output stream which buffers some bytes in memory to reduce system call overhead.
|
||
|
// - OR -
|
||
|
// An output stream that actually writes into some in-memory data structure and wants to give its
|
||
|
// caller a direct pointer to that memory to potentially avoid a copy.
|
||
|
|
||
|
public:
|
||
|
virtual ~BufferedOutputStream() noexcept(false);
|
||
|
|
||
|
virtual ArrayPtr<byte> getWriteBuffer() = 0;
|
||
|
// Get a direct pointer into the write buffer. The caller may choose to fill in some prefix of
|
||
|
// this buffer and then pass it to write(), in which case write() may avoid a copy. It is
|
||
|
// incorrect to pass to write any slice of this buffer which is not a prefix.
|
||
|
};
|
||
|
|
||
|
// =======================================================================================
|
||
|
// Buffered streams implemented as wrappers around regular streams
|
||
|
|
||
|
class BufferedInputStreamWrapper: public BufferedInputStream {
|
||
|
// Implements BufferedInputStream in terms of an InputStream.
|
||
|
//
|
||
|
// Note that the underlying stream's position is unpredictable once the wrapper is destroyed,
|
||
|
// unless the entire stream was consumed. To read a predictable number of bytes in a buffered
|
||
|
// way without going over, you'd need this wrapper to wrap some other wrapper which itself
|
||
|
// implements an artificial EOF at the desired point. Such a stream should be trivial to write
|
||
|
// but is not provided by the library at this time.
|
||
|
|
||
|
public:
|
||
|
explicit BufferedInputStreamWrapper(InputStream& inner, ArrayPtr<byte> buffer = nullptr);
|
||
|
// Creates a buffered stream wrapping the given non-buffered stream. No guarantee is made about
|
||
|
// the position of the inner stream after a buffered wrapper has been created unless the entire
|
||
|
// input is read.
|
||
|
//
|
||
|
// If the second parameter is non-null, the stream uses the given buffer instead of allocating
|
||
|
// its own. This may improve performance if the buffer can be reused.
|
||
|
|
||
|
KJ_DISALLOW_COPY(BufferedInputStreamWrapper);
|
||
|
~BufferedInputStreamWrapper() noexcept(false);
|
||
|
|
||
|
// implements BufferedInputStream ----------------------------------
|
||
|
ArrayPtr<const byte> tryGetReadBuffer() override;
|
||
|
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
|
||
|
void skip(size_t bytes) override;
|
||
|
|
||
|
private:
|
||
|
InputStream& inner;
|
||
|
Array<byte> ownedBuffer;
|
||
|
ArrayPtr<byte> buffer;
|
||
|
ArrayPtr<byte> bufferAvailable;
|
||
|
};
|
||
|
|
||
|
class BufferedOutputStreamWrapper: public BufferedOutputStream {
|
||
|
// Implements BufferedOutputStream in terms of an OutputStream. Note that writes to the
|
||
|
// underlying stream may be delayed until flush() is called or the wrapper is destroyed.
|
||
|
|
||
|
public:
|
||
|
explicit BufferedOutputStreamWrapper(OutputStream& inner, ArrayPtr<byte> buffer = nullptr);
|
||
|
// Creates a buffered stream wrapping the given non-buffered stream.
|
||
|
//
|
||
|
// If the second parameter is non-null, the stream uses the given buffer instead of allocating
|
||
|
// its own. This may improve performance if the buffer can be reused.
|
||
|
|
||
|
KJ_DISALLOW_COPY(BufferedOutputStreamWrapper);
|
||
|
~BufferedOutputStreamWrapper() noexcept(false);
|
||
|
|
||
|
void flush();
|
||
|
// Force the wrapper to write any remaining bytes in its buffer to the inner stream. Note that
|
||
|
// this only flushes this object's buffer; this object has no idea how to flush any other buffers
|
||
|
// that may be present in the underlying stream.
|
||
|
|
||
|
// implements BufferedOutputStream ---------------------------------
|
||
|
ArrayPtr<byte> getWriteBuffer() override;
|
||
|
void write(const void* buffer, size_t size) override;
|
||
|
|
||
|
private:
|
||
|
OutputStream& inner;
|
||
|
Array<byte> ownedBuffer;
|
||
|
ArrayPtr<byte> buffer;
|
||
|
byte* bufferPos;
|
||
|
UnwindDetector unwindDetector;
|
||
|
};
|
||
|
|
||
|
// =======================================================================================
|
||
|
// Array I/O
|
||
|
|
||
|
class ArrayInputStream: public BufferedInputStream {
|
||
|
public:
|
||
|
explicit ArrayInputStream(ArrayPtr<const byte> array);
|
||
|
KJ_DISALLOW_COPY(ArrayInputStream);
|
||
|
~ArrayInputStream() noexcept(false);
|
||
|
|
||
|
// implements BufferedInputStream ----------------------------------
|
||
|
ArrayPtr<const byte> tryGetReadBuffer() override;
|
||
|
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
|
||
|
void skip(size_t bytes) override;
|
||
|
|
||
|
private:
|
||
|
ArrayPtr<const byte> array;
|
||
|
};
|
||
|
|
||
|
class ArrayOutputStream: public BufferedOutputStream {
|
||
|
public:
|
||
|
explicit ArrayOutputStream(ArrayPtr<byte> array);
|
||
|
KJ_DISALLOW_COPY(ArrayOutputStream);
|
||
|
~ArrayOutputStream() noexcept(false);
|
||
|
|
||
|
ArrayPtr<byte> getArray() {
|
||
|
// Get the portion of the array which has been filled in.
|
||
|
return arrayPtr(array.begin(), fillPos);
|
||
|
}
|
||
|
|
||
|
// implements BufferedInputStream ----------------------------------
|
||
|
ArrayPtr<byte> getWriteBuffer() override;
|
||
|
void write(const void* buffer, size_t size) override;
|
||
|
|
||
|
private:
|
||
|
ArrayPtr<byte> array;
|
||
|
byte* fillPos;
|
||
|
};
|
||
|
|
||
|
class VectorOutputStream: public BufferedOutputStream {
|
||
|
public:
|
||
|
explicit VectorOutputStream(size_t initialCapacity = 4096);
|
||
|
KJ_DISALLOW_COPY(VectorOutputStream);
|
||
|
~VectorOutputStream() noexcept(false);
|
||
|
|
||
|
ArrayPtr<byte> getArray() {
|
||
|
// Get the portion of the array which has been filled in.
|
||
|
return arrayPtr(vector.begin(), fillPos);
|
||
|
}
|
||
|
|
||
|
// implements BufferedInputStream ----------------------------------
|
||
|
ArrayPtr<byte> getWriteBuffer() override;
|
||
|
void write(const void* buffer, size_t size) override;
|
||
|
|
||
|
private:
|
||
|
Array<byte> vector;
|
||
|
byte* fillPos;
|
||
|
|
||
|
void grow(size_t minSize);
|
||
|
};
|
||
|
|
||
|
// =======================================================================================
|
||
|
// File descriptor I/O
|
||
|
|
||
|
class AutoCloseFd {
|
||
|
// A wrapper around a file descriptor which automatically closes the descriptor when destroyed.
|
||
|
// The wrapper supports move construction for transferring ownership of the descriptor. If
|
||
|
// close() returns an error, the destructor throws an exception, UNLESS the destructor is being
|
||
|
// called during unwind from another exception, in which case the close error is ignored.
|
||
|
//
|
||
|
// If your code is not exception-safe, you should not use AutoCloseFd. In this case you will
|
||
|
// have to call close() yourself and handle errors appropriately.
|
||
|
|
||
|
public:
|
||
|
inline AutoCloseFd(): fd(-1) {}
|
||
|
inline AutoCloseFd(decltype(nullptr)): fd(-1) {}
|
||
|
inline explicit AutoCloseFd(int fd): fd(fd) {}
|
||
|
inline AutoCloseFd(AutoCloseFd&& other) noexcept: fd(other.fd) { other.fd = -1; }
|
||
|
KJ_DISALLOW_COPY(AutoCloseFd);
|
||
|
~AutoCloseFd() noexcept(false);
|
||
|
|
||
|
inline AutoCloseFd& operator=(AutoCloseFd&& other) {
|
||
|
AutoCloseFd old(kj::mv(*this));
|
||
|
fd = other.fd;
|
||
|
other.fd = -1;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline AutoCloseFd& operator=(decltype(nullptr)) {
|
||
|
AutoCloseFd old(kj::mv(*this));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline operator int() const { return fd; }
|
||
|
inline int get() const { return fd; }
|
||
|
|
||
|
operator bool() const = delete;
|
||
|
// Deleting this operator prevents accidental use in boolean contexts, which
|
||
|
// the int conversion operator above would otherwise allow.
|
||
|
|
||
|
inline bool operator==(decltype(nullptr)) { return fd < 0; }
|
||
|
inline bool operator!=(decltype(nullptr)) { return fd >= 0; }
|
||
|
|
||
|
private:
|
||
|
int fd;
|
||
|
UnwindDetector unwindDetector;
|
||
|
};
|
||
|
|
||
|
inline auto KJ_STRINGIFY(const AutoCloseFd& fd)
|
||
|
-> decltype(kj::toCharSequence(implicitCast<int>(fd))) {
|
||
|
return kj::toCharSequence(implicitCast<int>(fd));
|
||
|
}
|
||
|
|
||
|
class FdInputStream: public InputStream {
|
||
|
// An InputStream wrapping a file descriptor.
|
||
|
|
||
|
public:
|
||
|
explicit FdInputStream(int fd): fd(fd) {}
|
||
|
explicit FdInputStream(AutoCloseFd fd): fd(fd), autoclose(mv(fd)) {}
|
||
|
KJ_DISALLOW_COPY(FdInputStream);
|
||
|
~FdInputStream() noexcept(false);
|
||
|
|
||
|
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
|
||
|
|
||
|
inline int getFd() const { return fd; }
|
||
|
|
||
|
private:
|
||
|
int fd;
|
||
|
AutoCloseFd autoclose;
|
||
|
};
|
||
|
|
||
|
class FdOutputStream: public OutputStream {
|
||
|
// An OutputStream wrapping a file descriptor.
|
||
|
|
||
|
public:
|
||
|
explicit FdOutputStream(int fd): fd(fd) {}
|
||
|
explicit FdOutputStream(AutoCloseFd fd): fd(fd), autoclose(mv(fd)) {}
|
||
|
KJ_DISALLOW_COPY(FdOutputStream);
|
||
|
~FdOutputStream() noexcept(false);
|
||
|
|
||
|
void write(const void* buffer, size_t size) override;
|
||
|
void write(ArrayPtr<const ArrayPtr<const byte>> pieces) override;
|
||
|
|
||
|
inline int getFd() const { return fd; }
|
||
|
|
||
|
private:
|
||
|
int fd;
|
||
|
AutoCloseFd autoclose;
|
||
|
};
|
||
|
|
||
|
// =======================================================================================
|
||
|
// Win32 Handle I/O
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
|
||
|
class AutoCloseHandle {
|
||
|
// A wrapper around a Win32 HANDLE which automatically closes the handle when destroyed.
|
||
|
// The wrapper supports move construction for transferring ownership of the handle. If
|
||
|
// CloseHandle() returns an error, the destructor throws an exception, UNLESS the destructor is
|
||
|
// being called during unwind from another exception, in which case the close error is ignored.
|
||
|
//
|
||
|
// If your code is not exception-safe, you should not use AutoCloseHandle. In this case you will
|
||
|
// have to call close() yourself and handle errors appropriately.
|
||
|
|
||
|
public:
|
||
|
inline AutoCloseHandle(): handle((void*)-1) {}
|
||
|
inline AutoCloseHandle(decltype(nullptr)): handle((void*)-1) {}
|
||
|
inline explicit AutoCloseHandle(void* handle): handle(handle) {}
|
||
|
inline AutoCloseHandle(AutoCloseHandle&& other) noexcept: handle(other.handle) {
|
||
|
other.handle = (void*)-1;
|
||
|
}
|
||
|
KJ_DISALLOW_COPY(AutoCloseHandle);
|
||
|
~AutoCloseHandle() noexcept(false);
|
||
|
|
||
|
inline AutoCloseHandle& operator=(AutoCloseHandle&& other) {
|
||
|
AutoCloseHandle old(kj::mv(*this));
|
||
|
handle = other.handle;
|
||
|
other.handle = (void*)-1;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline AutoCloseHandle& operator=(decltype(nullptr)) {
|
||
|
AutoCloseHandle old(kj::mv(*this));
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline operator void*() const { return handle; }
|
||
|
inline void* get() const { return handle; }
|
||
|
|
||
|
operator bool() const = delete;
|
||
|
// Deleting this operator prevents accidental use in boolean contexts, which
|
||
|
// the void* conversion operator above would otherwise allow.
|
||
|
|
||
|
inline bool operator==(decltype(nullptr)) { return handle != (void*)-1; }
|
||
|
inline bool operator!=(decltype(nullptr)) { return handle == (void*)-1; }
|
||
|
|
||
|
private:
|
||
|
void* handle; // -1 (aka INVALID_HANDLE_VALUE) if not valid.
|
||
|
};
|
||
|
|
||
|
class HandleInputStream: public InputStream {
|
||
|
// An InputStream wrapping a Win32 HANDLE.
|
||
|
|
||
|
public:
|
||
|
explicit HandleInputStream(void* handle): handle(handle) {}
|
||
|
explicit HandleInputStream(AutoCloseHandle handle): handle(handle), autoclose(mv(handle)) {}
|
||
|
KJ_DISALLOW_COPY(HandleInputStream);
|
||
|
~HandleInputStream() noexcept(false);
|
||
|
|
||
|
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override;
|
||
|
|
||
|
private:
|
||
|
void* handle;
|
||
|
AutoCloseHandle autoclose;
|
||
|
};
|
||
|
|
||
|
class HandleOutputStream: public OutputStream {
|
||
|
// An OutputStream wrapping a Win32 HANDLE.
|
||
|
|
||
|
public:
|
||
|
explicit HandleOutputStream(void* handle): handle(handle) {}
|
||
|
explicit HandleOutputStream(AutoCloseHandle handle): handle(handle), autoclose(mv(handle)) {}
|
||
|
KJ_DISALLOW_COPY(HandleOutputStream);
|
||
|
~HandleOutputStream() noexcept(false);
|
||
|
|
||
|
void write(const void* buffer, size_t size) override;
|
||
|
|
||
|
private:
|
||
|
void* handle;
|
||
|
AutoCloseHandle autoclose;
|
||
|
};
|
||
|
|
||
|
#endif // _WIN32
|
||
|
|
||
|
} // namespace kj
|
||
|
|
||
|
#endif // KJ_IO_H_
|