//
// $Id: RunTime.m,v 1.3 2005/05/18 06:46:54 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004
 * Will Mason
 *
 * Portions:
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999 
 * Boris Fomitchev
 *
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * You may contact the author at will_mason@users.sourceforge.net.
 */

#if defined(__NEXT_RUNTIME__)

#import "RunTime.h"
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#import <ctype.h>
#import <stdlib.h>

int objc_aligned_size(const char* type)
{
    int size = objc_sizeof_type(type);
    int alignment = objc_alignof_type(type);
    return alignment * ((size + alignment - 1) / alignment);
}

int objc_alignof_type(const char* type)
{
    struct objc_struct_layout layout;
    int alignment;

    switch (*type)
    {
    case _C_CHR:
    case _C_UCHR:
        alignment = __alignof__(char);
        break;
    case _C_SHT:
    case _C_USHT:
        alignment = __alignof__(short);
        break;
    case _C_INT:
    case _C_UINT:
        alignment = __alignof__(int);
        break;
    case _C_LNG:
    case _C_ULNG:
        alignment = __alignof__(long);
        break;
    case _C_LNG_LNG:
    case _C_ULNG_LNG:
        alignment = __alignof__(long long);
        break;
    case _C_FLT:
        alignment = __alignof__(float);
        break;
    case _C_DBL:
        alignment = __alignof__(double);
        break;
    case _C_PTR:
    case _C_CHARPTR:
        alignment = __alignof__(void*);
        break;
    case _C_ID:
        alignment = __alignof__(id);
        break;
    case _C_CLASS:
        alignment = __alignof__(Class);
        break;
    case _C_SEL:
        alignment = __alignof__(SEL);
        break;
    case _C_ARY_B:
        do
        {
            type++;
        } while (isdigit(*type));
        alignment = objc_alignof_type(type);
        break;
    case _C_STRUCT_B:
        objc_layout_structure(type, &layout);
        while (objc_layout_structure_next_member(&layout));
        objc_layout_finish_structure(&layout, NULL, &alignment);
        break;
    case _C_UNION_B:
        while (*type != '=')
            type++;
        for (type++, alignment = 0 ; *type != _C_UNION_E; type = objc_skip_typespec(type))
            alignment = MAX(alignment, objc_sizeof_type(type));
        break;
    default:
        [NSException raise: NSInvalidArgumentException
            format: @"Invalid type: %s", type];
    }
    return alignment;
}

void objc_layout_finish_structure(struct objc_struct_layout* layout,
                                  int* size,
                                  int* align)
{
    if (layout->type != NULL &&
        *layout->type == _C_STRUCT_E)
    {
        layout->record_size =
            layout->record_align *
            ((layout->record_size + layout->record_align - 1) / layout->record_align);
        layout->type = NULL;
    }
    if (size != NULL)
        *size = layout->record_size;
    if (align != NULL)
        *align = layout->record_align;
}

void objc_layout_structure(const char* type, struct objc_struct_layout* layout)
{
    BOOL gotName = NO;

    layout->original_type = ++type;
    while (*type != _C_STRUCT_E &&
           *type != _C_STRUCT_B &&
           *type != _C_UNION_B)
    {
        if (*type++ == '=')
        {
            gotName = YES;
            break;
        }
    }
    layout->type = gotName ? type : layout->original_type;
    layout->prev_type = NULL;
    layout->record_size = 0;
    layout->record_align = 0;
}

void objc_layout_structure_get_info(struct objc_struct_layout* layout,
                                    int* offset,
                                    int* align,
                                    const char** type)
{
    if (offset != NULL)
        *offset = layout->record_size;
    if (align != NULL)
        *align = layout->record_align;
    if (type != NULL)
        *type = layout->prev_type;
}

