/*=--------------------------------------------------------------------------=*
 * NetBuffer.cpp -- Implementation of the BNetBuffer class.
 *
 * Written by S.T. Mansfield (thephantom@mac.com)
 *
 * Remarks:
 *   * This is basically a fancy-schmancy FIFO stack manager.  Thusly, it is
 *     considered an error if data is not popped off a particular instance's
 *     stack in the order it was pushed on.
 *   * We could possibly implement this class as a thin wrapper around the
 *     BMessage class, but I'm not sure what kind of payload the latter class
 *     brings to the party, so we'll do our own stack management.  We also
 *     cannot be sure that BMessage behaves as described above down the road.
 *   * Never use hot wax to soothe enraged lobsters.
 *=--------------------------------------------------------------------------=*
 * Copyright (c) 2002, The OpenBeOS project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *=--------------------------------------------------------------------------=*
 */


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

#include <NetBuffer.h>

/*
 * Parametric ctor initialization list defaults.
 */
#define CTOR_INIT_LIST \
    BArchivable( ), \
    m_init( B_NO_INIT ), \
    fData( NULL ), \
    fDataSize( 0 ), \
    fStackSize( 0 ), \
    fCapacity( 0 )


/*
 * Markers that surround a record in an instance's stack.
 */
#define DATA_START_MARKER   0x01c0ffee
#define DATA_END_MARKER     0xbeefeeee


/* BNetBuffer
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Class ctor.
 *
 * Input parameter:
 *     size         : Initial size of buffer (default is zero).
 *
 * Remarks:
 *     This bad boy doubles up as the defalt ctor.
 */
BNetBuffer::BNetBuffer( size_t size )
           : CTOR_INIT_LIST
{
    m_init = ( resize( size ) == B_OK ) ? B_OK : B_NO_INIT;
}


/* BNetBuffer
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Class copy ctor.
 *
 * Input parameter:
 *     refparam     : Instance to copy.
 */
BNetBuffer::BNetBuffer( const BNetBuffer& refparam )
            : CTOR_INIT_LIST
{
    m_init = ( clone( refparam ) == B_OK ) ? B_OK : B_NO_INIT;
}


/* BNetBuffer
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Ctor to instantiate from serialized data (BMessage).
 *
 * Input parameter:
 *     archive      : Serialized object to instantiate from.
 */
BNetBuffer::BNetBuffer( BMessage* archive )
           : CTOR_INIT_LIST
{
    const unsigned char* msgDataPtr;
    ssize_t msgNBytes;

    int32  dataSize;
    int32  stackSize;
    int32  capacity;

    if ( archive->FindInt32( "bnbuff_datasize", &dataSize ) != B_OK )
    {
        return;
    }

    if ( archive->FindInt32( "bnbuff_stacksize", &stackSize ) != B_OK )
    {
        return;
    }

    if ( archive->FindInt32( "bnbuff_capacity", &capacity ) != B_OK )
    {
        return;
    }

    if ( archive->FindData( "bnbuff_stack", B_RAW_TYPE,
                            ( const void** )&msgDataPtr, &msgNBytes ) != B_OK )
    {
        return;
    }

    if ( capacity )
    {
        if ( resize( capacity, true ) != B_OK )
        {
            return;
        }

        memcpy( fData, msgDataPtr, stackSize );
    }

    fDataSize = dataSize;
    fStackSize = stackSize;
    fCapacity = capacity;

    m_init = B_OK;
}


/* ~BNetBuffer
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Class dtor.
 */
BNetBuffer::~BNetBuffer( void )
{
    if ( fData != NULL )
    {
        delete fData;
        fData = NULL;
    }

    fDataSize = fStackSize = fCapacity = 0;

    m_init = B_NO_INIT;
}


/* operator=
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Class' assignment operator.
 *
 * Input parameter:
 *     refparam     : Instance to assign from.
 */
BNetBuffer& BNetBuffer::operator=( const BNetBuffer& refparam )
{
    m_init = clone( refparam );

    return *this;
}


