Compare commits

...
Author SHA1 Message Date
dgp 9ba756b6b7 another test 2020-05-06 21:44:13 +00:00
dgp 1670a55fc6 merge 8.7 2020-04-30 21:23:47 +00:00
dgp 20bfd7120b Work In Progress. Much trickiness about sorting out expectations. 2020-04-30 17:21:37 +00:00
dgp e66c8493dc merge 8.7 2020-04-29 22:22:22 +00:00
dgp f263518d45 First attempt at extending routine to deal with surrogate pairs.
Exposes problems with the interface.
2020-04-29 17:39:23 +00:00
dgp 34e15e0a4b Check reverse tests too. 2020-04-29 17:28:58 +00:00
dgp 8d0a74ad7f merge 8.7 2020-04-28 22:16:44 +00:00
dgp 942f2fe7e4 The comparison loop for UTF-16 string reps need revising to account
for surrogates.  For example \uD800\uDC00 decodes to \U10000 which
should be greater than, not less than, \uFFFF.
2020-04-27 21:16:51 +00:00
4 changed files with 181 additions and 6 deletions
+5
View File
@@ -4098,6 +4098,11 @@ MODULE_SCOPE Tcl_Obj * TclStringReplace(Tcl_Interp *interp, Tcl_Obj *objPtr,
int flags);
MODULE_SCOPE Tcl_Obj * TclStringReverse(Tcl_Obj *objPtr, int flags);
MODULE_SCOPE int TclUtf16Ncmp(const unsigned short int *units1,
const unsigned short int *units2,
size_t numUnits1, size_t numUnits2,
size_t numChars);
/* Flag values for the [string] ensemble functions. */
#define TCL_STRING_MATCH_NOCASE TCL_MATCH_NOCASE /* (1<<0) in tcl.h */
+48
View File
@@ -3435,7 +3435,55 @@ TclStringCmp(
s1len *= sizeof(Tcl_UniChar);
s2len *= sizeof(Tcl_UniChar);
} else {
#if TCL_UTF_MAX > 3
memCmpFn = (memCmpFn_t) Tcl_UniCharNcmp;
#else
int numUnits1, numUnits2;
const unsigned short int *units1
= Tcl_GetUnicodeFromObj(value1Ptr, &numUnits1);
const unsigned short int *units2
= Tcl_GetUnicodeFromObj(value2Ptr, &numUnits2);
/*
* WARNING WARNING! s1len and s2len were set above as the values
* returned by calls to Tcl_GetCharLength(). The name of this routine
* would suggest it reports the number of characters, but it reports
* the number of code units.
*/
length = (s1len < s2len) ? s1len : s2len;
/* length is # chars in shorter sequence */
if (reqlength > 0 && reqlength < length) {
/* Trim back to request it it matters */
length = reqlength;
} else if (reqlength < 0) {
/*
* The requested length is negative, so we ignore it by setting it
* to length + 1 so we correct the match var.
*/
/* TODO: Check this */
reqlength = length + 1;
}
if (checkEq && (s1len != s2len)) {
/* TODO: Check this */
match = 1; /* This will be reversed below. */
} else {
/*
* The comparison function should compare up to the minimum byte
* length only.
*/
match = TclUtf16Ncmp(units1, units2, numUnits1, numUnits2, length);
}
if ((match == 0) && (reqlength > length)) {
match = s1len - s2len;
}
match = (match > 0) ? 1 : (match < 0) ? -1 : 0;
goto matchdone;
#endif
}
}
}
+113
View File
@@ -1800,6 +1800,78 @@ Tcl_UniCharNcmp(
return 0;
#endif /* WORDS_BIGENDIAN */
}
/*
*----------------------------------------------------------------------
*
* TclUTF16Ncmp --
*
* Compare at most numChars characters from UTF-16 sequence units1
* against characters from UTF-16 sequence units2. Caller is expected
* to guarantee that each sequence contains at least numChars characters.
*
* Results:
* Return <0 if units1 < units2,
* 0 if units1 == units2,
* >0 if units1 > units,
* when considering up to numChar characters from each.
*
*----------------------------------------------------------------------
*/
static int
UTF16ToUCS4(
const unsigned short int *units,
/* Pointer into an array of UCS-2 units */
size_t count, /* How many units are available to read. */
int *ucs4Ptr) /* Write the UCS-4 result here. */
{
if (count == 0) {
/* Is this reasonable? Better to panic? */
*ucs4Ptr = 0;
return 0;
}
if ((sizeof(Tcl_UniChar) > 2) || (count == 1)
|| ((units[0] & 0xF800) != 0xD800)
|| ((units[1] & 0xF800) != 0xDC00)) {
/* We do not see a surrogate pair. Return 1st unit. */
*ucs4Ptr = units[0];
return 1;
}
/* Surrogate pair. Generate extended code point */
*ucs4Ptr = (0x10000 + ((units[0] & 0x3FF) << 10)) | (units[1] & 0x3FF);
return 2;
}
int
TclUtf16Ncmp(
const unsigned short int *units1, /* 1st UTF-16 sequence to compare */
const unsigned short int *units2, /* 2nd UTF-16 sequence to compare */
size_t numUnits1, /* # code units in 1st sequence */
size_t numUnits2, /* # code units in 2nd sequence */
size_t numChars) /* max # characters to compare */
{
while ((numChars > 0) && (numUnits1 > 0) && (numUnits2 > 0)) {
int ch1, ch2;
int delta1 = UTF16ToUCS4(units1, numUnits1, &ch1);
int delta2 = UTF16ToUCS4(units2, numUnits2, &ch2);
if (ch1 != ch2) {
return (ch1 - ch2);
}
units1 += delta1;
numUnits1 -= delta1;
units2 += delta2;
numUnits2 -= delta2;
numChars--;
}
if (numChars == 0) {
return 0;
}
/* Ran out of at least one sequence before seeing numChars characters */
Tcl_Panic("TclUtf16Ncmp request to compare too many characters");
}
/*
*----------------------------------------------------------------------
@@ -1819,6 +1891,46 @@ Tcl_UniCharNcmp(
*----------------------------------------------------------------------
*/
#if 0
int
Tcl_UniCharNcasecmp(
const Tcl_UniChar *ucs, /* Unicode string to compare to uct. */
const Tcl_UniChar *uct, /* Unicode string ucs is compared to. */
unsigned long numChars) /* Number of unichars to compare. */
{
unsigned long snum = numChars;
unsigned long tnum = numChars;
while ((snum != 0) && (tnum != 0)) {
int sch, tch;
int sdelta = UTF16ToUCS4(ucs, snum, &sch);
int tdelta = UTF16ToUCS4(uct, tnum, &tch);
/* TODO: Are two replacement characters always the same? */
/* Should replacement characters sort according to code point */
if (sch != tch) {
int lsch = Tcl_UniCharToLower(sch);
int ltch = Tcl_UniCharToLower(tch);
if (lsch != ltch) {
return (lsch - ltch);
}
}
ucs += sdelta;
snum -= sdelta;
uct += tdelta;
tnum -= tdelta;
}
/* All examined chars were same (modulo case)
* Then the longer string is greater.
*/
return snum ? 1 : tnum ? -1 : 0;
}
#else
int
Tcl_UniCharNcasecmp(
const Tcl_UniChar *ucs, /* Unicode string to compare to uct. */
@@ -1837,6 +1949,7 @@ Tcl_UniCharNcasecmp(
}
return 0;
}
#endif
/*
*----------------------------------------------------------------------
+15 -6
View File
@@ -1210,10 +1210,10 @@ test utf-19.1 {TclUniCharLen} -body {
unset -nocomplain foo
} -result {1 4}
test utf-20.1 {TclUniCharNcmp} {ucs4} {
test utf-20.1 {TclUniCharNcmp} {fullutf} {
string compare [string range [format %c 0xFFFF] 0 0] [string range [format %c 0x10000] 0 0]
} -1
test utf-20.2 {[4c591fa487] TclUniCharNcmp/TclUtfNcmp} knownBug {
test utf-20.2 {[4c591fa487] TclUniCharNcmp/TclUtfNcmp} {
set one [format %c 0xFFFF]
set two [format %c 0x10000]
set first [string compare $one $two]
@@ -1222,6 +1222,15 @@ test utf-20.2 {[4c591fa487] TclUniCharNcmp/TclUtfNcmp} knownBug {
set second [string compare $one $two]
expr {($first == $second) ? "agree" : "disagree"}
} agree
test utf-20.3 {[4c591fa487] TclUniCharNcmp/TclUtfNcmp} {
set one [format %c 0xFFFF]
set two [format %c 0x10000]
set first [string compare -length 1 $one $two]
string range $one 0 0
string range $two 0 0
set second [string compare -length 1 $one $two]
expr {($first == $second) ? "agree" : "disagree"}
} agree
test utf-21.1 {TclUniCharIsAlnum} {
# this returns 1 with Unicode 7 compliance
@@ -1340,10 +1349,10 @@ UniCharCaseCmpTest < a b
UniCharCaseCmpTest > b a
UniCharCaseCmpTest > B a
UniCharCaseCmpTest > aBcB abca
UniCharCaseCmpTest < \uFFFF [format %c 0x10000] ucs4
UniCharCaseCmpTest < \uFFFF \U10000 {Uesc ucs4}
UniCharCaseCmpTest > [format %c 0x10000] \uFFFF ucs4
UniCharCaseCmpTest > \U10000 \uFFFF {Uesc ucs4}
UniCharCaseCmpTest < \uFFFF [format %c 0x10000] {fullutf}
UniCharCaseCmpTest < \uFFFF \U10000 {Uesc fullutf}
UniCharCaseCmpTest > [format %c 0x10000] \uFFFF {fullutf}
UniCharCaseCmpTest > \U10000 \uFFFF {Uesc fullutf}