Milán Major

netscape-revival

1 branch
Code

sun-java/runtime/utf.c

/*
 * @(#)utf.c	1.10 95/10/16  
 *
 * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

#include "oobj.h"
#include <stdio.h>
#include <string.h>
#include "utf.h"
#include "sys_api.h"

/* This file implements the unicode -> UTF and UTF -> unicode translaters
 * needed by the various parts of the compiler and interpreter.
 *
 * UTF strings are streams of bytes, in which unicode characters are encoded
 * as follows:
 *       Unicode                  UTF
 *       00000000 0jklmnop       0jklmnop 
 *       00000fgh ijklmnop       110fghij 10klmnop
 *       abcdefgh ijklmnop       1110abcd 10efghij 10klmnop
 *
 * unicode bytes with 7 or fewer significant bits MUST be converted using the
 * first format.  bytes with 11 or fewer bits MUST be converted using the
 * second format.
 *
 * In JAVA/JAVAC, we deviate slightly from the above.  
 *    1) The null unicode character is represented using the 2-byte format
 *    2)  All UTF strings are null-terminated.
 * In this way, we do not need to separately maintain a length field for the
 * UTF string.
 */


/* Given a unicode string and its length, convert it to a utf string.  But 
 * the result into the given buffer, whose length is buflength.  The utf
 * string should include a null terminator.
 *
 * If both buffer and buflength are 0, then malloc an appropriately sized
 * buffer for the result.
 */

char *unicode2utf(unicode *unistring, uint32_t length, char *buffer, uint32_t buflength)
{
    int32_t i;
    unicode *uniptr;
    char *bufptr;
    unsigned bufleft;

    if ((buffer == 0) && (buflength == 0)) {
	buflength = unicode2utfstrlen(unistring, length);
	buffer = (char *) sysMalloc(buflength);
    }

    bufleft = buflength - 1; /* take note of null now! */

    for(i = length, uniptr = unistring, bufptr = buffer; --i >= 0; uniptr++) {
	unicode ch = *uniptr;
	if ((ch != 0) && (ch <=0x7f)) {
	    if ((int)(--bufleft) < 0)	/* no space for character */
	        break;
	    *bufptr++ = (char)ch;
	} else if (ch <= 0x7FF) { 
	    /* 11 bits or less. */
	    unsigned char high_five = (unsigned char) (ch >> 6);
	    unsigned char low_six = (unsigned char) (ch & 0x3F);
	    if ((int)(bufleft -= 2) < 0) /* no space for character */
	        break;
	    *bufptr++ = high_five | 0xC0; /* 110xxxxx */
	    *bufptr++ = low_six | 0x80;	  /* 10xxxxxx */
	} else {
	    /* possibly full 16 bits. */
	    char high_four = (char) (ch >> 12);
	    char mid_six = (char) ((ch >> 6) & 0x3F);
	    char low_six = (char) (ch & 0x3f);
	    if ((int)(bufleft -= 3) < 0) /* no space for character */
	        break;
	    *bufptr++ = high_four | 0xE0; /* 1110xxxx */
	    *bufptr++ = mid_six | 0x80;   /* 10xxxxxx */
	    *bufptr++ = low_six | 0x80;   /* 10xxxxxx*/
	}
    }
    *bufptr = 0;
    return buffer;
}

/* Return the number of characters that would be needed to hold the unicode
 * string in utf.  This INCLUDES the NULL!
 */
int unicode2utfstrlen(unicode *unistring, uint32_t unilength)
{
    int result_length = 1;
    
    for (; unilength > 0; unistring++, unilength--) {
	unicode ch = *unistring;
	if ((ch != 0) && (ch <= 0x7f)) /* 1 byte */
	    result_length++;
	else if (ch <= 0x7FF)
	    result_length += 2;	/* 2 byte character */
	else 
	    result_length += 3;	/* 3 byte character */
    }
    return result_length;
}

/* Give the number of unicode characters in a utf string */
int utfstrlen(char *utfstring) 
{
    int length;
    for (length = 0; *utfstring != 0; length++) 
        next_utf2unicode(&utfstring);
    return length;
}

/* Convert a utfstring to unicode in the buffer provided.  Put at most
 * max_length characters into the buffer.  Whether or not we actually overflow
 * the space, indicate the actual unicode length.
 *
 * Whether or not we overflow the space, return the actual number of
 * characters that we used.
 */

void
utf2unicode(char *utfstring, unicode *unistring, 
	    int max_length, int *lengthp)
{
    int length_remaining = max_length;
    
    while (length_remaining > 0 && *utfstring != 0) {
	*unistring++ = next_utf2unicode(&utfstring);
	length_remaining--;
    }

    if (length_remaining == 0) {
	*lengthp = max_length + utfstrlen(utfstring);
    } else {
	*lengthp = max_length - length_remaining;
    }
}

bool_t is_simple_utf(char *utfstring) 
{
    unsigned char *ptr;
    for (ptr = (unsigned char *)utfstring; *ptr != 0; ptr++) {
	if (*ptr > 0x80) return FALSE;
    }
    return TRUE;
}


unicode next_utf2unicode(char **utfstring_ptr) {
    unsigned char *ptr = (unsigned char *)(*utfstring_ptr);
    unsigned char ch, ch2, ch3;
    int length = 1;		/* default length */
    unicode result = 0x80;	/* default bad result; */
    switch ((ch = ptr[0]) >> 4) {
        default:
	    result = ch;
	    break;

	case 0x8: case 0x9: case 0xA: case 0xB: case 0xF:
	    /* Shouldn't happen. */
	    break;

	case 0xC: case 0xD:	
	    /* 110xxxxx  10xxxxxx */
	    if (((ch2 = ptr[1]) & 0xC0) == 0x80) {
		unsigned char high_five = ch & 0x1F;
		unsigned char low_six = ch2 & 0x3F;
		result = (high_five << 6) + low_six;
		length = 2;
	    } 
	    break;

	case 0xE:
	    /* 1110xxxx 10xxxxxx 10xxxxxx */
	    if (((ch2 = ptr[1]) & 0xC0) == 0x80) {
		if (((ch3 = ptr[2]) & 0xC0) == 0x80) {
		    unsigned char high_four = ch & 0x0f;
		    unsigned char mid_six = ch2 & 0x3f;
		    unsigned char low_six = ch3 & 0x3f;
		    result = (((high_four << 6) + mid_six) << 6) + low_six;
		    length = 3;
		} else {
		    length = 2;
		}
	    }
	    break;
	} /* end of switch */

    *utfstring_ptr = (char *)(ptr + length);
    return result;
}