xref: /haiku/src/kits/app/LinkReceiver.cpp (revision 3904a8dba0df1065db019e58a491c712cdf9cd83)
1 /*
2  * Copyright 2001-2008, 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  *		Artur Wyszynski <harakash@gmail.com>
10  */
11 
12 /** Class for low-overhead port-based messaging */
13 
14 #include <LinkReceiver.h>
15 
16 #include <stdlib.h>
17 #include <string.h>
18 #include <new>
19 
20 #include <ServerProtocol.h>
21 #include <String.h>
22 #include <Region.h>
23 #include <GradientLinear.h>
24 #include <GradientRadial.h>
25 #include <GradientRadialFocus.h>
26 #include <GradientDiamond.h>
27 #include <GradientConic.h>
28 
29 #include "link_message.h"
30 
31 //#define DEBUG_BPORTLINK
32 #ifdef DEBUG_BPORTLINK
33 #	include <stdio.h>
34 #	define STRACE(x) printf x
35 #else
36 #	define STRACE(x) ;
37 #endif
38 
39 //#define TRACE_LINK_RECEIVER_GRADIENTS
40 #ifdef TRACE_LINK_RECEIVER_GRADIENTS
41 #	include <OS.h>
42 #	define GTRACE(x) debug_printf x
43 #else
44 #	define GTRACE(x) ;
45 #endif
46 
47 
48 namespace BPrivate {
49 
50 LinkReceiver::LinkReceiver(port_id port)
51 	:
52 	fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
53 	fRecvBufferSize(0), fDataSize(0),
54 	fReplySize(0), fReadError(B_OK)
55 {
56 }
57 
58 
59 LinkReceiver::~LinkReceiver()
60 {
61 	free(fRecvBuffer);
62 }
63 
64 
65 void
66 LinkReceiver::SetPort(port_id port)
67 {
68 	fReceivePort = port;
69 }
70 
71 
72 status_t
73 LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout)
74 {
75 	fReadError = B_OK;
76 
77 	int32 remaining = fDataSize - (fRecvStart + fReplySize);
78 	STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
79 
80 	// find the position of the next message header in the buffer
81 	message_header *header;
82 	if (remaining <= 0) {
83 		status_t err = ReadFromPort(timeout);
84 		if (err < B_OK)
85 			return err;
86 		remaining = fDataSize;
87 		header = (message_header *)fRecvBuffer;
88 	} else {
89 		fRecvStart += fReplySize;	// start of the next message
90 		fRecvPosition = fRecvStart;
91 		header = (message_header *)(fRecvBuffer + fRecvStart);
92 	}
93 
94 	// check we have a well-formed message
95 	if (remaining < (int32)sizeof(message_header)) {
96 		// we don't have enough data for a complete header
97 		STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining));
98 		ResetBuffer();
99 		return B_ERROR;
100 	}
101 
102 	fReplySize = header->size;
103 	if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
104 		STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize));
105 		ResetBuffer();
106 		return B_ERROR;
107 	}
108 
109 	code = header->code;
110 	fRecvPosition += sizeof(message_header);
111 
112 	STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n",
113 		header->code, fReplySize, header->code, header->flags, fReceivePort));
114 
115 	return B_OK;
116 }
117 
118 
119 bool
120 LinkReceiver::HasMessages() const
121 {
122 	return fDataSize - (fRecvStart + fReplySize) > 0
123 		|| port_count(fReceivePort) > 0;
124 }
125 
126 
127 bool
128 LinkReceiver::NeedsReply() const
129 {
130 	if (fReplySize == 0)
131 		return false;
132 
133 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
134 	return (header->flags & kNeedsReply) != 0;
135 }
136 
137 
138 int32
139 LinkReceiver::Code() const
140 {
141 	if (fReplySize == 0)
142 		return B_ERROR;
143 
144 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
145 	return header->code;
146 }
147 
148 
149 void
150 LinkReceiver::ResetBuffer()
151 {
152 	fRecvPosition = 0;
153 	fRecvStart = 0;
154 	fDataSize = 0;
155 	fReplySize = 0;
156 }
157 
158 
159 status_t
160 LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
161 {
162 	// Here we take advantage of the compiler's dead-code elimination
163 	if (kInitialBufferSize == kMaxBufferSize) {
164 		// fixed buffer size
165 
166 		if (fRecvBuffer != NULL)
167 			return B_OK;
168 
169 		fRecvBuffer = (char *)malloc(kInitialBufferSize);
170 		if (fRecvBuffer == NULL)
171 			return B_NO_MEMORY;
172 
173 		fRecvBufferSize = kInitialBufferSize;
174 	} else {
175 		STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
176 
177 		ssize_t bufferSize;
178 		do {
179 			bufferSize = port_buffer_size_etc(fReceivePort,
180 				timeout == B_INFINITE_TIMEOUT ? B_RELATIVE_TIMEOUT : 0,
181 				timeout);
182 		} while (bufferSize == B_INTERRUPTED);
183 
184 		STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
185 
186 		if (bufferSize < 0)
187 			return (status_t)bufferSize;
188 
189 		// make sure our receive buffer is large enough
190 		if (bufferSize > fRecvBufferSize) {
191 			if (bufferSize <= (ssize_t)kInitialBufferSize)
192 				bufferSize = (ssize_t)kInitialBufferSize;
193 			else
194 				bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
195 
196 			if (bufferSize > (ssize_t)kMaxBufferSize)
197 				return B_ERROR;	// we can't continue
198 
199 			STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
200 			char *buffer = (char *)malloc(bufferSize);
201 			if (buffer == NULL)
202 				return B_NO_MEMORY;
203 
204 			free(fRecvBuffer);
205 			fRecvBuffer = buffer;
206 			fRecvBufferSize = bufferSize;
207 		}
208 	}
209 
210 	return B_OK;
211 }
212 
213 
214 status_t
215 LinkReceiver::ReadFromPort(bigtime_t timeout)
216 {
217 	// we are here so it means we finished reading the buffer contents
218 	ResetBuffer();
219 
220 	status_t err = AdjustReplyBuffer(timeout);
221 	if (err < B_OK)
222 		return err;
223 
224 	int32 code;
225 	ssize_t bytesRead;
226 
227 	STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
228 	while (true) {
229 		if (timeout != B_INFINITE_TIMEOUT) {
230 			do {
231 				bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
232 					fRecvBufferSize, B_TIMEOUT, timeout);
233 			} while (bytesRead == B_INTERRUPTED);
234 		} else {
235 			do {
236 				bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
237 					fRecvBufferSize);
238 			} while (bytesRead == B_INTERRUPTED);
239 		}
240 
241 		STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
242 		if (bytesRead < B_OK)
243 			return bytesRead;
244 
245 		// we just ignore incorrect messages, and don't bother our caller
246 
247 		if (code != kLinkCode) {
248 			STRACE(("wrong port message %lx received.\n", code));
249 			continue;
250 		}
251 
252 		// port read seems to be valid
253 		break;
254 	}
255 
256 	fDataSize = bytesRead;
257 	return B_OK;
258 }
259 
260 
261 status_t
262 LinkReceiver::Read(void *data, ssize_t passedSize)
263 {
264 //	STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
265 	ssize_t size = passedSize;
266 
267 	if (fReadError < B_OK)
268 		return fReadError;
269 
270 	if (data == NULL || size < 1) {
271 		fReadError = B_BAD_VALUE;
272 		return B_BAD_VALUE;
273 	}
274 
275 	if (fDataSize == 0 || fReplySize == 0)
276 		return B_NO_INIT;	// need to call GetNextReply() first
277 
278 	bool useArea = false;
279 	if ((size_t)size >= kMaxBufferSize) {
280 		useArea = true;
281 		size = sizeof(area_id);
282 	}
283 
284 	if (fRecvPosition + size > fRecvStart + fReplySize) {
285 		// reading past the end of current message
286 		fReadError = B_BAD_VALUE;
287 		return B_BAD_VALUE;
288 	}
289 
290 	if (useArea) {
291 		area_id sourceArea;
292 		memcpy((void*)&sourceArea, fRecvBuffer + fRecvPosition, size);
293 
294 		area_info areaInfo;
295 		if (get_area_info(sourceArea, &areaInfo) < B_OK)
296 			fReadError = B_BAD_VALUE;
297 
298 		if (fReadError >= B_OK) {
299 			void* areaAddress = areaInfo.address;
300 
301 			if (areaAddress && sourceArea >= B_OK) {
302 				memcpy(data, areaAddress, passedSize);
303 				delete_area(sourceArea);
304 			}
305 		}
306 	} else {
307 		memcpy(data, fRecvBuffer + fRecvPosition, size);
308 	}
309 	fRecvPosition += size;
310 	return fReadError;
311 }
312 
313 
314 status_t
315 LinkReceiver::ReadString(char** _string, size_t* _length)
316 {
317 	int32 length = 0;
318 	status_t status = Read<int32>(&length);
319 
320 	if (status < B_OK)
321 		return status;
322 
323 	char *string;
324 	if (length < 0) {
325 		status = B_ERROR;
326 		goto err;
327 	}
328 
329 	string = (char *)malloc(length + 1);
330 	if (string == NULL) {
331 		status = B_NO_MEMORY;
332 		goto err;
333 	}
334 
335 	if (length > 0) {
336 		status = Read(string, length);
337 		if (status < B_OK) {
338 			free(string);
339 			return status;
340 		}
341 	}
342 
343 	// make sure the string is null terminated
344 	string[length] = '\0';
345 
346 	if (_length)
347 		*_length = length;
348 
349 	*_string = string;
350 
351 	return B_OK;
352 
353 err:
354 	fRecvPosition -= sizeof(int32);
355 		// rewind the transaction
356 	return status;
357 }
358 
359 
360 status_t
361 LinkReceiver::ReadString(BString &string, size_t* _length)
362 {
363 	int32 length = 0;
364 	status_t status = Read<int32>(&length);
365 
366 	if (status < B_OK)
367 		return status;
368 
369 	if (length < 0) {
370 		status = B_ERROR;
371 		goto err;
372 	}
373 
374 	if (length > 0) {
375 		char* buffer = string.LockBuffer(length + 1);
376 		if (buffer == NULL) {
377 			status = B_NO_MEMORY;
378 			goto err;
379 		}
380 
381 		status = Read(buffer, length);
382 		if (status < B_OK) {
383 			string.UnlockBuffer();
384 			goto err;
385 		}
386 
387 		// make sure the string is null terminated
388 		buffer[length] = '\0';
389 		string.UnlockBuffer(length);
390 	} else
391 		string = "";
392 
393 	if (_length)
394 		*_length = length;
395 
396 	return B_OK;
397 
398 err:
399 	fRecvPosition -= sizeof(int32);
400 		// rewind the transaction
401 	return status;
402 }
403 
404 
405 status_t
406 LinkReceiver::ReadString(char *buffer, size_t bufferLength)
407 {
408 	int32 length = 0;
409 	status_t status = Read<int32>(&length);
410 
411 	if (status < B_OK)
412 		return status;
413 
414 	if (length >= (int32)bufferLength) {
415 		status = B_BUFFER_OVERFLOW;
416 		goto err;
417 	}
418 
419 	if (length < 0) {
420 		status = B_ERROR;
421 		goto err;
422 	}
423 
424 	if (length > 0) {
425 		status = Read(buffer, length);
426 		if (status < B_OK)
427 			goto err;
428 	}
429 
430 	// make sure the string is null terminated
431 	buffer[length] = '\0';
432 	return B_OK;
433 
434 err:
435 	fRecvPosition -= sizeof(int32);
436 		// rewind the transaction
437 	return status;
438 }
439 
440 status_t
441 LinkReceiver::ReadRegion(BRegion* region)
442 {
443 	status_t status = Read(&region->fCount, sizeof(int32));
444 	if (status >= B_OK)
445 		status = Read(&region->fBounds, sizeof(clipping_rect));
446 	if (status >= B_OK) {
447 		if (!region->_SetSize(region->fCount))
448 			status = B_NO_MEMORY;
449 		else {
450 			status = Read(region->fData,
451 				region->fCount * sizeof(clipping_rect));
452 		}
453 		if (status < B_OK)
454 			region->MakeEmpty();
455 	}
456 	return status;
457 }
458 
459 
460 static BGradient*
461 gradient_for_type(BGradient::Type type)
462 {
463 	switch (type) {
464 		case BGradient::TYPE_LINEAR:
465 			return new (std::nothrow) BGradientLinear();
466 		case BGradient::TYPE_RADIAL:
467 			return new (std::nothrow) BGradientRadial();
468 		case BGradient::TYPE_RADIAL_FOCUS:
469 			return new (std::nothrow) BGradientRadialFocus();
470 		case BGradient::TYPE_DIAMOND:
471 			return new (std::nothrow) BGradientDiamond();
472 		case BGradient::TYPE_CONIC:
473 			return new (std::nothrow) BGradientConic();
474 		case BGradient::TYPE_NONE:
475 			return new (std::nothrow) BGradient();
476 	}
477 	return NULL;
478 }
479 
480 
481 status_t
482 LinkReceiver::ReadGradient(BGradient** _gradient)
483 {
484 	GTRACE(("LinkReceiver::ReadGradient\n"));
485 	BGradient::Type gradientType;
486 	int32 colorsCount;
487 	status_t ret;
488 	if ((ret = Read(&gradientType, sizeof(BGradient::Type))) != B_OK)
489 		return ret;
490 	if ((ret = Read(&colorsCount, sizeof(int32))) != B_OK)
491 		return ret;
492 	BGradient* gradient = gradient_for_type(gradientType);
493 	if (!gradient)
494 		return B_NO_MEMORY;
495 
496 	*_gradient = gradient;
497 
498 	if (colorsCount > 0) {
499 		BGradient::ColorStop stop;
500 		for (int i = 0; i < colorsCount; i++) {
501 			if ((ret = Read(&stop, sizeof(BGradient::ColorStop))) != B_OK)
502 				return ret;
503 			if (!gradient->AddColorStop(stop, i))
504 				return B_NO_MEMORY;
505 		}
506 	}
507 
508 	switch(gradientType) {
509 		case BGradient::TYPE_LINEAR: {
510 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_LINEAR\n"));
511 			BGradientLinear* linear = (BGradientLinear*)gradient;
512 			BPoint start;
513 			BPoint end;
514 			if ((ret = Read(&start, sizeof(BPoint))) != B_OK)
515 				return ret;
516 			if ((ret = Read(&end, sizeof(BPoint))) != B_OK)
517 				return ret;
518 			linear->SetStart(start);
519 			linear->SetEnd(end);
520 			return B_OK;
521 		}
522 		case BGradient::TYPE_RADIAL: {
523 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL\n"));
524 			BGradientRadial* radial = (BGradientRadial*)gradient;
525 			BPoint center;
526 			float radius;
527 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
528 				return ret;
529 			if ((ret = Read(&radius, sizeof(float))) != B_OK)
530 				return ret;
531 			radial->SetCenter(center);
532 			radial->SetRadius(radius);
533 			return B_OK;
534 		}
535 		case BGradient::TYPE_RADIAL_FOCUS: {
536 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL_FOCUS\n"));
537 			BGradientRadialFocus* radialFocus =
538 				(BGradientRadialFocus*)gradient;
539 			BPoint center;
540 			BPoint focal;
541 			float radius;
542 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
543 				return ret;
544 			if ((ret = Read(&focal, sizeof(BPoint))) != B_OK)
545 				return ret;
546 			if ((ret = Read(&radius, sizeof(float))) != B_OK)
547 				return ret;
548 			radialFocus->SetCenter(center);
549 			radialFocus->SetFocal(focal);
550 			radialFocus->SetRadius(radius);
551 			return B_OK;
552 		}
553 		case BGradient::TYPE_DIAMOND: {
554 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_DIAMOND\n"));
555 			BGradientDiamond* diamond = (BGradientDiamond*)gradient;
556 			BPoint center;
557 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
558 				return ret;
559 			diamond->SetCenter(center);
560 			return B_OK;
561 		}
562 		case BGradient::TYPE_CONIC: {
563 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_CONIC\n"));
564 			BGradientConic* conic = (BGradientConic*)gradient;
565 			BPoint center;
566 			float angle;
567 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
568 				return ret;
569 			if ((ret = Read(&angle, sizeof(float))) != B_OK)
570 				return ret;
571 			conic->SetCenter(center);
572 			conic->SetAngle(angle);
573 			return B_OK;
574 		}
575 		case BGradient::TYPE_NONE: {
576 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_NONE\n"));
577 			break;
578 		}
579 	}
580 
581 	return B_ERROR;
582 }
583 
584 }	// namespace BPrivate
585