Files
haiku-beta6/src/kits/app/LinkMsgReader.cpp
T

276 lines
6.3 KiB
C++
Raw Normal View History

2005-06-07 14:30:50 +00:00
/*
* Copyright 2001-2005, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pahtz <[email protected]>
* Axel Dörfler
*/
/** Class for low-overhead port-based messaging */
#include <stdlib.h>
#include <string.h>
#include <new>
#include <ServerProtocol.h>
#include <LinkMsgReader.h>
2005-06-07 14:30:50 +00:00
#include "link_message.h"
//#define DEBUG_BPORTLINK
#ifdef DEBUG_BPORTLINK
# include <stdio.h>
# define STRACE(x) printf x
2005-06-07 14:30:50 +00:00
// those are defined in LinkMsgSender.cpp
extern const char *strcode(int32 code);
extern const char *bstrcode(int32 code);
#else
# define STRACE(x) ;
#endif
static const int32 kInitialReceiveBufferSize = 2048;
static const int32 kMaxReceiveBufferSize = 2048;
2005-06-07 14:30:50 +00:00
LinkMsgReader::LinkMsgReader(port_id port)
:
fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
fRecvBufferSize(0), fDataSize(0),
fReplySize(0), fReadError(B_OK)
{
}
2005-06-07 14:30:50 +00:00
LinkMsgReader::~LinkMsgReader()
{
2005-06-07 14:30:50 +00:00
free(fRecvBuffer);
}
2005-06-07 14:30:50 +00:00
void
LinkMsgReader::SetPort(port_id port)
{
2005-06-07 14:30:50 +00:00
fReceivePort = port;
}
2005-06-07 14:30:50 +00:00
port_id
LinkMsgReader::GetPort()
{
return fReceivePort;
}
2005-06-07 14:30:50 +00:00
status_t
LinkMsgReader::GetNextMessage(int32 *code, bigtime_t timeout)
{
int32 remaining;
fReadError = B_OK;
remaining = fDataSize - (fRecvStart + fReplySize);
STRACE(("info: LinkMsgReader GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
2005-06-07 14:30:50 +00:00
// 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;
2005-06-07 14:30:50 +00:00
header = (message_header *)fRecvBuffer;
} else {
fRecvStart += fReplySize; //start of the next message
fRecvPosition = fRecvStart;
2005-06-07 14:30:50 +00:00
header = (message_header *)(fRecvBuffer + fRecvStart);
}
2005-06-07 14:30:50 +00:00
// 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: LinkMsgReader remaining %ld bytes is less than header size.\n", remaining));
ResetBuffer();
return B_ERROR;
}
2005-06-07 14:30:50 +00:00
fReplySize = header->size;
if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
STRACE(("error info: LinkMsgReader message size of %ld bytes smaller than header size.\n", fReplySize));
ResetBuffer();
return B_ERROR;
}
2005-06-07 14:30:50 +00:00
*code = header->code;
fRecvPosition += sizeof(message_header);
2005-06-07 14:30:50 +00:00
STRACE(("info: LinkMsgReader got header %s [%ld %ld %ld] from port %ld.\n",
strcode(header->code), fReplySize, header->code, header->flags, fReceivePort));
return B_OK;
}
2005-06-07 14:30:50 +00:00
void
LinkMsgReader::ResetBuffer()
{
fRecvPosition = 0;
fRecvStart = 0;
fDataSize = 0;
fReplySize = 0;
}
2005-06-07 14:30:50 +00:00
status_t
LinkMsgReader::AdjustReplyBuffer(bigtime_t timeout)
{
2005-06-07 14:30:50 +00:00
// Here we take advantage of the compiler's dead-code elimination
if (kInitialReceiveBufferSize == kMaxReceiveBufferSize) {
// fixed buffer size
if (fRecvBuffer != NULL)
return B_OK;
fRecvBuffer = (char *)malloc(kInitialReceiveBufferSize);
if (fRecvBuffer == NULL)
return B_NO_MEMORY;
fRecvBufferSize = kInitialReceiveBufferSize;
2005-06-07 14:30:50 +00:00
} else {
STRACE(("info: LinkMsgReader getting port_buffer_size().\n"));
ssize_t buffersize;
if (timeout == B_INFINITE_TIMEOUT)
buffersize = port_buffer_size(fReceivePort);
else
buffersize = port_buffer_size_etc(fReceivePort, B_TIMEOUT, timeout);
STRACE(("info: LinkMsgReader got port_buffer_size() = %ld.\n", buffersize));
if (buffersize < 0)
return (status_t)buffersize;
2005-06-07 14:30:50 +00:00
// make sure our receive buffer is large enough
if (buffersize > fRecvBufferSize) {
if (buffersize <= kInitialReceiveBufferSize)
buffersize = kInitialReceiveBufferSize;
else
buffersize = (buffersize + B_PAGE_SIZE) - (buffersize % B_PAGE_SIZE);
if (buffersize > kMaxReceiveBufferSize)
return B_ERROR; //we can't continue
STRACE(("info: LinkMsgReader setting receive buffersize to %ld.\n", buffersize));
char *buffer = (char *)malloc(buffersize);
if (buffer == NULL)
return B_NO_MEMORY;
2005-06-07 14:30:50 +00:00
free(fRecvBuffer);
fRecvBuffer = buffer;
fRecvBufferSize = buffersize;
}
}
return B_OK;
}
2005-06-07 14:30:50 +00:00
status_t
LinkMsgReader::ReadFromPort(bigtime_t timeout)
{
2005-06-07 14:30:50 +00:00
// 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 protocol;
ssize_t bytesread;
STRACE(("info: LinkMsgReader reading port %ld.\n", fReceivePort));
2005-06-07 14:30:50 +00:00
if (timeout != B_INFINITE_TIMEOUT) {
do {
bytesread = read_port_etc(fReceivePort, &protocol, fRecvBuffer,
fRecvBufferSize, B_TIMEOUT, timeout);
} while(bytesread == B_INTERRUPTED);
2005-06-07 14:30:50 +00:00
} else {
do {
bytesread = read_port(fReceivePort, &protocol, fRecvBuffer,
fRecvBufferSize);
} while(bytesread == B_INTERRUPTED);
}
STRACE(("info: LinkMsgReader read %ld bytes.\n", bytesread));
if (bytesread < B_OK)
return bytesread;
2005-06-07 14:30:50 +00:00
// TODO: we only need AS_SERVER_PORTLINK when all OBOS uses Link messages
if (protocol != AS_SERVER_PORTLINK && protocol != AS_SERVER_SESSION)
return B_ERROR;
if (protocol == AS_SERVER_PORTLINK && bytesread != *((int32 *)fRecvBuffer))
2005-06-07 14:30:50 +00:00
// should only be one message for PORTLINK so the size declared in the header
// (the first int32 in the header) should be the same as bytesread
return B_ERROR;
fDataSize = bytesread;
return B_OK;
}
2005-06-07 14:30:50 +00:00
status_t
LinkMsgReader::Read(void *data, ssize_t size)
{
// STRACE(("info: LinkMsgReader Read()ing %ld bytes...\n", size));
if (fReadError < B_OK)
return fReadError;
2005-06-07 14:30:50 +00:00
if (size < 1) {
fReadError = B_BAD_VALUE;
return B_BAD_VALUE;
}
if (fDataSize == 0 || fReplySize == 0)
2005-06-07 14:30:50 +00:00
return B_NO_INIT; // need to call GetNextReply() first
2005-06-07 14:30:50 +00:00
if (fRecvPosition + size > fRecvStart + fReplySize) {
// reading past the end of current message
fReadError = B_BAD_VALUE;
return B_BAD_VALUE;
}
memcpy(data, fRecvBuffer + fRecvPosition, size);
fRecvPosition += size;
return fReadError;
}
2005-06-07 14:30:50 +00:00
status_t
LinkMsgReader::ReadString(char **string)
{
status_t err;
int32 len = 0;
err = Read<int32>(&len);
if (err < B_OK)
return err;
2005-06-07 14:30:50 +00:00
if (len) {
*string = (char *)malloc(len);
2005-06-07 14:30:50 +00:00
if (*string == NULL) {
fRecvPosition -= sizeof(int32); //rewind the transaction
return B_NO_MEMORY;
}
err = Read(*string, len);
2005-06-07 14:30:50 +00:00
if (err < B_OK) {
free(*string);
*string = NULL;
fRecvPosition -= sizeof(int32); //rewind the transaction
return err;
}
(*string)[len-1] = '\0';
return B_OK;
2005-06-07 14:30:50 +00:00
} else {
fRecvPosition -= sizeof(int32); //rewind the transaction
return B_ERROR;
}
}