#include <config.h>
#include <string.h>
#include <sys/types.h>
#include "giop-private.h"
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
#include <orbit/GIOP/giop.h>
#include "../util/orbit-purify.h"
#define GIOP_CHUNK_ALIGN 8
#define GIOP_CHUNK_SIZE (GIOP_CHUNK_ALIGN * 256)
static gboolean giop_blank_wire_data = FALSE;
static GSList *send_buffer_list = NULL;
static GMutex *send_buffer_list_lock = NULL;
static const char giop_zero_buf [GIOP_CHUNK_ALIGN * 10] = {0};
void
giop_send_buffer_init (gboolean wipe)
{
#ifdef ORBIT_PURIFY
giop_blank_wire_data = TRUE;
#else
giop_blank_wire_data = wipe;
#endif
send_buffer_list_lock = link_mutex_new ();
}
/* Marshal it at compile time so we don't have to do it over and over. This just stores codeset info to say that
we only speak UTF-8/UTF-16 */
static const CORBA_unsigned_long iop_service_context_data [] = {
1 /* num_contexts */,
1 /* ServiceId for CodeSets */,
12 /* length of encapsulation: 4 endianness+align, 4 charset_id, 4 wcharset_id */,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
0x01010101 /* start of encapsulation */,
#else
0,
#endif
0x05010001, /* UTF-8 */
0x00010109 /* UTF-16 */
};
static const GIOP_AddressingDisposition giop_1_2_target_type = GIOP_KeyAddr;
static gboolean
giop_send_buffer_is_oneway(const GIOPSendBuffer *buf)
{
g_assert (buf);
switch (buf->giop_version) {
case GIOP_1_0:
case GIOP_1_1:
return (buf->msg.u.request_1_0.response_expected ? FALSE : TRUE);
case GIOP_1_2:
return (buf->msg.u.request_1_2.response_flags ? FALSE : TRUE);
default:
break;
}
g_assert_not_reached();
return TRUE;
}
GIOPSendBuffer *
giop_send_buffer_use_request (GIOPVersion giop_version,
CORBA_unsigned_long request_id,
CORBA_boolean response_expected,
const CORBA_sequence_CORBA_octet *objkey,
const struct iovec *operation_vec,
const struct iovec *principal_vec)
{
GIOPSendBuffer *buf = giop_send_buffer_use (giop_version);
struct iovec zerovec;
if(!principal_vec) {
zerovec.iov_base = (gpointer) giop_zero_buf;
zerovec.iov_len = sizeof (CORBA_unsigned_long);
principal_vec = &zerovec;
}
buf->msg.header.message_type = GIOP_REQUEST;
giop_send_buffer_align (buf, sizeof(CORBA_unsigned_long));
switch (giop_version) {
case GIOP_1_0:
case GIOP_1_1:
buf->msg.u.request_1_0.request_id = request_id;
buf->msg.u.request_1_0.response_expected = response_expected;
giop_send_buffer_append (buf, (const guchar *)iop_service_context_data, sizeof(iop_service_context_data));
giop_send_buffer_append (buf, &buf->msg.u.request_1_0.request_id, sizeof(CORBA_unsigned_long));
giop_send_buffer_append (buf, &buf->msg.u.request_1_0.response_expected, sizeof(CORBA_boolean));
giop_send_buffer_append_aligned (buf, &objkey->_length, sizeof(CORBA_unsigned_long));
giop_send_buffer_append (buf, objkey->_buffer, objkey->_length);
giop_send_buffer_align (buf, sizeof(CORBA_unsigned_long));
giop_send_buffer_append (buf, operation_vec->iov_base, operation_vec->iov_len);
giop_send_buffer_append (buf, principal_vec->iov_base, principal_vec->iov_len);
break;
case GIOP_1_2:
buf->msg.u.request_1_2.request_id = request_id;
buf->msg.u.request_1_2.response_flags = response_expected ? 0x3 /* SYNC_WITH_TARGET */ : 0x0 /* SYNC_NONE */;
giop_send_buffer_align (buf, sizeof(CORBA_unsigned_long));
giop_send_buffer_append (buf, &buf->msg.u.request_1_2.request_id, sizeof(CORBA_unsigned_long));
giop_send_buffer_append (buf, &buf->msg.u.request_1_2.response_flags, sizeof(CORBA_octet));
giop_send_buffer_append (buf, giop_zero_buf, 3);
giop_send_buffer_append (buf, &giop_1_2_target_type, 2); /* We always use GIOP::KeyAddr addressing - the only sane way */
giop_send_buffer_append_aligned (buf, &objkey->_length, sizeof(CORBA_unsigned_long));
giop_send_buffer_append (buf, objkey->_buffer, objkey->_length);
giop_send_buffer_align (buf, sizeof(CORBA_unsigned_long));
giop_send_buffer_append (buf, operation_vec->iov_base, operation_vec->iov_len);
giop_send_buffer_append (buf, (const guchar *)iop_service_context_data, sizeof(iop_service_context_data));
giop_send_buffer_align (buf, 8); /* alignment for the body */
default:
break;
}
return buf;
}
GIOPSendBuffer *
giop_send_buffer_use_reply(GIOPVersion giop_version,
CORBA_unsigned_long request_id,
CORBA_unsigned_long reply_status)
{
GIOPSendBuffer *buf = giop_send_buffer_use(giop_version);
buf->msg.header.message_type = GIOP_REPLY;
switch(giop_version)
{
case GIOP_1_0:
case GIOP_1_1:
buf->msg.u.reply_1_0.reply_status = reply_status;
buf->msg.u.reply_1_0.request_id = request_id;
giop_send_buffer_append(buf, (const guchar *)iop_service_context_data, sizeof(iop_service_context_data));
giop_send_buffer_append(buf, &buf->msg.u.reply_1_0.request_id, sizeof(CORBA_unsigned_long));
giop_send_buffer_append(buf, &buf->msg.u.reply_1_0.reply_status, sizeof(CORBA_unsigned_long));
break;
case GIOP_1_2:
buf->msg.u.reply_1_2.reply_status = reply_status;
buf->msg.u.reply_1_2.request_id = request_id;
giop_send_buffer_append(buf, &buf->msg.u.reply_1_2.request_id, sizeof(CORBA_unsigned_long));
giop_send_buffer_append(buf, &buf->msg.u.reply_1_2.reply_status, sizeof(CORBA_unsigned_long));
giop_send_buffer_append(buf, (const guchar *)iop_service_context_data, sizeof(iop_service_context_data));
giop_send_buffer_align(buf, 8); /* alignment for the body */
default:
break;
}
return buf;
}
GIOPSendBuffer *
giop_send_buffer_use_locate_request (GIOPVersion giop_version,
CORBA_unsigned_long request_id,
const CORBA_sequence_CORBA_octet *objkey)
{
GIOPSendBuffer *buf = giop_send_buffer_use(giop_version);
buf->msg.header.message_type = GIOP_LOCATEREQUEST;
buf->msg.u.locate_request_1_0.request_id = request_id;
giop_send_buffer_append(buf, &buf->msg.u.locate_request_1_0.request_id, sizeof(CORBA_unsigned_long));
switch(giop_version)
{
case GIOP_1_0:
case GIOP_1_1:
giop_send_buffer_append_aligned(buf, &objkey->_length, sizeof(CORBA_unsigned_long));
giop_send_buffer_append(buf, objkey->_buffer, objkey->_length);
break;
case GIOP_1_2:
giop_send_buffer_append(buf, &giop_1_2_target_type, sizeof(giop_1_2_target_type));
giop_send_buffer_append_aligned(buf, &objkey->_length, sizeof(CORBA_unsigned_long));
giop_send_buffer_append(buf, objkey->_buffer, objkey->_length);
default:
break;
}
return buf;
}
GIOPSendBuffer *
giop_send_buffer_use_locate_reply(GIOPVersion giop_version,
CORBA_unsigned_long request_id,
CORBA_unsigned_long locate_status)
{
GIOPSendBuffer *buf = giop_send_buffer_use(giop_version);
buf->msg.header.message_type = GIOP_LOCATEREPLY;
buf->msg.u.locate_reply_1_0.request_id = request_id;
giop_send_buffer_append(buf, &buf->msg.u.locate_reply_1_0.request_id, sizeof(CORBA_unsigned_long));
buf->msg.u.locate_reply_1_0.locate_status = locate_status;
giop_send_buffer_append(buf, &buf->msg.u.locate_reply_1_0.locate_status, sizeof(CORBA_unsigned_long));
return buf;
}
GIOPSendBuffer *
giop_send_buffer_use_close_connection (GIOPVersion giop_version)
{
GIOPSendBuffer *buf = giop_send_buffer_use (giop_version);
buf->msg.header.message_type = GIOP_CLOSECONNECTION;
return buf;
}
GIOPSendBuffer *
giop_send_buffer_use_message_error (GIOPVersion giop_version)
{
GIOPSendBuffer *buf = giop_send_buffer_use (giop_version);
buf->msg.header.message_type = GIOP_MESSAGEERROR;
return buf;
}
void
giop_send_buffer_unuse (GIOPSendBuffer *buf)
{
int i;
for (i = 0; i < buf->num_indirects_used; i++) {
if (buf->indirects[i].size > GIOP_CHUNK_SIZE) {
buf->indirects [i].size = GIOP_CHUNK_SIZE;
buf->indirects [i].ptr = g_realloc (buf->indirects [i].ptr,
buf->indirects [i].size);
}
}
LINK_MUTEX_LOCK (send_buffer_list_lock);
send_buffer_list = g_slist_prepend (send_buffer_list, buf);
LINK_MUTEX_UNLOCK (send_buffer_list_lock);
}
static void
giop_send_buffer_append_real (GIOPSendBuffer *buf,
gconstpointer mem,
gulong len)
{
register gulong num_used;
register const guchar *lastptr;
g_assert (mem);
lastptr = buf->lastptr;
num_used = buf->num_used;
if(num_used && mem == lastptr)
buf->iovecs[num_used-1].iov_len += len;
else {
if(num_used >= buf->num_alloced) {
buf->num_alloced = MAX (buf->num_alloced, 4) * 2;
buf->iovecs = g_realloc (buf->iovecs,
buf->num_alloced *
sizeof (struct iovec));
}
buf->iovecs [num_used].iov_base = (gpointer) mem;
buf->iovecs [num_used].iov_len = len;
buf->num_used = num_used + 1;
}
buf->msg.header.message_size += len;
buf->lastptr = ((const guchar *) mem) + len;
}
/*
* get_next_indirect:
* @buf: the send buffer with an exhausted indirect.
* @for_size_hint: for very large buffers specify this
* so we don't allocate too much. If this is non 0 then
* buf->indirect will contain at least this much space.
*
* Pulls in the next indirect block into buf, and
* sets up @buf->indirect_left, and @buf->indirect
* to be correct.
*/
static void
get_next_indirect (GIOPSendBuffer *buf, gulong for_size_hint)
{
gulong max = buf->num_indirects_used;
if (max >= buf->num_indirects_alloced) {
gulong new_size;
buf->num_indirects_alloced++;
buf->indirects = g_realloc (
buf->indirects, buf->num_indirects_alloced * sizeof (GIOPIndirectChunk));
if (for_size_hint) {
new_size = (for_size_hint + 7) & ~7;
if (new_size < GIOP_CHUNK_SIZE)
new_size = GIOP_CHUNK_SIZE;
} else
new_size = GIOP_CHUNK_SIZE;
buf->indirects [max].size = new_size;
if (giop_blank_wire_data)
buf->indirects [max].ptr = g_malloc0 (new_size);
else
buf->indirects [max].ptr = g_malloc (new_size);
/*
* We assume that this is 8 byte aligned, for efficiency -
* so we can align to the memory address rather than the offset
* into the buffer.
*/
g_assert (((gulong)buf->indirects [max].ptr & 0x3) == 0);
}
buf->indirect = buf->indirects [max].ptr;
buf->indirect_left = buf->indirects [max].size;
buf->num_indirects_used = max + 1;
}
static void
giop_send_buffer_append_copy (GIOPSendBuffer *buf,
gconstpointer mem,
gulong len)
{
/* FIXME: should we fill up the full indirects ? */
if (buf->indirect_left < len)
get_next_indirect (buf, len);
memcpy (buf->indirect, mem, len);
giop_send_buffer_append_real (buf, buf->indirect, len);
buf->indirect += len;
buf->indirect_left -= len;
}
void
giop_send_buffer_append (GIOPSendBuffer *buf,
gconstpointer mem,
gulong len)
{
if (len <= 32)
giop_send_buffer_append_copy (buf, mem, len);
else
giop_send_buffer_append_real (buf, mem, len);
}
/**
* giop_send_buffer_align:
* @buf: the buffer
* @boundary: the boundary.
*
* Appends memory to the SendBuffer to align it to a boundary
* of size @boundary bytes - if neccessary.
**/
void
giop_send_buffer_align (GIOPSendBuffer *buf, gulong boundary)
{
gulong align_amt, ms;
/* 1. Figure out how much to align by */
ms = buf->msg.header.message_size + buf->header_size;
align_amt = ALIGN_VALUE(ms, boundary) - ms;
/* 2. Do the alignment */
if (align_amt) {
if (buf->indirect_left < align_amt)
get_next_indirect (buf, 0);
p_memzero (buf->indirect, align_amt);
giop_send_buffer_append_real (buf, buf->indirect, align_amt);
buf->indirect += align_amt;
buf->indirect_left -= align_amt;
}
}
/**
* giop_send_buffer_append_aligned:
* @buf: the buffer
* @mem: the memory pointer
* @align_len: the alignment and length of @mem.
*
* This routine alignes the send buffer to a byte boundary
* of size @align_len, and writes align_len bytes of memory
* pointed to by @mem to the buffer, or simply expands the
* buffer if mem is NULL by that much.
*
* Return value: a pointer to the beggining of the
* contiguous space available for @mem
*
* Note: do not assume anything about the physical
* alignment of the returned pointer.
**/
guchar *
giop_send_buffer_append_aligned (GIOPSendBuffer *buf,
gconstpointer mem,
gulong align_len)
{
guchar *indirect;
/* FIXME: could make this more efficient by in-lining the align
more aggressively here */
giop_send_buffer_align (buf, align_len);
if (buf->indirect_left < align_len)
get_next_indirect (buf, 0);
indirect = buf->indirect;
if (mem)
memcpy (indirect, mem, align_len);
else
p_memzero (indirect, align_len);
giop_send_buffer_append_real (buf, indirect, align_len);
buf->indirect += align_len;
buf->indirect_left -= align_len;
return indirect;
}
/**
* giop_send_buffer_write:
* @buf: the buffer to write
* @cnx: the connection to write it to.
*
* Writes @buf to @cnx as a block.
*
* Return value: 0 on sucess, non 0 on error.
**/
int
giop_send_buffer_write (GIOPSendBuffer *buf,
GIOPConnection *cnx,
gboolean blocking)
{
int retval;
LinkConnection *lcnx = LINK_CONNECTION (cnx);
static LinkWriteOpts *non_block = NULL;
if (!non_block)
non_block = link_write_options_new (FALSE);
/* FIXME: if a FRAGMENT, assert the 8 byte tail align,
&&|| giop_send_buffer_align (buf, 8); */
if (g_thread_supported ()
&& lcnx->timeout_msec
&& !lcnx->timeout_source_id
&& !giop_send_buffer_is_oneway (buf)) {
giop_timeout_add (cnx);
}
retval = link_connection_writev (lcnx,
buf->iovecs,
buf->num_used,
blocking ? NULL : non_block);
if (!blocking && retval == LINK_IO_QUEUED_DATA)
retval = 0;
/* FIXME: we need to flag the connection disconnected on fatal error */
return retval;
}
GIOPSendBuffer *
giop_send_buffer_use (GIOPVersion giop_version)
{
GIOPSendBuffer *buf;
g_return_val_if_fail (
((int) giop_version) >= 0 &&
giop_version < GIOP_NUM_VERSIONS, NULL);
LINK_MUTEX_LOCK (send_buffer_list_lock);
if (send_buffer_list) {
GSList *ltmp;
ltmp = send_buffer_list;
send_buffer_list = g_slist_remove_link (
send_buffer_list, ltmp);
LINK_MUTEX_UNLOCK (send_buffer_list_lock);
buf = ltmp->data;
g_slist_free_1 (ltmp);
buf->num_used = buf->indirect_left = 0;
if (giop_blank_wire_data) {
int i;
for (i = 0; i < buf->num_indirects_used; i++)
memset (buf->indirects [i].ptr, 0,
buf->indirects [i].size);
}
buf->num_indirects_used = 0;
} else {
LINK_MUTEX_UNLOCK (send_buffer_list_lock);
buf = g_new0 (GIOPSendBuffer, 1);
memcpy (buf->msg.header.magic, "GIOP", 4);
buf->msg.header.flags = GIOP_FLAG_ENDIANNESS;
buf->num_alloced = 8;
buf->iovecs = g_new (struct iovec, 8);
}
memcpy (buf->msg.header.version,
giop_version_ids [giop_version], 2);
buf->giop_version = giop_version;
g_assert (sizeof (buf->msg.header) == 12);
giop_send_buffer_append_real (
buf, (guchar *)&buf->msg.header, 12);
buf->msg.header.message_size = 0;
buf->header_size = 12;
return buf;
}
void
giop_send_buffer_append_string (GIOPSendBuffer *buf,
const char *str)
{
CORBA_unsigned_long len;
len = strlen (str) + 1;
/* FIXME: inline me ? */
giop_send_buffer_align (buf, 4);
/* be cleverer for short strings */
if (buf->indirect_left >= 4 + len) {
guchar *indirect = buf->indirect;
memcpy (indirect, &len, 4);
memcpy (indirect + 4, str, len);
giop_send_buffer_append_real (buf, indirect, 4 + len);
buf->indirect += 4 + len;
buf->indirect_left -= 4 + len;
} else {
giop_send_buffer_append_copy (buf, &len, 4);
giop_send_buffer_append (buf, str, len);
}
}
syntax highlighted by Code2HTML, v. 0.9.1