/*
* Copyright (c) Likewise Software. All rights Reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the license, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy
* of the GNU Lesser General Public License along with this program. If
* not, see .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Module Name:
*
* data-marshal.c
*
* Abstract:
*
* Marshalling API
*
* Authors: Brian Koropoff (bkoropoff@likewisesoftware.com)
*
*/
#include "data-private.h"
#include "util-private.h"
#include "type-private.h"
#include "convert.h"
#include "config.h"
#include
static LWMsgStatus
lwmsg_data_marshal_internal(
LWMsgDataContext* context,
LWMsgMarshalState* state,
LWMsgTypeIter* iter,
unsigned char* object,
LWMsgBuffer* buffer
);
static LWMsgStatus
lwmsg_object_is_zero(LWMsgMarshalState* state, LWMsgTypeIter* iter, unsigned char* object, int* is_zero)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
size_t i;
*is_zero = 1;
for (i = 0; i < iter->size; i++)
{
if (object[i] != 0)
{
*is_zero = 0;
break;
}
}
return status;
}
static LWMsgStatus
lwmsg_data_marshal_indirect_prologue(
LWMsgDataContext* context,
LWMsgMarshalState* state,
LWMsgTypeIter* iter,
unsigned char* object,
LWMsgBuffer* buffer,
LWMsgTypeIter* inner_iter,
size_t* count)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
unsigned char* element = NULL;
unsigned char implicit_length[4];
int is_zero;
lwmsg_type_enter(iter, inner_iter);
switch (iter->info.kind_indirect.term)
{
case LWMSG_TERM_STATIC:
*count = iter->info.kind_indirect.term_info.static_length;
break;
case LWMSG_TERM_MEMBER:
/* Extract the length out of the field of the actual structure */
BAIL_ON_ERROR(status = lwmsg_data_extract_length(
iter,
state->dominating_object,
count));
break;
case LWMSG_TERM_ZERO:
if (!object)
{
if (iter->attrs.nonnull)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED);
}
*count = 0;
}
else
{
element = object;
is_zero = 0;
/* We have to calculate the count by searching for the zero element */
for (*count = 0;;*count += 1)
{
BAIL_ON_ERROR(status = lwmsg_object_is_zero(
state,
inner_iter,
element,
&is_zero));
if (is_zero)
{
break;
}
element += inner_iter->size;
}
/* The length is implicitly written into the output to
facilitate easy unmarshalling */
BAIL_ON_ERROR(status = lwmsg_convert_integer(
count,
sizeof(count),
LWMSG_NATIVE_ENDIAN,
implicit_length,
4,
context->byte_order,
LWMSG_UNSIGNED));
BAIL_ON_ERROR(status = lwmsg_buffer_write(buffer, implicit_length, 4));
}
break;
}
error:
return status;
}
static LWMsgStatus
lwmsg_data_marshal_indirect(
LWMsgDataContext* context,
LWMsgMarshalState* state,
LWMsgTypeIter* iter,
unsigned char* object,
LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
size_t i;
size_t count = 0;
unsigned char* element = NULL;
LWMsgTypeIter inner_iter;
BAIL_ON_ERROR(status = lwmsg_data_marshal_indirect_prologue(
context,
state,
iter,
object,
buffer,
&inner_iter,
&count));
/* Enforce nullability. Even if the pointer is marked
as non-null, allow it to be null if there are no
elements to marshal */
if (iter->attrs.nonnull && !object && count != 0)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED);
}
if (inner_iter.kind == LWMSG_KIND_INTEGER &&
inner_iter.info.kind_integer.width == 1 &&
inner_iter.size == 1)
{
/* As an optimization, if the element type if an 1-byte integer both
packed and unpacked, we can write the entire array directly into
the output. This is important for character strings */
BAIL_ON_ERROR(status = lwmsg_buffer_write(
buffer,
object,
count));
}
else if (count)
{
/* Otherwise, marshal each element individually */
element = object;
for (i = 0; i < count; i++)
{
BAIL_ON_ERROR(status = lwmsg_data_marshal_internal(
context,
state,
&inner_iter,
element,
buffer));
element += inner_iter.size;
}
}
error:
return status;
}
static LWMsgStatus
lwmsg_data_marshal_integer(
LWMsgDataContext* context,
LWMsgMarshalState* state,
LWMsgTypeIter* iter,
unsigned char* object,
LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
unsigned char* in = object;
unsigned char out[MAX_INTEGER_SIZE];
size_t in_size;
size_t out_size;
out_size = iter->info.kind_integer.width;
in_size = iter->size;
/* If a valid range is defined, check value against it */
if (iter->attrs.range_low < iter->attrs.range_high)
{
BAIL_ON_ERROR(status = lwmsg_data_verify_range(
&context->error,
iter,
object,
in_size));
}
BAIL_ON_ERROR(status = lwmsg_convert_integer(in,
in_size,
LWMSG_NATIVE_ENDIAN,
out,
out_size,
context->byte_order,
iter->info.kind_integer.sign));
BAIL_ON_ERROR(status = lwmsg_buffer_write(buffer, out, out_size));
error:
return status;
}
static LWMsgStatus
lwmsg_data_marshal_custom(
LWMsgDataContext* context,
LWMsgMarshalState* state,
LWMsgTypeIter* iter,
unsigned char* object,
LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
BAIL_ON_ERROR(status = iter->info.kind_custom.typeclass->marshal(
context,
iter->size,
object,
&iter->attrs,
buffer,
iter->info.kind_custom.typedata));
error:
return status;
}
static LWMsgStatus
lwmsg_data_marshal_struct_member(
LWMsgDataContext* context,
LWMsgTypeIter* struct_iter,
LWMsgTypeIter* member_iter,
unsigned char* object,
LWMsgBuffer* buffer)
{
LWMsgMarshalState my_state;
unsigned char* member_object = object + member_iter->offset;
my_state.dominating_object = object;
return lwmsg_data_marshal_internal(
context,
&my_state,
member_iter,
member_object,
buffer);
}
static LWMsgStatus
lwmsg_data_marshal_struct(LWMsgDataContext* context, LWMsgTypeIter* iter, unsigned char* object, LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
LWMsgTypeIter member;
iter->dom_object = object;
for (lwmsg_type_enter(iter, &member);
lwmsg_type_valid(&member);
lwmsg_type_next(&member))
{
BAIL_ON_ERROR(status = lwmsg_data_marshal_struct_member(
context,
iter,
&member,
object,
buffer));
}
error:
return status;
}
static LWMsgStatus
lwmsg_data_marshal_union(LWMsgDataContext* context, LWMsgMarshalState* state, LWMsgTypeIter* iter, unsigned char* object, LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
LWMsgTypeIter arm;
/* Find the active arm */
BAIL_ON_ERROR(status = lwmsg_data_extract_active_arm(
iter,
state->dominating_object,
&arm));
/* Simply marshal the active arm */
BAIL_ON_ERROR(status = lwmsg_data_marshal_internal(
context,
state,
&arm,
object,
buffer));
error:
return status;
}
static LWMsgStatus
lwmsg_data_marshal_pointer(
LWMsgDataContext* context,
LWMsgMarshalState* state,
LWMsgTypeIter* iter,
unsigned char* object,
LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
unsigned char ptr_flag;
/* Indicator byte showing whether the pointer is set */
ptr_flag = *(void**) object ? 0xFF : 0x00;
/* Only write pointer flag for nullable pointers */
if (!iter->attrs.nonnull)
{
BAIL_ON_ERROR(status = lwmsg_buffer_write(buffer, &ptr_flag, 1));
}
if (ptr_flag || iter->attrs.nonnull)
{
/* Pointer is present, so also write pointee */
BAIL_ON_ERROR(status = lwmsg_data_marshal_indirect(
context,
state,
iter,
*(unsigned char**) object,
buffer));
}
error:
return status;
}
static LWMsgStatus
lwmsg_data_marshal_internal(
LWMsgDataContext* context,
LWMsgMarshalState* state,
LWMsgTypeIter* iter,
unsigned char* object,
LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
if (iter->verify)
{
BAIL_ON_ERROR(status = iter->verify(context, LWMSG_FALSE, iter->size, object, iter->verify_data));
}
switch (iter->kind)
{
case LWMSG_KIND_VOID:
/* Nothing to marshal */
break;
case LWMSG_KIND_INTEGER:
BAIL_ON_ERROR(status = lwmsg_data_marshal_integer(context, state, iter, object, buffer));
break;
case LWMSG_KIND_CUSTOM:
BAIL_ON_ERROR(status = lwmsg_data_marshal_custom(context, state, iter, object, buffer));
break;
case LWMSG_KIND_STRUCT:
BAIL_ON_ERROR(status = lwmsg_data_marshal_struct(context, iter, object, buffer));
break;
case LWMSG_KIND_UNION:
BAIL_ON_ERROR(status = lwmsg_data_marshal_union(context, state, iter, object, buffer));
break;
case LWMSG_KIND_POINTER:
BAIL_ON_ERROR(status = lwmsg_data_marshal_pointer(context, state, iter, object, buffer));
break;
case LWMSG_KIND_ARRAY:
BAIL_ON_ERROR(status = lwmsg_data_marshal_indirect(
context,
state,
iter,
object,
buffer));
break;
default:
BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED);
break;
}
error:
return status;
}
LWMsgStatus
lwmsg_data_marshal(LWMsgDataContext* context, LWMsgTypeSpec* type, void* object, LWMsgBuffer* buffer)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
LWMsgMarshalState state = {NULL};
LWMsgTypeIter iter;
lwmsg_type_iterate_promoted(type, &iter);
BAIL_ON_ERROR(status = lwmsg_data_marshal_internal(context, &state, &iter, (unsigned char*) &object, buffer));
if (buffer->wrap)
{
BAIL_ON_ERROR(status = buffer->wrap(buffer, 0));
}
error:
return status;
}
LWMsgStatus
lwmsg_data_marshal_flat(
LWMsgDataContext* context,
LWMsgTypeSpec* type,
void* object,
void* buffer,
size_t length
)
{
LWMsgBuffer mbuf;
mbuf.base = buffer;
mbuf.end = buffer + length;
mbuf.cursor = buffer;
mbuf.wrap = NULL;
return lwmsg_data_marshal(context, type, object, &mbuf);
}
static
LWMsgStatus
lwmsg_data_marshal_alloc_wrap(
LWMsgBuffer* buffer,
size_t needed
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
LWMsgDataContext* context = buffer->data;
size_t oldlen = buffer->end - buffer->base;
size_t newlen = oldlen == 0 ? 256 : oldlen * 2;
void* newmem = NULL;
size_t offset = 0;
BAIL_ON_ERROR(status = lwmsg_context_realloc(context->context, buffer->base, oldlen, newlen, &newmem));
offset = buffer->cursor - buffer->base;
buffer->base = newmem;
buffer->cursor = newmem + offset;
buffer->end = newmem + newlen;
error:
return status;
}
LWMsgStatus
lwmsg_data_marshal_flat_alloc(
LWMsgDataContext* context,
LWMsgTypeSpec* type,
void* object,
void** buffer,
size_t* length
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
LWMsgBuffer mbuf = {0};
mbuf.base = NULL;
mbuf.cursor = NULL;
mbuf.end = NULL;
mbuf.wrap = lwmsg_data_marshal_alloc_wrap;
mbuf.data = context;
BAIL_ON_ERROR(status = lwmsg_data_marshal(context, type, object, &mbuf));
*buffer = mbuf.base;
*length = (mbuf.cursor - mbuf.base);
done:
return status;
error:
if (mbuf.base)
{
lwmsg_context_free(context->context, mbuf.base);
}
*buffer = NULL;
*length = 0;
goto done;
}