BOOL objc_layout_structure_next_member(struct objc_struct_layout* layout)
{
    int possibleAlignment;

    if (layout->prev_type != NULL)
        layout->record_size += objc_sizeof_type(objc_skip_type_qualifiers(layout->prev_type));
    if (*layout->type == _C_STRUCT_E)
        return NO;
    possibleAlignment = objc_alignof_type(objc_skip_type_qualifiers(layout->type));
    layout->record_align = MAX(layout->record_align, possibleAlignment);
    if (layout->record_size % possibleAlignment)
    {
        layout->record_size =
            possibleAlignment * ((layout->record_size + possibleAlignment - 1) / possibleAlignment);
    }
    layout->prev_type = layout->type;
    layout->type = objc_skip_typespec(layout->type);
    return YES;
}

int objc_sizeof_type(const char* type)
{
    struct objc_struct_layout layout;
    int size;
    char* endOfNum;
    unsigned arrayCount;

    switch (*type)
    {
    case _C_CHR:
    case _C_UCHR:
        size = sizeof(char);
        break;
    case _C_SHT:
    case _C_USHT:
        size = sizeof(short);
        break;
    case _C_INT:
    case _C_UINT:
        size = sizeof(int);
        break;
    case _C_LNG:
    case _C_ULNG:
        size = sizeof(long);
        break;
    case _C_LNG_LNG:
    case _C_ULNG_LNG:
        size = sizeof(long long);
        break;
    case _C_FLT:
        size = sizeof(float);
        break;
    case _C_DBL:
        size = sizeof(double);
        break;
    case _C_PTR:
    case _C_CHARPTR:
        size = sizeof(void*);
        break;
    case _C_ID:
        size = sizeof(id);
        break;
    case _C_CLASS:
        size = sizeof(Class);
        break;
    case _C_SEL:
        size = sizeof(SEL);
        break;
    case _C_ARY_B:
        arrayCount = strtol(++type, &endOfNum, 10);
        size = arrayCount * objc_aligned_size(endOfNum);
        break;
    case _C_STRUCT_B:
        objc_layout_structure(type, &layout);
        while (objc_layout_structure_next_member(&layout));
        objc_layout_finish_structure(&layout, NULL, &size);
        break;
    case _C_UNION_B:
        while (*type != '=')
            type++;
        for (type++, size = 0 ; *type != _C_UNION_E; type = objc_skip_typespec(type))
            size = MAX(size, objc_sizeof_type(type));
        break;
    default:
        [NSException raise: NSInvalidArgumentException
            format: @"Invalid type: %s", type];
    }
    return size;
}

const char* objc_skip_type_qualifiers(const char* type)
{
    while (*type == _C_CONST ||
           *type == _C_IN ||
           *type == _C_INOUT ||
           *type == _C_OUT ||
           *type == _C_BYCOPY ||
           *type == _C_BYREF ||
           *type == _C_ONEWAY)
    {
        type++;
    }
    return type;
}

const char* objc_skip_typespec(const char* type)
{
    type = objc_skip_type_qualifiers(type);
    switch (*type)
    {
    case _C_CHR:
    case _C_UCHR:
    case _C_SHT:
    case _C_USHT:
    case _C_INT:
    case _C_UINT:
    case _C_LNG:
    case _C_ULNG:
    case _C_LNG_LNG:
    case _C_ULNG_LNG:
    case _C_FLT:
    case _C_DBL:
    case _C_CHARPTR:
    case _C_ID:
    case _C_CLASS:
    case _C_SEL:
        type++;
        break;
    case _C_PTR:
        type = objc_skip_typespec(++type);
        break;
    case _C_ARY_B:
        while (isdigit(*++type));
        type = objc_skip_typespec(type) + 1;
        break;
    case _C_STRUCT_B:
        while (*type != _C_STRUCT_E && *type++ != '=');
        while (*type != _C_STRUCT_E)
            type = objc_skip_typespec(type);
        type++;
        break;
    case _C_UNION_B:
        while (*type != _C_UNION_E && *type++ != '=');
        while (*type != _C_UNION_E)
            type = objc_skip_typespec(type);
        type++;
        break;
    default:
        [NSException raise: NSInvalidArgumentException
            format: @"Invalid type: %s", type];
    }
    return type;
}

#endif
