xref: /haiku/src/kits/app/LinkReceiver.cpp (revision d9cebac2b77547b7064f22497514eecd2d047160)
1 /*
2  * Copyright 2001-2007, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Pahtz <pahtz@yahoo.com.au>
7  *		Axel Dörfler
8  *		Stephan Aßmus <superstippi@gmx.de>
9  */
10 
11 /** Class for low-overhead port-based messaging */
12 
13 #include <LinkReceiver.h>
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <new>
18 
19 #include <ServerProtocol.h>
20 #include <String.h>
21 #include <Region.h>
22 
23 #include "link_message.h"
24 
25 
26 //#define DEBUG_BPORTLINK
27 #ifdef DEBUG_BPORTLINK
28 #	include <stdio.h>
29 #	define STRACE(x) printf x
30 #else
31 #	define STRACE(x) ;
32 #endif
33 
34 namespace BPrivate {
35 
36 LinkReceiver::LinkReceiver(port_id port)
37 	:
38 	fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
39 	fRecvBufferSize(0), fDataSize(0),
40 	fReplySize(0), fReadError(B_OK)
41 {
42 }
43 
44 
45 LinkReceiver::~LinkReceiver()
46 {
47 	free(fRecvBuffer);
48 }
49 
50 
51 void
52 LinkReceiver::SetPort(port_id port)
53 {
54 	fReceivePort = port;
55 }
56 
57 
58 status_t
59 LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout)
60 {
61 	int32 remaining;
62 
63 	fReadError = B_OK;
64 
65 	remaining = fDataSize - (fRecvStart + fReplySize);
66 	STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
67 
68 	// find the position of the next message header in the buffer
69 	message_header *header;
70 	if (remaining <= 0) {
71 		status_t err = ReadFromPort(timeout);
72 		if (err < B_OK)
73 			return err;
74 		remaining = fDataSize;
75 		header = (message_header *)fRecvBuffer;
76 	} else {
77 		fRecvStart += fReplySize;	// start of the next message
78 		fRecvPosition = fRecvStart;
79 		header = (message_header *)(fRecvBuffer + fRecvStart);
80 	}
81 
82 	// check we have a well-formed message
83 	if (remaining < (int32)sizeof(message_header)) {
84 		// we don't have enough data for a complete header
85 		STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining));
86 		ResetBuffer();
87 		return B_ERROR;
88 	}
89 
90 	fReplySize = header->size;
91 	if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
92 		STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize));
93 		ResetBuffer();
94 		return B_ERROR;
95 	}
96 
97 	code = header->code;
98 	fRecvPosition += sizeof(message_header);
99 
100 	STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n",
101 		header->code, fReplySize, header->code, header->flags, fReceivePort));
102 
103 	return B_OK;
104 }
105 
106 
107 bool
108 LinkReceiver::HasMessages() const
109 {
110 	return fDataSize - (fRecvStart + fReplySize) > 0
111 		|| port_count(fReceivePort) > 0;
112 }
113 
114 
115 bool
116 LinkReceiver::NeedsReply() const
117 {
118 	if (fReplySize == 0)
119 		return false;
120 
121 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
122 	return (header->flags & kNeedsReply) != 0;
123 }
124 
125 
126 int32
127 LinkReceiver::Code() const
128 {
129 	if (fReplySize == 0)
130 		return B_ERROR;
131 
132 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
133 	return header->code;
134 }
135 
136 
137 void
138 LinkReceiver::ResetBuffer()
139 {
140 	fRecvPosition = 0;
141 	fRecvStart = 0;
142 	fDataSize = 0;
143 	fReplySize = 0;
144 }
145 
146 
147 status_t
148 LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
149 {
150 	// Here we take advantage of the compiler's dead-code elimination
151 	if (kInitialBufferSize == kMaxBufferSize) {
152 		// fixed buffer size
153 
154 		if (fRecvBuffer != NULL)
155 			return B_OK;
156 
157 		fRecvBuffer = (char *)malloc(kInitialBufferSize);
158 		if (fRecvBuffer == NULL)
159 			return B_NO_MEMORY;
160 
161 		fRecvBufferSize = kInitialBufferSize;
162 	} else {
163 		STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
164 
165 		ssize_t bufferSize;
166 		do {
167 			bufferSize = port_buffer_size_etc(fReceivePort,
168 				timeout == B_INFINITE_TIMEOUT ? B_RELATIVE_TIMEOUT : 0,
169 				timeout);
170 		} while (bufferSize == B_INTERRUPTED);
171 
172 		STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
173 
174 		if (bufferSize < 0)
175 			return (status_t)bufferSize;
176 
177 		// make sure our receive buffer is large enough
178 		if (bufferSize > fRecvBufferSize) {
179 			if (bufferSize <= (ssize_t)kInitialBufferSize)
180 				bufferSize = (ssize_t)kInitialBufferSize;
181 			else
182 				bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
183 
184 			if (bufferSize > (ssize_t)kMaxBufferSize)
185 				return B_ERROR;	// we can't continue
186 
187 			STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
188 			char *buffer = (char *)malloc(bufferSize);
189 			if (buffer == NULL)
190 				return B_NO_MEMORY;
191 
192 			free(fRecvBuffer);
193 			fRecvBuffer = buffer;
194 			fRecvBufferSize = bufferSize;
195 		}
196 	}
197 
198 	return B_OK;
199 }
200 
201 
202 status_t
203 LinkReceiver::ReadFromPort(bigtime_t timeout)
204 {
205 	// we are here so it means we finished reading the buffer contents
206 	ResetBuffer();
207 
208 	status_t err = AdjustReplyBuffer(timeout);
209 	if (err < B_OK)
210 		return err;
211 
212 	int32 code;
213 	ssize_t bytesRead;
214 
215 	STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
216 	while (true) {
217 		if (timeout != B_INFINITE_TIMEOUT) {
218 			do {
219 				bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
220 					fRecvBufferSize, B_TIMEOUT, timeout);
221 			} while (bytesRead == B_INTERRUPTED);
222 		} else {
223 			do {
224 				bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
225 					fRecvBufferSize);
226 			} while (bytesRead == B_INTERRUPTED);
227 		}
228 
229 		STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
230 		if (bytesRead < B_OK)
231 			return bytesRead;
232 
233 		// we just ignore incorrect messages, and don't bother our caller
234 
235 		if (code != kLinkCode) {
236 			STRACE(("wrong port message %lx received.\n", code));
237 			continue;
238 		}
239 
240 		// port read seems to be valid
241 		break;
242 	}
243 
244 	fDataSize = bytesRead;
245 	return B_OK;
246 }
247 
248 
249 status_t
250 LinkReceiver::Read(void *data, ssize_t size)
251 {
252 //	STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
253 	if (fReadError < B_OK)
254 		return fReadError;
255 
256 	if (data == NULL || size < 1) {
257 		fReadError = B_BAD_VALUE;
258 		return B_BAD_VALUE;
259 	}
260 
261 	if (fDataSize == 0 || fReplySize == 0)
262 		return B_NO_INIT;	// need to call GetNextReply() first
263 
264 	if (fRecvPosition + size > fRecvStart + fReplySize) {
265 		// reading past the end of current message
266 		fReadError = B_BAD_VALUE;
267 		return B_BAD_VALUE;
268 	}
269 
270 	memcpy(data, fRecvBuffer + fRecvPosition, size);
271 	fRecvPosition += size;
272 	return fReadError;
273 }
274 
275 
276 status_t
277 LinkReceiver::ReadString(char** _string, size_t* _length)
278 {
279 	int32 length = 0;
280 	status_t status;
281 
282 	status = Read<int32>(&length);
283 	if (status < B_OK)
284 		return status;
285 
286 	char *string;
287 
288 	if (length < 0) {
289 		status = B_ERROR;
290 		goto err;
291 	}
292 
293 	string = (char *)malloc(length + 1);
294 	if (string == NULL) {
295 		status = B_NO_MEMORY;
296 		goto err;
297 	}
298 
299 	if (length > 0) {
300 		status = Read(string, length);
301 		if (status < B_OK) {
302 			free(string);
303 			return status;
304 		}
305 	}
306 
307 	// make sure the string is null terminated
308 	string[length] = '\0';
309 
310 	if (_length)
311 		*_length = length;
312 	*_string = string;
313 
314 	return B_OK;
315 
316 err:
317 	fRecvPosition -= sizeof(int32);
318 		// rewind the transaction
319 	return status;
320 }
321 
322 
323 status_t
324 LinkReceiver::ReadString(BString &string, size_t* _length)
325 {
326 	int32 length = 0;
327 	status_t status;
328 
329 	status = Read<int32>(&length);
330 	if (status < B_OK)
331 		return status;
332 
333 	if (length < 0) {
334 		status = B_ERROR;
335 		goto err;
336 	}
337 
338 	if (length > 0) {
339 		char* buffer = string.LockBuffer(length + 1);
340 		if (buffer == NULL) {
341 			status = B_NO_MEMORY;
342 			goto err;
343 		}
344 
345 		status = Read(buffer, length);
346 		if (status < B_OK) {
347 			string.UnlockBuffer();
348 			goto err;
349 		}
350 
351 		// make sure the string is null terminated
352 		buffer[length] = '\0';
353 		string.UnlockBuffer(length);
354 	} else
355 		string = "";
356 
357 	if (_length)
358 		*_length = length;
359 
360 	return B_OK;
361 
362 err:
363 	fRecvPosition -= sizeof(int32);
364 		// rewind the transaction
365 	return status;
366 }
367 
368 
369 status_t
370 LinkReceiver::ReadString(char *buffer, size_t bufferLength)
371 {
372 	int32 length = 0;
373 	status_t status;
374 
375 	status = Read<int32>(&length);
376 	if (status < B_OK)
377 		return status;
378 
379 	if (length >= (int32)bufferLength) {
380 		status = B_BUFFER_OVERFLOW;
381 		goto err;
382 	}
383 
384 	if (length < 0) {
385 		status = B_ERROR;
386 		goto err;
387 	}
388 
389 	if (length > 0) {
390 		status = Read(buffer, length);
391 		if (status < B_OK)
392 			goto err;
393 	}
394 
395 	// make sure the string is null terminated
396 	buffer[length] = '\0';
397 	return B_OK;
398 
399 err:
400 	fRecvPosition -= sizeof(int32);
401 		// rewind the transaction
402 	return status;
403 }
404 
405 status_t
406 LinkReceiver::ReadRegion(BRegion* region)
407 {
408 	status_t status = Read(&region->fCount, sizeof(int32));
409 	if (status >= B_OK)
410 		status = Read(&region->fBounds, sizeof(clipping_rect));
411 	if (status >= B_OK) {
412 		if (!region->_SetSize(region->fCount))
413 			status = B_NO_MEMORY;
414 		else {
415 			status = Read(region->fData,
416 				region->fCount * sizeof(clipping_rect));
417 		}
418 		if (status < B_OK)
419 			region->MakeEmpty();
420 	}
421 	return status;
422 }
423 
424 
425 }	// namespace BPrivate
426 
427 
428