/* ==================================================================== 
 * The Kannel Software License, Version 1.0 
 * 
 * Copyright (c) 2001-2018 Kannel Group  
 * Copyright (c) 1998-2001 WapIT Ltd.   
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in 
 *    the documentation and/or other materials provided with the 
 *    distribution. 
 * 
 * 3. The end-user documentation included with the redistribution, 
 *    if any, must include the following acknowledgment: 
 *       "This product includes software developed by the 
 *        Kannel Group (http://www.kannel.org/)." 
 *    Alternately, this acknowledgment may appear in the software itself, 
 *    if and wherever such third-party acknowledgments normally appear. 
 * 
 * 4. The names "Kannel" and "Kannel Group" must not be used to 
 *    endorse or promote products derived from this software without 
 *    prior written permission. For written permission, please  
 *    contact org@kannel.org. 
 * 
 * 5. Products derived from this software may not be called "Kannel", 
 *    nor may "Kannel" appear in their name, without prior written 
 *    permission of the Kannel Group. 
 * 
 * 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 KANNEL GROUP OR ITS 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. 
 * ==================================================================== 
 * 
 * This software consists of voluntary contributions made by many 
 * individuals on behalf of the Kannel Group.  For more information on  
 * the Kannel Group, please see <http://www.kannel.org/>. 
 * 
 * Portions of this software are based upon software originally written at  
 * WapIT Ltd., Helsinki, Finland for the Kannel project.  
 */ 

/*
 *
 * wsbc.c
 *
 * Author: Markku Rossi <mtr@iki.fi>
 *
 * Copyright (c) 1999-2000 WAPIT OY LTD.
 *		 All rights reserved.
 *
 * Byte-code handling functions.
 *
 */

#include "wsint.h"
#include "wsbc.h"

/********************* Prototypes for static functions ******************/

/* Add a new pragma of type `type' to the byte-code `bc'.  The
 * function returns a pointer to an internal pragma structure that
 * must not be freed by the caller.  It is freed when the byte-code
 * `bc' is freed.  The function returns NULL if the pragma structure
 * could not be allocated. */
static WsBcPragma *add_pragma(WsBc *bc, WsBcPragmaType type);

/********************* Manipulating byte-code structure *****************/

WsBc *ws_bc_alloc(WsBcStringEncoding string_encoding)
{
    WsBc *bc = ws_calloc(1, sizeof(WsBc));

    if (bc == NULL)
        return NULL;

    bc->string_encoding = string_encoding;

    return bc;
}


void ws_bc_free(WsBc *bc)
{
    WsUInt16 i;
    WsUInt8 j;

    if (bc == NULL)
        return;

    /* Free constants. */
    for (i = 0; i < bc->num_constants; i++) {
        WsBcConstant *c = &bc->constants[i];

        if (c->type == WS_BC_CONST_TYPE_UTF8_STRING)
            ws_free(c->u.v_string.data);
    }
    ws_free(bc->constants);

    /* Free pragmas. */
    ws_free(bc->pragmas);

    /* Free function names. */
    for (j = 0; j < bc->num_function_names; j++)
        ws_free(bc->function_names[j].name);
    ws_free(bc->function_names);

    /* Free functions. */
    for (j = 0; j < bc->num_functions; j++)
        ws_free(bc->functions[j].code);
    ws_free(bc->functions);

    /* Free the byte-code structure. */
    ws_free(bc);
}