/* InitCheck
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Determine whether or not this instance is properly initialized.
 *
 * Returns:
 *     B_OK if this instance is initialized, B_ERROR if not.
 */
status_t BNetBuffer::InitCheck( void )
{
    return ( m_init == B_OK ) ? B_OK : B_ERROR;
}


/* Archive
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Serialize this instance into the passed BMessage parameter.
 *
 * Input parameter:
 *     deep         : [ignored] default==true.
 *
 * Output parameter:
 *     into         : BMessage object to serialize into.
 *
 * Returns:
 *     B_OK/BERROR on success/failure.  Returns B_NO_INIT if instance not
 *     properly initialized.
 */
status_t BNetBuffer::Archive( BMessage* into, bool deep ) const
{
    if ( m_init != B_OK )
    {
        return B_NO_INIT;
    }

    if ( into->AddInt32( "bnbuff_datasize", fDataSize ) )
    {
        return B_ERROR;
    }

    if ( into->AddInt32( "bnbuff_stacksize", fStackSize ) )
    {
        return B_ERROR;
    }

    if ( into->AddInt32( "bnbuff_capacity", fCapacity ) )
    {
        return B_ERROR;
    }

    if ( into->AddData( "bnbuff_stack", B_RAW_TYPE, fData, fStackSize ) != B_OK )
    //STM: Should we store the *whole* stack instead?......^^^^^^^^^^fCapacity
    {
        return B_ERROR;
    }

    return B_OK;
}


/* Instantiate
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Un-serialize and instantiate from the passed BMessage parameter.
 *
 * Input parameter:
 *     archive      : Archived BMessage object for (de)serialization.
 *
 * Returns:
 *     NULL if a BNetBuffer instance can not be initialized, otherwise
 *     a new BNetBuffer object instantiated from the BMessage parameter.
 */
BArchivable* BNetBuffer::Instantiate( BMessage* archive )
{
    if ( !validate_instantiation( archive, "BNetBuffer" ) )
    {
        return NULL;
    }

    BNetBuffer* bnb = new BNetBuffer( archive );
    if ( bnb == NULL )
    {
        return NULL;
    }

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

    return bnb;
}


/* AppendXXX
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Append some data to this buffer (think PUSHing onto a FIFO stack).
 *
 * Returns:
 *     B_OK/B_ERROR for success/failure.  B_NO_INIT if instance not properly
 *     initialized or stuck in some unknown state during construction.
 *
 * Remarks:
 *   * Where appropriate numeric data such as (u)int16, et al, are converted
 *     to network byte order during storage, no need to do this in advance.
 */
status_t BNetBuffer::AppendInt8( int8 Data )
{
    return dpush( B_INT8_TYPE, sizeof( int8 ), &Data );
}

status_t BNetBuffer::AppendUint8( uint8 Data )
{
    return dpush( B_UINT8_TYPE, sizeof( uint8 ), &Data );
}

status_t BNetBuffer::AppendInt16( int16 Data )
{
    int16 locData = B_HOST_TO_BENDIAN_INT16( Data );
    return dpush( B_INT16_TYPE, sizeof( int16 ), &locData );
}

status_t BNetBuffer::AppendUint16( uint16 Data )
{
    uint16 locData = B_HOST_TO_BENDIAN_INT16( Data );
    return dpush( B_UINT16_TYPE, sizeof( uint16 ), &locData );
}

status_t BNetBuffer::AppendInt32( int32 Data )
{
    int32 locData = B_HOST_TO_BENDIAN_INT32( Data );
    return dpush( B_INT32_TYPE, sizeof( int32 ), &locData );
}

status_t BNetBuffer::AppendUint32( uint32 Data )
{
    uint32 locData = B_HOST_TO_BENDIAN_INT32( Data );
    return dpush( B_UINT32_TYPE, sizeof( uint32 ), &locData );
}

status_t BNetBuffer::AppendInt64( int64 Data )
{
    int64 locData = B_HOST_TO_BENDIAN_INT64( Data );
    return dpush( B_INT64_TYPE, sizeof( int64 ), &locData );
}

