/src/CMake/Source/cmUVStreambuf.h
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #pragma once |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cstring> |
7 | | #include <streambuf> |
8 | | #include <vector> |
9 | | |
10 | | #include <cm3p/uv.h> |
11 | | |
12 | | #include "cmUVHandlePtr.h" |
13 | | |
14 | | /* |
15 | | * This file is based on example code from: |
16 | | * |
17 | | * https://web.archive.org/web/20170515211805/ |
18 | | * http://www.mr-edd.co.uk/blog/beginners_guide_streambuf |
19 | | * |
20 | | * The example code was distributed under the following license: |
21 | | * |
22 | | * Copyright 2007 Edd Dawson. |
23 | | * Distributed under the Boost Software License, Version 1.0. |
24 | | * |
25 | | * Boost Software License - Version 1.0 - August 17th, 2003 |
26 | | * |
27 | | * Permission is hereby granted, free of charge, to any person or organization |
28 | | * obtaining a copy of the software and accompanying documentation covered by |
29 | | * this license (the "Software") to use, reproduce, display, distribute, |
30 | | * execute, and transmit the Software, and to prepare derivative works of the |
31 | | * Software, and to permit third-parties to whom the Software is furnished to |
32 | | * do so, all subject to the following: |
33 | | * |
34 | | * The copyright notices in the Software and this entire statement, including |
35 | | * the above license grant, this restriction and the following disclaimer, |
36 | | * must be included in all copies of the Software, in whole or in part, and |
37 | | * all derivative works of the Software, unless such copies or derivative |
38 | | * works are solely in the form of machine-executable object code generated by |
39 | | * a source language processor. |
40 | | * |
41 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
42 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
43 | | * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
44 | | * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
45 | | * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
46 | | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
47 | | * DEALINGS IN THE SOFTWARE. |
48 | | */ |
49 | | |
50 | | template <typename CharT, typename Traits = std::char_traits<CharT>> |
51 | | class cmBasicUVStreambuf : public std::basic_streambuf<CharT, Traits> |
52 | | { |
53 | | public: |
54 | | cmBasicUVStreambuf(std::size_t bufSize = 256, std::size_t putBack = 8); |
55 | | ~cmBasicUVStreambuf() override; |
56 | | |
57 | | bool is_open() const; |
58 | | |
59 | | cmBasicUVStreambuf* open(uv_stream_t* stream); |
60 | | |
61 | | cmBasicUVStreambuf* close(); |
62 | | |
63 | | protected: |
64 | | typename cmBasicUVStreambuf<CharT, Traits>::int_type underflow() override; |
65 | | std::streamsize showmanyc() override; |
66 | | |
67 | | // FIXME: Add write support |
68 | | |
69 | | private: |
70 | | uv_stream_t* Stream = nullptr; |
71 | | void* OldStreamData = nullptr; |
72 | | std::size_t const PutBack = 0; |
73 | | std::vector<CharT> InputBuffer; |
74 | | bool EndOfFile = false; |
75 | | |
76 | | void StreamReadStartStop(); |
77 | | |
78 | | void StreamRead(ssize_t nread); |
79 | | void HandleAlloc(uv_buf_t* buf); |
80 | | }; |
81 | | |
82 | | template <typename CharT, typename Traits> |
83 | | cmBasicUVStreambuf<CharT, Traits>::cmBasicUVStreambuf(std::size_t bufSize, |
84 | | std::size_t putBack) |
85 | 0 | : PutBack(std::max<std::size_t>(putBack, 1)) |
86 | 0 | , InputBuffer(std::max<std::size_t>(this->PutBack, bufSize) + this->PutBack) |
87 | 0 | { |
88 | 0 | this->close(); |
89 | 0 | } |
90 | | |
91 | | template <typename CharT, typename Traits> |
92 | | cmBasicUVStreambuf<CharT, Traits>::~cmBasicUVStreambuf() |
93 | 0 | { |
94 | 0 | this->close(); |
95 | 0 | } |
96 | | |
97 | | template <typename CharT, typename Traits> |
98 | | bool cmBasicUVStreambuf<CharT, Traits>::is_open() const |
99 | 0 | { |
100 | 0 | return this->Stream != nullptr; |
101 | 0 | } |
102 | | |
103 | | template <typename CharT, typename Traits> |
104 | | cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::open( |
105 | | uv_stream_t* stream) |
106 | 0 | { |
107 | 0 | this->close(); |
108 | 0 | this->Stream = stream; |
109 | 0 | this->EndOfFile = false; |
110 | 0 | if (this->Stream) { |
111 | 0 | this->OldStreamData = this->Stream->data; |
112 | 0 | this->Stream->data = this; |
113 | 0 | } |
114 | 0 | this->StreamReadStartStop(); |
115 | 0 | return this; |
116 | 0 | } |
117 | | |
118 | | template <typename CharT, typename Traits> |
119 | | cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::close() |
120 | 0 | { |
121 | 0 | if (this->Stream) { |
122 | 0 | uv_read_stop(this->Stream); |
123 | 0 | this->Stream->data = this->OldStreamData; |
124 | 0 | } |
125 | 0 | this->Stream = nullptr; |
126 | 0 | CharT* readEnd = this->InputBuffer.data() + this->InputBuffer.size(); |
127 | 0 | this->setg(readEnd, readEnd, readEnd); |
128 | 0 | return this; |
129 | 0 | } |
130 | | |
131 | | template <typename CharT, typename Traits> |
132 | | typename cmBasicUVStreambuf<CharT, Traits>::int_type |
133 | | cmBasicUVStreambuf<CharT, Traits>::underflow() |
134 | 0 | { |
135 | 0 | if (!this->is_open()) { |
136 | 0 | return Traits::eof(); |
137 | 0 | } |
138 | | |
139 | 0 | if (this->gptr() < this->egptr()) { |
140 | 0 | return Traits::to_int_type(*this->gptr()); |
141 | 0 | } |
142 | | |
143 | 0 | this->StreamReadStartStop(); |
144 | 0 | while (this->in_avail() == 0) { |
145 | 0 | uv_run(this->Stream->loop, UV_RUN_ONCE); |
146 | 0 | } |
147 | 0 | if (this->in_avail() == -1) { |
148 | 0 | return Traits::eof(); |
149 | 0 | } |
150 | 0 | return Traits::to_int_type(*this->gptr()); |
151 | 0 | } |
152 | | |
153 | | template <typename CharT, typename Traits> |
154 | | std::streamsize cmBasicUVStreambuf<CharT, Traits>::showmanyc() |
155 | 0 | { |
156 | 0 | if (!this->is_open() || this->EndOfFile) { |
157 | 0 | return -1; |
158 | 0 | } |
159 | 0 | return 0; |
160 | 0 | } |
161 | | |
162 | | template <typename CharT, typename Traits> |
163 | | void cmBasicUVStreambuf<CharT, Traits>::StreamReadStartStop() |
164 | 0 | { |
165 | 0 | if (this->Stream) { |
166 | 0 | uv_read_stop(this->Stream); |
167 | 0 | if (this->gptr() >= this->egptr()) { |
168 | 0 | uv_read_start( |
169 | 0 | this->Stream, |
170 | 0 | [](uv_handle_t* handle, size_t /* unused */, uv_buf_t* buf) { |
171 | 0 | auto streambuf = |
172 | 0 | static_cast<cmBasicUVStreambuf<CharT, Traits>*>(handle->data); |
173 | 0 | streambuf->HandleAlloc(buf); |
174 | 0 | }, |
175 | 0 | [](uv_stream_t* stream2, ssize_t nread, uv_buf_t const* /* unused */) { |
176 | 0 | auto streambuf = |
177 | 0 | static_cast<cmBasicUVStreambuf<CharT, Traits>*>(stream2->data); |
178 | 0 | streambuf->StreamRead(nread); |
179 | 0 | }); |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | template <typename CharT, typename Traits> |
185 | | void cmBasicUVStreambuf<CharT, Traits>::HandleAlloc(uv_buf_t* buf) |
186 | 0 | { |
187 | 0 | auto size = this->egptr() - this->gptr(); |
188 | 0 | std::memmove(this->InputBuffer.data(), this->gptr(), |
189 | 0 | this->egptr() - this->gptr()); |
190 | 0 | this->setg(this->InputBuffer.data(), this->InputBuffer.data(), |
191 | 0 | this->InputBuffer.data() + size); |
192 | 0 | buf->base = this->egptr(); |
193 | | #ifdef _WIN32 |
194 | | # define BUF_LEN_TYPE ULONG |
195 | | #else |
196 | 0 | # define BUF_LEN_TYPE size_t |
197 | 0 | #endif |
198 | 0 | buf->len = BUF_LEN_TYPE( |
199 | 0 | (this->InputBuffer.data() + this->InputBuffer.size() - this->egptr()) * |
200 | 0 | sizeof(CharT)); |
201 | 0 | #undef BUF_LEN_TYPE |
202 | 0 | } |
203 | | |
204 | | template <typename CharT, typename Traits> |
205 | | void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t nread) |
206 | 0 | { |
207 | 0 | if (nread > 0) { |
208 | 0 | this->setg(this->eback(), this->gptr(), |
209 | 0 | this->egptr() + nread / sizeof(CharT)); |
210 | 0 | uv_read_stop(this->Stream); |
211 | 0 | } else if (nread < 0 /*|| nread == UV_EOF*/) { |
212 | 0 | this->EndOfFile = true; |
213 | 0 | uv_read_stop(this->Stream); |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | using cmUVStreambuf = cmBasicUVStreambuf<char>; |