Rework BCollator API

- Strength is now set once, instead of at each comparison, to improve
  performance and fix potential locking issues
- Add a way to enable "numeric" collation (aka "natural order")
This commit is contained in:
Adrien Destugues
2017-05-29 08:52:43 +02:00
parent affb4e25fe
commit a19a18f553
4 changed files with 68 additions and 97 deletions
+29 -37
View File
@@ -35,9 +35,9 @@
natural number sorting so that 2 is sorted before 10 unlike byte-based natural number sorting so that 2 is sorted before 10 unlike byte-based
sorting. sorting.
\warning This class is not multithread-safe, as Compare() change the \warning This class is not multithread-safe. So if you want to use a
ICUCollator (the strength). So if you want to use a BCollator BCollator from more than one thread you need to protect it with
from more than one thread you need to protect it with a lock. a lock.
\since Haiku R1 \since Haiku R1
*/ */
@@ -121,12 +121,9 @@
/*! /*!
\fn void BCollator::SetDefaultStrength(int8 strength) \fn void BCollator::SetStrength(int8 strength)
\brief Set the \a strength of the collator. \brief Set the \a strength of the collator.
Note that the \a strength can also be chosen on a case-by-case basis
when calling other methods.
\param strength The collator class provide four level of \a strength. \param strength The collator class provide four level of \a strength.
\li \c B_COLLATE_PRIMARY doesn't differentiate e from é, \li \c B_COLLATE_PRIMARY doesn't differentiate e from é,
\li \c B_COLLATE_SECONDARY takes letter accents into account, \li \c B_COLLATE_SECONDARY takes letter accents into account,
@@ -138,16 +135,6 @@
*/ */
/*!
\fn int8 BCollator::DefaultStrength() const
\brief Get the current strength of this catalog.
\returns The current strength of the catalog.
\since Haiku R1
*/
/*! /*!
\fn void BCollator::SetIgnorePunctuation(bool ignore) \fn void BCollator::SetIgnorePunctuation(bool ignore)
\brief Enable or disable punctuation handling. \brief Enable or disable punctuation handling.
@@ -172,8 +159,21 @@
/*! /*!
\fn status_t BCollator::GetSortKey(const char* string, BString* key, \fn void BCollator::SetNumericSorting(bool ignore)
int8 strength) const \brief Enable or disable numeric order sorting.
Numeric sorting enables the collator to identify strings of digits as
numbers, and sort them in ascending number. For example, the string "123"
is sorted after "234". Numbers and other characters can be mixed in the
same string.
\since Haiku R1
*/
/*!
\fn status_t BCollator::GetSortKey(const char* string, BString* key)
const
\brief Compute the sortkey of a \a string. \brief Compute the sortkey of a \a string.
The sortkey is a modified version of the input \a string that you can use The sortkey is a modified version of the input \a string that you can use
@@ -184,7 +184,6 @@
\param string String from which to compute the sortkey. \param string String from which to compute the sortkey.
\param key The resulting sortkey. \param key The resulting sortkey.
\param strength The \a strength to use to compute the sortkey.
\retval B_OK if everything went well. \retval B_OK if everything went well.
\retval B_ERROR if an error occurred generating the sortkey. \retval B_ERROR if an error occurred generating the sortkey.
@@ -194,17 +193,15 @@
/*! /*!
\fn int BCollator::Compare(const char* s1, const char* s2, \fn int BCollator::Compare(const char* s1, const char* s2)
int8 strength) const const
\brief Returns the difference betweens the two strings according to the \brief Returns the difference betweens the two strings.
collation defined by the \a strength parameter.
This method should be used in place of the strcmp() function to perform This method should be used in place of the strcmp() function to perform
locale-aware comparisons. locale-aware comparisons.
\param s1 The first string to compare. \param s1 The first string to compare.
\param s2 The second string to compare. \param s2 The second string to compare.
\param strength The \a strength to use for the string comparison.
\returns An integer value representing how the strings compare to each \returns An integer value representing how the strings compare to each
other. other.
@@ -219,8 +216,8 @@
/*! /*!
\fn bool BCollator::Equal(const char* s1, const char* s2, \fn bool BCollator::Equal(const char* s1, const char* s2)
int8 strength) const const
\brief Compares two strings for equality. \brief Compares two strings for equality.
Note that strings that are not byte-by-byte identical may end up being Note that strings that are not byte-by-byte identical may end up being
@@ -232,7 +229,6 @@
\param s1 The first string to compare. \param s1 The first string to compare.
\param s2 The second string to compare. \param s2 The second string to compare.
\param strength The \a strength to use for the string comparison.
\returns \c true if the strings are identical, \c false otherwise. \returns \c true if the strings are identical, \c false otherwise.
@@ -241,8 +237,8 @@
/*! /*!
\fn bool BCollator::Greater(const char* s1, const char* s2, \fn bool BCollator::Greater(const char* s1, const char* s2)
int8 strength = B_COLLATE_DEFAULT) const const
\brief Determine if a string is greater than another. \brief Determine if a string is greater than another.
\note !Greater(s1, s2) is the same as GreaterOrEqual(s2, s1). This means \note !Greater(s1, s2) is the same as GreaterOrEqual(s2, s1). This means
@@ -250,7 +246,6 @@
\param s1 The first string to compare. \param s1 The first string to compare.
\param s2 The second string to compare. \param s2 The second string to compare.
\param strength The \a strength to use for the string comparison.
\returns \c true if s1 is greater than, but not equal to, s2. \returns \c true if s1 is greater than, but not equal to, s2.
@@ -259,15 +254,14 @@
/*! /*!
\fn bool BCollator::GreaterOrEqual(const char* s1, const char* s2, \fn bool BCollator::GreaterOrEqual(const char* s1, const char* s2)
int8 strength = B_COLLATE_DEFAULT) const const
\brief Determines if one string is greater than another. \brief Determines if one string is greater than another.
\note !GreaterOrEqual(s1, s2) is the same as Greater(s2, s1). \note !GreaterOrEqual(s1, s2) is the same as Greater(s2, s1).
\param s1 The first string to compare. \param s1 The first string to compare.
\param s2 The second string to compare. \param s2 The second string to compare.
\param strength The \a strength to use for the string comparison.
\returns \c true if s1 is greater or equal than s2. \returns \c true if s1 is greater or equal than s2.
@@ -280,9 +274,7 @@
\brief Unarchive the collator \brief Unarchive the collator
This method allows you to restore a collator that you previously This method allows you to restore a collator that you previously
archived. It is faster to archive and unarchive a collator than it is archived.
to create a new one up each time you need a BCollator object with the
same settings.
\param archive The message to restore the collator from. \param archive The message to restore the collator from.
+19 -23
View File
@@ -34,9 +34,6 @@ enum collator_strengths {
}; };
// N.B.: This class is not multithread-safe, as Compare() and GetKey() change
// the ICUCollator (the strength). So if you want to use a BCollator from
// more than one thread, you need to protect it with a lock
class BCollator : public BArchivable { class BCollator : public BArchivable {
public: public:
BCollator(); BCollator();
@@ -51,55 +48,54 @@ public:
BCollator& operator=(const BCollator& source); BCollator& operator=(const BCollator& source);
void SetDefaultStrength(int8 strength); status_t SetStrength(int8 strength) const;
int8 DefaultStrength() const;
void SetIgnorePunctuation(bool ignore); void SetIgnorePunctuation(bool ignore);
bool IgnorePunctuation() const; bool IgnorePunctuation() const;
status_t GetSortKey(const char* string, BString* key, status_t SetNumericSorting(bool enable);
int8 strength = B_COLLATE_DEFAULT) const;
int Compare(const char* s1, const char* s2, status_t GetSortKey(const char* string, BString* key)
int8 strength = B_COLLATE_DEFAULT) const; const;
bool Equal(const char* s1, const char* s2,
int8 strength = B_COLLATE_DEFAULT) const; int Compare(const char* s1, const char* s2)
bool Greater(const char* s1, const char* s2, const;
int8 strength = B_COLLATE_DEFAULT) const; bool Equal(const char* s1, const char* s2)
bool GreaterOrEqual(const char* s1, const char* s2, const;
int8 strength = B_COLLATE_DEFAULT) const; bool Greater(const char* s1, const char* s2)
const;
bool GreaterOrEqual(const char* s1, const char* s2)
const;
// (un-)archiving API // (un-)archiving API
status_t Archive(BMessage* archive, bool deep) const; status_t Archive(BMessage* archive, bool deep) const;
static BArchivable* Instantiate(BMessage* archive); static BArchivable* Instantiate(BMessage* archive);
private: private:
status_t _SetStrength(int8 strength) const;
mutable U_ICU_NAMESPACE::Collator* fICUCollator; mutable U_ICU_NAMESPACE::Collator* fICUCollator;
int8 fDefaultStrength;
bool fIgnorePunctuation; bool fIgnorePunctuation;
}; };
inline bool inline bool
BCollator::Equal(const char *s1, const char *s2, int8 strength) const BCollator::Equal(const char *s1, const char *s2) const
{ {
return Compare(s1, s2, strength) == 0; return Compare(s1, s2) == 0;
} }
inline bool inline bool
BCollator::Greater(const char *s1, const char *s2, int8 strength) const BCollator::Greater(const char *s1, const char *s2) const
{ {
return Compare(s1, s2, strength) > 0; return Compare(s1, s2) > 0;
} }
inline bool inline bool
BCollator::GreaterOrEqual(const char *s1, const char *s2, int8 strength) const BCollator::GreaterOrEqual(const char *s1, const char *s2) const
{ {
return Compare(s1, s2, strength) >= 0; return Compare(s1, s2) >= 0;
} }
+16 -33
View File
@@ -24,7 +24,6 @@
BCollator::BCollator() BCollator::BCollator()
: :
fDefaultStrength(B_COLLATE_PRIMARY),
fIgnorePunctuation(true) fIgnorePunctuation(true)
{ {
// TODO: the collator construction will have to change; the default // TODO: the collator construction will have to change; the default
@@ -33,16 +32,17 @@ BCollator::BCollator()
UErrorCode error = U_ZERO_ERROR; UErrorCode error = U_ZERO_ERROR;
fICUCollator = Collator::createInstance(error); fICUCollator = Collator::createInstance(error);
SetStrength(B_COLLATE_TERTIARY);
} }
BCollator::BCollator(const char* locale, int8 strength, bool ignorePunctuation) BCollator::BCollator(const char* locale, int8 strength, bool ignorePunctuation)
: :
fDefaultStrength(strength),
fIgnorePunctuation(ignorePunctuation) fIgnorePunctuation(ignorePunctuation)
{ {
UErrorCode error = U_ZERO_ERROR; UErrorCode error = U_ZERO_ERROR;
fICUCollator = Collator::createInstance(locale, error); fICUCollator = Collator::createInstance(locale, error);
SetStrength(strength);
} }
@@ -50,15 +50,8 @@ BCollator::BCollator(BMessage* archive)
: :
BArchivable(archive), BArchivable(archive),
fICUCollator(NULL), fICUCollator(NULL),
fDefaultStrength(B_COLLATE_PRIMARY),
fIgnorePunctuation(true) fIgnorePunctuation(true)
{ {
int32 data;
if (archive->FindInt32("loc:strength", &data) == B_OK)
fDefaultStrength = (uint8)data;
else
fDefaultStrength = B_COLLATE_PRIMARY;
archive->FindBool("loc:punctuation", &fIgnorePunctuation); archive->FindBool("loc:punctuation", &fIgnorePunctuation);
UErrorCode error = U_ZERO_ERROR; UErrorCode error = U_ZERO_ERROR;
@@ -101,7 +94,6 @@ BCollator& BCollator::operator=(const BCollator& source)
fICUCollator = source.fICUCollator != NULL fICUCollator = source.fICUCollator != NULL
? source.fICUCollator->clone() ? source.fICUCollator->clone()
: NULL; : NULL;
fDefaultStrength = source.fDefaultStrength;
fIgnorePunctuation = source.fIgnorePunctuation; fIgnorePunctuation = source.fIgnorePunctuation;
} }
@@ -109,20 +101,6 @@ BCollator& BCollator::operator=(const BCollator& source)
} }
void
BCollator::SetDefaultStrength(int8 strength)
{
fDefaultStrength = strength;
}
int8
BCollator::DefaultStrength() const
{
return fDefaultStrength;
}
void void
BCollator::SetIgnorePunctuation(bool ignore) BCollator::SetIgnorePunctuation(bool ignore)
{ {
@@ -138,10 +116,19 @@ BCollator::IgnorePunctuation() const
status_t status_t
BCollator::GetSortKey(const char* string, BString* key, int8 strength) const BCollator::SetNumericSorting(bool enable)
{ {
_SetStrength(strength); UErrorCode error = U_ZERO_ERROR;
fICUCollator->setAttribute(UCOL_NUMERIC_COLLATION,
enable ? UCOL_ON : UCOL_OFF, error);
return error == U_ZERO_ERROR ? B_OK : B_ERROR;
}
status_t
BCollator::GetSortKey(const char* string, BString* key) const
{
// TODO : handle fIgnorePunctuation // TODO : handle fIgnorePunctuation
int length = strlen(string); int length = strlen(string);
@@ -175,10 +162,8 @@ BCollator::GetSortKey(const char* string, BString* key, int8 strength) const
int int
BCollator::Compare(const char* s1, const char* s2, int8 strength) const BCollator::Compare(const char* s1, const char* s2) const
{ {
_SetStrength(strength);
// TODO : handle fIgnorePunctuation // TODO : handle fIgnorePunctuation
UErrorCode error = U_ZERO_ERROR; UErrorCode error = U_ZERO_ERROR;
@@ -193,8 +178,6 @@ BCollator::Archive(BMessage* archive, bool deep) const
if (status < B_OK) if (status < B_OK)
return status; return status;
if (status == B_OK)
status = archive->AddInt32("loc:strength", fDefaultStrength);
if (status == B_OK) if (status == B_OK)
status = archive->AddBool("loc:punctuation", fIgnorePunctuation); status = archive->AddBool("loc:punctuation", fIgnorePunctuation);
@@ -229,10 +212,10 @@ BCollator::Instantiate(BMessage* archive)
status_t status_t
BCollator::_SetStrength(int8 strength) const BCollator::SetStrength(int8 strength) const
{ {
if (strength == B_COLLATE_DEFAULT) if (strength == B_COLLATE_DEFAULT)
strength = fDefaultStrength; strength = B_COLLATE_TERTIARY;
Collator::ECollationStrength icuStrength; Collator::ECollationStrength icuStrength;
switch (strength) { switch (strength) {
+4 -4
View File
@@ -50,11 +50,11 @@ CollatorTest::TestSortKeys()
for (int32 strength = B_COLLATE_PRIMARY; strength < 4; strength++) { for (int32 strength = B_COLLATE_PRIMARY; strength < 4; strength++) {
BString a, b; BString a, b;
collator.GetSortKey(tests[i].first, &a, strength); collator.SetStrength(strength);
collator.GetSortKey(tests[i].second, &b, strength); collator.GetSortKey(tests[i].first, &a);
collator.GetSortKey(tests[i].second, &b);
int difference = collator.Compare(tests[i].first, tests[i].second, int difference = collator.Compare(tests[i].first, tests[i].second);
strength);
CPPUNIT_ASSERT_EQUAL(tests[i].sign[strength - 1], difference); CPPUNIT_ASSERT_EQUAL(tests[i].sign[strength - 1], difference);
int keydiff = strcmp(a.String(), b.String()); int keydiff = strcmp(a.String(), b.String());
// Check that the keys compare the same as the strings. Either both // Check that the keys compare the same as the strings. Either both