blob: c4cb7b17293646b9cba23e115efef84f163dee5e [file] [log] [blame]
/*
*******************************************************************************
*
* Copyright (C) 2002-2005, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
* file name: propsvec.c
* encoding: US-ASCII
* tab size: 8 (not used)
* indentation:4
*
* created on: 2002feb22
* created by: Markus W. Scherer
*
* Store additional Unicode character properties in bit set vectors.
*/
#include <stdlib.h>
#include "unicode/utypes.h"
#include "cmemory.h"
#include "utrie.h"
#include "uarrsort.h"
#include "propsvec.h"
static uint32_t *
_findRow(uint32_t *pv, UChar32 rangeStart) {
uint32_t *row;
int32_t *hdr;
int32_t columns, i, start, limit, prevRow, rows;
hdr=(int32_t *)pv;
columns=hdr[UPVEC_COLUMNS];
limit=hdr[UPVEC_ROWS];
prevRow=hdr[UPVEC_PREV_ROW];
rows=hdr[UPVEC_ROWS];
pv+=UPVEC_HEADER_LENGTH;
/* check the vicinity of the last-seen row */
if(prevRow<rows) {
row=pv+prevRow*columns;
if(rangeStart>=(UChar32)row[0]) {
if(rangeStart<(UChar32)row[1]) {
/* same row as last seen */
return row;
} else if(
++prevRow<rows &&
rangeStart>=(UChar32)(row+=columns)[0] && rangeStart<(UChar32)row[1]
) {
/* next row after the last one */
hdr[UPVEC_PREV_ROW]=prevRow;
return row;
}
}
}
/* do a binary search for the start of the range */
start=0;
while(start<limit-1) {
i=(start+limit)/2;
row=pv+i*columns;
if(rangeStart<(UChar32)row[0]) {
limit=i;
} else if(rangeStart<(UChar32)row[1]) {
hdr[UPVEC_PREV_ROW]=i;
return row;
} else {
start=i;
}
}
/* must be found because all ranges together always cover all of Unicode */
hdr[UPVEC_PREV_ROW]=start;
return pv+start*columns;
}
U_CAPI uint32_t * U_EXPORT2
upvec_open(int32_t columns, int32_t maxRows) {
uint32_t *pv, *row;
int32_t length;
if(columns<1 || maxRows<1) {
return NULL;
}
columns+=2; /* count range start and limit columns */
length=UPVEC_HEADER_LENGTH+maxRows*columns;
pv=(uint32_t *)uprv_malloc(length*4);
if(pv!=NULL) {
/* set header */
pv[UPVEC_COLUMNS]=(uint32_t)columns;
pv[UPVEC_MAXROWS]=(uint32_t)maxRows;
pv[UPVEC_ROWS]=1;
pv[UPVEC_PREV_ROW]=0;
/* set initial row */
row=pv+UPVEC_HEADER_LENGTH;
*row++=0;
*row++=0x110000;
columns-=2;
do {
*row++=0;
} while(--columns>0);
}
return pv;
}
U_CAPI void U_EXPORT2
upvec_close(uint32_t *pv) {
if(pv!=NULL) {
uprv_free(pv);
}
}
U_CAPI UBool U_EXPORT2
upvec_setValue(uint32_t *pv,
UChar32 start, UChar32 limit,
int32_t column,
uint32_t value, uint32_t mask,
UErrorCode *pErrorCode) {
uint32_t *firstRow, *lastRow;
int32_t columns;
UBool splitFirstRow, splitLastRow;
/* argument checking */
if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
return FALSE;
}
if( pv==NULL ||
start<0 || start>limit || limit>0x110000 ||
column<0 || (uint32_t)(column+1)>=pv[UPVEC_COLUMNS]
) {
*pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
return FALSE;
}
if(start==limit) {
/* empty range, nothing to do */
return TRUE;
}
/* initialize */
columns=(int32_t)pv[UPVEC_COLUMNS];
column+=2; /* skip range start and limit columns */
value&=mask;
/* find the rows whose ranges overlap with the input range */
/* find the first row, always successful */
firstRow=_findRow(pv, start);
/* find the last row, always successful */
lastRow=firstRow;
while(limit>(UChar32)lastRow[1]) {
lastRow+=columns;
}
/*
* Rows need to be split if they partially overlap with the
* input range (only possible for the first and last rows)
* and if their value differs from the input value.
*/
splitFirstRow= (UBool)(start!=(UChar32)firstRow[0] && value!=(firstRow[column]&mask));
splitLastRow= (UBool)(limit!=(UChar32)lastRow[1] && value!=(lastRow[column]&mask));
/* split first/last rows if necessary */
if(splitFirstRow || splitLastRow) {
int32_t count, rows;
rows=(int32_t)pv[UPVEC_ROWS];
if((rows+splitFirstRow+splitLastRow)>(int32_t)pv[UPVEC_MAXROWS]) {
*pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
return FALSE;
}
/* count the number of row cells to move after the last row, and move them */
count = (int32_t)((pv+UPVEC_HEADER_LENGTH+rows*columns)-(lastRow+columns));
if(count>0) {
uprv_memmove(
lastRow+(1+splitFirstRow+splitLastRow)*columns,
lastRow+columns,
count*4);
}
pv[UPVEC_ROWS]=rows+splitFirstRow+splitLastRow;
/* split the first row, and move the firstRow pointer to the second part */
if(splitFirstRow) {
/* copy all affected rows up one and move the lastRow pointer */
count = (int32_t)((lastRow-firstRow)+columns);
uprv_memmove(firstRow+columns, firstRow, count*4);
lastRow+=columns;
/* split the range and move the firstRow pointer */
firstRow[1]=firstRow[columns]=(uint32_t)start;
firstRow+=columns;
}
/* split the last row */
if(splitLastRow) {
/* copy the last row data */
uprv_memcpy(lastRow+columns, lastRow, columns*4);
/* split the range and move the firstRow pointer */
lastRow[1]=lastRow[columns]=(uint32_t)limit;
}
}
/* set the "row last seen" to the last row for the range */
pv[UPVEC_PREV_ROW]=(uint32_t)((lastRow-(pv+UPVEC_HEADER_LENGTH))/columns);
/* set the input value in all remaining rows */
firstRow+=column;
lastRow+=column;
mask=~mask;
for(;;) {
*firstRow=(*firstRow&mask)|value;
if(firstRow==lastRow) {
break;
}
firstRow+=columns;
}
return TRUE;
}
U_CAPI uint32_t U_EXPORT2
upvec_getValue(uint32_t *pv, UChar32 c, int32_t column) {
uint32_t *row;
if(pv==NULL || c<0 || c>=0x110000) {
return 0;
}
row=_findRow(pv, c);
return row[2+column];
}
U_CAPI uint32_t * U_EXPORT2
upvec_getRow(uint32_t *pv, int32_t rowIndex,
UChar32 *pRangeStart, UChar32 *pRangeLimit) {
uint32_t *row;
int32_t columns;
if(pv==NULL || rowIndex<0 || rowIndex>=(int32_t)pv[UPVEC_ROWS]) {
return NULL;
}
columns=(int32_t)pv[UPVEC_COLUMNS];
row=pv+UPVEC_HEADER_LENGTH+rowIndex*columns;
if(pRangeStart!=NULL) {
*pRangeStart=row[0];
}
if(pRangeLimit!=NULL) {
*pRangeLimit=row[1];
}
return row+2;
}
static int32_t U_CALLCONV
upvec_compareRows(const void *context, const void *l, const void *r) {
const uint32_t *left=(const uint32_t *)l, *right=(const uint32_t *)r;
const uint32_t *pv=(const uint32_t *)context;
int32_t i, count, columns;
count=columns=(int32_t)pv[UPVEC_COLUMNS]; /* includes start/limit columns */
/* start comparing after start/limit but wrap around to them */
i=2;
do {
if(left[i]!=right[i]) {
return left[i]<right[i] ? -1 : 1;
}
if(++i==columns) {
i=0;
}
} while(--count>0);
return 0;
}
U_CAPI int32_t U_EXPORT2
upvec_compact(uint32_t *pv, UPVecCompactHandler *handler, void *context, UErrorCode *pErrorCode) {
uint32_t *row;
int32_t columns, valueColumns, rows, count;
UChar32 start, limit;
/* argument checking */
if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
return 0;
}
if(pv==NULL || handler==NULL) {
*pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
row=pv+UPVEC_HEADER_LENGTH;
columns=(int32_t)pv[UPVEC_COLUMNS];
rows=(int32_t)pv[UPVEC_ROWS];
if(rows==0) {
return 0;
}
/* sort the properties vectors to find unique vector values */
if(rows>1) {
uprv_sortArray(pv+UPVEC_HEADER_LENGTH, rows, columns*4,
upvec_compareRows, pv, FALSE, pErrorCode);
}
if(U_FAILURE(*pErrorCode)) {
return 0;
}
/*
* Move vector contents up to a contiguous array with only unique
* vector values, and call the handler function for each vector.
*
* This destroys the Properties Vector structure and replaces it
* with an array of just vector values.
*/
valueColumns=columns-2; /* not counting start & limit */
count=-valueColumns;
do {
/* fetch these first before memmove() may overwrite them */
start=(UChar32)row[0];
limit=(UChar32)row[1];
/* add a new values vector if it is different from the current one */
if(count<0 || 0!=uprv_memcmp(row+2, pv+count, valueColumns*4)) {
count+=valueColumns;
uprv_memmove(pv+count, row+2, valueColumns*4);
}
handler(context, start, limit, count, pv+count, valueColumns, pErrorCode);
if(U_FAILURE(*pErrorCode)) {
return 0;
}
row+=columns;
} while(--rows>0);
/* count is at the beginning of the last vector, add valueColumns to include that last vector */
return count+valueColumns;
}
U_CAPI void U_CALLCONV
upvec_compactToTrieHandler(void *context,
UChar32 start, UChar32 limit,
int32_t rowIndex, uint32_t *row, int32_t columns,
UErrorCode *pErrorCode) {
if(!utrie_setRange32((UNewTrie *)context, start, limit, (uint32_t)rowIndex, FALSE)) {
*pErrorCode=U_BUFFER_OVERFLOW_ERROR;
}
}