WsBool ws_bc_encode(WsBc *bc, unsigned char **data_return,
                    size_t *data_len_return)
{
    WsBuffer buffer;
    WsUInt32 ui;
    unsigned char data[64];
    unsigned char *p, *mb;
    size_t len;

    ws_buffer_init(&buffer);

    /* Append space for the header.  We do not know yet the size of the
       resulting byte-code. */
    if (!ws_buffer_append_space(&buffer, NULL, WS_BC_MAX_HEADER_LEN))
        goto error;


    /* Constants. */

    if (!ws_encode_buffer(&buffer,
                          WS_ENC_MB_UINT16, bc->num_constants,
                          WS_ENC_MB_UINT16, (WsUInt16) bc->string_encoding,
                          WS_ENC_END))
        goto error;

    for (ui = 0 ; ui < bc->num_constants; ui++) {
        switch (bc->constants[ui].type) {
        case WS_BC_CONST_TYPE_INT:
            if (WS_INT8_MIN <= bc->constants[ui].u.v_int
                && bc->constants[ui].u.v_int <= WS_INT8_MAX) {
                if (!ws_encode_buffer(&buffer,
                                      WS_ENC_UINT8, (WsUInt8) WS_BC_CONST_INT8,
                                      WS_ENC_INT8,
                                      (WsInt8) bc->constants[ui].u.v_int,
                                      WS_ENC_END))
                    goto error;
            } else if (WS_INT16_MIN <= bc->constants[ui].u.v_int
                       && bc->constants[ui].u.v_int <= WS_INT16_MAX) {
                if (!ws_encode_buffer(&buffer,
                                      WS_ENC_UINT8, (WsUInt8) WS_BC_CONST_INT16,
                                      WS_ENC_INT16,
                                      (WsInt16) bc->constants[ui].u.v_int,
                                      WS_ENC_END))
                    goto error;
            } else {
                if (!ws_encode_buffer(&buffer,
                                      WS_ENC_UINT8, (WsUInt8) WS_BC_CONST_INT32,
                                      WS_ENC_INT32, bc->constants[ui].u.v_int,
                                      WS_ENC_END))
                    goto error;
            }
            break;

        case WS_BC_CONST_TYPE_FLOAT32:
        case WS_BC_CONST_TYPE_FLOAT32_NAN:
        case WS_BC_CONST_TYPE_FLOAT32_POSITIVE_INF:
        case WS_BC_CONST_TYPE_FLOAT32_NEGATIVE_INF:
            switch (bc->constants[ui].type) {
            case WS_BC_CONST_TYPE_FLOAT32:
                ws_ieee754_encode_single(bc->constants[ui].u.v_float, data);
                p = data;
                break;

            case WS_BC_CONST_TYPE_FLOAT32_NAN:
                p = ws_ieee754_nan;
                break;

            case WS_BC_CONST_TYPE_FLOAT32_POSITIVE_INF:
                p = ws_ieee754_positive_inf;
                break;

            case WS_BC_CONST_TYPE_FLOAT32_NEGATIVE_INF:
                p = ws_ieee754_negative_inf;
                break;

            default:
                ws_fatal("ws_bc_encode(): internal inconsistency");
                /* NOTREACHED */
                p = NULL; 		/* Initialized to keep compiler quiet. */
                break;
            }

            if (!ws_encode_buffer(&buffer,
                                  WS_ENC_UINT8, (WsUInt8) WS_BC_CONST_FLOAT32,
                                  WS_ENC_DATA, p, 4,
                                  WS_ENC_END))
                goto error;
            break;

            break;

        case WS_BC_CONST_TYPE_UTF8_STRING:
            /* Encode the strings as requested. */
            switch (bc->string_encoding) {
            case WS_BC_STRING_ENC_ISO_8859_1:
                {
                    WsUtf8String *string = ws_utf8_alloc();
                    unsigned char *latin1;
                    size_t latin1_len;
                    WsBool success;

                    if (string == NULL)
                        goto error;

                    /* Create an UTF-8 string. */
                    if (!ws_utf8_set_data(string,
                                          bc->constants[ui].u.v_string.data,
                                          bc->constants[ui].u.v_string.len)) {
                        ws_utf8_free(string);
                        goto error;
                    }

                    /* Convert it to latin1. */
                    latin1 = ws_utf8_to_latin1(string, '?', &latin1_len);

                    /* We'r done with the UTF-8 string. */
                    ws_utf8_free(string);

                    if (latin1 == NULL)
                        goto error;

                    /* Encode it. */
                    success = ws_encode_buffer(
                                  &buffer,
                                  WS_ENC_UINT8,
                                  (WsUInt8) WS_BC_CONST_EXT_ENC_STRING,

                                  WS_ENC_MB_UINT32, (WsUInt32) latin1_len,
                                  WS_ENC_DATA, latin1, latin1_len,

                                  WS_ENC_END);
                    ws_utf8_free_data(latin1);

                    if (!success)
                        goto error;
                }
                break;

            case WS_BC_STRING_ENC_UTF8:
                if (!ws_encode_buffer(
                        &buffer,
                        WS_ENC_UINT8,
                        (WsUInt8) WS_BC_CONST_UTF8_STRING,

                        WS_ENC_MB_UINT32,
                        (WsUInt32) bc->constants[ui].u.v_string.len,

                        WS_ENC_DATA,
                        bc->constants[ui].u.v_string.data,
                        bc->constants[ui].u.v_string.len,

                        WS_ENC_END))
                    goto error;
                break;
            }
            break;

        case WS_BC_CONST_TYPE_EMPTY_STRING:
            if (!ws_encode_buffer(&buffer,
                                  WS_ENC_UINT8,
                                  (WsUInt8) WS_BC_CONST_EMPTY_STRING,
                                  WS_ENC_END))
                goto error;
            break;
        }
    }


    /* Pragmas. */

    if (!ws_encode_buffer(&buffer,
                          WS_ENC_MB_UINT16, bc->num_pragmas,
                          WS_ENC_END))
        goto error;

    for (ui = 0; ui < bc->num_pragmas; ui++) {
        switch (bc->pragmas[ui].type) {
        case WS_BC_PRAGMA_TYPE_ACCESS_DOMAIN:
            if (!ws_encode_buffer(&buffer,
                                  WS_ENC_UINT8,
                                  (WsUInt8) WS_BC_PRAGMA_ACCESS_DOMAIN,

                                  WS_ENC_MB_UINT16, bc->pragmas[ui].index_1,
                                  WS_ENC_END))
                goto error;
            break;

        case WS_BC_PRAGMA_TYPE_ACCESS_PATH:
            if (!ws_encode_buffer(&buffer,
                                  WS_ENC_UINT8,
                                  (WsUInt8) WS_BC_PRAGMA_ACCESS_PATH,
                                  WS_ENC_MB_UINT16, bc->pragmas[ui].index_1,
                                  WS_ENC_END))
                goto error;
            break;

        case WS_BC_PRAGMA_TYPE_USER_AGENT_PROPERTY:
            if (!ws_encode_buffer(&buffer,
                                  WS_ENC_UINT8,
                                  (WsUInt8) WS_BC_PRAGMA_USER_AGENT_PROPERTY,
                                  WS_ENC_MB_UINT16, bc->pragmas[ui].index_1,
                                  WS_ENC_MB_UINT16, bc->pragmas[ui].index_2,
                                  WS_ENC_END))
                goto error;
            break;

        case WS_BC_PRAGMA_TYPE_USER_AGENT_PROPERTY_AND_SCHEME:
            if (!ws_encode_buffer(
                    &buffer,
                    WS_ENC_UINT8,
                    (WsUInt8) WS_BC_PRAGMA_USER_AGENT_PROPERTY_AND_SCHEME,
                    WS_ENC_MB_UINT16, bc->pragmas[ui].index_1,
                    WS_ENC_MB_UINT16, bc->pragmas[ui].index_2,
                    WS_ENC_MB_UINT16, bc->pragmas[ui].index_3,
                    WS_ENC_END))
                goto error;
            break;
        }
    }


    /* Function pool. */

    if (!ws_encode_buffer(&buffer,
                          WS_ENC_UINT8, bc->num_functions,
                          WS_ENC_END))
        goto error;

    /* Function names. */

    if (!ws_encode_buffer(&buffer,
                          WS_ENC_UINT8, bc->num_function_names,
                          WS_ENC_END))
        goto error;

    for (ui = 0; ui < bc->num_function_names; ui++) {
        size_t name_len = strlen(bc->function_names[ui].name);

        if (!ws_encode_buffer(&buffer,
                              WS_ENC_UINT8, bc->function_names[ui].index,
                              WS_ENC_UINT8, (WsUInt8) name_len,
                              WS_ENC_DATA, bc->function_names[ui].name, name_len,
                              WS_ENC_END))
            goto error;
    }

    /* Functions. */

    for (ui = 0; ui < bc->num_functions; ui++) {
        if (!ws_encode_buffer(&buffer,
                              WS_ENC_UINT8, bc->functions[ui].num_arguments,
                              WS_ENC_UINT8, bc->functions[ui].num_locals,
                              WS_ENC_MB_UINT32, bc->functions[ui].code_size,
                              WS_ENC_DATA, bc->functions[ui].code,
                              (size_t) bc->functions[ui].code_size,
                              WS_ENC_END))
            goto error;
    }


    /* Fix the byte-code header. */

    p = ws_buffer_ptr(&buffer);

    /* Encode the size of the byte-code excluding the byte-code header. */
    mb = ws_encode_mb_uint32(ws_buffer_len(&buffer) - WS_BC_MAX_HEADER_LEN,
                             data, &len);
    memcpy(p + WS_BC_MAX_HEADER_LEN - len, mb, len);

    /* Set the byte-code file version information. */
    WS_PUT_UINT8(p + WS_BC_MAX_HEADER_LEN - len - 1, WS_BC_VERSION);

    /* Calculate the beginning of the bc-array and its size. */
    *data_return = p + WS_BC_MAX_HEADER_LEN - len - 1;
    *data_len_return = ws_buffer_len(&buffer) - WS_BC_MAX_HEADER_LEN + len + 1;

    /* All done. */
    return WS_TRUE;


    /*
     * Error handling.
     */

error:

    ws_buffer_uninit(&buffer);
    *data_return = NULL;
    *data_len_return = 0;

    return WS_FALSE;
}