status_t BNetBuffer::AppendUint64( uint64 Data )
{
    uint64 locData = B_HOST_TO_BENDIAN_INT64( Data );
    return dpush( B_UINT64_TYPE, sizeof( uint64 ), &locData );
}

status_t BNetBuffer::AppendFloat( float Data )
{
    return dpush( B_FLOAT_TYPE, sizeof( float ), &Data );
}

status_t BNetBuffer::AppendDouble( double Data )
{
    return dpush( B_DOUBLE_TYPE, sizeof( double ), &Data );
}

status_t BNetBuffer::AppendString( const char* Data )
{
    return dpush( B_STRING_TYPE, strlen( Data ), Data );
}

status_t BNetBuffer::AppendData( const void* Data, size_t Length )
{
    return dpush( B_RAW_TYPE, Length, Data );
}

status_t BNetBuffer::AppendMessage( const BMessage& Msg )
{
    status_t result;
    ssize_t msgLen;
    char* msgData;

    msgLen = Msg.FlattenedSize( );
    if ( msgLen == 0 )
    {
        //STM: It's possible, but SHOULD we store an empty BMessage?
        //STM: Is an empty BMessage instance even legit in Be/OBOS?
        return B_ERROR;
    }

    if ( ( msgData = new char[msgLen] ) == NULL )
    {
        return B_ERROR; //STM: B_NO_MEM???
    }

    // Don't get tripped on this, just trying to err on the side of caution.
    result = B_ERROR;

    if ( Msg.Flatten( msgData, msgLen ) == B_OK )
    {
        result = dpush( B_MESSAGE_TYPE, msgLen, msgData );
    }

    delete msgData;
    return result;
}


/* RemoveXXXX
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Remove some data from this buffer, think POPping from a FIFO stack.
 *
 * Returns:
 *     B_OK/B_ERROR for success/failure.  B_NO_INIT if instance not properly
 *     initialized or stuck in some unknown state during construction.
 *
 * Remarks:
 *     Where appropriate numeric data such as (u)int16, et al, are converted
 *     from network byte order.  No need to do this ipso post facto.
 */
status_t BNetBuffer::RemoveInt8( int8& Data )
{
    return dpop( B_INT8_TYPE, sizeof( int8 ), &Data );
}

status_t BNetBuffer::RemoveUint8( uint8& Data )
{
    return dpop( B_UINT8_TYPE, sizeof( uint8 ), &Data );
}

status_t BNetBuffer::RemoveInt16( int16& Data )
{
    // dpop will convert this bad boy from network to host byte order.
    return dpop( B_INT16_TYPE, sizeof( int16 ), &Data );
}

status_t BNetBuffer::RemoveUint16( uint16& Data )
{
    // dpop will convert this beast from network to host byte order.
    return dpop( B_UINT16_TYPE, sizeof( uint16 ), &Data );
}

status_t BNetBuffer::RemoveInt32( int32& Data )
{
    // dpop will convert this thang from network to host byte order.
    return dpop( B_INT32_TYPE, sizeof( int32 ), &Data );
}

status_t BNetBuffer::RemoveUint32( uint32& Data )
{
    // dpop will convert this variable from network to host byte order.
    return dpop( B_UINT32_TYPE, sizeof( uint32 ), &Data );
}

status_t BNetBuffer::RemoveInt64( int64& Data )
{
    // dpop will convert this bag-o-bits from network to host byte order.
    return dpop( B_INT64_TYPE, sizeof( int64 ), &Data );
}

status_t BNetBuffer::RemoveUint64( uint64& Data )
{
    // dpop will convert this construct from network to host byte order.
    return dpop( B_UINT64_TYPE, sizeof( uint64 ), &Data );
}

status_t BNetBuffer::RemoveFloat( float& Data )
{
    return dpop( B_FLOAT_TYPE, sizeof( float ), &Data );
}

