/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once #include #include #include #include #include #include "cmUVHandlePtr.h" /* * This file is based on example code from: * * https://web.archive.org/web/20170515211805/ * http://www.mr-edd.co.uk/blog/beginners_guide_streambuf * * The example code was distributed under the following license: * * Copyright 2007 Edd Dawson. * Distributed under the Boost Software License, Version 1.0. * * Boost Software License - Version 1.0 - August 17th, 2003 * * Permission is hereby granted, free of charge, to any person or organization * obtaining a copy of the software and accompanying documentation covered by * this license (the "Software") to use, reproduce, display, distribute, * execute, and transmit the Software, and to prepare derivative works of the * Software, and to permit third-parties to whom the Software is furnished to * do so, all subject to the following: * * The copyright notices in the Software and this entire statement, including * the above license grant, this restriction and the following disclaimer, * must be included in all copies of the Software, in whole or in part, and * all derivative works of the Software, unless such copies or derivative * works are solely in the form of machine-executable object code generated by * a source language processor. * * 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ template > class cmBasicUVStreambuf : public std::basic_streambuf { public: cmBasicUVStreambuf(std::size_t bufSize = 256, std::size_t putBack = 8); ~cmBasicUVStreambuf() override; bool is_open() const; cmBasicUVStreambuf* open(uv_stream_t* stream); cmBasicUVStreambuf* close(); protected: typename cmBasicUVStreambuf::int_type underflow() override; std::streamsize showmanyc() override; // FIXME: Add write support private: uv_stream_t* Stream = nullptr; void* OldStreamData = nullptr; const std::size_t PutBack = 0; std::vector InputBuffer; bool EndOfFile = false; void StreamReadStartStop(); void StreamRead(ssize_t nread); void HandleAlloc(uv_buf_t* buf); }; template cmBasicUVStreambuf::cmBasicUVStreambuf(std::size_t bufSize, std::size_t putBack) : PutBack(std::max(putBack, 1)) , InputBuffer(std::max(this->PutBack, bufSize) + this->PutBack) { this->close(); } template cmBasicUVStreambuf::~cmBasicUVStreambuf() { this->close(); } template bool cmBasicUVStreambuf::is_open() const { return this->Stream != nullptr; } template cmBasicUVStreambuf* cmBasicUVStreambuf::open( uv_stream_t* stream) { this->close(); this->Stream = stream; this->EndOfFile = false; if (this->Stream) { this->OldStreamData = this->Stream->data; this->Stream->data = this; } this->StreamReadStartStop(); return this; } template cmBasicUVStreambuf* cmBasicUVStreambuf::close() { if (this->Stream) { uv_read_stop(this->Stream); this->Stream->data = this->OldStreamData; } this->Stream = nullptr; CharT* readEnd = this->InputBuffer.data() + this->InputBuffer.size(); this->setg(readEnd, readEnd, readEnd); return this; } template typename cmBasicUVStreambuf::int_type cmBasicUVStreambuf::underflow() { if (!this->is_open()) { return Traits::eof(); } if (this->gptr() < this->egptr()) { return Traits::to_int_type(*this->gptr()); } this->StreamReadStartStop(); while (this->in_avail() == 0) { uv_run(this->Stream->loop, UV_RUN_ONCE); } if (this->in_avail() == -1) { return Traits::eof(); } return Traits::to_int_type(*this->gptr()); } template std::streamsize cmBasicUVStreambuf::showmanyc() { if (!this->is_open() || this->EndOfFile) { return -1; } return 0; } template void cmBasicUVStreambuf::StreamReadStartStop() { if (this->Stream) { uv_read_stop(this->Stream); if (this->gptr() >= this->egptr()) { uv_read_start( this->Stream, [](uv_handle_t* handle, size_t /* unused */, uv_buf_t* buf) { auto streambuf = static_cast*>(handle->data); streambuf->HandleAlloc(buf); }, [](uv_stream_t* stream2, ssize_t nread, const uv_buf_t* /* unused */) { auto streambuf = static_cast*>(stream2->data); streambuf->StreamRead(nread); }); } } } template void cmBasicUVStreambuf::HandleAlloc(uv_buf_t* buf) { auto size = this->egptr() - this->gptr(); std::memmove(this->InputBuffer.data(), this->gptr(), this->egptr() - this->gptr()); this->setg(this->InputBuffer.data(), this->InputBuffer.data(), this->InputBuffer.data() + size); buf->base = this->egptr(); #ifdef _WIN32 # define BUF_LEN_TYPE ULONG #else # define BUF_LEN_TYPE size_t #endif buf->len = BUF_LEN_TYPE( (this->InputBuffer.data() + this->InputBuffer.size() - this->egptr()) * sizeof(CharT)); #undef BUF_LEN_TYPE } template void cmBasicUVStreambuf::StreamRead(ssize_t nread) { if (nread > 0) { this->setg(this->eback(), this->gptr(), this->egptr() + nread / sizeof(CharT)); uv_read_stop(this->Stream); } else if (nread < 0 /*|| nread == UV_EOF*/) { this->EndOfFile = true; uv_read_stop(this->Stream); } } using cmUVStreambuf = cmBasicUVStreambuf;