1 /*------------------------------------------------------------------------------
2 * Copyright (C) 2003-2006 Jos van den Oever
3 *
4 * Distributable under the terms of either the Apache License (Version 2.0) or
5 * the GNU Lesser General Public License, as specified in the COPYING file.
6 ------------------------------------------------------------------------------*/
7 #ifndef JSTREAMS_BUFFEREDSTREAM_H
8 #define JSTREAMS_BUFFEREDSTREAM_H
9
10 #include "_streambase.h"
11 #include "_streambuffer.h"
12 #include <cassert>
13
CL_NS_DEF(util)14 CL_NS_DEF(util)
15
16 /**
17 * @brief Abstract implementation class providing a buffered input stream.
18 *
19 * You can inherit this class to provide buffered access to a
20 * resource. You just need to implement fillBuffer, and
21 * BufferedStream will do the rest.
22 */
23 template <class T>
24 class BufferedStreamImpl : public StreamBase<T> {
25 private:
26 StreamBuffer<T> buffer;
27 bool finishedWritingToBuffer;
28
29 void writeToBuffer(int32_t minsize, int32_t maxsize);
30 protected:
31 /**
32 * @brief Fill the buffer with the provided data
33 *
34 * This function should be implemented by subclasses.
35 * It should write up to @p space characters from the
36 * stream to the buffer position pointed to by @p start.
37 *
38 * If the end of the stream is encountered, -1 should be
39 * returned.
40 *
41 * If an error occurs, the status should be set to Error,
42 * an error message should be set and -1 should be returned.
43 *
44 * You should @em not call this function yourself.
45 *
46 * @param start where the data should be written to
47 * @param space the maximum amount of data to write
48 * @return Number of characters written, or -1 on error
49 **/
50 virtual int32_t fillBuffer(T* start, int32_t space) = 0;
51 /**
52 * @brief Resets the buffer, allowing it to be used again
53 *
54 * This function resets the buffer, allowing it to be re-used.
55 */
56 void resetBuffer() {
57 StreamBase<T>::m_size = -1;
58 StreamBase<T>::m_position = 0;
59 StreamBase<T>::m_error.assign("");
60 StreamBase<T>::m_status = Ok;
61 buffer.readPos = buffer.start;
62 buffer.avail = 0;
63 finishedWritingToBuffer = false;
64 }
65 /**
66 * @brief Sets the minimum size of the buffer
67 */
68 void setMinBufSize(int32_t s) {
69 buffer.makeSpace(s);
70 }
71 BufferedStreamImpl<T>();
72 public:
73 int32_t read(const T*& start, int32_t min, int32_t max);
74 int64_t reset(int64_t pos);
75 virtual int64_t skip(int64_t ntoskip);
76 };
77
78
79 /** Abstract class for a buffered stream of bytes */
80 typedef BufferedStreamImpl<signed char> BufferedInputStreamImpl;
81
82 /** Abstract class for a buffered stream of Unicode characters */
83 typedef BufferedStreamImpl<TCHAR> BufferedReaderImpl;
84
85
86 template <class T>
BufferedStreamImpl()87 BufferedStreamImpl<T>::BufferedStreamImpl() {
88 finishedWritingToBuffer = false;
89 }
90
91 template <class T>
92 void
writeToBuffer(int32_t ntoread,int32_t maxread)93 BufferedStreamImpl<T>::writeToBuffer(int32_t ntoread, int32_t maxread) {
94 int32_t missing = ntoread - buffer.avail;
95 int32_t nwritten = 0;
96 while (missing > 0 && nwritten >= 0) {
97 int32_t space;
98 space = buffer.makeSpace(missing);
99 if (maxread >= ntoread && space > maxread) {
100 space = maxread;
101 }
102 T* start = buffer.readPos + buffer.avail;
103 nwritten = fillBuffer(start, space);
104 assert(StreamBase<T>::m_status != Eof);
105 if (nwritten > 0) {
106 buffer.avail += nwritten;
107 missing = ntoread - buffer.avail;
108 }
109 }
110 if (nwritten < 0) {
111 finishedWritingToBuffer = true;
112 }
113 }
114 template <class T>
115 int32_t
read(const T * & start,int32_t min,int32_t max)116 BufferedStreamImpl<T>::read(const T*& start, int32_t min, int32_t max) {
117 if (StreamBase<T>::m_status == Error) return -2;
118 if (StreamBase<T>::m_status == Eof) return -1;
119
120 // do we need to read data into the buffer?
121 if (min > max) max = 0;
122 if (!finishedWritingToBuffer && min > buffer.avail) {
123 // do we have enough space in the buffer?
124 writeToBuffer(min, max);
125 if (StreamBase<T>::m_status == Error) return -2;
126 }
127
128 int32_t nread = buffer.read(start, max);
129
130 StreamBase<T>::m_position += nread;
131 if (StreamBase<T>::m_position > StreamBase<T>::m_size
132 && StreamBase<T>::m_size > 0) {
133 // error: we read more than was specified in size
134 // this is an error because all dependent code might have been labouring
135 // under a misapprehension
136 StreamBase<T>::m_status = Error;
137 StreamBase<T>::m_error = "Stream is longer than specified.";
138 nread = -2;
139 } else if (StreamBase<T>::m_status == Ok && buffer.avail == 0
140 && finishedWritingToBuffer) {
141 StreamBase<T>::m_status = Eof;
142 if (StreamBase<T>::m_size == -1) {
143 StreamBase<T>::m_size = StreamBase<T>::m_position;
144 }
145 // save one call to read() by already returning -1 if no data is there
146 if (nread == 0) nread = -1;
147 }
148 return nread;
149 }
150 template <class T>
151 int64_t
reset(int64_t newpos)152 BufferedStreamImpl<T>::reset(int64_t newpos) {
153 assert(newpos >= 0);
154 if (StreamBase<T>::m_status == Error) return -2;
155 // check to see if we have this position
156 int64_t d = StreamBase<T>::m_position - newpos;
157 if (buffer.readPos - d >= buffer.start && -d < buffer.avail) {
158 StreamBase<T>::m_position -= d;
159 buffer.avail += (int32_t)d;
160 buffer.readPos -= d;
161 StreamBase<T>::m_status = Ok;
162 }
163 return StreamBase<T>::m_position;
164 }
165 template <class T>
166 int64_t
skip(int64_t ntoskip)167 BufferedStreamImpl<T>::skip(int64_t ntoskip) {
168 const T *begin;
169 int32_t nread;
170 int64_t skipped = 0;
171 while (ntoskip) {
172 int32_t step = (int32_t)((ntoskip > buffer.size) ?buffer.size :ntoskip);
173 nread = read(begin, 1, step);
174 if (nread <= 0) {
175 return skipped;
176 }
177 ntoskip -= nread;
178 skipped += nread;
179 }
180 return skipped;
181 }
182
183 CL_NS_END
184
185 #endif
186