xref: /haiku/src/kits/support/BufferIO.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  *	Copyright (c) 2001-2008, Haiku
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 
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 
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
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 + fBufferUsed
75 		|| pos + size > fBufferStart + fBufferUsed) {
76 		if (fBufferIsDirty)
77 			Flush(); // If there are pending writes, do them.
78 
79 		// ...cache as much as we can from the stream
80 		fBufferUsed = fStream->ReadAt(pos, fBuffer, fBufferSize);
81 
82 		if (fBufferUsed > 0)
83 			fBufferStart = pos; // The data is buffered starting from this offset
84 	}
85 
86 	size = min_c(size, fBufferUsed);
87 
88 	// copy data from the cache to the given buffer
89 	memcpy(buffer, fBuffer + pos - fBufferStart, size);
90 
91 	return size;
92 }
93 
94 
95 ssize_t
96 BBufferIO::WriteAt(off_t pos, const void* buffer, size_t size)
97 {
98 	if (fStream == NULL)
99 		return B_NO_INIT;
100 	if (buffer == NULL)
101 		return B_BAD_VALUE;
102 
103 	// If data doesn't fit into the buffer, write it directly to the stream
104 	if (size > fBufferSize || fBuffer == NULL)
105 		return fStream->WriteAt(pos, buffer, size);
106 
107 	// If we have cached data in the buffer, whose offset into the stream
108 	// is > 0, and the buffer isn't dirty, drop the data.
109 	if (!fBufferIsDirty && fBufferStart > pos) {
110 		fBufferStart = 0;
111 		fBufferUsed = 0;
112 	}
113 
114 	// If we want to write beyond the cached data...
115 	if (pos > fBufferStart + fBufferUsed
116 		|| pos < fBufferStart) {
117 		ssize_t read;
118 		off_t where = pos;
119 
120 		if (pos + size <= fBufferSize) // Can we just cache from the beginning ?
121 			where = 0;
122 
123 		// ...cache more.
124 		read = fStream->ReadAt(where, fBuffer, fBufferSize);
125 		if (read > 0) {
126 			fBufferUsed = read;
127 			fBufferStart = where;
128 		}
129 	}
130 
131 	memcpy(fBuffer + pos - fBufferStart, buffer, size);
132 
133 	fBufferIsDirty = true;
134 	fBufferUsed = max_c((size + pos), fBufferUsed);
135 
136 	return size;
137 }
138 
139 
140 off_t
141 BBufferIO::Seek(off_t position, uint32 seekMode)
142 {
143 	if (fStream == NULL)
144 		return B_NO_INIT;
145 
146 	off_t newPosition = fPosition;
147 
148 	switch (seekMode) {
149 		case SEEK_CUR:
150 			newPosition += position;
151 			break;
152 		case SEEK_SET:
153 			newPosition = position;
154 			break;
155 		case SEEK_END:
156 		{
157 			off_t size;
158 			status_t status = fStream->GetSize(&size);
159 			if (status != B_OK)
160 				return status;
161 
162 			newPosition = size - position;
163 			break;
164 		}
165 	}
166 
167 	if (newPosition < 0)
168 		return B_BAD_VALUE;
169 
170 	fPosition = newPosition;
171 	return newPosition;
172 }
173 
174 
175 off_t
176 BBufferIO::Position() const
177 {
178 	return fPosition;
179 }
180 
181 
182 status_t
183 BBufferIO::SetSize(off_t size)
184 {
185 	if (fStream == NULL)
186 		return B_NO_INIT;
187 
188 	return fStream->SetSize(size);
189 }
190 
191 
192 status_t
193 BBufferIO::Flush()
194 {
195 	if (!fBufferIsDirty)
196 		return B_OK;
197 
198 	// Write the cached data to the stream
199 	ssize_t bytesWritten = fStream->WriteAt(fBufferStart, fBuffer, fBufferUsed);
200 	if (bytesWritten > 0)
201 		fBufferIsDirty = false;
202 
203 	return (bytesWritten < 0) ? bytesWritten : B_OK;
204 }
205 
206 
207 BPositionIO*
208 BBufferIO::Stream() const
209 {
210 	return fStream;
211 }
212 
213 
214 size_t
215 BBufferIO::BufferSize() const
216 {
217 	return fBufferSize;
218 }
219 
220 
221 bool
222 BBufferIO::OwnsStream() const
223 {
224 	return fOwnsStream;
225 }
226 
227 
228 void
229 BBufferIO::SetOwnsStream(bool ownsStream)
230 {
231 	fOwnsStream = ownsStream;
232 }
233 
234 
235 void
236 BBufferIO::PrintToStream() const
237 {
238 	printf("stream %p\n", fStream);
239 	printf("buffer %p\n", fBuffer);
240 	printf("start  %lld\n", fBufferStart);
241 	printf("used   %ld\n", fBufferUsed);
242 	printf("phys   %ld\n", fBufferSize);
243 	printf("dirty  %s\n", (fBufferIsDirty) ? "true" : "false");
244 	printf("owns   %s\n", (fOwnsStream) ? "true" : "false");
245 }
246 
247 
248 //	#pragma mark -
249 
250 
251 // These functions are here to maintain future binary
252 // compatibility.
253 status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; }
254 status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; }
255 status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; }
256 status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; }
257 status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; }
258