OSSP CVS Repository

ossp - ossp-pkg/xds/xds.c
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/xds/xds.c
/*
**  OSSP xds - Extensible Data Serialization
**  Copyright (c) 2001-2005 Ralf S. Engelschall <rse@engelschall.com>
**  Copyright (c) 2001-2005 The OSSP Project <http://www.ossp.org/>
**  Copyright (c) 2001-2005 Cable & Wireless <http://www.cw.com/>
**
**  This file is part of OSSP xds, an extensible data serialization
**  library which can be found at http://www.ossp.org/pkg/lib/xds/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
**
**  xds.c: XDS library framework
*/

#include <string.h>
#include <ctype.h>
#include <errno.h>

#include "xds_p.h"

int xds_init(xds_t **xds, xds_mode_t mode)
{
    xds_t *ctx;

    /* Sanity check parameter. */
    assert(xds != NULL);
    if (xds == NULL)
        return XDS_ERR_INVALID_ARG;
    assert(mode == XDS_ENCODE || mode == XDS_DECODE);
    if (mode != XDS_ENCODE && mode != XDS_DECODE)
        return XDS_ERR_INVALID_ARG;

    /* Allocate context structure. */
    if ((ctx = malloc(sizeof (struct xds_context))) == NULL)
        return XDS_ERR_SYSTEM; /* errno set by malloc(3) */

    /* Set mode of operation in context. */
    ctx->mode = mode;

    /* Initialize buffer handling. */
    ctx->buffer = NULL;
    ctx->buffer_len = 0;
    ctx->buffer_capacity = 0;
    ctx->we_own_buffer = XDS_FALSE;

    /* Initialize engines map. */
    ctx->engines = NULL;
    ctx->engines_len = 0;
    ctx->engines_capacity = 0;

    *xds = ctx;

    return XDS_OK;
}

int xds_destroy(xds_t *xds)
{
    size_t i;

    /* Sanity check parameter. */
    assert(xds != NULL);
    if (xds == NULL)
        return XDS_ERR_INVALID_ARG;

    /* Free allocated memory. */
    assert(xds->buffer != NULL
           || (xds->buffer_capacity == 0 && xds->buffer_len == 0));
    if (xds->buffer != NULL && xds->we_own_buffer)
        free(xds->buffer);
    assert(xds->engines != NULL || xds->engines_capacity == 0);
    if (xds->engines != NULL) {
        for (i = 0; i < xds->engines_len; i++) {
            assert(xds->engines[i].name != NULL);
            free(xds->engines[i].name);
        }
        free(xds->engines);
    }
    free(xds);

    return XDS_OK;
}

int xds_setbuffer(xds_t *xds, xds_scope_t flag, void *buffer,
                  size_t buffer_len)
{
    /* Sanity check parameters. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(flag == XDS_GIFT || flag == XDS_LOAN);
    xds_check_parameter((buffer != NULL && buffer_len != 0)
                        || flag == XDS_GIFT);

    /* Free the old buffer if there is one. */
    if (xds->buffer != NULL && xds->we_own_buffer)
        free(xds->buffer);
    xds->buffer_len = 0;

    if (flag == XDS_GIFT) {
        xds->buffer = buffer;
        if (buffer == NULL)
            xds->buffer_capacity = 0;
        else
            xds->buffer_capacity = buffer_len;
        xds->we_own_buffer = XDS_TRUE;
    }
    else {
        xds->buffer = buffer;
        xds->buffer_capacity = buffer_len;
        xds->we_own_buffer = XDS_FALSE;
    }

    return XDS_OK;
}

int xds_getbuffer(xds_t *xds, xds_scope_t flag, void **buffer,
                  size_t *buffer_len)
{
    /* Sanity check parameters. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(flag == XDS_GIFT || flag == XDS_LOAN);
    xds_check_parameter(buffer != NULL);
    xds_check_parameter(buffer_len != NULL);

    /* Return the buffer to the caller. */
    *buffer = xds->buffer;
    *buffer_len = xds->buffer_len;
    if (flag == XDS_GIFT) {
        xds->buffer = NULL;
        xds->buffer_capacity = 0;
        xds->buffer_len = 0;
    }
    else
        xds->buffer_len = 0;

    return XDS_OK;
}

static int xds_set_capacity(void **array, size_t *array_capacity,
                            size_t new_capacity, size_t elem_size,
                            size_t initial_capacity)
{
    void *buf;
    size_t size;

    /* Sanity checks. */
    xds_check_parameter(array != NULL);
    xds_check_parameter(array_capacity != NULL);
    xds_check_parameter(elem_size != 0);
    xds_check_parameter(initial_capacity != 0);

    /* Do we need to re-allocate? */
    if (*array_capacity > new_capacity)
        return XDS_OK;

    /* Find the correct capacity. */
    size = (*array_capacity != 0) ? (*array_capacity * 2) : initial_capacity;
    while (size < new_capacity)
        size *= 2;

    /* Allocate the array and store the new values. */
    if ((buf = realloc(*array, size * elem_size)) == NULL)
        return XDS_ERR_NO_MEM;
    *array = buf;
    *array_capacity = size;

    return XDS_OK;
}