void ws_bc_data_free(unsigned char *data)
{
    size_t len = WS_MB_UINT32_MAX_ENCODED_LEN;

    if (data == NULL)
        return;

    /* Decode the mb-encoded length so we know how much space it uses. */
    (void) ws_decode_mb_uint32(data + 1, &len);

    /* Now we can compute the beginning of the array `data'. */
    ws_free(data - (WS_MB_UINT32_MAX_ENCODED_LEN - len));
}


/* A helper macro to update the data pointers during the decoding of
   byte-code data. */
#define WS_UPDATE_DATA	\
    data += decoded;	\
    data_len -= decoded

/* A helper macro to check the validity of the constant string index
   `idx'. */
#define WS_CHECK_STRING(idx)				\
    if ((idx) >= bc->num_constants			\
        || ((bc->constants[(idx)].type			\
             != WS_BC_CONST_TYPE_UTF8_STRING)		\
            && (bc->constants[(idx)].type		\
                != WS_BC_CONST_TYPE_EMPTY_STRING)))	\
        goto error;

WsBc *ws_bc_decode(const unsigned char *data, size_t data_len)
{
    WsBc *bc = ws_bc_alloc(WS_BC_STRING_ENC_ISO_8859_1);
    WsByte b;
    WsUInt32 ui32;
    WsUInt16 ui16, j;
    WsUInt16 ui16b;
    WsUInt8 ui8, num_functions, k, l;
    WsInt8 i8;
    WsInt16 i16;
    WsInt32 i32;
    WsIeee754Result ieee754;
    unsigned char *ucp;
    size_t decoded;

    /* Decode the byte-code header. */
    decoded = ws_decode_buffer(data, data_len,
                               WS_ENC_BYTE, &b,
                               WS_ENC_MB_UINT32, &ui32,
                               WS_ENC_END);

    if (!decoded
        || b != WS_BC_VERSION
        || ui32 != data_len - decoded)
        /* This is not a valid (or supported) byte-code header. */
        goto error;

    WS_UPDATE_DATA;

    /* Constant pool. */

    decoded = ws_decode_buffer(data, data_len,
                               WS_ENC_MB_UINT16, &ui16,
                               WS_ENC_MB_UINT16, &ui16b,
                               WS_ENC_END);
    if (!decoded)
        goto error;

    bc->string_encoding = ui16b;

    bc->constants = ws_calloc(ui16, sizeof(WsBcConstant));
    if (bc->constants == NULL)
        goto error;
    bc->num_constants = ui16;

    WS_UPDATE_DATA;

    for (j = 0; j < bc->num_constants; j++) {
        WsBcConstant *c = &bc->constants[j];

        decoded = ws_decode_buffer(data, data_len,
                                   WS_ENC_UINT8, &ui8,
                                   WS_ENC_END);
        if (decoded != 1)
            goto error;

        WS_UPDATE_DATA;

        switch (ui8) {
        case WS_BC_CONST_INT8:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_INT8, &i8,
                                       WS_ENC_END);
            if (decoded != 1)
                goto error;

            WS_UPDATE_DATA;

            c->type = WS_BC_CONST_TYPE_INT;
            c->u.v_int = i8;
            break;

        case WS_BC_CONST_INT16:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_INT16, &i16,
                                       WS_ENC_END);
            if (decoded != 2)
                goto error;

            WS_UPDATE_DATA;

            c->type = WS_BC_CONST_TYPE_INT;
            c->u.v_int = i16;
            break;

        case WS_BC_CONST_INT32:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_INT32, &i32,
                                       WS_ENC_END);
            if (decoded != 4)
                goto error;

            WS_UPDATE_DATA;

            c->type = WS_BC_CONST_TYPE_INT;
            c->u.v_int = i32;
            break;

        case WS_BC_CONST_FLOAT32:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_DATA, &ucp, (size_t) 4,
                                       WS_ENC_END);
            if (decoded != 4)
                goto error;

            WS_UPDATE_DATA;

            ieee754 = ws_ieee754_decode_single(ucp, &c->u.v_float);

            switch (ieee754) {
            case WS_IEEE754_OK:
                c->type = WS_BC_CONST_TYPE_FLOAT32;
                break;

            case WS_IEEE754_NAN:
                c->type = WS_BC_CONST_TYPE_FLOAT32_NAN;
                break;

            case WS_IEEE754_POSITIVE_INF:
                c->type = WS_BC_CONST_TYPE_FLOAT32_POSITIVE_INF;
                break;

            case WS_IEEE754_NEGATIVE_INF:
                c->type = WS_BC_CONST_TYPE_FLOAT32_NEGATIVE_INF;
                break;
            }

            break;

        case WS_BC_CONST_UTF8_STRING:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_MB_UINT32, &ui32,
                                       WS_ENC_END);
            if (decoded == 0)
                goto error;

            WS_UPDATE_DATA;

            c->type = WS_BC_CONST_TYPE_UTF8_STRING;
            c->u.v_string.len = ui32;

            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_DATA, &ucp, c->u.v_string.len,
                                       WS_ENC_END);
            if (decoded != ui32)
                goto error;

            WS_UPDATE_DATA;

            c->u.v_string.data = ws_memdup(ucp, ui32);
            if (c->u.v_string.data == NULL)
                goto error;

            /* Check the validity of the data. */
            if (!ws_utf8_verify(c->u.v_string.data, c->u.v_string.len,
                                &c->u.v_string.num_chars))
                goto error;
            break;

        case WS_BC_CONST_EMPTY_STRING:
            c->type = WS_BC_CONST_TYPE_EMPTY_STRING;
            break;

        case WS_BC_CONST_EXT_ENC_STRING:
            ws_fatal("external character encoding not implemented yet");
            break;

        default:
            /* Reserved. */
            goto error;
            break;
        }
    }

    /* Pragma pool. */

    decoded = ws_decode_buffer(data, data_len,
                               WS_ENC_MB_UINT16, &ui16,
                               WS_ENC_END);
    if (!decoded)
        goto error;

    bc->pragmas = ws_calloc(ui16, sizeof(WsBcPragma));
    if (bc->pragmas == NULL)
        goto error;
    bc->num_pragmas = ui16;

    WS_UPDATE_DATA;

    for (j = 0; j < bc->num_pragmas; j++) {
        WsBcPragma *p = &bc->pragmas[j];

        decoded = ws_decode_buffer(data, data_len,
                                   WS_ENC_UINT8, &ui8,
                                   WS_ENC_END);
        if (decoded != 1)
            goto error;

        WS_UPDATE_DATA;

        p->type = ui8;

        switch (ui8) {
        case WS_BC_PRAGMA_ACCESS_DOMAIN:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_MB_UINT16, &p->index_1,
                                       WS_ENC_END);
            if (!decoded)
                goto error;

            WS_CHECK_STRING(p->index_1);
            break;

        case WS_BC_PRAGMA_ACCESS_PATH:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_MB_UINT16, &p->index_1,
                                       WS_ENC_END);
            if (!decoded)
                goto error;

            WS_CHECK_STRING(p->index_1);
            break;

        case WS_BC_PRAGMA_USER_AGENT_PROPERTY:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_MB_UINT16, &p->index_1,
                                       WS_ENC_MB_UINT16, &p->index_2,
                                       WS_ENC_END);
            if (!decoded)
                goto error;

            WS_CHECK_STRING(p->index_1);
            WS_CHECK_STRING(p->index_2);
            break;

        case WS_BC_PRAGMA_USER_AGENT_PROPERTY_AND_SCHEME:
            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_MB_UINT16, &p->index_1,
                                       WS_ENC_MB_UINT16, &p->index_2,
                                       WS_ENC_MB_UINT16, &p->index_3,
                                       WS_ENC_END);
            if (!decoded)
                goto error;

            WS_CHECK_STRING(p->index_1);
            WS_CHECK_STRING(p->index_2);
            WS_CHECK_STRING(p->index_3);
            break;

        default:
            goto error;
            break;
        }

        WS_UPDATE_DATA;
    }

    /* Function pool. */

    decoded = ws_decode_buffer(data, data_len,
                               WS_ENC_UINT8, &num_functions,
                               WS_ENC_END);
    if (decoded != 1)
        goto error;

    WS_UPDATE_DATA;

    /* Function names. */

    decoded = ws_decode_buffer(data, data_len,
                               WS_ENC_UINT8, &ui8,
                               WS_ENC_END);
    if (decoded != 1)
        goto error;

    WS_UPDATE_DATA;

    if (ui8) {
        /* We have function names. */
        bc->function_names = ws_calloc(ui8, sizeof(WsBcFunctionName));
        if (bc->function_names == NULL)
            goto error;
        bc->num_function_names = ui8;

        for (k = 0; k < bc->num_function_names; k++) {
            WsBcFunctionName *n = &bc->function_names[k];

            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_UINT8, &n->index,
                                       WS_ENC_UINT8, &ui8,
                                       WS_ENC_END);
            if (decoded != 2)
                goto error;

            WS_UPDATE_DATA;

            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_DATA, &ucp, (size_t) ui8,
                                       WS_ENC_END);
            if (decoded != ui8)
                goto error;

            WS_UPDATE_DATA;

            n->name = ws_memdup(ucp, ui8);
            if (n->name == NULL)
                goto error;

            /* Check the validity of the name. */

            if (!ws_utf8_verify((unsigned char *) n->name, ui8, NULL))
                goto error;

            /* Just check that the data contains only valid characters. */
            for (l = 0; l < ui8; l++) {
                unsigned int ch = (unsigned char) n->name[l];

                if (('a' <= ch && ch <= 'z')
                    || ('A' <= ch && ch <= 'Z')
                    || ch == '_'
                    || (l > 0 && ('0' <= ch && ch <= '9')))
                    /* Ok. */
                    continue;

                /* Invalid character in the function name. */
                goto error;
            }

            /* Is the index valid? */
            if (n->index >= num_functions)
                goto error;
        }
    }

    /* Functions. */

    if (num_functions) {
        /* We have functions. */
        bc->functions = ws_calloc(num_functions, sizeof(WsBcFunction));
        if (bc->functions == NULL)
            goto error;
        bc->num_functions = num_functions;

        for (k = 0; k < bc->num_functions; k++) {
            WsBcFunction *f = &bc->functions[k];

            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_UINT8, &f->num_arguments,
                                       WS_ENC_UINT8, &f->num_locals,
                                       WS_ENC_MB_UINT32, &f->code_size,
                                       WS_ENC_END);
            if (!decoded)
                goto error;

            WS_UPDATE_DATA;

            decoded = ws_decode_buffer(data, data_len,
                                       WS_ENC_DATA, &ucp, f->code_size,
                                       WS_ENC_END);
            if (decoded != f->code_size)
                goto error;

            WS_UPDATE_DATA;

            if (f->code_size) {
                /* It is not an empty function. */
                f->code = ws_memdup(ucp, f->code_size);
                if (f->code == NULL)
                    goto error;
            }
        }
    }

    /* Did we process it all? */
    if (data_len != 0)
        goto error;

    /* All done. */
    return bc;

    /*
     * Error handling.
     */

