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