static int xds_find_engine(const engine_map_t *engines, size_t last,
                    const char *name, size_t *pos)
{
    size_t first;

    /* Sanity checks. */
    xds_check_parameter(engines != NULL || last == 0);
    xds_check_parameter(name != NULL);
    xds_check_parameter(pos != NULL);

    /* Use binary search to find "name" in "engines". */
    for (first = 0; (last - first) > 0; ) {
        size_t half = first + ((last - first) / 2);
        int rc = strcmp(engines[half].name, name);
        if (rc < 0)
            first = half + 1;
        else if (rc == 0) { /* found it */
            *pos = half;
            return XDS_TRUE;
        }
        else
            last = half;
        assert(first <= last);
    }
    *pos = first;

    return XDS_FALSE;
}

int xds_register(xds_t *xds, const char *name, xds_engine_t engine,
                 void *engine_context)
{
    size_t pos;

    /* Sanity checks. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(name != NULL);
    xds_check_parameter(engine != NULL);
    for (pos = 0; name[pos] != '\0'; ++pos) {
        if (!isalnum((int)name[pos]) && name[pos] != '-' && name[pos] != '_')
            return XDS_ERR_INVALID_ARG;
    }

    /* Copy the name argument into our own memory. */
    name = strdup(name);
    if (name == NULL)
        return XDS_ERR_NO_MEM;

    /* Search engines for the entry. */
    if (xds_find_engine(xds->engines, xds->engines_len, name, &pos)) {
        /* overwrite existing entry */
        free(xds->engines[pos].name);
    }
    else {
        /* insert new entry */
        int rc = xds_set_capacity((void **)&xds->engines,
                                  &xds->engines_capacity,
                                  xds->engines_len + 1,
                                  sizeof (engine_map_t),
                                  XDS_INITIAL_ENGINES_CAPACITY);
        assert(rc == XDS_OK || rc == XDS_ERR_NO_MEM);
        if (rc != XDS_OK)
            return rc;
        memmove(&xds->engines[pos + 1], &xds->engines[pos],
                (xds->engines_len - pos) * sizeof (engine_map_t));
        xds->engines_len++;
    }

    /* Insert entry. */
    xds->engines[pos].name    = (char *)name;
    xds->engines[pos].engine  = engine;
    xds->engines[pos].context = engine_context;

    /* Everything is fine. */
    return XDS_OK;
}

int xds_unregister(xds_t *xds, const char *name)
{
    size_t pos;
    int rc;

    /* Sanity checks. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(name != NULL);
    for (pos = 0; name[pos] != '\0'; pos++) {
        if (!isalnum((int)name[pos]) && name[pos] != '-' && name[pos] != '_')
            return XDS_ERR_INVALID_ARG;
    }

    /* Find the entry we're supposed to delete. */
    if (!xds_find_engine(xds->engines, xds->engines_len, name, &pos))
        return XDS_ERR_UNKNOWN_ENGINE;

    /* Free the memory allocated for this entry and move the entries behind
       it back if necessary. */
    assert(xds->engines[pos].name != NULL);
    free(xds->engines[pos].name);
    memmove(&xds->engines[pos], &xds->engines[pos + 1],
            (xds->engines_len - (pos + 1)) * sizeof (engine_map_t));
    xds->engines_len--;

    /* Lower capacity if necessary. */
    rc = xds_set_capacity((void **)&xds->engines, &xds->engines_capacity,
                          xds->engines_len, sizeof (engine_map_t),
                          XDS_INITIAL_ENGINES_CAPACITY);
    assert(rc == XDS_OK || rc == XDS_ERR_NO_MEM);
    if (rc != XDS_OK)
        return rc;

    return XDS_OK;
}

