iostream_ext.cxx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file iostream_ext.cxx
  10. * @author rdb
  11. * @date 2017-07-24
  12. */
  13. #include "iostream_ext.h"
  14. #ifdef HAVE_PYTHON
  15. #ifndef CPPPARSER
  16. extern struct Dtool_PyTypedObject Dtool_std_istream;
  17. #endif
  18. /**
  19. * Reads the given number of bytes from the stream, returned as bytes object.
  20. * If the given size is -1, all bytes are read from the stream.
  21. */
  22. PyObject *Extension<istream>::
  23. read(Py_ssize_t size) {
  24. if (size < 0) {
  25. return readall();
  26. }
  27. char *buffer = nullptr;
  28. std::streamsize read_bytes = 0;
  29. if (size > 0) {
  30. std::streambuf *buf = _this->rdbuf();
  31. nassertr(buf != nullptr, nullptr);
  32. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  33. PyThreadState *_save;
  34. Py_UNBLOCK_THREADS
  35. #endif
  36. buffer = (char *)alloca((size_t)size);
  37. read_bytes = buf->sgetn(buffer, (size_t)size);
  38. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  39. Py_BLOCK_THREADS
  40. #endif
  41. }
  42. return PyBytes_FromStringAndSize(buffer, read_bytes);
  43. }
  44. /**
  45. * Reads from the underlying stream, but using at most one call. The number
  46. * of returned bytes may therefore be less than what was requested, but it
  47. * will always be greater than 0 until EOF is reached.
  48. */
  49. PyObject *Extension<istream>::
  50. read1(Py_ssize_t size) {
  51. std::streambuf *buf = _this->rdbuf();
  52. nassertr(buf != nullptr, nullptr);
  53. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  54. PyThreadState *_save;
  55. Py_UNBLOCK_THREADS
  56. #endif
  57. std::streamsize avail = buf->in_avail();
  58. if (avail == 0) {
  59. avail = 4096;
  60. }
  61. if (size >= 0 && (std::streamsize)size < avail) {
  62. avail = (std::streamsize)size;
  63. }
  64. // Don't read more than 4K at a time
  65. if (avail > 4096) {
  66. avail = 4096;
  67. }
  68. char *buffer = (char *)alloca(avail);
  69. std::streamsize read_bytes = buf->sgetn(buffer, avail);
  70. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  71. Py_BLOCK_THREADS
  72. #endif
  73. return PyBytes_FromStringAndSize(buffer, read_bytes);
  74. }
  75. /**
  76. * Reads all of the bytes in the stream.
  77. */
  78. PyObject *Extension<istream>::
  79. readall() {
  80. std::streambuf *buf = _this->rdbuf();
  81. nassertr(buf != nullptr, nullptr);
  82. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  83. PyThreadState *_save;
  84. Py_UNBLOCK_THREADS
  85. #endif
  86. std::vector<unsigned char> result;
  87. static const size_t buffer_size = 4096;
  88. char buffer[buffer_size];
  89. std::streamsize count = buf->sgetn(buffer, buffer_size);
  90. while (count != 0) {
  91. thread_consider_yield();
  92. result.insert(result.end(), buffer, buffer + count);
  93. count = buf->sgetn(buffer, buffer_size);
  94. }
  95. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  96. Py_BLOCK_THREADS
  97. #endif
  98. return PyBytes_FromStringAndSize((char *)result.data(), result.size());
  99. }
  100. /**
  101. * Reads bytes into a preallocated, writable, bytes-like object, returning the
  102. * number of bytes read.
  103. */
  104. std::streamsize Extension<istream>::
  105. readinto(PyObject *b) {
  106. std::streambuf *buf = _this->rdbuf();
  107. nassertr(buf != nullptr, 0);
  108. Py_buffer view;
  109. if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG) == -1) {
  110. PyErr_SetString(PyExc_TypeError,
  111. "write() requires a contiguous, read-write bytes-like object");
  112. return 0;
  113. }
  114. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  115. PyThreadState *_save;
  116. Py_UNBLOCK_THREADS
  117. #endif
  118. std::streamsize count = buf->sgetn((char *)view.buf, view.len);
  119. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  120. Py_BLOCK_THREADS
  121. #endif
  122. PyBuffer_Release(&view);
  123. return count;
  124. }
  125. /**
  126. * Extracts one line up to and including the trailing newline character.
  127. * Returns empty string when the end of file is reached.
  128. */
  129. PyObject *Extension<istream>::
  130. readline(Py_ssize_t size) {
  131. std::streambuf *buf = _this->rdbuf();
  132. nassertr(buf != nullptr, nullptr);
  133. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  134. PyThreadState *_save;
  135. Py_UNBLOCK_THREADS
  136. #endif
  137. std::string line;
  138. int ch = buf->sbumpc();
  139. while (ch != EOF && (--size) != 0) {
  140. line.push_back(ch);
  141. if (ch == '\n') {
  142. // Here's the newline character.
  143. break;
  144. }
  145. ch = buf->sbumpc();
  146. }
  147. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  148. Py_BLOCK_THREADS
  149. #endif
  150. return PyBytes_FromStringAndSize(line.data(), line.size());
  151. }
  152. /**
  153. * Reads all the lines at once and returns a list. Also see the documentation
  154. * for readline().
  155. */
  156. PyObject *Extension<istream>::
  157. readlines(Py_ssize_t hint) {
  158. PyObject *lst = PyList_New(0);
  159. if (lst == nullptr) {
  160. return nullptr;
  161. }
  162. PyObject *py_line = readline(-1);
  163. if (hint < 0) {
  164. while (Py_SIZE(py_line) > 0) {
  165. PyList_Append(lst, py_line);
  166. Py_DECREF(py_line);
  167. py_line = readline(-1);
  168. }
  169. } else {
  170. Py_ssize_t totchars = 0;
  171. while (Py_SIZE(py_line) > 0) {
  172. totchars += Py_SIZE(py_line);
  173. PyList_Append(lst, py_line);
  174. Py_DECREF(py_line);
  175. if (totchars > hint) {
  176. break;
  177. }
  178. py_line = readline(-1);
  179. }
  180. }
  181. return lst;
  182. }
  183. /**
  184. * Yields continuously to read all the lines from the istream.
  185. */
  186. static PyObject *gen_next(PyObject *self) {
  187. istream *stream = nullptr;
  188. if (!Dtool_Call_ExtractThisPointer(self, Dtool_std_istream, (void **)&stream)) {
  189. return nullptr;
  190. }
  191. PyObject *line = invoke_extension(stream).readline();
  192. if (Py_SIZE(line) > 0) {
  193. return line;
  194. } else {
  195. PyErr_SetObject(PyExc_StopIteration, nullptr);
  196. return nullptr;
  197. }
  198. }
  199. /**
  200. * Iterates over the lines of the file.
  201. */
  202. PyObject *Extension<istream>::
  203. __iter__(PyObject *self) {
  204. return Dtool_NewGenerator(self, &gen_next);
  205. }
  206. /**
  207. * Writes the bytes object to the stream.
  208. */
  209. void Extension<ostream>::
  210. write(PyObject *b) {
  211. std::streambuf *buf = _this->rdbuf();
  212. nassertv(buf != nullptr);
  213. Py_buffer view;
  214. if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG_RO) == -1) {
  215. PyErr_SetString(PyExc_TypeError, "write() requires a contiguous buffer");
  216. return;
  217. }
  218. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  219. PyThreadState *_save;
  220. Py_UNBLOCK_THREADS
  221. buf->sputn((const char *)view.buf, view.len);
  222. Py_BLOCK_THREADS
  223. #else
  224. buf->sputn((const char *)view.buf, view.len);
  225. #endif
  226. PyBuffer_Release(&view);
  227. }
  228. /**
  229. * Write a list of lines to the stream. Line separators are not added, so it
  230. * is usual for each of the lines provided to have a line separator at the
  231. * end.
  232. */
  233. void Extension<ostream>::
  234. writelines(PyObject *lines) {
  235. PyObject *seq = PySequence_Fast(lines, "writelines() expects a sequence");
  236. if (seq == nullptr) {
  237. return;
  238. }
  239. PyObject **items = PySequence_Fast_ITEMS(seq);
  240. Py_ssize_t len = PySequence_Fast_GET_SIZE(seq);
  241. for (Py_ssize_t i = 0; i < len; ++i) {
  242. write(items[i]);
  243. }
  244. Py_DECREF(seq);
  245. }
  246. #endif // HAVE_PYTHON