root/src/kits/network/libnetapi/NetBuffer.cpp
/*
 * Copyright 2009, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *                              Scott T. Mansfield, thephantom@mac.com
 *              Bruno Albuquerque, bga@bug-br.org.br
 */

#include <ByteOrder.h>
#include <Message.h>
#include <TypeConstants.h>

#include "DynamicBuffer.h"
#include "NetBuffer.h"

#include <new>
#include <string.h>

BNetBuffer::BNetBuffer(size_t size) :
        BArchivable(),
        fInit(B_NO_INIT)
{
        fImpl = new (std::nothrow) DynamicBuffer(size);
        if (fImpl != NULL)
                fInit = fImpl->InitCheck();
}


BNetBuffer::~BNetBuffer()
{
        delete fImpl;
}


BNetBuffer::BNetBuffer(const BNetBuffer& buffer) :
        BArchivable(),
        fInit(B_NO_INIT)
{
        fImpl = new (std::nothrow) DynamicBuffer(*buffer.GetImpl());
        if (fImpl != NULL)
                fInit = fImpl->InitCheck();
}


BNetBuffer::BNetBuffer(BMessage* archive) :
        BArchivable(),
        fInit(B_NO_INIT)
{
        const unsigned char* bufferPtr;
        ssize_t bufferSize;

        if (archive->FindData("buffer", B_RAW_TYPE, (const void**)&bufferPtr,
                &bufferSize) == B_OK) {
                fImpl = new (std::nothrow) DynamicBuffer(bufferSize);
                if (fImpl != NULL) {
                        ssize_t result = fImpl->Write(bufferPtr, bufferSize);
                        if (result >= 0)
                                fInit = fImpl->InitCheck();
                        else
                                fInit = result;
                }
        }
}

BNetBuffer&
BNetBuffer::operator=(const BNetBuffer& buffer)
{
        if (&buffer != this) {
                delete fImpl;

                fImpl = new (std::nothrow) DynamicBuffer(*buffer.GetImpl());
                if (fImpl != NULL)
                        fInit = fImpl->InitCheck();
        }
        return *this;
}


status_t
BNetBuffer::Archive(BMessage* into, bool deep) const
{
        if (fInit != B_OK)
                return B_NO_INIT;

        status_t result = into->AddData("buffer", B_RAW_TYPE, fImpl->Data(),
                fImpl->BytesRemaining());

        return result;
}


BArchivable*
BNetBuffer::Instantiate(BMessage* archive)
{
    if (!validate_instantiation(archive, "BNetBuffer"))
        return NULL;

    BNetBuffer* buffer = new (std::nothrow) BNetBuffer(archive);
    if (buffer == NULL)
        return NULL;

    if (buffer->InitCheck() != B_OK) {
        delete buffer;
        return NULL;
    }

    return buffer;
}


status_t
BNetBuffer::InitCheck()
{
        return fInit;
}


status_t
BNetBuffer::AppendInt8(int8 data)
{
        return AppendData((const void*)&data, sizeof(int8));
}


status_t
BNetBuffer::AppendUint8(uint8 data)
{
        return AppendData((const void*)&data, sizeof(int8));
}


status_t
BNetBuffer::AppendInt16(int16 data)
{
        int16 be_data = B_HOST_TO_BENDIAN_INT16(data);
        return AppendData((const void*)&be_data, sizeof(int16));
}


status_t
BNetBuffer::AppendUint16(uint16 data)
{
        uint16 be_data = B_HOST_TO_BENDIAN_INT16(data);
        return AppendData((const void*)&be_data, sizeof(uint16));
}


status_t
BNetBuffer::AppendInt32(int32 data)
{
        int32 be_data = B_HOST_TO_BENDIAN_INT32(data);
        return AppendData((const void*)&be_data, sizeof(int32));
}


status_t
BNetBuffer::AppendUint32(uint32 data)
{
        uint32 be_data = B_HOST_TO_BENDIAN_INT32(data);
        return AppendData((const void*)&be_data, sizeof(uint32));
}


status_t
BNetBuffer::AppendFloat(float data)
{
        return AppendData((const void*)&data, sizeof(float));
}


status_t
BNetBuffer::AppendDouble(double data)
{
        return AppendData((const void*)&data, sizeof(double));
}


status_t
BNetBuffer::AppendString(const char* data)
{
        return AppendData((const void*)data, strlen(data) + 1);
}


status_t
BNetBuffer::AppendData(const void* data, size_t size)
{
        if (fInit != B_OK)
                return B_NO_INIT;

        ssize_t bytesWritten = fImpl->Write(data, size);
        if (bytesWritten < 0)
                return (status_t)bytesWritten;
        return (size_t)bytesWritten == size ? B_OK : B_ERROR;
}


#define STACK_BUFFER_SIZE 2048

status_t
BNetBuffer::AppendMessage(const BMessage& data)
{
        char stackFlattenedData[STACK_BUFFER_SIZE];

        ssize_t dataSize = data.FlattenedSize();

        if (dataSize < 0)
                return dataSize;

        if (dataSize == 0)
                return B_ERROR;

        status_t result = B_OK;

        if (dataSize > STACK_BUFFER_SIZE) {
                char* flattenedData = new (std::nothrow) char[dataSize];
                if (flattenedData == NULL)
                        return B_NO_MEMORY;

                if (data.Flatten(flattenedData, dataSize) == B_OK)
                        result = AppendData((const void*)&flattenedData, dataSize);

                delete[] flattenedData;
        } else {
                if (data.Flatten(stackFlattenedData, dataSize) == B_OK)
                        result = AppendData((const void*)&stackFlattenedData, dataSize);
        }

        return result;
}