error:

    ws_bc_free(bc);

    return NULL;
}

/********************* Adding constant elements *************************/

WsBool ws_bc_add_const_int(WsBc *bc, WsUInt16 *index_return, WsInt32 value)
{
    WsUInt16 i;
    WsBcConstant *nc;

    /* Do we already have a suitable integer constant? */
    for (i = 0; i < bc->num_constants; i++) {
        if (bc->constants[i].type == WS_BC_CONST_TYPE_INT
            && bc->constants[i].u.v_int == value) {
            *index_return = i;
            return WS_TRUE;
        }
    }

    /* Must add a new constant. */

    nc = ws_realloc(bc->constants,
                    (bc->num_constants + 1) * sizeof(WsBcConstant));
    if (nc == NULL)
        return WS_FALSE;

    bc->constants = nc;
    bc->constants[bc->num_constants].type = WS_BC_CONST_TYPE_INT;
    bc->constants[bc->num_constants].u.v_int = value;

    *index_return = bc->num_constants++;

    return WS_TRUE;
}


WsBool ws_bc_add_const_float(WsBc *bc, WsUInt16 *index_return, WsFloat value)
{
    WsUInt16 i;
    WsBcConstant *nc;

    /* Do we already have a suitable float32 constant? */
    for (i = 0; i < bc->num_constants; i++) {
        if (bc->constants[i].type == WS_BC_CONST_TYPE_FLOAT32
            && bc->constants[i].u.v_float == value) {
            *index_return = i;
            return WS_TRUE;
        }
    }

    /* Must add a new constant. */

    nc = ws_realloc(bc->constants,
                    (bc->num_constants + 1) * sizeof(WsBcConstant));
    if (nc == NULL)
        return WS_FALSE;

    bc->constants = nc;
    bc->constants[bc->num_constants].type = WS_BC_CONST_TYPE_FLOAT32;
    bc->constants[bc->num_constants].u.v_float = value;

    *index_return = bc->num_constants++;

    return WS_TRUE;
}