status_t BNetBuffer::RemoveDouble( double& Data )
{
    return dpop( B_DOUBLE_TYPE, sizeof( double ), &Data );
}

status_t BNetBuffer::RemoveString( char* Data, size_t Length )
{
    return dpop( B_STRING_TYPE, Length, Data );
}

status_t BNetBuffer::RemoveData( void* Data, size_t Length )
{
    return dpop( B_RAW_TYPE, Length, Data );
}

status_t BNetBuffer::RemoveMessage( BMessage& message )
{
    status_t result;
    int32 v;
    unsigned char* stackPtr = fData;
    char* msgData;

    /* 
     * We have to cheat a little bit here and peek ahead of dpop so we can
     * get at the size parameter.  Perform basic checks along the way so we
     * can make sure that we really get to the size parameter and not some
     * bogus value.  Redundant, but necessary.
     */
    v = *( int32 * )stackPtr;
    if ( v != DATA_START_MARKER )
    {
        return B_ERROR;
    }

    stackPtr += sizeof( int32 );
    v = *( int32 * )stackPtr;
    if ( v != B_MESSAGE_TYPE )
    {
        return B_ERROR;
    }

    // Snarf the data size.
    stackPtr += sizeof( int32 );
    v = *( int32 * )stackPtr;

    if ( ( msgData = new char[v] ) == NULL )
    {
        return B_ERROR; //STM: B_NO_MEM???
    }

    if ( dpop( B_MESSAGE_TYPE, v, msgData ) != B_OK )
    {
        delete msgData;
        return B_ERROR;
    }

    result = message.Unflatten( msgData );
    delete msgData;
    return result;
}


/* Data
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Class accessor.
 *
 * Returns:
 *   Pointer to class' data suitable for those functions and methods that
 *   expect some kind of "generic" pointer.
 *
 * Remarks:
 *   * This is WEAK and WRONG, as it allows non-members to directly
 *     manipulate an instance's member data without the usual checks in
 *     place.  At the very least the return value should be const.  Ideally,
 *     this method should not exist. <author steps off soapbox>
 */
unsigned char* BNetBuffer::Data( void ) const
{
    // Have I mentioned that this is a Really Bad Thing to do?
    return fData;
    // In case I missed it: returning a non-const pointer to our member data
    // is not a good thing.  Caveat emptor.
}


/* Size
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Class accessor.
 *
 * Returns:
 *     How many bytes of data are contained.  This *does not* include the
 *     overhead of the stack control variables (delineators and size).
 */
size_t BNetBuffer::Size( void ) const
{
    return fDataSize;
}


/* BytesRemaining
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Class accessor ...erm... sort of.
 *
 * Returns:
 *     How much capacity this instance has left to store data.
 *
 * Remarks:
 *     This method is essentially meaningless as the class' storage capacity
 *     will grow (or shrink) as necessary (that's an obfuscated way to say
 *     "dynamically re-allocated" boys and girls).  Do not rely on this
 *     particular method in normal use as it refers to a moving target.
 */
size_t BNetBuffer::BytesRemaining( void ) const
{
    return ( fCapacity - fStackSize );
}


/* clone
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Private copy helper method.
 *
 * Input parameter:
 *     RefParam: Instance to clone.
 *
 * Returns:
 *     status_t indicating success or point of failure (see resize()).
 *     B_NO_INIT if RefParam not properly initialized.
 */
status_t BNetBuffer::clone( const BNetBuffer& RefParam )
{
    if ( !RefParam.m_init )
    {
        return B_NO_INIT;
    }

    if ( resize( RefParam.fCapacity, true ) != B_OK )
    {
        return B_ERROR;
    }

    fDataSize = RefParam.fDataSize;
    fStackSize = RefParam.fStackSize;
    memcpy( fData, RefParam.fData, fStackSize );

    return B_OK;
}


