blob: aa16f29f453cde5b57dd063e3a91ecaf039ee614 [file] [log] [blame]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* etchflexbuf.c
* flex buffer
*/
#include "etch_flexbuffer.h"
#include "etch_objecttypes.h"
#include "etch_log.h"
#include "etch_mem.h"
#define ETCH_INIT_FLEXBUFSIZE 2048
#define ETCH_MAX_FLEXBUFSIZE (4*1024*1024)
static void etch_flexbuf_fix_length (etch_flexbuffer*);
static int etch_flexbuf_ensure_size (etch_flexbuffer*, size_t);
/*
* java binding syntax is confusing and ambiguous so we change it a bit. java
* calls the data region in the buffer "length". since "length" is non-specific,
* and there are other "length"s, such as "buffer.length", and "object.length",
* we'll call it "datalen", to mean "meaningful bytes within the buffer". java
* also uses "buffer.length", which is the size of the allocated buffer. c of
* course does not associate properties with arrays, so we carry an additional
* property "bufsize" to indicate the allocated length of the byte array.
*/
/**
* new_flexbuffer()
* etch_flexbuffer constructor
*/
etch_flexbuffer *new_flexbuffer (size_t bufsize)
{
void *buf;
if (bufsize <= 0 || bufsize > ETCH_MAX_FLEXBUFSIZE) bufsize = ETCH_INIT_FLEXBUFSIZE;
buf = etch_malloc(bufsize, ETCHTYPEB_BYTES);
memset(buf, 0, bufsize);
return etch_flexbuf_create_bi (buf, bufsize, 0, 0);
}
/**
* new_flexwriter_from()
* etch_flexbuffer constructor with index set to write
*/
etch_flexbuffer *new_flexwriter_from (void *buf, size_t datalen, size_t bufsize)
{
return new_flexbuffer_from (buf, datalen, bufsize, datalen);
}
/**
* new_flexbuffer_from()
* etch_flexbuffer constructor.
* @param buf data which is to become the internal buffer. caller relinquishes this memory.
* @param datalen length of real data in bytes.
* @param bufsize size of buffer in bytes.
* @param buffer index at which to start.
*/
etch_flexbuffer *new_flexbuffer_from (void* buf, size_t datalen, size_t bufsize, size_t index)
{
if (bufsize <= 0 || bufsize > ETCH_MAX_FLEXBUFSIZE) bufsize = ETCH_INIT_FLEXBUFSIZE;
if (datalen > bufsize || (!buf && datalen)) return NULL;
if (NULL == buf)
{ buf = etch_malloc(bufsize, ETCHTYPEB_BYTES);
memset(buf, 0, bufsize);
}
return etch_flexbuf_create_bi (buf, bufsize, datalen, index);
}
/**
* etch_flexbuf_create_b()
* create a flex buffer out of an existing buffer, ready to read to,
* using specified size and an index of zero.
*
* @param buf the existing buffer.
* @param bufsize the buffer size.
* @param datalen the data length in the buffer
*
* @return the created and initialized the flex buffer.
*
*/
etch_flexbuffer *etch_flexbuf_create_b (void *buf, size_t bufsize, size_t datalen)
{
return etch_flexbuf_create_bi(buf, bufsize, datalen, 0);
}
/**
* etch_flexbuf_create_bi()
* create a flex buffer out of an existing buffer, ready to read with specified
* index and size.
* @param buf the existing buffer.
* @param bufsize the buffer size.
* @param datalen the data length in the buffer
* @param index current position of the buffer.
*/
etch_flexbuffer *etch_flexbuf_create_bi(void *buf, size_t bufsize, size_t datalen, size_t index)
{
etch_flexbuffer *fbuf = (etch_flexbuffer*) new_object(sizeof(etch_flexbuffer), ETCHTYPEB_FLEXBUF, CLASSID_FLEXBUF);
((etch_object*)fbuf)->destroy = destroy_etch_flexbuffer;
fbuf->buf = buf;
fbuf->bufsize = bufsize;
fbuf->datalen = datalen;
fbuf->index = index;
fbuf->is_littleendian = IS_ETCH_TRANSPORT_LITTLEENDIAN;
return fbuf;
}
/**
* etch_flexbuf_get_buffer()
* @return the current byte array. might change if any operation
* needs to extend length past the end of the array.
*/
byte* etch_flexbuf_get_buffer (etch_flexbuffer *fbuf)
{
return fbuf->buf;
}
/**
* etch_flexbuf_clear()
* zero fill the internal buffer
*/
void etch_flexbuf_clear (etch_flexbuffer *fbuf)
{
memset (fbuf->buf, 0, fbuf->bufsize);
}
/**
* etch_flexbuf_ensure_size().
* verify sufficient buffer capacity, reallocating if necessary.
* @return boolean value indicating if buffer is as requested.
*/
static int etch_flexbuf_ensure_size (etch_flexbuffer *fbuf, size_t reqsize)
{
byte *newbuf = NULL;
size_t newsize = fbuf->bufsize;
if (reqsize < ETCH_INIT_FLEXBUFSIZE)
reqsize = ETCH_INIT_FLEXBUFSIZE;
if (reqsize <= newsize) return TRUE;
if (reqsize > ETCH_MAX_FLEXBUFSIZE) return FALSE;
while(reqsize > newsize) newsize += ETCH_INIT_FLEXBUFSIZE;
newbuf = etch_realloc (fbuf->buf, newsize, 0);
fbuf->buf = newbuf;
fbuf->bufsize = newsize;
return TRUE;
}
/**
* destroy_etch_flexbuffer()
* etch_flexbuffer destructor
*/
int destroy_etch_flexbuffer(void* obj)
{
etch_flexbuffer* fbuf = (etch_flexbuffer*)obj;
if (fbuf == NULL) return 0;
if (!is_etchobj_static_content(fbuf))
etch_free(fbuf->buf); /* OK if null */
destroy_objectex((etch_object*) fbuf);
return 0;
}
/**
* sets a new data length. If the index is larger than new length,
* the index is set to the new length as well.
* the method name was retained from java, however it is more properly
* set_data_length, since as noted previously, "length" is ambiguous,
* referring to data length, not to allocated bytes.
*/
int etch_flexbuf_set_length (etch_flexbuffer *fbuf, size_t new_datalen)
{
int result = 0;
if (new_datalen >= 0 && etch_flexbuf_ensure_size(fbuf, new_datalen))
{
fbuf->datalen = new_datalen;
if (fbuf->index > new_datalen)
fbuf->index = new_datalen;
}
else result = -1;
return result;
}
/**
* sets a new buffer index. the index must be greater than 0 and less
* than the buffer size
* @param fbuf the buffer pointer to be set.
* @param index the new index for the buffer.
*/
int etch_flexbuf_set_index (etch_flexbuffer *fbuf, size_t index)
{
if (index < 0 || index > fbuf->datalen) return -1;
fbuf->index = index;
return 0;
}
/**
* get the number of bytes available in the buffer.
* @return the available space in the buffer.
*/
size_t etch_flexbuf_avail (etch_flexbuffer *fbuf)
{
return (fbuf->datalen - fbuf->index);
}
/**
* etch_flexbuffer_reset_to()
* set index to zero and length to specified length.
*/
int etch_flexbuffer_reset_to (etch_flexbuffer *fbuf, size_t new_datalen)
{
const int result = etch_flexbuf_set_length (fbuf, new_datalen);
fbuf->index = 0;
return result;
}
/**
* etch_flexbuf_reset()
* set index and length to zero, same as etch_flexbuf_set_index(buf, 0)
*/
etch_flexbuffer *etch_flexbuf_reset (etch_flexbuffer *fbuf)
{
fbuf->index = fbuf->datalen = 0;
return fbuf;
}
/**
* Compacts the buffer by moving remaining data (from index to length)
* to the front of the buffer. Sets index to 0, and sets length to
* avail (before index was changed).
* @return this flex buffer object.
*/
etch_flexbuffer *etch_flexbuf_compact(etch_flexbuffer *fbuf)
{
size_t curlen;
if (fbuf->index == 0) return fbuf;
if (0 == (curlen = etch_flexbuf_avail(fbuf)))
{
etch_flexbuf_set_length(fbuf, 0);
return fbuf;
}
memmove(fbuf->buf, fbuf->buf+fbuf->index, curlen);
fbuf->index = 0;
fbuf->datalen = curlen;
return fbuf;
}
/**
* Copies data from the internal buffer to buf.
*
* @param fbuf the flex buffer to get data from.
* @param buf a buffer to receive the data. At most
* min( len, avail() ) bytes are transferred, starting
* at off.
* @param off the index in buf to receive the data.
* Off must be >= 0 && <= buf.datalen.
* @param len the max amount of data to transfer. Len
* must be >= 0 and <= buf.datalen - off.
* @return the amount of data transferred.
*/
size_t etch_flexbuf_get(etch_flexbuffer *fbuf, byte *buf, size_t off, size_t len)
{
size_t bytecount = 0, avail = etch_flexbuf_avail(fbuf);
if (len <= 0 || NULL == buf) return 0;
bytecount = len < avail ? len : avail ;
/* changed 6/16 to add index */
memcpy(buf + off, fbuf->buf + fbuf->index + off, bytecount);
fbuf->index += bytecount;
return bytecount;
}
/**
* etch_flexbuf_get_allfrom()
* return buffer contents starting at index, in a byte vector, caller owns returned memory
*/
byte* etch_flexbuf_get_allfrom(etch_flexbuffer* fbuf, size_t index, size_t* out_count)
{
byte* newbuf = 0;
size_t bytecount = 0;
if (index < 0) index = fbuf->index;
if (index > fbuf->datalen) index = fbuf->datalen;
fbuf->index = index; /* 6/16 so etch_flexbuf_get can skip index */
bytecount = fbuf->datalen - index;
newbuf = etch_malloc(bytecount, ETCHTYPEB_BYTES);
bytecount = etch_flexbuf_get(fbuf, newbuf, 0, bytecount);
if (out_count) *out_count = bytecount;
return newbuf;
}
/**
* etch_flexbuf_get_all()
* return buffer contents in a byte array, caller owns memory
*/
byte* etch_flexbuf_get_all(etch_flexbuffer* fbuf, size_t* out_count)
{
return etch_flexbuf_get_allfrom(fbuf, fbuf->index, out_count);
}
/**
* return next byte in buffer
*/
int etch_flexbuf_get_byte(etch_flexbuffer *fbuf, byte* out)
{
if (etch_flexbuf_avail(fbuf) < sizeof(byte)) return -1;
*out = fbuf->buf[fbuf->index++];
return 0;
}
/**
* etch_flexbuf_get_short()
* @return a short composed from the next 2 bytes.
*/
int etch_flexbuf_get_short(etch_flexbuffer *fbuf, short* out)
{
int value, svalue;
if (etch_flexbuf_avail(fbuf) < sizeof(short)) return -1;
if (fbuf->is_littleendian)
{
value = fbuf->buf[fbuf->index++] & 255;
svalue = (short) (value + ((fbuf->buf[fbuf->index++] & 255) << 8));
}
else
{ value = fbuf->buf[fbuf->index++]; /* big endian */
svalue = (short) ((value << 8) + (fbuf->buf[fbuf->index++] & 255));
}
*out = svalue;
return 0;
}
/**
* etch_flexbuf_get_int()
* @return an int composed from the next 4 bytes.
*/
int etch_flexbuf_get_int(etch_flexbuffer *fbuf, int32* out)
{
int32 value = 0;
size_t bytes_avail = 0;
bytes_avail = etch_flexbuf_avail(fbuf);
if(bytes_avail < sizeof(int32))
{
return -1;
}
if(fbuf->is_littleendian)
{
value = value | fbuf->buf[fbuf->index+0] << 0;
value = value | fbuf->buf[fbuf->index+1] << 8;
value = value | fbuf->buf[fbuf->index+2] << 16;
value = value | fbuf->buf[fbuf->index+3] << 24;
fbuf->index += sizeof(int32);
}
else
{
value = value | fbuf->buf[fbuf->index+3] << 0;
value = value | fbuf->buf[fbuf->index+2] << 8;
value = value | fbuf->buf[fbuf->index+1] << 16;
value = value | fbuf->buf[fbuf->index+0] << 24;
fbuf->index += sizeof(int32);
}
*out = value;
return 0;
}
/**
* etch_flexbuf_get_long()
* @return a long composed from the next 8 bytes.
* note jim are we taking sign into consideration on reversal?
*/
int etch_flexbuf_get_long(etch_flexbuffer *fbuf, int64* out)
{
int64 value = 0;
size_t bytes_avail = 0;
bytes_avail = etch_flexbuf_avail(fbuf);
if(bytes_avail < sizeof(int64))
{
return -1;
}
if (fbuf->is_littleendian)
{
// little endian
value = value | (uint64)fbuf->buf[fbuf->index+0] << 0;
value = value | (uint64)fbuf->buf[fbuf->index+1] << 8;
value = value | (uint64)fbuf->buf[fbuf->index+2] << 16;
value = value | (uint64)fbuf->buf[fbuf->index+3] << 24;
value = value | (int64)fbuf->buf[fbuf->index+4] << 32;
value = value | (int64)fbuf->buf[fbuf->index+5] << 40;
value = value | (int64)fbuf->buf[fbuf->index+6] << 48;
value = value | (int64)fbuf->buf[fbuf->index+7] << 56;
fbuf->index += sizeof(int64);
}
else
{
// big endian
value = value | (uint64)fbuf->buf[fbuf->index+7] << 0;
value = value | (uint64)fbuf->buf[fbuf->index+6] << 8;
value = value | (uint64)fbuf->buf[fbuf->index+5] << 16;
value = value | (uint64)fbuf->buf[fbuf->index+4] << 24;
value = value | (uint64)fbuf->buf[fbuf->index+3] << 32;
value = value | (uint64)fbuf->buf[fbuf->index+2] << 40;
value = value | (uint64)fbuf->buf[fbuf->index+1] << 48;
value = value | (uint64)fbuf->buf[fbuf->index+0] << 56;
fbuf->index += sizeof(int64);
}
*out = value;
return 0;
}
/**
* etch_flexbuf_get_float()
* @return a float from the next available bytes.
*/
int etch_flexbuf_get_float(etch_flexbuffer *fbuf, float* out)
{
float value;
if (-1 == etch_flexbuf_get_int(fbuf, (int*) &value)) return -1;
*out = value;
return 0;
}
/**
* etch_flexbuf_get_double()
* @return a double from the next available bytes.
*/
int etch_flexbuf_get_double(etch_flexbuffer *fbuf, double* out)
{
double value;
if (-1 == etch_flexbuf_get_long(fbuf, (int64*) &value)) return -1;
*out = value;
return 0;
}
/**
* fills a buffer fully from the next available bytes.
* @param b
* @throws IOException if avail() < b.datalen.
*/
size_t etch_flexbuf_get_fully( etch_flexbuffer *fbuf, byte *b, size_t bufsize )
{
return etch_flexbuf_get( fbuf, b, 0, bufsize );
}
/**
* If index has moved past length during a put, then adjust length
* to track index.
*/
static void etch_flexbuf_fix_length(etch_flexbuffer *fbuf)
{
if (fbuf->index > fbuf->datalen)
fbuf->datalen = fbuf->index;
}
/**
* Puts some bytes into the buffer as if by repeated calls to put().
* @param buf the source of the bytes to put.
* @param off the index to the first byte to put.
* @param bytecount the number of bytes to put.
* @return count of bytes put
*/
size_t etch_flexbuf_put (etch_flexbuffer *fbuf, byte *buf, size_t off, size_t bytecount)
{
if (bytecount <= 0) return 0;
if (!etch_flexbuf_ensure_size (fbuf, fbuf->index + bytecount)) return 0;
memcpy(fbuf->buf + fbuf->index, buf + off, bytecount);
fbuf->index += bytecount;
etch_flexbuf_fix_length (fbuf);
return bytecount;
}
/**
* Copies the specified number of bytes from buf[index] into buffer
* as if by repeated execution of put( buf.get() ).
* @param buf the source of the bytes to put.
* @param len the number of bytes to put. if -1, copy everything.
* @return number of bytes put
*/
size_t etch_flexbuf_put_from(etch_flexbuffer *dstfb, etch_flexbuffer *srcfb, size_t bytecount)
{
size_t bytes_put = 0;
if (bytecount == -1)
bytecount = srcfb->datalen - srcfb->index;
bytes_put = etch_flexbuf_put (dstfb, srcfb->buf, srcfb->index, bytecount);
etch_flexbuf_skip (srcfb, bytes_put, FALSE);
return bytes_put;
}
/**
* etch_flexbuf_put_byte()
* @return number of bytes put
*/
size_t etch_flexbuf_put_byte (etch_flexbuffer *fbuf, byte value)
{
size_t bytes_put = 0;
if (etch_flexbuf_ensure_size(fbuf, fbuf->index + 1))
{
fbuf->buf[fbuf->index++] = value;
etch_flexbuf_fix_length(fbuf);
bytes_put = sizeof(value);
}
return bytes_put;
}
/**
* etch_flexbuf_put_short()
*/
size_t etch_flexbuf_put_short(etch_flexbuffer *fbuf, short value)
{
if (!etch_flexbuf_ensure_size( fbuf, fbuf->index + sizeof(value) ))
return 0;
if (fbuf->is_littleendian)
{
fbuf->buf[fbuf->index++] = (byte) value;
fbuf->buf[fbuf->index++] = (byte) (value >> 8);
}
else
{
fbuf->buf[fbuf->index++] = (byte) (((unsigned short)value) >> 8);
fbuf->buf[fbuf->index++] = (byte) value;
}
etch_flexbuf_fix_length(fbuf);
return sizeof(value);
}
/**
* etch_flexbuf_put_int()
*/
size_t etch_flexbuf_put_int( etch_flexbuffer *fbuf, int value )
{
if (!etch_flexbuf_ensure_size( fbuf, fbuf->index + sizeof(int) ))
return 0;
if (fbuf->is_littleendian)
{
fbuf->buf[fbuf->index++] = (byte) value;
fbuf->buf[fbuf->index++] = (byte) (value >> 8);
fbuf->buf[fbuf->index++] = (byte) (value >> 16);
fbuf->buf[fbuf->index++] = (byte) (value >> 24);
}
else
{
fbuf->buf[fbuf->index++] = (byte) ( ((unsigned int) value) >> 24);
fbuf->buf[fbuf->index++] = (byte) ( ((unsigned int) value) >> 16);
fbuf->buf[fbuf->index++] = (byte) ( ((unsigned int) value) >> 8);
fbuf->buf[fbuf->index++] = (byte) value;
}
etch_flexbuf_fix_length(fbuf);
return sizeof(value);
}
/**
* etch_flexbuf_put_long()
*/
size_t etch_flexbuf_put_long(etch_flexbuffer *fbuf, int64 value )
{
if (!etch_flexbuf_ensure_size( fbuf, fbuf->index + sizeof(value) ))
return 0;
if (fbuf->is_littleendian)
{
fbuf->buf[fbuf->index++] = (byte) value;
fbuf->buf[fbuf->index++] = (byte) (value >> 8);
fbuf->buf[fbuf->index++] = (byte) (value >> 16);
fbuf->buf[fbuf->index++] = (byte) (value >> 24);
fbuf->buf[fbuf->index++] = (byte) (value >> 32);
fbuf->buf[fbuf->index++] = (byte) (value >> 40);
fbuf->buf[fbuf->index++] = (byte) (value >> 48);
fbuf->buf[fbuf->index++] = (byte) (value >> 56);
}
else
{
fbuf->buf[fbuf->index++] = (byte) ( ((uint64) value ) >> 56);
fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 48);
fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 40);
fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 32);
fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 24);
fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 16);
fbuf->buf[fbuf->index++] = (byte) (( (uint64) value ) >> 8);
fbuf->buf[fbuf->index++] = (byte) value;
}
etch_flexbuf_fix_length(fbuf);
return sizeof(value);
}
/**
* etch_flexbuf_put_float()
*/
size_t etch_flexbuf_put_float( etch_flexbuffer *fbuf, float value )
{
/* return etch_flexbuf_put_int( fbuf, * ((int *)( &value )) ); */
const unsigned int u = * ( (int*)&value );
return etch_flexbuf_put_int(fbuf, u);
}
/**
* etch_flexbuf_put_double()
*/
size_t etch_flexbuf_put_double(etch_flexbuffer *fbuf, double value )
{
/* return etch_flexbuf_put_long( fbuf, *( (int64*) &value ) ); */
const int64 u = * ( (int64*)&value );
return etch_flexbuf_put_long(fbuf, u);
}
/**
* Adjusts index as if by a get or put but without transferring
* any data. This could be used to skip over a data item in an
* input or output buffer.
*
* @param len the number of bytes to skip over. Len must be
* >= 0. If put is false, it is an error if len > avail().
* If put is true, the buffer may be extended (and the buffer
* length adjusted).
*
* @param put if true it is ok to extend the length of the
* buffer.
*
* @return this flex buffer object.
*
* @throws EOFException if put is false and len > avail().
*
* @throws IOException if the max buffer size is exceeded.
*/
etch_flexbuffer *etch_flexbuf_skip(etch_flexbuffer *fbuf, size_t len, int put )
{
if (len < 0) return NULL;
if (len == 0) return fbuf;
if (put)
{
etch_flexbuf_ensure_size( fbuf, fbuf->index+len );
fbuf->index += len;
etch_flexbuf_fix_length(fbuf);
return fbuf;
}
fbuf->index += len;
return fbuf;
}