xref: /haiku/src/kits/app/LinkReceiver.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 /*
2  * Copyright 2001-2006, 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 		ssize_t bufferSize;
165 		if (timeout == B_INFINITE_TIMEOUT)
166 			bufferSize = port_buffer_size(fReceivePort);
167 		else
168 			bufferSize = port_buffer_size_etc(fReceivePort, B_TIMEOUT, timeout);
169 		STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
170 
171 		if (bufferSize < 0)
172 			return (status_t)bufferSize;
173 
174 		// make sure our receive buffer is large enough
175 		if (bufferSize > fRecvBufferSize) {
176 			if (bufferSize <= (ssize_t)kInitialBufferSize)
177 				bufferSize = (ssize_t)kInitialBufferSize;
178 			else
179 				bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
180 
181 			if (bufferSize > (ssize_t)kMaxBufferSize)
182 				return B_ERROR;	// we can't continue
183 
184 			STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
185 			char *buffer = (char *)malloc(bufferSize);
186 			if (buffer == NULL)
187 				return B_NO_MEMORY;
188 
189 			free(fRecvBuffer);
190 			fRecvBuffer = buffer;
191 			fRecvBufferSize = bufferSize;
192 		}
193 	}
194 
195 	return B_OK;
196 }
197 
198 
199 status_t
200 LinkReceiver::ReadFromPort(bigtime_t timeout)
201 {
202 	// we are here so it means we finished reading the buffer contents
203 	ResetBuffer();
204 
205 	status_t err = AdjustReplyBuffer(timeout);
206 	if (err < B_OK)
207 		return err;
208 
209 	int32 code;
210 	ssize_t bytesRead;
211 
212 	STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
213 	while (true) {
214 		if (timeout != B_INFINITE_TIMEOUT) {
215 			do {
216 				bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
217 					fRecvBufferSize, B_TIMEOUT, timeout);
218 			} while (bytesRead == B_INTERRUPTED);
219 		} else {
220 			do {
221 				bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
222 					fRecvBufferSize);
223 			} while (bytesRead == B_INTERRUPTED);
224 		}
225 
226 		STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
227 		if (bytesRead < B_OK)
228 			return bytesRead;
229 
230 		// we just ignore incorrect messages, and don't bother our caller
231 
232 		if (code != kLinkCode) {
233 			STRACE(("wrong port message %lx received.\n", code));
234 			continue;
235 		}
236 
237 		// port read seems to be valid
238 		break;
239 	}
240 
241 	fDataSize = bytesRead;
242 	return B_OK;
243 }
244 
245 
246 status_t
247 LinkReceiver::Read(void *data, ssize_t size)
248 {
249 //	STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
250 	if (fReadError < B_OK)
251 		return fReadError;
252 
253 	if (data == NULL || size < 1) {
254 		fReadError = B_BAD_VALUE;
255 		return B_BAD_VALUE;
256 	}
257 
258 	if (fDataSize == 0 || fReplySize == 0)
259 		return B_NO_INIT;	// need to call GetNextReply() first
260 
261 	if (fRecvPosition + size > fRecvStart + fReplySize) {
262 		// reading past the end of current message
263 		fReadError = B_BAD_VALUE;
264 		return B_BAD_VALUE;
265 	}
266 
267 	memcpy(data, fRecvBuffer + fRecvPosition, size);
268 	fRecvPosition += size;
269 	return fReadError;
270 }
271 
272 
273 status_t
274 LinkReceiver::ReadString(char** _string, size_t* _length)
275 {
276 	int32 length = 0;
277 	status_t status;
278 
279 	status = Read<int32>(&length);
280 	if (status < B_OK)
281 		return status;
282 
283 	char *string;
284 
285 	if (length < 0) {
286 		status = B_ERROR;
287 		goto err;
288 	}
289 
290 	string = (char *)malloc(length + 1);
291 	if (string == NULL) {
292 		status = B_NO_MEMORY;
293 		goto err;
294 	}
295 
296 	if (length > 0) {
297 		status = Read(string, length);
298 		if (status < B_OK) {
299 			free(string);
300 			return status;
301 		}
302 	}
303 
304 	// make sure the string is null terminated
305 	string[length] = '\0';
306 
307 	if (_length)
308 		*_length = length;
309 	*_string = string;
310 
311 	return B_OK;
312 
313 err:
314 	fRecvPosition -= sizeof(int32);
315 		// rewind the transaction
316 	return status;
317 }
318 
319 
320 status_t
321 LinkReceiver::ReadString(BString &string, size_t* _length)
322 {
323 	int32 length = 0;
324 	status_t status;
325 
326 	status = Read<int32>(&length);
327 	if (status < B_OK)
328 		return status;
329 
330 	if (length < 0) {
331 		status = B_ERROR;
332 		goto err;
333 	}
334 
335 	if (length > 0) {
336 		char* buffer = string.LockBuffer(length + 1);
337 		if (buffer == NULL) {
338 			status = B_NO_MEMORY;
339 			goto err;
340 		}
341 
342 		status = Read(buffer, length);
343 		if (status < B_OK) {
344 			string.UnlockBuffer();
345 			goto err;
346 		}
347 
348 		// make sure the string is null terminated
349 		buffer[length] = '\0';
350 		string.UnlockBuffer(length);
351 	} else
352 		string = "";
353 
354 	if (_length)
355 		*_length = length;
356 
357 	return B_OK;
358 
359 err:
360 	fRecvPosition -= sizeof(int32);
361 		// rewind the transaction
362 	return status;
363 }
364 
365 
366 status_t
367 LinkReceiver::ReadString(char *buffer, size_t bufferLength)
368 {
369 	int32 length = 0;
370 	status_t status;
371 
372 	status = Read<int32>(&length);
373 	if (status < B_OK)
374 		return status;
375 
376 	if (length >= (int32)bufferLength) {
377 		status = B_BUFFER_OVERFLOW;
378 		goto err;
379 	}
380 
381 	if (length < 0) {
382 		status = B_ERROR;
383 		goto err;
384 	}
385 
386 	if (length > 0) {
387 		status = Read(buffer, length);
388 		if (status < B_OK)
389 			goto err;
390 	}
391 
392 	// make sure the string is null terminated
393 	buffer[length] = '\0';
394 	return B_OK;
395 
396 err:
397 	fRecvPosition -= sizeof(int32);
398 		// rewind the transaction
399 	return status;
400 }
401 
402 status_t
403 LinkReceiver::ReadRegion(BRegion* region)
404 {
405 	status_t status = Read(&region->count, sizeof(int32));
406 	if (status >= B_OK)
407 		status = Read(&region->bound, sizeof(clipping_rect));
408 	if (status >= B_OK) {
409 		region->set_size(region->count);
410 		status = Read(region->data, region->count * sizeof(clipping_rect));
411 		if (status < B_OK)
412 			region->MakeEmpty();
413 	}
414 	return status;
415 }
416 
417 
418 }	// namespace BPrivate
419 
420 
421