/* dpop
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Pop some data from our stack.
 *
 * Input parameters:
 *     Type         : Expected data type for this record.
 *     Length       : Expected data size for this record.
 *
 * Output parameter:
 *     Data         : Recepticle for popped data.  You are responsible for
 *                    insuring that this is large enough to hold your
 *                    intended data.  If this parameter is NULL, you'll get
 *                    back a B_ERROR.  If the space allocated that this
 *                    parameter points to is not sufficient to contain the
 *                    intended data, you'll likely over-run something else
 *                    in user-space and piss off a few folks in the process.
 *
 * Returns:
 *     B_OK/B_ERROR on success/failure.
 *     B_NO_INIT if this instance not properly initialized.
 */
status_t BNetBuffer::dpop( int32 Type, int32 Length, void* Data )
{
    if ( m_init != B_OK )
    {
        return B_NO_INIT;
    }

    if ( Data == NULL )
    {
        return B_ERROR;
    }

    unsigned char* stackPtr = fData;
    unsigned char* userDataPtr;
    int32 int32size = sizeof( int32 ); // <-- Performance: called a lot here.
    int32 recordSize = ( ( int32size * 4 ) + Length );
    int32 tmp;

    // Validate the start marker.
    tmp = *( int32 * )stackPtr;
    if ( tmp != (int32) DATA_START_MARKER )
    {
        return B_ERROR;
    }

    // Validate the data type.
    stackPtr += int32size;
    tmp =  *( int32 * )stackPtr;
    if ( tmp != Type )
    {
        return B_ERROR;
    }

    // Validate the data size.
    stackPtr += int32size;
    tmp =  *( int32 * )stackPtr;
    if ( tmp != Length )
    {
        return B_ERROR;
    }

    // Stash a pointer to the contained user data, used when we really "pop."
    stackPtr += int32size;
    userDataPtr = stackPtr;

    // Validate the end marker.
    stackPtr += Length;
    tmp = *( int32 * )stackPtr;
    if ( tmp != (int32) DATA_END_MARKER )
    {
        return B_ERROR;
    }

    // Point to the next entry in the stack.
    stackPtr += int32size;

    // Extract the contained data.
    memcpy( Data, userDataPtr, Length );

    // Convert from network byte order if necessary.
    switch ( Type )
    {
        case B_INT16_TYPE:
        case B_UINT16_TYPE:
            *( uint16 * )Data = B_BENDIAN_TO_HOST_INT16( *( uint16 * )Data );
            break;

        case B_INT32_TYPE:
        case B_UINT32_TYPE:
            *( uint32 * )Data = B_BENDIAN_TO_HOST_INT32( *( uint32 * )Data );
            break;

        case B_INT64_TYPE:
        case B_UINT64_TYPE:
            *( uint64 * )Data = B_BENDIAN_TO_HOST_INT64( *( uint64 * )Data );
            break;

        default:
            break;
    }

    // Condense the stack.
    fStackSize -= recordSize;
    memmove( fData, stackPtr, fStackSize );

    // Zero out the unused area of the stack where we memmove'd from.
    memset( &fData[fStackSize], 0, ( fCapacity - fStackSize ) );

    // Update the extents.
    fCapacity += recordSize;
    fDataSize -= Length;

    return B_OK;
}


/* dpush
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Push some data onto our buffer.
 *
 * Input parameters:
 *     Type         : Data type for this record.
 *     Length       : Data length for this record.
 *     Data         : Pointer to data to store.  NO-OP (B_ERROR) if NULL.
 *
 * Returns:
 *     B_OK/B_ERROR on success/failure.
 *     B_NO_INIT if this instance not properly initialized.
 *
 * Remarks:
 *   * No need to worry about host-to-network byte ordering conversion --
 *     since this is a /private/ helper said conversion, where applicable,
 *     takes place in our caller (see AddIntXX if you haven't already passed
 *     them on your way down to this humble helper).
 */
