Qore Programming Language  0.9.4.6
BufferedStreamReader.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  BufferedStreamReader.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2016 - 2017 Qore Technologies, s.r.o.
8 
9  Permission is hereby granted, free of charge, to any person obtaining a
10  copy of this software and associated documentation files (the "Software"),
11  to deal in the Software without restriction, including without limitation
12  the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  and/or sell copies of the Software, and to permit persons to whom the
14  Software is furnished to do so, subject to the following conditions:
15 
16  The above copyright notice and this permission notice shall be included in
17  all copies or substantial portions of the Software.
18 
19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  DEALINGS IN THE SOFTWARE.
26 
27  Note that the Qore library is released under a choice of three open-source
28  licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29  information.
30 */
31 
32 #ifndef _QORE_BUFFEREDSTREAMREADER_H
33 #define _QORE_BUFFEREDSTREAMREADER_H
34 
35 #include <cstdint>
36 
37 #include "qore/qore_bitopts.h"
38 #include "qore/InputStream.h"
39 #include "qore/intern/StreamReader.h"
40 
41 // this corresponds to the Qore constant size in QC_BufferedStreamReader; these values must be identical
42 #define DefaultStreamBufferSize 4096
43 
46 public:
47  DLLLOCAL BufferedStreamReader(ExceptionSink* xsink, InputStream* is, const QoreEncoding* encoding, int64 bufsize = DefaultStreamBufferSize) :
48  StreamReader(xsink, is, encoding),
49  bufCapacity((size_t)bufsize),
50  bufCount(0),
51  buf(0) {
52  if (bufsize <= 0) {
53  xsink->raiseException("STREAM-BUFFER-ERROR", "the buffer size must be > 0 (value provided: " QLLD ")", bufsize);
54  return;
55  }
56 
57  // +1 is added to the real capacity for terminating null-character.
58  buf = new char[bufCapacity + 1];
59  }
60 
61  DLLLOCAL virtual ~BufferedStreamReader() {
62  if (buf)
63  delete [] buf;
64  }
65 
66  DLLLOCAL virtual const char* getName() const override { return "BufferedStreamReader"; }
67 
68 private:
70 
77  DLLLOCAL virtual qore_offset_t readData(ExceptionSink* xsink, void* dest, qore_size_t limit, bool require_all = true) override {
78  assert(dest);
79  assert(limit);
80 
81  char* destPtr = static_cast<char*>(dest);
82  size_t read = 0;
83 
84  if (bufCount) {
85  read = QORE_MIN(limit, bufCount);
86  memmove(destPtr, buf, read);
87  if (limit < bufCount) {
88  shiftBuffer(limit);
89  return limit;
90  }
91  bufCount = 0;
92  if (!limit - read)
93  return read;
94  }
95 
96  // read in data directly into the target buffer until the amount left to read >= 1/2 the buffer capacity
97  while (true) {
98  size_t to_read = limit - read;
99  if (to_read <= (bufCapacity / 2))
100  break;
101  int64 rc = in->read(destPtr + read, to_read, xsink);
102  if (*xsink)
103  return -1;
104  if (!rc) {
105  if (require_all) {
106  xsink->raiseException("END-OF-STREAM-ERROR", "there is not enough data available in the stream; " QSD " bytes were requested, and " QSD " were read", limit, read);
107  return -1;
108  }
109  break;
110  }
111  to_read -= rc;
112  read += rc;
113  }
114 
115  // here we try to populate the buffer first and the target second
116  while (true) {
117  size_t to_read = limit - read;
118  if (!to_read)
119  break;
120 
121  assert(!bufCount);
122  int64 rc = fillBuffer(bufCapacity, xsink);
123  if (*xsink)
124  return -1;
125  if (!rc) {
126  if (require_all) {
127  xsink->raiseException("END-OF-STREAM-ERROR", "there is not enough data available in the stream; " QSD " bytes were requested, and " QSD " were read", limit, read);
128  return -1;
129  }
130  break;
131  }
132  assert(rc > 0);
133  size_t len = QORE_MIN((size_t)rc, to_read);
134  memcpy(destPtr + read, buf, len);
135  shiftBuffer(len);
136  read += len;
137  assert(((limit - read) && !bufCount) || !(limit - read));
138  }
139 
140  return read;
141  }
142 
148  virtual int64 peek(ExceptionSink* xsink) override {
149  if (!bufCount) {
150  int rc = fillBuffer(bufCapacity, xsink);
151  if (!rc)
152  return -1;
153  if (rc < 0)
154  return -2;
155  }
156  return buf[0];
157  }
158 
160  DLLLOCAL int64 fillBuffer(qore_size_t bytes, ExceptionSink* xsink) {
161  assert(bytes);
162  assert(bufCount + bytes <= bufCapacity);
163  int64 rc = in->read(buf + bufCount, bytes, xsink);
164  if (*xsink)
165  return 0;
166  bufCount += rc;
167  return rc;
168  }
169 
170  DLLLOCAL bool prepareEnoughData(qore_size_t bytes, ExceptionSink* xsink) {
171  if (bytes > bufCapacity) {
172  xsink->raiseException("STREAM-BUFFER-ERROR", "a read of " QSD " bytes was attempted on a BufferedStreamReader with a capacity of " QSD " bytes", bytes, bufCapacity);
173  return false;
174  }
175  if (bufCount < bytes) {
176  while (true) {
177  int64 rc = fillBuffer(bytes - bufCount, xsink);
178  if (*xsink) {
179  return false;
180  }
181  if (rc == 0) {
182  xsink->raiseException("END-OF-STREAM-ERROR", "a read of " QSD " bytes cannot be performed because there is not enough data available in the stream to satisfy the request", bytes);
183  return false;
184  }
185  if (bufCount >= bytes)
186  break;
187  }
188  }
189  return true;
190  }
191 
192  DLLLOCAL void shiftBuffer(qore_size_t bytes) {
193  assert(bytes <= bufCount && bytes > 0);
194  bufCount -= bytes;
195  memmove(buf, buf+bytes, bufCount);
196  }
197 
199 
203  DLLLOCAL const char* findEolInBuffer(const QoreStringNode* eol, qore_size_t& eolLen, bool endOfStream, char& pmatch) const {
204  pmatch = '\0';
205  if (eol) {
206  const char* p = strstr(buf, eol->getBuffer());
207  eolLen = eol->strlen();
208  return p;
209  }
210  else {
211  const char* p = strpbrk(buf, "\n\r"); // Find first occurence of '\n' or '\r'.
212  if (p) { // Found end of line.
213  if (*p == '\n') {
214  eolLen = 1;
215  }
216  else { // *p == '\r'
217  if (*(p+1) == '\n') {
218  eolLen = 2;
219  }
220  // '\r' is the last character in the buffer, '\n' could be next in the stream.
221  // Unless this is the end of the stream.
222  else if (static_cast<qore_size_t>(p - buf + 1) == bufCount) {
223  eolLen = 1;
224  if (!endOfStream) {
225  pmatch = *p;
226  p = 0;
227  }
228  }
229  else {
230  eolLen = 1;
231  }
232  }
233  }
234  else {
235  eolLen = 0;
236  }
237  return p;
238  }
239  }
240 
241 private:
242  qore_size_t bufCapacity;
243  qore_size_t bufCount;
244  char* buf;
245 };
246 
247 #endif // _QORE_BUFFEREDSTREAMREADER_H
Private data for the Qore::BufferedStreamReader class.
Definition: BufferedStreamReader.h:45
defines string encoding functions in Qore
Definition: QoreEncoding.h:83
ReferenceHolder< InputStream > in
Source input stream.
Definition: StreamReader.h:348
virtual int64 read(void *ptr, int64 limit, ExceptionSink *xsink)=0
Reads up to `limit` bytes from the input stream.
size_t qore_size_t
used for sizes (same range as a pointer)
Definition: common.h:73
virtual DLLLOCAL qore_offset_t read(ExceptionSink *xsink, void *dest, qore_size_t limit, bool require_all=true)
Read data until a limit.
Definition: StreamReader.h:337
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
Qore&#39;s string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
DLLEXPORT const char * getBuffer() const
returns the string&#39;s buffer; this data should not be changed
DLLEXPORT qore_size_t strlen() const
returns number of bytes in the string (not including the null pointer)
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:46
Interface for private data of input streams.
Definition: InputStream.h:44
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260
intptr_t qore_offset_t
used for offsets that could be negative
Definition: common.h:76
#define QORE_MIN(a, b)
macro to return the minimum of 2 numbers
Definition: QoreLib.h:535
Private data for the Qore::StreamReader class.
Definition: StreamReader.h:45