WsBool ws_bc_add_const_utf8_string(WsBc *bc, WsUInt16 *index_return,
                                   const unsigned char *data, size_t len)
{
    WsUInt16 i;
    WsBcConstant *nc;

    /* Do we already have a suitable UFT-8 constant? */
    for (i = 0; i < bc->num_constants; i++) {
        if (bc->constants[i].type == WS_BC_CONST_TYPE_UTF8_STRING
            && bc->constants[i].u.v_string.len == len
            && memcmp(bc->constants[i].u.v_string.data,
                      data, len) == 0) {
            *index_return = i;
            return WS_TRUE;
        }
    }

    /* Must add a new constant. */

    nc = ws_realloc(bc->constants,
                    (bc->num_constants + 1) * sizeof(WsBcConstant));
    if (nc == NULL)
        return WS_FALSE;

    bc->constants = nc;
    bc->constants[bc->num_constants].type = WS_BC_CONST_TYPE_UTF8_STRING;
    bc->constants[bc->num_constants].u.v_string.len = len;
    bc->constants[bc->num_constants].u.v_string.data
    = ws_memdup(data, len);
    if (bc->constants[bc->num_constants].u.v_string.data == NULL)
        return WS_FALSE;

    *index_return = bc->num_constants++;

    return WS_TRUE;
}



