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(®ion->fCount, sizeof(int32)); 447 if (status >= B_OK) 448 status = Read(®ion->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(¢er, 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(¢er, 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(¢er, 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(¢er, 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