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