WsBool ws_bc_add_const_empty_string(WsBc *bc, WsUInt16 *index_return)
{
    WsUInt16 i;
    WsBcConstant *nc;

    /* Do we already have a suitable empty string constant? */
    for (i = 0; i < bc->num_constants; i++) {
        if (bc->constants[i].type == WS_BC_CONST_TYPE_EMPTY_STRING) {
            *index_return = i;
            return WS_TRUE;
        }
    }

    /* Must add a new constant. */

    nc = ws_realloc(bc->constants,
                    (bc->num_constants + 1) * sizeof(WsBcConstant));
    if (nc == NULL)
        return WS_FALSE;

    bc->constants = nc;
    bc->constants[bc->num_constants].type = WS_BC_CONST_TYPE_EMPTY_STRING;

    *index_return = bc->num_constants++;

    return WS_TRUE;
}

/********************* Adding pragmas ***********************************/

WsBool ws_bc_add_pragma_access_domain(WsBc *bc, const unsigned char *domain,
                                      size_t domain_len)
{
    WsBcPragma *p = add_pragma(bc, WS_BC_PRAGMA_TYPE_ACCESS_DOMAIN);

    if (p == NULL)
        return WS_FALSE;

    if (!ws_bc_add_const_utf8_string(bc, &p->index_1, domain, domain_len))
        return WS_FALSE;

    return WS_TRUE;
}