status_t
BNetBuffer::AppendInt64(int64 data)
{
        int64 be_data = B_HOST_TO_BENDIAN_INT64(data);
        return AppendData((const void*)&be_data, sizeof(int64));
}


status_t
BNetBuffer::AppendUint64(uint64 data)
{
        uint64 be_data = B_HOST_TO_BENDIAN_INT64(data);
        return AppendData((const void*)&be_data, sizeof(uint64));
}


status_t
BNetBuffer::RemoveInt8(int8& data)
{
        return RemoveData((void*)&data, sizeof(int8));
}


status_t
BNetBuffer::RemoveUint8(uint8& data)
{
        return RemoveData((void*)&data, sizeof(uint8));
}


status_t
BNetBuffer::RemoveInt16(int16& data)
{
        int16 be_data;
        status_t result = RemoveData((void*)&be_data, sizeof(int16));
        if (result != B_OK)
                return result;

        data = B_BENDIAN_TO_HOST_INT16(be_data);

        return B_OK;
}


status_t
BNetBuffer::RemoveUint16(uint16& data)
{
        uint16 be_data;
        status_t result = RemoveData((void*)&be_data, sizeof(uint16));
        if (result != B_OK)
                return result;

        data = B_BENDIAN_TO_HOST_INT16(be_data);

        return B_OK;
}


status_t
BNetBuffer::RemoveInt32(int32& data)
{
        int32 be_data;
        status_t result = RemoveData((void*)&be_data, sizeof(int32));
        if (result != B_OK)
                return result;

        data = B_BENDIAN_TO_HOST_INT32(be_data);

        return B_OK;
}


status_t
BNetBuffer::RemoveUint32(uint32& data)
{
        uint32 be_data;
        status_t result = RemoveData((void*)&be_data, sizeof(uint32));
        if (result != B_OK)
                return result;

        data = B_BENDIAN_TO_HOST_INT32(be_data);

        return B_OK;
}


status_t
BNetBuffer::RemoveFloat(float& data)
{
        return RemoveData((void*)&data, sizeof(float));
}


status_t
BNetBuffer::RemoveDouble(double& data)
{
        return RemoveData((void*)&data, sizeof(double));
}


status_t
BNetBuffer::RemoveString(char* data, size_t size)
{
        // TODO(bga): Should we do anything specific to handle the terminating
        // NULL byte?
        return RemoveData((void*)data, size);
}


status_t
BNetBuffer::RemoveData(void* data, size_t size)
{
        if (fInit != B_OK)
                return B_NO_INIT;

        ssize_t bytesRead = fImpl->Read(data, size);
        if (bytesRead < 0)
                return (status_t)bytesRead;
        return (size_t)bytesRead == size ? B_OK : B_BUFFER_OVERFLOW;
}


status_t
BNetBuffer::RemoveMessage(BMessage& data)
{
        if (fInit != B_OK)
                return B_NO_INIT;

        unsigned char* bufferPtr = fImpl->Data();

        if (*(int32*)bufferPtr != B_MESSAGE_TYPE)
                return B_ERROR;

        bufferPtr += sizeof(int32);
        int32 dataSize = *(int32*)bufferPtr;

        char* flattenedData = new (std::nothrow) char[dataSize];
        if (flattenedData == NULL)
                return B_NO_MEMORY;

        status_t result = RemoveData(flattenedData, dataSize);
        if (result == B_OK)
                result = data.Unflatten(flattenedData);

        delete[] flattenedData;

        return result;
}


status_t
BNetBuffer::RemoveInt64(int64& data)
{
        int64 be_data;
        status_t result = RemoveData((void*)&be_data, sizeof(int64));
        if (result != B_OK)
                return result;

        data = B_BENDIAN_TO_HOST_INT64(be_data);

        return B_OK;
}


status_t
BNetBuffer::RemoveUint64(uint64& data)
{
        uint64 be_data;
        status_t result = RemoveData((void*)&be_data, sizeof(uint64));
        if (result != B_OK)
                return result;

        data = B_BENDIAN_TO_HOST_INT64(be_data);

        return B_OK;
}


unsigned char*
BNetBuffer::Data() const
{
        if (fInit != B_OK)
                return NULL;

        return fImpl->Data();
}


size_t
BNetBuffer::Size() const
{
        if (fInit != B_OK)
                return 0;

        return fImpl->Size();
}


size_t
BNetBuffer::BytesRemaining() const
{
        if (fInit != B_OK)
                return 0;

        return fImpl->BytesRemaining();
}


void
BNetBuffer::_ReservedBNetBufferFBCCruft1()
{
}


void
BNetBuffer::_ReservedBNetBufferFBCCruft2()
{
}


void
BNetBuffer::_ReservedBNetBufferFBCCruft3()
{
}


void
BNetBuffer::_ReservedBNetBufferFBCCruft4()
{
}


void
BNetBuffer::_ReservedBNetBufferFBCCruft5()
{
}


void
BNetBuffer::_ReservedBNetBufferFBCCruft6()
{
}