/* * Copyright 2001-2011, Haiku. * Distributed under the terms of the MIT License. * * Authors: * Pahtz * Axel Dörfler * Stephan Aßmus * Artur Wyszynski */ /*! Class for low-overhead port-based messaging */ #include #include #include #include #include #include #include #include #include #include #include #include #include "link_message.h" //#define DEBUG_BPORTLINK #ifdef DEBUG_BPORTLINK # include # define STRACE(x) printf x #else # define STRACE(x) ; #endif //#define TRACE_LINK_RECEIVER_GRADIENTS #ifdef TRACE_LINK_RECEIVER_GRADIENTS # include # define GTRACE(x) debug_printf x #else # define GTRACE(x) ; #endif namespace BPrivate { LinkReceiver::LinkReceiver(port_id port) : fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0), fRecvBufferSize(0), fDataSize(0), fReplySize(0), fReadError(B_OK) { } LinkReceiver::~LinkReceiver() { free(fRecvBuffer); } void LinkReceiver::SetPort(port_id port) { fReceivePort = port; } status_t LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout) { fReadError = B_OK; int32 remaining = fDataSize - (fRecvStart + fReplySize); STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining)); // find the position of the next message header in the buffer message_header *header; if (remaining <= 0) { status_t err = ReadFromPort(timeout); if (err < B_OK) return err; remaining = fDataSize; header = (message_header *)fRecvBuffer; } else { fRecvStart += fReplySize; // start of the next message fRecvPosition = fRecvStart; header = (message_header *)(fRecvBuffer + fRecvStart); } // check we have a well-formed message if (remaining < (int32)sizeof(message_header)) { // we don't have enough data for a complete header STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining)); ResetBuffer(); return B_ERROR; } fReplySize = header->size; if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) { STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize)); ResetBuffer(); return B_ERROR; } code = header->code; fRecvPosition += sizeof(message_header); STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n", header->code, fReplySize, header->code, header->flags, fReceivePort)); return B_OK; } bool LinkReceiver::HasMessages() const { return fDataSize - (fRecvStart + fReplySize) > 0 || port_count(fReceivePort) > 0; } bool LinkReceiver::NeedsReply() const { if (fReplySize == 0) return false; message_header *header = (message_header *)(fRecvBuffer + fRecvStart); return (header->flags & kNeedsReply) != 0; } int32 LinkReceiver::Code() const { if (fReplySize == 0) return B_ERROR; message_header *header = (message_header *)(fRecvBuffer + fRecvStart); return header->code; } void LinkReceiver::ResetBuffer() { fRecvPosition = 0; fRecvStart = 0; fDataSize = 0; fReplySize = 0; } status_t LinkReceiver::AdjustReplyBuffer(bigtime_t timeout) { // Here we take advantage of the compiler's dead-code elimination if (kInitialBufferSize == kMaxBufferSize) { // fixed buffer size if (fRecvBuffer != NULL) return B_OK; fRecvBuffer = (char *)malloc(kInitialBufferSize); if (fRecvBuffer == NULL) return B_NO_MEMORY; fRecvBufferSize = kInitialBufferSize; } else { STRACE(("info: LinkReceiver getting port_buffer_size().\n")); ssize_t bufferSize; do { bufferSize = port_buffer_size_etc(fReceivePort, timeout == B_INFINITE_TIMEOUT ? 0 : B_RELATIVE_TIMEOUT, timeout); } while (bufferSize == B_INTERRUPTED); STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize)); if (bufferSize < 0) return (status_t)bufferSize; // make sure our receive buffer is large enough if (bufferSize > fRecvBufferSize) { if (bufferSize <= (ssize_t)kInitialBufferSize) bufferSize = (ssize_t)kInitialBufferSize; else bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); if (bufferSize > (ssize_t)kMaxBufferSize) return B_ERROR; // we can't continue STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize)); char *buffer = (char *)malloc(bufferSize); if (buffer == NULL) return B_NO_MEMORY; free(fRecvBuffer); fRecvBuffer = buffer; fRecvBufferSize = bufferSize; } } return B_OK; } status_t LinkReceiver::ReadFromPort(bigtime_t timeout) { // we are here so it means we finished reading the buffer contents ResetBuffer(); status_t err = AdjustReplyBuffer(timeout); if (err < B_OK) return err; int32 code; ssize_t bytesRead; STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort)); while (true) { if (timeout != B_INFINITE_TIMEOUT) { do { bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer, fRecvBufferSize, B_TIMEOUT, timeout); } while (bytesRead == B_INTERRUPTED); } else { do { bytesRead = read_port(fReceivePort, &code, fRecvBuffer, fRecvBufferSize); } while (bytesRead == B_INTERRUPTED); } STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead)); if (bytesRead < B_OK) return bytesRead; // we just ignore incorrect messages, and don't bother our caller if (code != kLinkCode) { STRACE(("wrong port message %lx received.\n", code)); continue; } // port read seems to be valid break; } fDataSize = bytesRead; return B_OK; } status_t LinkReceiver::Read(void *data, ssize_t passedSize) { // STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size)); ssize_t size = passedSize; if (fReadError < B_OK) return fReadError; if (data == NULL || size < 1) { fReadError = B_BAD_VALUE; return B_BAD_VALUE; } if (fDataSize == 0 || fReplySize == 0) return B_NO_INIT; // need to call GetNextReply() first bool useArea = false; if ((size_t)size >= kMaxBufferSize) { useArea = true; size = sizeof(area_id); } if (fRecvPosition + size > fRecvStart + fReplySize) { // reading past the end of current message fReadError = B_BAD_VALUE; return B_BAD_VALUE; } if (useArea) { area_id sourceArea; memcpy((void*)&sourceArea, fRecvBuffer + fRecvPosition, size); area_info areaInfo; if (get_area_info(sourceArea, &areaInfo) < B_OK) fReadError = B_BAD_VALUE; if (fReadError >= B_OK) { void* areaAddress = areaInfo.address; if (areaAddress && sourceArea >= B_OK) { memcpy(data, areaAddress, passedSize); delete_area(sourceArea); } } } else { memcpy(data, fRecvBuffer + fRecvPosition, size); } fRecvPosition += size; return fReadError; } status_t LinkReceiver::ReadString(char** _string, size_t* _length) { int32 length = 0; status_t status = Read(&length); if (status < B_OK) return status; char *string; if (length < 0) { status = B_ERROR; goto err; } string = (char *)malloc(length + 1); if (string == NULL) { status = B_NO_MEMORY; goto err; } if (length > 0) { status = Read(string, length); if (status < B_OK) { free(string); return status; } } // make sure the string is null terminated string[length] = '\0'; if (_length) *_length = length; *_string = string; return B_OK; err: fRecvPosition -= sizeof(int32); // rewind the transaction return status; } status_t LinkReceiver::ReadString(BString &string, size_t* _length) { int32 length = 0; status_t status = Read(&length); if (status < B_OK) return status; if (length < 0) { status = B_ERROR; goto err; } if (length > 0) { char* buffer = string.LockBuffer(length + 1); if (buffer == NULL) { status = B_NO_MEMORY; goto err; } status = Read(buffer, length); if (status < B_OK) { string.UnlockBuffer(); goto err; } // make sure the string is null terminated buffer[length] = '\0'; string.UnlockBuffer(length); } else string = ""; if (_length) *_length = length; return B_OK; err: fRecvPosition -= sizeof(int32); // rewind the transaction return status; } status_t LinkReceiver::ReadString(char *buffer, size_t bufferLength) { int32 length = 0; status_t status = Read(&length); if (status < B_OK) return status; if (length >= (int32)bufferLength) { status = B_BUFFER_OVERFLOW; goto err; } if (length < 0) { status = B_ERROR; goto err; } if (length > 0) { status = Read(buffer, length); if (status < B_OK) goto err; } // make sure the string is null terminated buffer[length] = '\0'; return B_OK; err: fRecvPosition -= sizeof(int32); // rewind the transaction return status; } status_t LinkReceiver::ReadRegion(BRegion* region) { status_t status = Read(®ion->fCount, sizeof(int32)); if (status >= B_OK) status = Read(®ion->fBounds, sizeof(clipping_rect)); if (status >= B_OK) { if (!region->_SetSize(region->fCount)) status = B_NO_MEMORY; else { status = Read(region->fData, region->fCount * sizeof(clipping_rect)); } if (status < B_OK) region->MakeEmpty(); } return status; } static BGradient* gradient_for_type(BGradient::Type type) { switch (type) { case BGradient::TYPE_LINEAR: return new (std::nothrow) BGradientLinear(); case BGradient::TYPE_RADIAL: return new (std::nothrow) BGradientRadial(); case BGradient::TYPE_RADIAL_FOCUS: return new (std::nothrow) BGradientRadialFocus(); case BGradient::TYPE_DIAMOND: return new (std::nothrow) BGradientDiamond(); case BGradient::TYPE_CONIC: return new (std::nothrow) BGradientConic(); case BGradient::TYPE_NONE: return new (std::nothrow) BGradient(); } return NULL; } status_t LinkReceiver::ReadGradient(BGradient** _gradient) { GTRACE(("LinkReceiver::ReadGradient\n")); BGradient::Type gradientType; int32 colorsCount; Read(&gradientType, sizeof(BGradient::Type)); status_t status = Read(&colorsCount, sizeof(int32)); if (status != B_OK) return status; BGradient* gradient = gradient_for_type(gradientType); if (!gradient) return B_NO_MEMORY; *_gradient = gradient; if (colorsCount > 0) { BGradient::ColorStop stop; for (int i = 0; i < colorsCount; i++) { if ((status = Read(&stop, sizeof(BGradient::ColorStop))) != B_OK) return status; if (!gradient->AddColorStop(stop, i)) return B_NO_MEMORY; } } switch (gradientType) { case BGradient::TYPE_LINEAR: { GTRACE(("LinkReceiver::ReadGradient> type == TYPE_LINEAR\n")); BGradientLinear* linear = (BGradientLinear*)gradient; BPoint start; BPoint end; Read(&start, sizeof(BPoint)); if ((status = Read(&end, sizeof(BPoint))) != B_OK) return status; linear->SetStart(start); linear->SetEnd(end); return B_OK; } case BGradient::TYPE_RADIAL: { GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL\n")); BGradientRadial* radial = (BGradientRadial*)gradient; BPoint center; float radius; Read(¢er, sizeof(BPoint)); if ((status = Read(&radius, sizeof(float))) != B_OK) return status; radial->SetCenter(center); radial->SetRadius(radius); return B_OK; } case BGradient::TYPE_RADIAL_FOCUS: { GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL_FOCUS\n")); BGradientRadialFocus* radialFocus = (BGradientRadialFocus*)gradient; BPoint center; BPoint focal; float radius; Read(¢er, sizeof(BPoint)); Read(&focal, sizeof(BPoint)); if ((status = Read(&radius, sizeof(float))) != B_OK) return status; radialFocus->SetCenter(center); radialFocus->SetFocal(focal); radialFocus->SetRadius(radius); return B_OK; } case BGradient::TYPE_DIAMOND: { GTRACE(("LinkReceiver::ReadGradient> type == TYPE_DIAMOND\n")); BGradientDiamond* diamond = (BGradientDiamond*)gradient; BPoint center; if ((status = Read(¢er, sizeof(BPoint))) != B_OK) return status; diamond->SetCenter(center); return B_OK; } case BGradient::TYPE_CONIC: { GTRACE(("LinkReceiver::ReadGradient> type == TYPE_CONIC\n")); BGradientConic* conic = (BGradientConic*)gradient; BPoint center; float angle; Read(¢er, sizeof(BPoint)); if ((status = Read(&angle, sizeof(float))) != B_OK) return status; conic->SetCenter(center); conic->SetAngle(angle); return B_OK; } case BGradient::TYPE_NONE: { GTRACE(("LinkReceiver::ReadGradient> type == TYPE_NONE\n")); break; } } return B_ERROR; } } // namespace BPrivate