1 /*
2 * Copyright 2001-2008 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license
4 *
5 * Authors:
6 * Stefano Ceccherini, burton666@libero.it
7 */
8
9
10 #include <BufferIO.h>
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16
BBufferIO(BPositionIO * stream,size_t bufferSize,bool ownsStream)17 BBufferIO::BBufferIO(BPositionIO* stream, size_t bufferSize, bool ownsStream)
18 :
19 fBufferStart(0),
20 fStream(stream),
21 fBuffer(NULL),
22 fBufferUsed(0),
23 fBufferIsDirty(false),
24 fOwnsStream(ownsStream)
25
26 {
27 fBufferSize = max_c(bufferSize, 512);
28 fPosition = stream->Position();
29
30 // What can we do if this malloc fails ?
31 // I think R5 uses new, but doesn't catch the thrown exception
32 // (if you specify a very big buffer, the application just
33 // terminates with abort).
34 fBuffer = (char*)malloc(fBufferSize);
35 }
36
37
~BBufferIO()38 BBufferIO::~BBufferIO()
39 {
40 if (fBufferIsDirty) {
41 // Write pending changes to the stream
42 Flush();
43 }
44
45 free(fBuffer);
46
47 if (fOwnsStream)
48 delete fStream;
49 }
50
51
52 ssize_t
ReadAt(off_t pos,void * buffer,size_t size)53 BBufferIO::ReadAt(off_t pos, void* buffer, size_t size)
54 {
55 // We refuse to crash, even if
56 // you were lazy and didn't give a valid
57 // stream on construction.
58 if (fStream == NULL)
59 return B_NO_INIT;
60 if (buffer == NULL)
61 return B_BAD_VALUE;
62
63 // If the amount of data we want doesn't fit in the buffer, just
64 // read it directly from the disk (and don't touch the buffer).
65 if (size > fBufferSize || fBuffer == NULL) {
66 if (fBufferIsDirty)
67 Flush();
68 return fStream->ReadAt(pos, buffer, size);
69 }
70
71 // If the data we are looking for is not in the buffer...
72 if (size > fBufferUsed
73 || pos < fBufferStart
74 || pos > fBufferStart + (off_t)fBufferUsed
75 || pos + size > fBufferStart + fBufferUsed) {
76 if (fBufferIsDirty) {
77 // If there are pending writes, do them.
78 Flush();
79 }
80
81 // ...cache as much as we can from the stream
82 ssize_t sizeRead = fStream->ReadAt(pos, fBuffer, fBufferSize);
83 if (sizeRead < 0)
84 return sizeRead;
85
86 fBufferUsed = sizeRead;
87 if (fBufferUsed > 0) {
88 // The data is buffered starting from this offset
89 fBufferStart = pos;
90 }
91 }
92
93 size = min_c(size, fBufferUsed);
94
95 // copy data from the cache to the given buffer
96 memcpy(buffer, fBuffer + pos - fBufferStart, size);
97
98 return size;
99 }
100
101
102 ssize_t
WriteAt(off_t pos,const void * buffer,size_t size)103 BBufferIO::WriteAt(off_t pos, const void* buffer, size_t size)
104 {
105 if (fStream == NULL)
106 return B_NO_INIT;
107 if (buffer == NULL)
108 return B_BAD_VALUE;
109
110 // If data doesn't fit into the buffer, write it directly to the stream
111 if (size > fBufferSize || fBuffer == NULL)
112 return fStream->WriteAt(pos, buffer, size);
113
114 // If we have cached data in the buffer, whose offset into the stream
115 // is > 0, and the buffer isn't dirty, drop the data.
116 if (!fBufferIsDirty && fBufferStart > pos) {
117 fBufferStart = 0;
118 fBufferUsed = 0;
119 }
120
121 // If we want to write beyond the cached data...
122 if (pos > fBufferStart + (off_t)fBufferUsed
123 || pos < fBufferStart) {
124 ssize_t read;
125 off_t where = pos;
126
127 // Can we just cache from the beginning?
128 if (pos + size <= fBufferSize)
129 where = 0;
130
131 // ...cache more.
132 read = fStream->ReadAt(where, fBuffer, fBufferSize);
133 if (read > 0) {
134 fBufferUsed = read;
135 fBufferStart = where;
136 }
137 }
138
139 memcpy(fBuffer + pos - fBufferStart, buffer, size);
140
141 fBufferIsDirty = true;
142 fBufferUsed = max_c((size + pos), fBufferUsed);
143
144 return size;
145 }
146
147
148 off_t
Seek(off_t position,uint32 seekMode)149 BBufferIO::Seek(off_t position, uint32 seekMode)
150 {
151 if (fStream == NULL)
152 return B_NO_INIT;
153
154 off_t newPosition = fPosition;
155
156 switch (seekMode) {
157 case SEEK_CUR:
158 newPosition += position;
159 break;
160 case SEEK_SET:
161 newPosition = position;
162 break;
163 case SEEK_END:
164 {
165 off_t size;
166 status_t status = fStream->GetSize(&size);
167 if (status != B_OK)
168 return status;
169
170 newPosition = size - position;
171 break;
172 }
173 }
174
175 if (newPosition < 0)
176 return B_BAD_VALUE;
177
178 fPosition = newPosition;
179 return newPosition;
180 }
181
182
183 off_t
Position() const184 BBufferIO::Position() const
185 {
186 return fPosition;
187 }
188
189
190 status_t
SetSize(off_t size)191 BBufferIO::SetSize(off_t size)
192 {
193 if (fStream == NULL)
194 return B_NO_INIT;
195
196 return fStream->SetSize(size);
197 }
198
199
200 status_t
Flush()201 BBufferIO::Flush()
202 {
203 if (!fBufferIsDirty)
204 return B_OK;
205
206 // Write the cached data to the stream
207 ssize_t bytesWritten = fStream->WriteAt(fBufferStart, fBuffer, fBufferUsed);
208 if (bytesWritten > 0)
209 fBufferIsDirty = false;
210
211 return (bytesWritten < 0) ? bytesWritten : B_OK;
212 }
213
214
215 BPositionIO*
Stream() const216 BBufferIO::Stream() const
217 {
218 return fStream;
219 }
220
221
222 size_t
BufferSize() const223 BBufferIO::BufferSize() const
224 {
225 return fBufferSize;
226 }
227
228
229 bool
OwnsStream() const230 BBufferIO::OwnsStream() const
231 {
232 return fOwnsStream;
233 }
234
235
236 void
SetOwnsStream(bool ownsStream)237 BBufferIO::SetOwnsStream(bool ownsStream)
238 {
239 fOwnsStream = ownsStream;
240 }
241
242
243 void
PrintToStream() const244 BBufferIO::PrintToStream() const
245 {
246 printf("stream %p\n", fStream);
247 printf("buffer %p\n", fBuffer);
248 printf("start %" B_PRId64 "\n", fBufferStart);
249 printf("used %ld\n", fBufferUsed);
250 printf("phys %ld\n", fBufferSize);
251 printf("dirty %s\n", (fBufferIsDirty) ? "true" : "false");
252 printf("owns %s\n", (fOwnsStream) ? "true" : "false");
253 }
254
255
256 // #pragma mark - FBC padding
257
258
259 // These functions are here to maintain future binary
260 // compatibility.
_Reserved_BufferIO_0(void *)261 status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; }
_Reserved_BufferIO_1(void *)262 status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; }
_Reserved_BufferIO_2(void *)263 status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; }
_Reserved_BufferIO_3(void *)264 status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; }
_Reserved_BufferIO_4(void *)265 status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; }
266