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