Skip to content

Commit

Permalink
Merge branch 'task/sdk-4061-recover-natural-sorting' into 'release/v7…
Browse files Browse the repository at this point in the history
….4.0'

SDK-4061. (hotfix v7.4.0) Revert search results to natural sorting

See merge request sdk/sdk!5621
  • Loading branch information
antonioborondo committed Jun 21, 2024
2 parents 48faa87 + 3bda92c commit 3dfe420
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 127 deletions.
7 changes: 7 additions & 0 deletions include/mega/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,13 @@ ScopedValue<T> makeScopedValue(T& what, T value)
return ScopedValue<T>(what, std::move(value));
}

/**
* @brief Sorts input char strings using natural sorting ignoring case
*
* @returns 0 if i==j, +1 if i goes first, -1 if j goes first.
*/
int naturalsorting_compare(const char* i, const char* j);

} // namespace mega

#endif // MEGA_UTILS_H
41 changes: 31 additions & 10 deletions src/db/sqlite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ SqliteDbTable *SqliteDbAccess::open(PrnGen &rng, FileSystemAccess &fsAccess, con

}

// An adapter around naturalsorting_compare
static int
sqlite_naturalsorting_compare(void*, int size1, const void* data1, int size2, const void* data2)
{
// We need to ensure that the strings to compare are null terminated
std::string s1{static_cast<const char*>(data1), static_cast<size_t>(size1)};
std::string s2{static_cast<const char*>(data2), static_cast<size_t>(size2)};
return naturalsorting_compare(s1.c_str(), s2.c_str());
}

DbTable *SqliteDbAccess::openTableWithNodes(PrnGen &rng, FileSystemAccess &fsAccess, const string &name, const int flags, DBErrorCallback dBErrorCallBack)
{
sqlite3 *db = nullptr;
Expand All @@ -146,6 +156,18 @@ DbTable *SqliteDbAccess::openTableWithNodes(PrnGen &rng, FileSystemAccess &fsAcc
return nullptr;
}

if (sqlite3_create_collation(db,
"NATURALNOCASE",
SQLITE_UTF8,
nullptr,
sqlite_naturalsorting_compare))
{
LOG_err << "Data base error(sqlite3_create_collation NATURALNOCASE): "
<< sqlite3_errmsg(db);
sqlite3_close(db);
return nullptr;
}

// Create specific table for handle nodes
std::string sql = "CREATE TABLE IF NOT EXISTS nodes (nodehandle int64 PRIMARY KEY NOT NULL, "
"parenthandle int64, name text, fingerprint BLOB, origFingerprint BLOB, "
Expand Down Expand Up @@ -2527,10 +2549,11 @@ std::string OrderByClause::get(int order, int sqlParamIndex)
// - attribute: depends on DESC/ASC (inverted for fav and label)
// - nodehandle: depends on DESC/ASC

static const std::string nameSort = "name COLLATE NATURALNOCASE";
// clang-format off
static const std::string fieldToSort =
"WHEN " + std::to_string(DEFAULT_ASC) + " THEN name COLLATE NOCASE \n"
"WHEN " + std::to_string(DEFAULT_DESC) + " THEN name COLLATE NOCASE \n"
"WHEN " + std::to_string(DEFAULT_ASC) + " THEN "+ nameSort + " \n"
"WHEN " + std::to_string(DEFAULT_DESC) + " THEN "+ nameSort + " \n"
"WHEN " + std::to_string(SIZE_ASC) + " THEN size \n"
"WHEN " + std::to_string(SIZE_DESC) + " THEN size \n"
"WHEN " + std::to_string(CTIME_ASC) + " THEN ctime \n"
Expand All @@ -2543,16 +2566,14 @@ std::string OrderByClause::get(int order, int sqlParamIndex)
"WHEN " + std::to_string(FAV_DESC) + " THEN fav \n";
// clang-format on

const std::string x = '?' + std::to_string(sqlParamIndex) + ' ';

const std::bitset<2> dirs = getDescendingDirs(order);
const std::bitset<2> directions = getDescendingDirs(order);
static const std::array<std::string, 2> boolToDesc{"", "DESC"};

static const std::string typeSort = "type DESC";
const std::string attrSort =
"CASE ?" + std::to_string(sqlParamIndex) + " " + fieldToSort + "END " + boolToDesc[dirs[0]];
const std::string nhSort = "nodehandle " + boolToDesc[dirs[1]];
return typeSort + ", \n" + attrSort + ", \n" + nhSort;
const std::string attrSort = "CASE ?" + std::to_string(sqlParamIndex) + " " + fieldToSort +
"END " + boolToDesc[directions[0]];
const std::string tiebreaker = nameSort + " " + boolToDesc[directions[1]];
return typeSort + ", \n" + attrSort + ", \n" + tiebreaker;
}

