Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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>;