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
50namespace BPrivate {
51
52
53LinkReceiver::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
62LinkReceiver::~LinkReceiver()
63{
64	free(fRecvBuffer);
65}
66
67
68void
69LinkReceiver::SetPort(port_id port)
70{
71	fReceivePort = port;
72}
73
74
75status_t
76LinkReceiver::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
122bool
123LinkReceiver::HasMessages() const
124{
125	return fDataSize - (fRecvStart + fReplySize) > 0
126		|| port_count(fReceivePort) > 0;
127}
128
129
130bool
131LinkReceiver::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
141int32
142LinkReceiver::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
152void
153LinkReceiver::ResetBuffer()
154{
155	fRecvPosition = 0;
156	fRecvStart = 0;
157	fDataSize = 0;
158	fReplySize = 0;
159}
160
161
162status_t
163LinkReceiver::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 ? 0 : B_RELATIVE_TIMEOUT,
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
217status_t
218LinkReceiver::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
264status_t
265LinkReceiver::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
317status_t
318LinkReceiver::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
356err:
357	fRecvPosition -= sizeof(int32);
358		// rewind the transaction
359	return status;
360}
361
362
363status_t
364LinkReceiver::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
401err:
402	fRecvPosition -= sizeof(int32);
403		// rewind the transaction
404	return status;
405}
406
407
408status_t
409LinkReceiver::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
437err:
438	fRecvPosition -= sizeof(int32);
439		// rewind the transaction
440	return status;
441}
442
443status_t
444LinkReceiver::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
463static BGradient*
464gradient_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
484status_t
485LinkReceiver::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