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