From 0a62fd2f87396c8edd9d1e7a4829c5dee36f0dcb Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Fri, 27 Oct 2017 14:28:00 +0200 Subject: [PATCH] ENH: allow passing of comparator to sortToc methods - this increases the flexibility of the interface - Add stringOps 'natural' string sorting comparison. Digits are sorted in their natural order, which means that (file10.txt file05.txt file2.txt) are sorted as (file2.txt file05.txt file10.txt) STYLE: consistent naming of template parameters for comparators - Compare for normal binary predicates - ListComparePredicate for list compare binary predicates --- applications/test/sort/Test-sortList.C | 140 +++++++- src/OpenFOAM/Make/files | 1 + .../DictionaryBase/DictionaryBase.C | 11 + .../DictionaryBase/DictionaryBase.H | 4 + .../HashTables/HashTable/HashTable.C | 16 +- .../HashTables/HashTable/HashTable.H | 4 + .../containers/Lists/ListOps/ListOps.H | 40 ++- .../Lists/ListOps/ListOpsTemplates.C | 51 +-- .../Lists/SortableList/SortableList.C | 5 +- .../Lists/SortableList/SortableList.H | 4 +- src/OpenFOAM/containers/Lists/UList/UList.C | 12 +- src/OpenFOAM/containers/Lists/UList/UList.H | 40 +-- src/OpenFOAM/db/dictionary/dictionary.H | 4 + .../db/dictionary/dictionaryTemplates.C | 7 + .../primitives/predicates/predicates.H | 38 +- .../primitives/strings/stringOps/stringOps.H | 1 + .../strings/stringOps/stringOpsSort.C | 325 ++++++++++++++++++ .../strings/stringOps/stringOpsSort.H | 133 +++++++ 18 files changed, 757 insertions(+), 79 deletions(-) create mode 100644 src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.C create mode 100644 src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.H diff --git a/applications/test/sort/Test-sortList.C b/applications/test/sort/Test-sortList.C index 1fbfcfcf39..8b208f4f50 100644 --- a/applications/test/sort/Test-sortList.C +++ b/applications/test/sort/Test-sortList.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,6 +27,8 @@ Description #include "SortableList.H" #include "ListOps.H" +#include "HashSet.H" +#include "stringOps.H" using namespace Foam; @@ -175,6 +177,142 @@ int main(int argc, char *argv[]) Info<< "flat = " << values << endl; } + // Sort strings + { + HashSet hashed + { + "2.txt", + "05.txt", + "15.txt", + "other.bak04", + "other.bak1", + "file1.txt", + "file10.txt", + "file2.txt", + "file100.txt", + "file.txt", + "file011.txt", + "file15.txt", + "file0009.txt", + "abcd.txt", + + // Some regular processor directories + "processor0", + "processor1", + "processor9", + "processor10", + "processor11", + "processor20", + "processor21", + "processor35", + "processors", + + // Aggregate processor directories + "processor0-31", + "processor32-63", + "processor64-95", + "processor96-127", + "processor128-159", + "processor160-191", + "processor192-223", + "processor224-255", + }; + + Info<< nl << "Test string sorting" << nl << endl; + + // Using hash toc + if (true) + { + Info<< "Unsorted" << hashed.toc() << endl; + Info<< "sortedToc" << hashed.sortedToc() << endl; + Info<< "natural" + << hashed.sortedToc(stringOps::natural_sort()) << endl; + + Info<< "reverse natural" + << hashed.sortedToc(stringOps::natural_sort::reverse()) + << endl; + } + + // Normal list + if (true) + { + labelList order; + + List strings(hashed.toc()); + Info<< nl << "stringList:" << strings << endl; + + sort(strings); + Info<< "normal sort:" << strings << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort()); + Info<< "natural sort:" << strings << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort::reverse()); + Info<< "reverse natural:" << strings << endl; + + strings = hashed.toc(); + + Info<< nl << "test sorted order" << endl; + Info<< nl << "list:" << strings << endl; + + sortedOrder(strings, order); + Info<< "sortedOrder:" << flatOutput(order) << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort()); + Info<< "reverse natural:" << strings << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort::reverse()); + Info<< "reverse natural:" << strings << endl; + + sortedOrder + ( + strings, + order, + stringOps::natural_sort::less(strings) + ); + Info<< "natural sortedOrder: " << flatOutput(order) << endl; + } + + // SortableList + if (false) + { + SortableList sortable; + Info<< nl << "Testing sortable list"; + + // Assign to ensure list is initially unsorted + sortable = hashed.toc(); + Info<< nl << "input:" << sortable << endl; + + sortable.sort(); + Info<< nl << "normal:" << sortable << endl; + + // This is still a bother (looks fairly ugly) + // so not implemented for now + + /// // Assign to ensure list is initially unsorted + /// sortable = hashed.toc(); + /// sortable.sort + /// ( + /// stringOps::natural_sort::less(sortable) + /// ); + /// Info<< nl << "natural:" << sortable << endl; + + /// // Assign to ensure list is initially unsorted + /// sortable = hashed.toc(); + /// sortable.sort + /// ( + /// stringOps::natural_sort::greater(sortable) + /// ); + /// Info<< nl << "natural:" << sortable << endl; + } + + } + + Info<< "\nEnd\n" << endl; return 0; diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files index acf60a63d0..4a6adb98a1 100644 --- a/src/OpenFOAM/Make/files +++ b/src/OpenFOAM/Make/files @@ -114,6 +114,7 @@ $(strings)/wordRe/wordRe.C $(strings)/wordRes/wordRes.C $(strings)/lists/hashedWordList.C $(strings)/stringOps/stringOps.C +$(strings)/stringOps/stringOpsSort.C $(strings)/parsing/parsing.C ops = primitives/ops diff --git a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C index 60b390223c..2f95d40f71 100644 --- a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C +++ b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C @@ -189,6 +189,17 @@ Foam::wordList Foam::DictionaryBase::sortedToc() const } +template +template +Foam::wordList Foam::DictionaryBase::sortedToc +( + const Compare& comp +) const +{ + return hashedTs_.sortedToc(comp); +} + + template void Foam::DictionaryBase::insert(const word& keyword, T* tPtr) { diff --git a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H index f4d864d82c..8c48737427 100644 --- a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H +++ b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H @@ -131,6 +131,10 @@ public: //- Return the table of contents as a sorted list wordList sortedToc() const; + //- Return table of contents sorted using the specified comparator + template + wordList sortedToc(const Compare& comp) const; + // Editing diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C index 1f209ef350..1c1d7b4360 100644 --- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C +++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C @@ -268,13 +268,27 @@ Foam::List Foam::HashTable::toc() const template Foam::List Foam::HashTable::sortedToc() const { - List keyLst = this->toc(); + List keyLst(this->toc()); Foam::sort(keyLst); return keyLst; } +template +template +Foam::List Foam::HashTable::sortedToc +( + const Compare& comp +) const +{ + List keyLst(this->toc()); + Foam::sort(keyLst, comp); + + return keyLst; +} + + template template Foam::List Foam::HashTable::tocKeys diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H index aa848e475c..f9dda76f9c 100644 --- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H +++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H @@ -347,6 +347,10 @@ public: //- Return the table of contents as a sorted list List sortedToc() const; + //- Return table of contents sorted using the specified comparator + template + List sortedToc(const Compare& comp) const; + //- Return the sorted table of contents with keys that satisfy // the unary predicate, optionally with inverted logic. template diff --git a/src/OpenFOAM/containers/Lists/ListOps/ListOps.H b/src/OpenFOAM/containers/Lists/ListOps/ListOps.H index d7c93bef02..c3a6406da9 100644 --- a/src/OpenFOAM/containers/Lists/ListOps/ListOps.H +++ b/src/OpenFOAM/containers/Lists/ListOps/ListOps.H @@ -110,24 +110,44 @@ void inplaceMapKey(const labelUList& oldToNew, Container& lst); template void sortedOrder(const UList& lst, labelList& order); -template -void sortedOrder(const UList& lst, labelList& order, const Cmp& cmp); +//- Sort using specified list compare predicate +template +void sortedOrder +( + const UList& lst, + labelList& order, + const ListComparePredicate& comp +); //- Generate (sorted) indices corresponding to duplicate list values template void duplicateOrder(const UList& lst, labelList& order); -template -void duplicateOrder(const UList& lst, labelList& order, const Cmp& cmp); +//- Generate (sorted) indices corresponding to duplicate list values +// sort using specified list compare predicate +template +void duplicateOrder +( + const UList& lst, + labelList& order, + const ListComparePredicate& comp +); //- Generate (sorted) indices corresponding to unique list values template void uniqueOrder(const UList& lst, labelList& order); -template -void uniqueOrder(const UList& lst, labelList& order, const Cmp& cmp); +//- Generate (sorted) indices corresponding to unique list values +// sort using specified list compare predicate +template +void uniqueOrder +( + const UList& lst, + labelList& order, + const ListComparePredicate& comp +); //- Inplace sorting and removal of duplicates. @@ -137,8 +157,12 @@ void inplaceUniqueSort(ListType& lst); //- Inplace sorting and removal of duplicates. // Do not use FixedList for the input list, since it doesn't resize. -template -void inplaceUniqueSort(ListType& lst, const Cmp& cmp); +template +void inplaceUniqueSort +( + ListType& lst, + const ListComparePredicate& comp +); //- Extract elements of List when select is a certain value. diff --git a/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C b/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C index 641aa4ade0..a55b8c714c 100644 --- a/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C +++ b/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C @@ -211,27 +211,30 @@ void Foam::sortedOrder } -template +template void Foam::sortedOrder ( const UList& lst, labelList& order, - const Cmp& cmp + const ListComparePredicate& comp ) { + const label len = lst.size(); + // list lengths must be identical - if (order.size() != lst.size()) + if (order.size() != len) { - // avoid copying any elements, they are overwritten anyhow + // Avoid copying any elements, they are overwritten anyhow order.clear(); - order.setSize(lst.size()); + order.setSize(len); } - forAll(order, elemI) + for (label i=0; i +template void Foam::duplicateOrder ( const UList& lst, labelList& order, - const Cmp& cmp + const ListComparePredicate& comp ) { if (lst.size() < 2) @@ -260,7 +263,7 @@ void Foam::duplicateOrder return; } - sortedOrder(lst, order, cmp); + sortedOrder(lst, order, comp); const label last = (order.size()-1); label n = 0; @@ -286,15 +289,15 @@ void Foam::uniqueOrder } -template +template void Foam::uniqueOrder ( const UList& lst, labelList& order, - const Cmp& cmp + const ListComparePredicate& comp ) { - sortedOrder(lst, order, cmp); + sortedOrder(lst, order, comp); if (order.size() > 1) { @@ -324,18 +327,24 @@ void Foam::inplaceUniqueSort(ListType& lst) } -template -void Foam::inplaceUniqueSort(ListType& lst, const Cmp& cmp) +template +void Foam::inplaceUniqueSort +( + ListType& lst, + const ListComparePredicate& comp +) { labelList order; - uniqueOrder(lst, order, cmp); + uniqueOrder(lst, order, comp); - ListType newLst(order.size()); - newLst.setSize(order.size()); // Consistent sizing (eg, DynamicList) + const label len = order.size(); - forAll(order, elemI) + ListType newLst(len); + newLst.setSize(len); // Consistent sizing (eg, DynamicList) + + for (label i=0; i& Foam::SortableList::shrink() template void Foam::SortableList::sort() { - sortedOrder(*this, indices_); + Foam::sortedOrder(*this, indices_); List lst(*this, indices_); // Copy with indices for mapping List::transfer(lst); @@ -143,7 +143,7 @@ void Foam::SortableList::sort() template void Foam::SortableList::reverseSort() { - sortedOrder(*this, indices_, typename UList::greater(*this)); + Foam::sortedOrder(*this, indices_, typename UList::greater(*this)); List lst(*this, indices_); // Copy with indices for mapping List::transfer(lst); @@ -157,6 +157,7 @@ void Foam::SortableList::swap(SortableList& lst) indices_.swap(lst.indices_); } + template Foam::Xfer> Foam::SortableList::xfer() { diff --git a/src/OpenFOAM/containers/Lists/SortableList/SortableList.H b/src/OpenFOAM/containers/Lists/SortableList/SortableList.H index 8ef6aefa19..083e69386f 100644 --- a/src/OpenFOAM/containers/Lists/SortableList/SortableList.H +++ b/src/OpenFOAM/containers/Lists/SortableList/SortableList.H @@ -119,8 +119,8 @@ public: //- Clear the indices and return a reference to the underlying List List& shrink(); - //- (stable) sort the list (if changed after construction time) - // also resizes the indices as required + //- (stable) sort the list (if changed after construction time). + // Resizes the indices as required void sort(); //- Reverse (stable) sort the list diff --git a/src/OpenFOAM/containers/Lists/UList/UList.C b/src/OpenFOAM/containers/Lists/UList/UList.C index b87e524b59..9258fbf637 100644 --- a/src/OpenFOAM/containers/Lists/UList/UList.C +++ b/src/OpenFOAM/containers/Lists/UList/UList.C @@ -293,10 +293,10 @@ void Foam::sort(UList& a) } -template -void Foam::sort(UList& a, const Cmp& cmp) +template +void Foam::sort(UList& a, const Compare& comp) { - std::sort(a.begin(), a.end(), cmp); + std::sort(a.begin(), a.end(), comp); } @@ -307,10 +307,10 @@ void Foam::stableSort(UList& a) } -template -void Foam::stableSort(UList& a, const Cmp& cmp) +template +void Foam::stableSort(UList& a, const Compare& comp) { - std::stable_sort(a.begin(), a.end(), cmp); + std::stable_sort(a.begin(), a.end(), comp); } diff --git a/src/OpenFOAM/containers/Lists/UList/UList.H b/src/OpenFOAM/containers/Lists/UList/UList.H index 5c7999e229..fcdbd7c0da 100644 --- a/src/OpenFOAM/containers/Lists/UList/UList.H +++ b/src/OpenFOAM/containers/Lists/UList/UList.H @@ -139,39 +139,35 @@ public: // Public classes - //- Less function class that can be used for sorting - class less + //- A list compare binary predicate for normal sort + struct less { - const UList& values_; + const UList& values; - public: - - less(const UList& values) + less(const UList& list) : - values_(values) + values(list) {} - bool operator()(const label a, const label b) + bool operator()(const label a, const label b) const { - return values_[a] < values_[b]; + return values[a] < values[b]; } }; - //- Greater function class that can be used for sorting - class greater + //- A list compare binary predicate for reverse sort + struct greater { - const UList& values_; + const UList& values; - public: - - greater(const UList& values) + greater(const UList& list) : - values_(values) + values(list) {} - bool operator()(const label a, const label b) + bool operator()(const label a, const label b) const { - return values_[a] > values_[b]; + return values[a] > values[b]; } }; @@ -487,14 +483,14 @@ public: template void sort(UList& a); -template -void sort(UList& a, const Cmp& cmp); +template +void sort(UList& a, const Compare& comp); template void stableSort(UList& a); -template -void stableSort(UList& a, const Cmp& cmp); +template +void stableSort(UList& a, const Compare& comp); template void shuffle(UList& a); diff --git a/src/OpenFOAM/db/dictionary/dictionary.H b/src/OpenFOAM/db/dictionary/dictionary.H index fc34a32d99..f0c66b97a8 100644 --- a/src/OpenFOAM/db/dictionary/dictionary.H +++ b/src/OpenFOAM/db/dictionary/dictionary.H @@ -452,6 +452,10 @@ public: //- Return the sorted table of contents wordList sortedToc() const; + //- Return table of contents sorted using the specified comparator + template + wordList sortedToc(const Compare& comp) const; + //- Return the list of available keys or patterns List keys(bool patterns = false) const; diff --git a/src/OpenFOAM/db/dictionary/dictionaryTemplates.C b/src/OpenFOAM/db/dictionary/dictionaryTemplates.C index b247dfe812..1e46e5da33 100644 --- a/src/OpenFOAM/db/dictionary/dictionaryTemplates.C +++ b/src/OpenFOAM/db/dictionary/dictionaryTemplates.C @@ -28,6 +28,13 @@ License // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // +template +Foam::wordList Foam::dictionary::sortedToc(const Compare& comp) const +{ + return hashedEntries_.sortedToc(comp); +} + + template T Foam::dictionary::lookupType ( diff --git a/src/OpenFOAM/primitives/predicates/predicates.H b/src/OpenFOAM/primitives/predicates/predicates.H index 0d7d8539fa..a44b4eec7e 100644 --- a/src/OpenFOAM/primitives/predicates/predicates.H +++ b/src/OpenFOAM/primitives/predicates/predicates.H @@ -49,37 +49,40 @@ namespace predicates Class always Declaration \*---------------------------------------------------------------------------*/ -//- Unary and binary predicates returning true, useful for templating. -class always +//- Unary and binary predicates that always return true, useful for templating. +struct always { -public: typedef always value_type; - //- Construct null + //- Null constructible inline always() {} - //- Evaluated as a bool - return true + //- Evaluated as a bool + // \return true inline operator bool() const { return true; } - //- Unary predicate returning true + //- Unary predicate + // \return true template inline bool operator()(const T&) const { return true; } - //- Binary predicate returning true + //- Binary predicate + // \return true template inline bool operator()(const T1&, const T2&) const { return true; } - //- String match returning true + //- String match + // \return true inline bool match(const std::string&, bool literal=false) const { return true; @@ -91,37 +94,40 @@ public: Class never Declaration \*---------------------------------------------------------------------------*/ -//- Unary and binary predicates returning false, useful for templating. -class never +//- Unary and binary predicates that never return true, useful for templating. +struct never { -public: typedef never value_type; - //- Construct null + //- Null constructible inline never() {} - //- Evaluated as a bool - return false + //- Evaluated as a bool + // \return false inline operator bool() const { return false; } - //- Unary predicate returning false + //- Unary predicate + // \return false template inline bool operator()(const T&) const { return false; } - //- Binary predicate returning false + //- Binary predicate + // \return false template inline bool operator()(const T1&, const T2&) const { return false; } - //- String match returning false + //- String match + // \return false inline bool match(const std::string&, bool literal=false) const { return false; diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H index 92092f9d52..0d84856f52 100644 --- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H @@ -41,6 +41,7 @@ SourceFiles #include "word.H" #include "dictionary.H" #include "HashTable.H" +#include "stringOpsSort.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.C b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.C new file mode 100644 index 0000000000..aeeb29fa14 --- /dev/null +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.C @@ -0,0 +1,325 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +Changes for OpenFOAM + + Code cleanup, reduction, elimination of redundant code. +\*---------------------------------------------------------------------------*/ + +//======================================================================== +// Copyright (c) 1998-2010,2011 Free Software Foundation, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, distribute with modifications, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +// THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the above copyright +// holders shall not be used in advertising or otherwise to promote the +// sale, use or other dealings in this Software without prior written +// authorization. +//======================================================================== + +//======================================================================== +// Original Author: Jan-Marten Spit +//======================================================================== + +#include "stringOpsSort.H" +#include +#include + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Local tweaks/preferences: +// +// [DIGITS_ALWAYS_FIRST] : as per original code +// This results in "file123.txt" sorting before "file.txt", which is +// inconsistent with what 'ls -v' produces +// - normally do not want this (Mark Olesen: Oct-2017) +#undef DIGITS_ALWAYS_FIRST + +// [IGNORE_LEADING_ZEROS] : as per original code +// This results in "file0005.txt" sorting before "file06.txt" +// -> normally want this (Mark Olesen: Oct-2017) +#define IGNORE_LEADING_ZEROS + +// [MANUAL_NUMCOMPARE] : handwritten code instead of strncmp +// The orignal code has a mix of strncmp for equality but handwritten code +// for greater-than/less-than. +// +// -> this does to be unneeded, rely on strncmp() return values +// (Mark Olesen: Oct-2017) +#undef MANUAL_NUMCOMPARE + +// [DEBUG_NATSTRCMP] : debug info to std::cerr (for development purposes only) +#undef DEBUG_NATSTRCMP + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#ifdef DEBUG_NATSTRCMP +#include + +template +static inline void debugPrint(const char* text, T item1, T item2) +{ + std::cerr << text << ": <" << item1 << "> <" << item2 << ">\n"; +} + +// Character sequences, end pointer is _inclusive_. +static inline void debugPrint +( + const char* b1, const char* e1, + const char* b2, const char* e2 +) +{ + std::cerr << "<"; + while (b1 < e1) { std::cerr << *b1++; } + std::cerr << *e1 << "> "; + + std::cerr << "<"; + while (b2 < e2) { std::cerr << *b2++; } + std::cerr << *e2 << ">\n"; +} +#endif + +#ifdef MANUAL_NUMCOMPARE +// Manual comparison of identical length (digit) sequences +static inline int manual_numcompare(const char* s1, const char* s2) +{ + while (*s1 && *s2) + { + const int cmp = (*s1 - *s2); + if (!cmp) return cmp; + + ++s1; ++s2; + } + return 0; // Length check done before in caller. +} +#endif + + +// ------------------------------------------------------------------------- // + +int Foam::stringOps::natstrcmp(const char* s1, const char* s2) +{ + #ifdef DEBUG_NATSTRCMP + debugPrint("natstrcmp", s1, s2); + #endif + + // States for state engine + enum stateType { SCAN, ALPHA, NUMERIC }; + + // Number of leading zeroes + unsigned zeros1 = 0; + unsigned zeros2 = 0; + + // Pointers to begin/end of integer sequences (without leading zeros) + const char* numbeg1 = nullptr; + const char* numbeg2 = nullptr; + const char* numend1 = nullptr; + const char* numend2 = nullptr; + + stateType state = SCAN; + + const char* p1 = s1; + const char* p2 = s2; + + while (*p1 && *p2) + { + // Bitmask for digits vs alpha + // - 0: neither are digits + // - 1: p1 is the only digit + // - 2: p2 is the only digit + // - 3: both p1 and p2 are digits + + const unsigned digitMask = + ((isdigit(*p2) ? 2:0) | (isdigit(*p1) ? 1:0)); + + switch (state) + { + case SCAN: + { + #ifdef DEBUG_NATSTRCMP + debugPrint("SCAN", *p1, *p2); + #endif + + switch (digitMask) + { + case 0: // (alpha,alpha) + { + state = ALPHA; + + if (*p1 == *p2) + { + ++p1; ++p2; + } + else + { + // Lexical compare + return (*p1 - *p2); + } + break; + } + + #ifdef DIGITS_ALWAYS_FIRST + case 0x1: // (digit,alpha) : digit < alpha + { + return -1; + break; + } + case 0x2: // (alpha,digit) : alpha > digit + { + return 1; + break; + } + #else /* DIGITS_ALWAYS_FIRST */ + case 0x1: // (digit,alpha) + case 0x2: // (alpha,digit) + { + // Lexical compare for digits/alpha + return (*p1 - *p2); + break; + } + #endif /* DIGITS_ALWAYS_FIRST */ + + default: // (digit,digit) + { + state = NUMERIC; + + #ifdef IGNORE_LEADING_ZEROS + if (!zeros1) // Start skip of leading zeros + { + while (*p1 == '0') { ++p1; ++zeros1; } + } + else + #endif + { + while (*p1 == '0') { ++p1; } + } + #ifdef IGNORE_LEADING_ZEROS + if (!zeros2) // Start skip of leading zeros + { + while (*p2 == '0') { ++p2; ++zeros2; } + } + else + #endif + { + while (*p2 == '0') { ++p2; } + } + + if (zeros1 == zeros2) + { + // Same number of zeros - so irrelevant + zeros1 = zeros2 = 0; + } + + if (!isdigit(*p1)) --p1; + if (!isdigit(*p2)) --p2; + + numbeg1 = numend1 = p1; + numbeg2 = numend2 = p2; + + break; + } + } + break; + } + + case ALPHA: + { + #ifdef DEBUG_NATSTRCMP + debugPrint("ALPHA", *p1, *p2); + #endif + + if (digitMask) + { + state = SCAN; + } + else // (alpha,alpha) + { + if (*p1 == *p2) + { + ++p1; ++p2; + } + else + { + // Lexical compare + return (*p1 - *p2); + } + } + break; + } + + case NUMERIC: + { + while (isdigit(*p1)) numend1 = p1++; + while (isdigit(*p2)) numend2 = p2++; + + #ifdef DEBUG_NATSTRCMP + debugPrint("NUMERIC", *p1, *p2); + debugPrint(numbeg1,numend1, numbeg2,numend2); + #endif + + // Length (minus 1) of each sequence + const size_t len1 = (numend1 - numbeg1); + const size_t len2 = (numend2 - numbeg2); + + if (len1 < len2) + { + return -1; + } + else if (len1 > len2) + { + return 1; + } + + // Same number of digits, leading zeros have been skipped. + // - so lexical and numerical compares are equivalent. + + const int cmp = strncmp(numbeg1, numbeg2, len1+1); + if (!cmp) + { + // Identical (digit) sequence - continue + state = SCAN; + } + else + { + #ifdef MANUAL_STRNCMP + return manual_numcompare(numbeg1, numbeg2); + #else + return cmp; + #endif + } + break; + } + } + } + + if (zeros1 < zeros2) return -1; + if (zeros1 > zeros2) return 1; + if (!*p1 && *p2) return -1; // s1 shorter than s2 + if (*p1 && !*p2) return 1; // s1 longer than s2 + + return 0; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.H b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.H new file mode 100644 index 0000000000..8e6344f27a --- /dev/null +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.H @@ -0,0 +1,133 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +InNamespace + Foam::stringOps + +Description + Specialized string sorting. + +SourceFiles + stringOpsSort.C + +\*---------------------------------------------------------------------------*/ + +#ifndef stringOpsSort_H +#define stringOpsSort_H + +#include "stringOps.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace stringOps +{ + + //- 'Natural' compare for C-strings + // Uses algorithm and code from Jan-Marten Spit + // + // In the 'natural' comparison, strings are compared alphabetically + // and numerically. Thus 'file010.txt' sorts after 'file2.txt' + // + // \param s1 left string + // \param s2 right string + // \return -1 when s1 < s2, 0 when s1 == s2, 1 when s1 > s2 + int natstrcmp(const char* s1, const char* s2); + + + //- Encapsulation of natural order sorting for algorithms + struct natural_sort + { + //- Natural compare for std::string + // \return -1 when s1 < s2, 0 when s1 == s2, 1 when s1 > s2 + static inline int compare + ( + const std::string& s1, + const std::string& s2 + ) + { + return natstrcmp(s1.data(), s2.data()); + } + + //- Default (forward) natural sorting + bool operator()(const std::string& s1, const std::string& s2) const + { + return natural_sort::compare(s1, s2) < 0; + } + + //- Reverse natural sorting + struct reverse + { + //- Reverse natural sorting + bool operator()(const std::string& s1, const std::string& s2) const + { + return natural_sort::compare(s1, s2) > 0; + } + }; + + + //- A list compare binary predicate for natural sort + template + struct less + { + const UList& values; + + less(const UList& list) + : + values(list) + {} + + bool operator()(const label a, const label b) const + { + return natural_sort::compare(values[a], values[b]) < 0; + } + }; + + + //- A list compare binary predicate for reverse natural sort + template + struct greater + { + const UList& values; + + greater(const UList& list) + : + values(list) + {} + + bool operator()(const label a, const label b) const + { + return natural_sort::compare(values[a], values[b]) > 0; + } + }; + }; + +} // End namespace stringOps +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* //