/*
 * $Id: /mirror/trunk/languages/dotnet/pmc/dotnetsignature.pmc 29983 2008-08-02T22:45:13.271765Z allison  $
 * Copyright (C) 2006-2008, The Perl Foundation.
 */

/* .NET CLI Signature PMC */


#include "parrot/extend.h"
#include "tableinfo.h"
#include "structures.h"


pmclass DotNetSignature dynpmc group dotnet {

    /* Instance initialization. We need a custom destroy. */
    void init()
    {
        PObj_active_destroy_SET(SELF);
        PMC_struct_val(SELF) = NULL;
    }


    /* Destructor. */
    void destroy()
    {
        /* Cleanup any memory we're using. */
        if (PMC_struct_val(SELF)) {
            dotnet_signature *sig = (dotnet_signature *)PMC_struct_val(SELF);

            if (sig->data)
                mem_sys_free(sig->data);

            mem_sys_free(sig);
            PMC_struct_val(SELF) = NULL;
        }
    }


    /* set_string_native copies the data held in the string to the internal
       blob data buffer so we can easily walk over it. */
    void set_string_native(STRING* s)
    {
        dotnet_signature *sig = (dotnet_signature *)PMC_struct_val(SELF);

        /* Do we have a struct already allocated? */
        if (sig != NULL)
        {
            /* Yes; free up old string. */
            if (sig->data)
                free(sig->data);
        }
        else
        {
            /* No; need to allocate a new structure. */
            sig                  = mem_allocate_zeroed_typed(dotnet_signature);
            PMC_struct_val(SELF) = sig;
        }

        /* Get blob data and its size and reset current position. */
        sig->data      = string_to_cstring(INTERP, s);
        sig->data_size = string_length(INTERP, s);
        sig->cur_pos   = 0;
    }


    /* Read an unsigned 8-bit integer. */
    METHOD INTVAL read_uint8()
    {
        dotnet_signature *sig = (dotnet_signature *)PMC_struct_val(SELF);
        INTVAL            result;

        /* Handle cases where we've an invalid PMC. */
        if (!sig || !sig->data)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
               "Invalid DotNetSignature PMC");

        /* Ensure we won't read past the end. */
        if (sig->cur_pos >= sig->data_size)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
               "Read past end of signature");

        /* Otherwise read the value. */
        result = (INTVAL)sig->data[sig->cur_pos];
        sig->cur_pos++;
        RETURN(INTVAL result);
    }


    /* Read a compressed integer. */
    METHOD INTVAL read_compressed()
    {
        dotnet_signature *sig = (dotnet_signature *)PMC_struct_val(SELF);
        INTVAL val;
        unsigned char b1, b2, b3, b4;

        /* Handle cases where we've an invalid PMC. */
        if (!sig || !sig->data)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
               "Invalid DotNetSignature PMC");

        /* Check first byte read is OK. */
        if (sig->cur_pos >= sig->data_size)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
               "Read past end of signature");

        /* Read first byte and decide if it's a single encoded byte (top bit
           clear). */
        b1 = sig->data[sig->cur_pos];

        if ((b1 & 0x80) == 0)
        {
            val = b1;
            sig->cur_pos++;
        }

        /* Is it a 2 byte integer (top bits 10)? */
        else if ((b1 & 0x80) != 0 && (b1 & 0x40) == 0)
        {
            /* Check we can read 2 bytes. */
            if (sig->cur_pos + 1 >= sig->data_size)
                Parrot_ex_throw_from_c_args(INTERP, NULL,
                    EXCEPTION_INVALID_OPERATION, "Read past end of signature");

            /* Read second byte and make value. */
            b2  = sig->data[sig->cur_pos + 1];
            val = ((b1 & 0x3F) << 8) | b2;
            sig->cur_pos += 2;
        }

        /* Is it a 4 byte integer? (top bits 110)? */
        else if ((b1 & 0x80) != 0 && (b1 & 0x40) != 0 && (b1 & 0x20) == 0) {
            /* Check we can read 4 bytes. */
            if (sig->cur_pos + 3 >= sig->data_size)
                Parrot_ex_throw_from_c_args(INTERP, NULL,
                    EXCEPTION_INVALID_OPERATION, "Read past end of signature");

            /* Read remaining bytes and make value. */
            b2 = sig->data[sig->cur_pos + 1];
            b3 = sig->data[sig->cur_pos + 2];
            b4 = sig->data[sig->cur_pos + 3];
            val = ((b1 & 0x1F) << 24) | (b2 << 16) | (b3 << 8) | b4;
            sig->cur_pos += 4;
        }

        /* Otherwise, it's a null byte. RT #48128 Need special handling? */
        else {
            val = 0;
            sig->cur_pos++;
        }

        /* Return the value. */
        RETURN(INTVAL val);
    }
}


/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
