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