|
|
|
/* 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:
|
|
|
|
*
|
|
|
|
* 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 <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>;
|