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