WsBool ws_bc_add_pragma_access_path(WsBc *bc, const unsigned char *path,
                                    size_t path_len)
{
    WsBcPragma *p = add_pragma(bc, WS_BC_PRAGMA_TYPE_ACCESS_PATH);

    if (p == NULL)
        return WS_FALSE;

    if (!ws_bc_add_const_utf8_string(bc, &p->index_1, path, path_len))
        return WS_FALSE;

    return WS_TRUE;
}


WsBool ws_bc_add_pragma_user_agent_property(WsBc *bc,
                                            const unsigned char *name,
                                            size_t name_len,
                                            const unsigned char *property,
                                            size_t property_len)
{
    WsBcPragma *p = add_pragma(bc, WS_BC_PRAGMA_TYPE_USER_AGENT_PROPERTY);

    if (p == NULL)
        return WS_FALSE;

    if (!ws_bc_add_const_utf8_string(bc, &p->index_1, name, name_len)
        || !ws_bc_add_const_utf8_string(bc, &p->index_2, property, property_len))
        return WS_FALSE;

    return WS_TRUE;
}


WsBool ws_bc_add_pragma_user_agent_property_and_scheme(
    WsBc *bc,
    const unsigned char *name,
    size_t name_len,
    const unsigned char *property,
    size_t property_len,
    const unsigned char *scheme,
    size_t scheme_len)
{
    WsBcPragma *p;

    p = add_pragma(bc, WS_BC_PRAGMA_TYPE_USER_AGENT_PROPERTY_AND_SCHEME);

    if (p == NULL)
        return WS_FALSE;

    if (!ws_bc_add_const_utf8_string(bc, &p->index_1, name, name_len)
        || !ws_bc_add_const_utf8_string(bc, &p->index_2, property, property_len)
        || !ws_bc_add_const_utf8_string(bc, &p->index_3, scheme, scheme_len))
        return WS_FALSE;

    return WS_TRUE;
}