int xds_encode(xds_t *xds, const char *fmt, ...)
{
    int rc;
    va_list args;

    /* Sanity checks. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(fmt != NULL);

    va_start(args, fmt);
    rc = xds_vencode(xds, fmt, args);
    va_end(args);
    return rc;
}

int xds_vencode(xds_t *xds, const char *fmt_arg, va_list args)
{
    va_list args_backup;
    size_t buffer_len_backup;
    char *name;
    char *p;
    char *fmt;
    int rc;

    /* Sanity checks. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(fmt_arg != NULL);
    assert(xds->mode == XDS_ENCODE);
    if (xds->mode != XDS_ENCODE)
        return XDS_ERR_INVALID_MODE;

    /* Ensure we have a buffer allocated ready for use. */
    if (xds->buffer == NULL) {
        /* allocate a new buffer */
        rc = xds_set_capacity((void **)&xds->buffer, &xds->buffer_capacity,
                              XDS_INITIAL_BUFFER_CAPACITY, sizeof (char),
                              XDS_INITIAL_BUFFER_CAPACITY);
        assert(rc == XDS_OK || rc == XDS_ERR_NO_MEM);
        if (rc != XDS_OK)
            return rc;
        xds->buffer_len = 0;
        xds->we_own_buffer = XDS_TRUE;
    }

    /* Iterate through items in format string and execute apropriate engines. */
    fmt = p = strdup(fmt_arg);
    if (fmt == NULL)
        return XDS_ERR_NO_MEM;
    buffer_len_backup = xds->buffer_len;
    for (name = p; *p != '\0'; name = p) {
        while (isalnum((int)*p) || *p == '-' || *p == '_')
            p++;
        if (*p != '\0')
            *p++ = '\0';
        else
            *p = '\0';

        if (strlen(name) > 0) {
            int restart_engine;
            size_t used_buffer_size;
            size_t pos;

            if (xds_find_engine(xds->engines, xds->engines_len, name, &pos)
                == XDS_FALSE) {
                rc = XDS_ERR_UNKNOWN_ENGINE;
                goto leave;
            }

            do {
                /* Ensure the buffer has free space. */
                assert(xds->buffer_len <= xds->buffer_capacity);
                if (xds->buffer_len == xds->buffer_capacity) {
                    if (xds->we_own_buffer) {
                        rc = xds_set_capacity((void **)&xds->buffer,
                                              &xds->buffer_capacity,
                                              xds->buffer_len + 1, sizeof (char),
                                              XDS_INITIAL_BUFFER_CAPACITY);
                        assert(rc == XDS_OK || rc == XDS_ERR_NO_MEM);
                        if (rc != XDS_OK)
                            goto leave;
                    }
                    else {
                        rc = XDS_ERR_OVERFLOW;
                        goto leave;
                    }
                }

                /* Execute the engine. */
                used_buffer_size = 0;
                args_backup = args;
                rc = (*xds->engines[pos].engine)(
                         xds, xds->engines[pos].context,
                         xds->buffer + xds->buffer_len,
                         xds->buffer_capacity - xds->buffer_len,
                         &used_buffer_size, &args);
                assert(rc <= 0);
                if (rc == XDS_OK) {
                    restart_engine = XDS_FALSE;
                    xds->buffer_len += used_buffer_size;
                }
                else if (rc == XDS_ERR_OVERFLOW) {  /* enlarge buffer */
                    if (!xds->we_own_buffer)
                        goto leave;

                    restart_engine = XDS_TRUE;
                    args = args_backup;

                    rc = xds_set_capacity(
                            (void **)&xds->buffer, &xds->buffer_capacity,
                            xds->buffer_capacity +
                            ((used_buffer_size == 0) ? 1 : used_buffer_size),
                            sizeof (char), XDS_INITIAL_BUFFER_CAPACITY);
                    assert(rc == XDS_OK || rc == XDS_ERR_NO_MEM);
                    if (rc != XDS_OK)
                        goto leave;
                }
                else
                    goto leave;
            }
            while (restart_engine);
        }
    }
    rc = XDS_OK;

    /* Clean up and leave. */
    leave:
    free(fmt);
    if (rc != XDS_OK)
        xds->buffer_len = buffer_len_backup;
    return rc;
}

int xds_decode(xds_t *xds, const char *fmt, ...)
{
    int rc;
    va_list args;

    /* Sanity checks. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(fmt != NULL);

    va_start(args, fmt);
    rc = xds_vdecode(xds, fmt, args);
    va_end(args);
    return rc;
}

int xds_vdecode(xds_t *xds, const char *fmt_arg, va_list args)
{
    size_t buffer_len_backup;
    char *name;
    char *p;
    char *fmt;
    int rc;

    /* Sanity checks. */
    xds_check_parameter(xds != NULL);
    xds_check_parameter(fmt_arg != NULL);
    assert(xds->mode == XDS_DECODE);
    if (xds->mode != XDS_DECODE)
        return XDS_ERR_INVALID_MODE;

    /* Ensure we have a buffer to decode. */
    if (xds->buffer == NULL || xds->buffer_capacity == 0)
        return XDS_ERR_UNDERFLOW;

    /* Iterate through the items in the format string and execute the
       apropriate engines. */
    fmt = p = strdup(fmt_arg);
    if (fmt == NULL)
        return XDS_ERR_NO_MEM;
    buffer_len_backup = xds->buffer_len;
    for (name = p; *p != '\0'; name = p) {
        while (isalnum((int)*p) || *p == '-' || *p == '_')
            p++;
        if (*p != '\0')
            *p++ = '\0';
        else
            *p = '\0';

        if (strlen(name) > 0) {
            size_t pos;
            size_t used_buffer_size = 0;

            if (xds_find_engine(xds->engines, xds->engines_len, name, &pos)) {
                rc = (*xds->engines[pos].engine)(
                         xds, xds->engines[pos].context,
                         xds->buffer + xds->buffer_len,
                         xds->buffer_capacity - xds->buffer_len,
                         &used_buffer_size, &args);
                assert(rc <= 0);
                if (rc == XDS_OK)
                    xds->buffer_len += used_buffer_size;
                else
                    goto leave;
            }
            else {
                rc = XDS_ERR_UNKNOWN_ENGINE;
                goto leave;
            }
        }
    }
    rc = XDS_OK;

    /* Clean up and leave. */
    leave:
    free(fmt);
    if (rc != XDS_OK)
        xds->buffer_len = buffer_len_backup;
    return rc;
}


CVSTrac 2.0.1