status_t BNetBuffer::dpush( int32 Type, int32 Length, const void* Data )
{
    if ( m_init != B_OK )
    {
        return B_NO_INIT;
    }

    if ( Data == NULL )
    {
        return B_ERROR;
    }

    int32 int32size = sizeof( int32 ); // <--Performance: called a lot here.
    int32 recordSize = ( ( int32size * 4 ) + Length );

    if ( resize( fCapacity + recordSize ) != B_OK )
    {
        return B_ERROR;
    }

    unsigned char* stackPtr = &fData[fStackSize];
    *( int32 * )stackPtr = DATA_START_MARKER;

    stackPtr += int32size;
    *( int32 * )stackPtr = Type;

    stackPtr += int32size;
    *( int32 * )stackPtr = Length;

    stackPtr += int32size;
    memcpy( stackPtr, Data, Length );

    stackPtr += Length;
    *( int32 * )stackPtr = DATA_END_MARKER;

    // Groovy!  All stored, dot our t's and cross our i's.
    fDataSize += Length;
    fStackSize += recordSize;

    return B_OK;
}


/* resize
 *=--------------------------------------------------------------------------=*
 * Purpose:
 *     Grow the internal data buffer to accommodate the requested size.
 *
 * //STM: What about shrinking the buffer to conserve resources?
 *
 * Input parameter:
 *     NewSize      : New size of data buffer.  Duh.
 *     RegenStack   : If true, do not try to preserve the existing stack.
 *                    Default is false (preserve existing data).
 *
 * Returns:
 *     B_OK: Success.
 *     B_NO_MEMORY: if we cannot allocate memory for the data buffer.
 *
 * Remarks:
 *   * In keeping with the "contract programming" development model: this is
 *     a "safe copy" method in that the class' existing member data will not
 *     be disturbed in the event of failure.  When our caller gets back a
 *     success status (B_OK), said caller is guaranteed that all is well.
 *     Laterally, in the event this method returns failure, the caller is
 *     guaranteed that the class' existing member data is left untouched,
 *     thereby preserving the existing instance's integrity.
 *
 *   * Buffer size is adjusted on a 16-byte granularity to keep from beating
 *     the bleep out of the memory pool (can you say "fragmentation" boys and
 *     girls?).  To tweak the granularity, change the GRANULARITY token at the
 *     beginning of this method.  For best results, GRANULARITY should be a
 *     power of two, thankyouverymuch.
 *
 *   * 'RegenStack' and its intented behavior is a bit of a double-negative.
 *     May have to change this to eliminate said double negative if it proves
 *     to be too confusing.
 */
status_t BNetBuffer::resize( int32 NewSize, bool RegenStack )
{
#define GRANULARITY 16

    unsigned char* newData;
    int32 newCapacity;

    newCapacity = ( ( ( NewSize / GRANULARITY ) + 1 ) * GRANULARITY );

    // Don't waste cycles or thrash the memory pool if we don't need to...
    if ( newCapacity <= fCapacity )
    {
        return B_OK;
    }

/*  STM: If we decide to shrink as well as grow, then replace above with:
    if ( newCapacity == fCapacity )
    {
        return B_OK;
    }
*/

    newData = new unsigned char[newCapacity];
    if ( newData == NULL )
    {
        return B_NO_MEMORY;
    }

    // Paranoid?  Pffffsh.  You bet.  :-)
    memset( newData, 0, newCapacity );

    if ( fData != NULL )
    {
        if ( !RegenStack )
        {
            memcpy( newData, fData, fStackSize );
        }

        delete fData;
    }

    fData = newData;
    fCapacity = newCapacity;

    return B_OK;

#undef GRANULARITY
}


/* Reserved methods, for future evolution */

void BNetBuffer::_ReservedBNetBufferFBCCruft1()
{
}

void BNetBuffer::_ReservedBNetBufferFBCCruft2()
{
}

void BNetBuffer::_ReservedBNetBufferFBCCruft3()
{
}

void BNetBuffer::_ReservedBNetBufferFBCCruft4()
{
}

void BNetBuffer::_ReservedBNetBufferFBCCruft5()
{
}

void BNetBuffer::_ReservedBNetBufferFBCCruft6()
{
}


/*=------------------------------------------------------------------- End -=*/