/********************* Adding functions *********************************/

WsBool ws_bc_add_function(WsBc *bc, WsUInt8 *index_return, char *name,
                          WsUInt8 num_arguments, WsUInt8 num_locals,
                          WsUInt32 code_size, unsigned char *code)
{
    WsBcFunction *nf;

    /* First, add the function to the function pool. */

    nf = ws_realloc(bc->functions,
                    (bc->num_functions + 1) * sizeof(WsBcFunction));
    if (nf == NULL)
        return WS_FALSE;

    bc->functions = nf;
    bc->functions[bc->num_functions].num_arguments = num_arguments;
    bc->functions[bc->num_functions].num_locals = num_locals;
    bc->functions[bc->num_functions].code_size = code_size;
    bc->functions[bc->num_functions].code = ws_memdup(code, code_size);

    if (bc->functions[bc->num_functions].code == NULL)
        return WS_FALSE;

    /* Save the index of the function. */
    *index_return = bc->num_functions++;

    /* For external functions (which have name), add a name entry to the
       function name pool. */
    if (name) {
        WsBcFunctionName *nfn;

        nfn = ws_realloc(bc->function_names,
                         ((bc->num_function_names + 1)
                          * sizeof(WsBcFunctionName)));
        if (nfn == NULL)
            return WS_FALSE;

        bc->function_names = nfn;
        bc->function_names[bc->num_function_names].index = *index_return;
        bc->function_names[bc->num_function_names].name = ws_strdup(name);

        if (bc->function_names[bc->num_function_names].name == NULL)
            return WS_FALSE;

        bc->num_function_names++;
    }

    /* All done. */
    return WS_TRUE;
}

/********************* Static functions *********************************/

static WsBcPragma *add_pragma(WsBc *bc, WsBcPragmaType type)
{
    WsBcPragma *np;

    /* Add a new pragma slot. */
    np = ws_realloc(bc->pragmas, (bc->num_pragmas + 1) * sizeof(WsBcPragma));
    if (np == NULL)
        return NULL;

    bc->pragmas = np;
    bc->pragmas[bc->num_pragmas].type = type;

    return &bc->pragmas[bc->num_pragmas++];
}
