xref: /haiku/src/kits/support/BufferIO.cpp (revision 62f5ba006a08b0df30631375878effaf67ae5dbc)
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  			// 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
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 + 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
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
184  BBufferIO::Position() const
185  {
186  	return fPosition;
187  }
188  
189  
190  status_t
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
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*
216  BBufferIO::Stream() const
217  {
218  	return fStream;
219  }
220  
221  
222  size_t
223  BBufferIO::BufferSize() const
224  {
225  	return fBufferSize;
226  }
227  
228  
229  bool
230  BBufferIO::OwnsStream() const
231  {
232  	return fOwnsStream;
233  }
234  
235  
236  void
237  BBufferIO::SetOwnsStream(bool ownsStream)
238  {
239  	fOwnsStream = ownsStream;
240  }
241  
242  
243  void
244  BBufferIO::PrintToStream() const
245  {
246  	printf("stream %p\n", fStream);
247  	printf("buffer %p\n", fBuffer);
248  	printf("start  %lld\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 -
257  
258  
259  // These functions are here to maintain future binary
260  // compatibility.
261  status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; }
262  status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; }
263  status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; }
264  status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; }
265  status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; }
266