| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing for details. */ | |
| #pragma once | |
| #include <algorithm> | |
| #include <cstring> | |
| #include <streambuf> | |
| #include <vector> | |
| #include <cm3p/uv.h> | |
| #include "cmUVHandlePtr.h" | |
| /* | |
| * This file is based on example code from: | |
| * | |
| * http://www.voidcn.com/article/p-vjnlygmc-gy.html | |
| * | |
| * 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 <typename CharT, typename Traits = std::char_traits<CharT>> | |
| class cmBasicUVStreambuf : public std::basic_streambuf<CharT, Traits> | |
| { | |
| 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<CharT, Traits>::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<CharT> InputBuffer; | |
| bool EndOfFile = false; | |
| void StreamReadStartStop(); | |
| void StreamRead(ssize_t nread); | |
| void HandleAlloc(uv_buf_t* buf); | |
| }; | |
| template <typename CharT, typename Traits> | |
| cmBasicUVStreambuf<CharT, Traits>::cmBasicUVStreambuf(std::size_t bufSize, | |
| std::size_t putBack) | |
| : PutBack(std::max<std::size_t>(putBack, 1)) | |
| , InputBuffer(std::max<std::size_t>(this->PutBack, bufSize) + this->PutBack) | |
| { | |
| this->close(); | |
| } | |
| template <typename CharT, typename Traits> | |
| cmBasicUVStreambuf<CharT, Traits>::~cmBasicUVStreambuf() | |
| { | |
| this->close(); | |
| } | |
| template <typename CharT, typename Traits> | |
| bool cmBasicUVStreambuf<CharT, Traits>::is_open() const | |
| { | |
| return this->Stream != nullptr; | |
| } | |
| template <typename CharT, typename Traits> | |
| cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::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 <typename CharT, typename Traits> | |
| cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::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 CharT, typename Traits> | |
| typename cmBasicUVStreambuf<CharT, Traits>::int_type | |
| cmBasicUVStreambuf<CharT, Traits>::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 <typename CharT, typename Traits> | |
| std::streamsize cmBasicUVStreambuf<CharT, Traits>::showmanyc() | |
| { | |
| if (!this->is_open() || this->EndOfFile) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| template <typename CharT, typename Traits> | |
| void cmBasicUVStreambuf<CharT, Traits>::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<cmBasicUVStreambuf<CharT, Traits>*>(handle->data); | |
| streambuf->HandleAlloc(buf); | |
| }, | |
| [](uv_stream_t* stream2, ssize_t nread, const uv_buf_t* /* unused */) { | |
| auto streambuf = | |
| static_cast<cmBasicUVStreambuf<CharT, Traits>*>(stream2->data); | |
| streambuf->StreamRead(nread); | |
| }); | |
| } | |
| } | |
| } | |
| template <typename CharT, typename Traits> | |
| void cmBasicUVStreambuf<CharT, Traits>::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 <typename CharT, typename Traits> | |
| void cmBasicUVStreambuf<CharT, Traits>::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<char>; |