size_t OrderByClause::getId(int order)
Expand Down Expand Up @@ -2583,7 +2604,7 @@ std::bitset<2> OrderByClause::getDescendingDirs(int order)
std::bitset<2> directions;
directions[0] = directions[1] = isDescOrder(order);

// For attr [0], fav and label are inverted
// For attr [0], fav and label are inverted to show favs/labels first in ASC
const bool isLabel = order == LABEL_ASC || order == LABEL_DESC;
const bool isFav = order == FAV_ASC || order == FAV_DESC;
if (isLabel || isFav)
Expand Down
103 changes: 0 additions & 103 deletions src/megaapi_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17454,109 +17454,6 @@ bool MegaApiImpl::isFilesystemAvailable()
return client->nodeByHandle(client->mNodeManager.getRootNodeFiles()) != NULL;
}

// returns 0 if i==j, +1 if i goes first, -1 if j goes first.
int naturalsorting_compare (const char *i, const char *j)
{
static uint64_t maxNumber = (ULONG_MAX - 57) / 10; // 57 --> ASCII code for '9'

bool stringMode = true;

while (*i && *j)
{
if (stringMode)
{
char char_i, char_j;
while ( (char_i = *i) && (char_j = *j) )
{
bool char_i_isDigit = is_digit(*i);
bool char_j_isDigit = is_digit(*j);

if (char_i_isDigit && char_j_isDigit)
{
stringMode = false;
break;
}

if(char_i_isDigit)
{
return -1;
}

if(char_j_isDigit)
{
return 1;
}

int difference = strncasecmp((char *)&char_i, (char *)&char_j, 1);
if (difference)
{
return difference;
}

++i;
++j;
}
}
else // we are comparing numbers on both strings
{
uint64_t number_i = 0;
unsigned int i_overflow_count = 0;
while (*i && is_digit(*i))
{
number_i = number_i * 10 + (*i - 48); // '0' ASCII code is 48
++i;

// check the number won't overflow upon addition of next char
if (number_i >= maxNumber)
{
number_i -= maxNumber;
i_overflow_count++;
}
}

uint64_t number_j = 0;
unsigned int j_overflow_count = 0;
while (*j && is_digit(*j))
{
number_j = number_j * 10 + (*j - 48);
++j;

// check the number won't overflow upon addition of next char
if (number_j >= maxNumber)
{
number_j -= maxNumber;
j_overflow_count++;
}
}

int difference = i_overflow_count - j_overflow_count;
if (difference)
{
return difference;
}

if (number_i != number_j)
{
return number_i > number_j ? 1 : -1;
}

stringMode = true;
}
}

if (*j)
{
return -1;
}

if (*i)
{
return 1;
}

return 0;
}

std::function<bool (Node*, Node*)> MegaApiImpl::getComparatorFunction(int order, MegaClient& mc)
{
switch (order)
Expand Down
102 changes: 102 additions & 0 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3546,5 +3546,107 @@ SplitResult split(const std::string& value, char delimiter)
return split(value.data(), value.size(), delimiter);
}

int naturalsorting_compare(const char* i, const char* j)
{
static uint64_t maxNumber = (ULONG_MAX - 57) / 10; // 57 --> ASCII code for '9'

bool stringMode = true;

while (*i && *j)
{
if (stringMode)
{
char char_i, char_j;
while ((char_i = *i) && (char_j = *j))
{
bool char_i_isDigit = is_digit(*i);
bool char_j_isDigit = is_digit(*j);

if (char_i_isDigit && char_j_isDigit)
{
stringMode = false;
break;
}

if (char_i_isDigit)
{
return -1;
}

if (char_j_isDigit)
{
return 1;
}

int difference = strncasecmp((char*)&char_i, (char*)&char_j, 1);
if (difference)
{
return difference;
}

++i;
++j;
}
}
else // we are comparing numbers on both strings
{
uint64_t number_i = 0;
unsigned int i_overflow_count = 0;
while (*i && is_digit(*i))
{
number_i = number_i * 10 + (*i - 48); // '0' ASCII code is 48
++i;

// check the number won't overflow upon addition of next char
if (number_i >= maxNumber)
{
number_i -= maxNumber;
i_overflow_count++;
}
}

uint64_t number_j = 0;
unsigned int j_overflow_count = 0;
while (*j && is_digit(*j))
{
number_j = number_j * 10 + (*j - 48);
++j;

// check the number won't overflow upon addition of next char
if (number_j >= maxNumber)
{
number_j -= maxNumber;
j_overflow_count++;
}
}

int difference = i_overflow_count - j_overflow_count;
if (difference)
{
return difference;
}

if (number_i != number_j)
{
return number_i > number_j ? 1 : -1;
}

stringMode = true;
}
}

if (*j)
{
return -1;
}

if (*i)
{
return 1;
}

return 0;
}

} // namespace mega

Loading

0 comments on commit 3dfe420

Please sign in to comment.