#include <new>
#include <AutoDeleter.h>
#include "AreaSupport.h"
#include "Compatibility.h"
#include "Port.h"
using std::nothrow;
static const int32 kMinPortSize = 1024;
static const int32 kMaxPortSize = 64 * 1024;
Port::Port(int32 size)
:
fBuffer(NULL),
fCapacity(0),
fReservedSize(0),
fInitStatus(B_NO_INIT),
fOwner(true)
{
if (size < kMinPortSize)
size = kMinPortSize;
else if (size > kMaxPortSize)
size = kMaxPortSize;
fBuffer = new(nothrow) uint8[size];
if (!fBuffer) {
fInitStatus = B_NO_MEMORY;
return;
}
fInfo.owner_port = create_port(1, "port owner port");
if (fInfo.owner_port < 0) {
fInitStatus = fInfo.owner_port;
return;
}
fInfo.client_port = create_port(1, "port client port");
if (fInfo.client_port < 0) {
fInitStatus = fInfo.client_port;
return;
}
fInfo.size = size;
fCapacity = size;
fInitStatus = B_OK;
}
Port::Port(const Info* info)
:
fBuffer(NULL),
fCapacity(0),
fReservedSize(0),
fInitStatus(B_NO_INIT),
fOwner(false)
{
if (!info || info->owner_port < 0 || info->client_port < 0
|| info->size < kMinPortSize || info->size > kMaxPortSize) {
return;
}
fBuffer = new(nothrow) uint8[info->size];
if (!fBuffer) {
fInitStatus = B_NO_MEMORY;
return;
}
fInfo.owner_port = info->owner_port;
fInfo.client_port = info->client_port;
fInfo.size = info->size;
fCapacity = info->size;
fInitStatus = B_OK;
}
Port::~Port()
{
Close();
delete[] fBuffer;
}
void
Port::Close()
{
if (fInitStatus != B_OK)
return;
fInitStatus = B_NO_INIT;
if (fOwner) {
if (fInfo.owner_port >= 0)
delete_port(fInfo.owner_port);
if (fInfo.client_port >= 0)
delete_port(fInfo.client_port);
}
fInfo.owner_port = -1;
fInfo.client_port = -1;
}
status_t
Port::InitCheck() const
{
return fInitStatus;
}
const Port::Info*
Port::GetInfo() const
{
return &fInfo;
}
void
Port::Reserve(int32 endOffset)
{
if (endOffset > fReservedSize)
fReservedSize = endOffset;
}
void
Port::Unreserve(int32 endOffset)
{
if (endOffset < fReservedSize)
fReservedSize = endOffset;
}
status_t
Port::Send(const void* message, int32 size)
{
if (fInitStatus != B_OK)
return fInitStatus;
if (size <= 0)
return B_BAD_VALUE;
port_id port = (fOwner ? fInfo.client_port : fInfo.owner_port);
status_t error;
do {
error = write_port(port, 0, message, size);
} while (error == B_INTERRUPTED);
return (fInitStatus = error);
}
status_t
Port::Receive(void** _message, size_t* _size, bigtime_t timeout)
{
if (fInitStatus != B_OK)
return fInitStatus;
uint32 timeoutFlags = 0;
if (timeout < 0) {
timeout = 0;
} else if (timeout == 0) {
timeoutFlags = B_RELATIVE_TIMEOUT;
} else if (timeout >= 0) {
timeout += system_time();
timeoutFlags = B_ABSOLUTE_TIMEOUT;
}
port_id port = (fOwner ? fInfo.owner_port : fInfo.client_port);
status_t error = B_OK;
ssize_t bufferSize;
do {
bufferSize = port_buffer_size_etc(port, timeoutFlags, timeout);
if (bufferSize < 0)
error = bufferSize;
} while (error == B_INTERRUPTED);
if (error == B_TIMED_OUT || error == B_WOULD_BLOCK)
return error;
if (error != B_OK)
return (fInitStatus = error);
void* message = malloc(bufferSize);
if (message == NULL)
return (fInitStatus = B_NO_MEMORY);
MemoryDeleter messageDeleter(message);
int32 code;
ssize_t bytesRead = read_port_etc(port, &code, message, bufferSize,
B_RELATIVE_TIMEOUT, 0);
if (bytesRead < 0)
return fInitStatus = bytesRead;
if (bytesRead != bufferSize)
return fInitStatus = B_BAD_DATA;
messageDeleter.Detach();
*_message = message;
*_size = bytesRead;
return B_OK;
}