cmUVStreambuf.h 6.5 KB

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