/*
* 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:
*
* convert.c
*
* Abstract:
*
* Primitive type conversion API
*
* Authors: Brian Koropoff (bkoropoff@likewisesoftware.com)
*
*/
#include
#include
#include "util-private.h"
#include "convert.h"
#include
#include
#include
static inline void
swap_memcpy(void* out, const void* in, size_t n)
{
size_t i;
unsigned char* in_bytes = (unsigned char*) in;
unsigned char* out_bytes = (unsigned char*) out;
for (i = 0; i < n; i++)
{
out_bytes[n - 1 - i] = in_bytes[i];
}
}
LWMsgStatus
lwmsg_convert_integer(
void* in,
size_t in_size,
LWMsgByteOrder in_order,
void* out,
size_t out_size,
LWMsgByteOrder out_order,
LWMsgSignage signage
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
unsigned char* in_bytes = (unsigned char*) in;
unsigned char* out_bytes = (unsigned char*) out;
/* Widening conversion */
if (in_size <= out_size)
{
/* Start of lower order (in_size) bytes in out_bytes */
unsigned char *out_low_bytes;
/* Start of higher order (out_size - in_size) bytes in out_bytes */
unsigned char *out_high_bytes;
/* Byte that will contain sign bit in out_bytes after copying
but before sign extension */
unsigned char *out_sign_byte;
/* Decide where our lower order, higher order,
and sign bytes are in out_bytes */
if (out_order == LWMSG_LITTLE_ENDIAN)
{
out_low_bytes = out_bytes;
out_high_bytes = out_bytes + in_size;
out_sign_byte = out_low_bytes + in_size - 1;
}
else
{
out_low_bytes = out_bytes + out_size - in_size;
out_high_bytes = out_bytes;
out_sign_byte = out_low_bytes;
}
/* Copy into the lower order bytes, swapping order
if necessary */
if (in_order == out_order)
{
memcpy(out_low_bytes, in_bytes, in_size);
}
else
{
swap_memcpy(out_low_bytes, in_bytes, in_size);
}
/* Perform sign extension or clear high bytes */
if (signage == LWMSG_SIGNED && *out_sign_byte & 0x80)
{
memset(out_high_bytes, 0xFF, out_size - in_size);
}
else
{
memset(out_high_bytes, 0x00, out_size - in_size);
}
}
/* Truncating conversion */
else
{
/* Lowest order (out_size) bytes of in_bytes */
unsigned char *in_low_bytes;
/* Highest order (in_size - out_size) bytes of in_bytes */
unsigned char *in_high_bytes;
/* Byte which will contain the sign bit after truncation */
unsigned char *in_sign_byte;
/* Expected value of high bytes if no overflow/underflow is to occur */
unsigned char expected_high_byte;
unsigned char* i;
if (in_order == LWMSG_LITTLE_ENDIAN)
{
in_low_bytes = in_bytes;
in_high_bytes = in_bytes + out_size;
in_sign_byte = in_low_bytes + out_size - 1;
}
else
{
in_low_bytes = in_bytes + in_size - out_size;
in_high_bytes = in_bytes;
in_sign_byte = in_low_bytes;
}
/* Decide if conversion will cause overflow/underflow */
if (signage == LWMSG_SIGNED && *in_sign_byte & 0x80)
{
/* Output will end up being negative */
expected_high_byte = 0xFF;
}
else
{
/* Output will end up being positive */
expected_high_byte = 0x00;
}
for (i = in_high_bytes; i < (in_order == LWMSG_LITTLE_ENDIAN ? in_bytes + in_size : in_low_bytes); i++)
{
if (*i != expected_high_byte)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_OVERFLOW);
}
}
/* Copy lowest order bytes of in_bytes into out_bytes,
swapping byte order if necessary */
if (in_order == out_order)
{
memcpy(out_bytes, in_low_bytes, out_size);
}
else
{
swap_memcpy(out_bytes, in_low_bytes, out_size);
}
}
error:
return status;
}
uint32_t
lwmsg_convert_uint32(
uint32_t in,
LWMsgByteOrder in_order,
LWMsgByteOrder out_order
)
{
uint32_t out;
lwmsg_convert_integer(
&in,
sizeof(in),
in_order,
&out,
sizeof(out),
out_order,
LWMSG_UNSIGNED);
return out;
}
uint16_t
lwmsg_convert_uint16(
uint16_t in,
LWMsgByteOrder in_order,
LWMsgByteOrder out_order
)
{
uint16_t out;
lwmsg_convert_integer(
&in,
sizeof(in),
in_order,
&out,
sizeof(out),
out_order,
LWMSG_UNSIGNED);
return out;
}