mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-17 13:19:59 +01:00
Cleanup source tree
Move all C++ source files in the src subfolder. Adapted the cmake process accordingly and cleanup.
This commit is contained in:
337
src/BaseToolkit.cpp
Normal file
337
src/BaseToolkit.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* This file is part of vimix - video live mixer
|
||||
*
|
||||
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
#include "BaseToolkit.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <map>
|
||||
|
||||
#include <locale>
|
||||
#include <unicode/ustream.h>
|
||||
#include <unicode/translit.h>
|
||||
|
||||
uint64_t BaseToolkit::uniqueId()
|
||||
{
|
||||
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
|
||||
// 64-bit int 18446744073709551615UL
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000000000000UL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string BaseToolkit::uniqueName(const std::string &basename, std::list<std::string> existingnames)
|
||||
{
|
||||
std::string tentativename = basename;
|
||||
int count = 1;
|
||||
int max = 100;
|
||||
|
||||
// while tentativename can be found in the list of existingnames
|
||||
while ( std::find( existingnames.begin(), existingnames.end(), tentativename ) != existingnames.end() )
|
||||
{
|
||||
for( auto it = existingnames.cbegin(); it != existingnames.cend(); ++it) {
|
||||
if ( it->find(tentativename) != std::string::npos)
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
// check if the name is ending by "_i", with i being a number
|
||||
int i = 0;
|
||||
size_t delim = tentativename.find_last_of('_');
|
||||
if (delim != std::string::npos && BaseToolkit::is_a_number(tentativename.substr(++delim), &i) )
|
||||
// keep same ending "_i" and increment i
|
||||
tentativename = tentativename.substr(0, delim) + std::to_string(++i);
|
||||
else
|
||||
// add the "_i" ending
|
||||
tentativename = basename + "_" + std::to_string( count );
|
||||
}
|
||||
else
|
||||
tentativename += "_";
|
||||
|
||||
if ( --max < 0 ) // for safety only, should never be needed
|
||||
break;
|
||||
}
|
||||
|
||||
return tentativename;
|
||||
}
|
||||
|
||||
// Using ICU transliteration :
|
||||
// https://unicode-org.github.io/icu/userguide/transforms/general/#icu-transliterators
|
||||
|
||||
std::string BaseToolkit::transliterate(const std::string &input)
|
||||
{
|
||||
// because icu::Transliterator is slow, we keep a dictionary of already
|
||||
// transliterated texts to be faster during repeated calls (update of user interface)
|
||||
static std::map<std::string, std::string> dictionary_;
|
||||
std::map<std::string, std::string>::const_iterator existingentry = dictionary_.find(input);
|
||||
|
||||
if (existingentry == dictionary_.cend()) {
|
||||
|
||||
auto ucs = icu::UnicodeString::fromUTF8(input);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::Transliterator *firstTrans = icu::Transliterator::createInstance(
|
||||
"any-NFKD ; [:Nonspacing Mark:] Remove; NFKC; Latin", UTRANS_FORWARD, status);
|
||||
firstTrans->transliterate(ucs);
|
||||
delete firstTrans;
|
||||
|
||||
icu::Transliterator *secondTrans = icu::Transliterator::createInstance(
|
||||
"any-NFKD ; [:Nonspacing Mark:] Remove; [@!#$*%~] Remove; NFKC", UTRANS_FORWARD, status);
|
||||
secondTrans->transliterate(ucs);
|
||||
delete secondTrans;
|
||||
|
||||
std::ostringstream output;
|
||||
output << ucs;
|
||||
|
||||
// remember for future
|
||||
dictionary_[input] = output.str();
|
||||
}
|
||||
|
||||
// return remembered transliterated text
|
||||
return dictionary_[input];
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::unspace(const std::string &input, char delim)
|
||||
{
|
||||
std::string output = input;
|
||||
std::replace_if( output.begin(), output.end(), ::isspace, delim);
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string BaseToolkit::byte_to_string(long b)
|
||||
{
|
||||
double numbytes = static_cast<double>(b);
|
||||
std::ostringstream oss;
|
||||
|
||||
std::list<std::string> list = {" Bytes", " KB", " MB", " GB", " TB"};
|
||||
std::list<std::string>::iterator i = list.begin();
|
||||
|
||||
while(numbytes >= 1024.0 && i != list.end())
|
||||
{
|
||||
++i;
|
||||
numbytes /= 1024.0;
|
||||
}
|
||||
oss << std::fixed << std::setprecision(2) << numbytes;
|
||||
if (i != list.end()) oss << *i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string BaseToolkit::bits_to_string(long b)
|
||||
{
|
||||
double numbytes = static_cast<double>(b);
|
||||
std::ostringstream oss;
|
||||
|
||||
std::list<std::string> list = {" bit", " Kbit", " Mbit", " Gbit", " Tbit"};
|
||||
std::list<std::string>::iterator i = list.begin();
|
||||
|
||||
while(numbytes >= 1000.0 && i != list.end())
|
||||
{
|
||||
++i;
|
||||
numbytes /= 1000.0;
|
||||
}
|
||||
oss << std::fixed << std::setprecision(2) << numbytes;
|
||||
if (i != list.end()) oss << *i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::truncated(const std::string& str, int N)
|
||||
{
|
||||
std::string trunc = str;
|
||||
int l = str.size();
|
||||
if ( l > N ) {
|
||||
trunc = std::string("...") + str.substr( l - N + 3 );
|
||||
}
|
||||
return trunc;
|
||||
}
|
||||
|
||||
std::list<std::string> BaseToolkit::splitted(const std::string& str, char delim)
|
||||
{
|
||||
std::list<std::string> strings;
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
while ((start = str.find_first_not_of(delim, end)) != std::string::npos) {
|
||||
end = str.find(delim, start);
|
||||
size_t delta = start > 0 ? 1 : 0;
|
||||
strings.push_back(str.substr( start -delta, end - start + delta));
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::joinned(std::list<std::string> strlist, char separator)
|
||||
{
|
||||
std::string str;
|
||||
for (auto it = strlist.cbegin(); it != strlist.cend(); ++it) {
|
||||
if (it != strlist.cbegin())
|
||||
str += separator;
|
||||
str += (*it);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool BaseToolkit::is_a_number(const std::string& str, int *val)
|
||||
{
|
||||
bool isanumber = false;
|
||||
|
||||
try {
|
||||
*val = std::stoi(str);
|
||||
isanumber = true;
|
||||
}
|
||||
catch (const std::invalid_argument&) {
|
||||
// avoids crash
|
||||
}
|
||||
catch (const std::out_of_range&) {
|
||||
// avoids crash
|
||||
}
|
||||
|
||||
return isanumber;
|
||||
}
|
||||
|
||||
std::string BaseToolkit::common_prefix( const std::list<std::string> & allStrings )
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
}
|
||||
|
||||
return std::string(s0.cbegin(), _end);
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_suffix(const std::list<std::string> & allStrings)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
return suffix;
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_pattern(const std::list<std::string> &allStrings)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
// find common prefix and suffix
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
|
||||
return std::string(s0.cbegin(), _end) + "*" + suffix;
|
||||
}
|
||||
|
||||
std::string BaseToolkit::common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
// find common prefix and suffix
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
// range of middle string, after prefix and before suffix
|
||||
size_t pos_prefix = std::distance(s0.cbegin(), _end);
|
||||
size_t pos_suffix = s0.size() - pos_prefix - std::distance(s0.crbegin(), r_end);
|
||||
|
||||
int n = -1;
|
||||
*max = 0;
|
||||
*min = INT_MAX;
|
||||
// loop over all strings to verify there are numbers between prefix and suffix
|
||||
for (auto it = allStrings.cbegin(); it != allStrings.cend(); ++it)
|
||||
{
|
||||
// get middle string, after prefix and before suffix
|
||||
std::string s = it->substr(pos_prefix, pos_suffix);
|
||||
// is this central string ONLY made of digits?
|
||||
if (s.end() == std::find_if(s.begin(), s.end(), [](unsigned char c)->bool { return !isdigit(c); })) {
|
||||
// yes, validate
|
||||
*max = std::max(*max, std::atoi(s.c_str()) );
|
||||
*min = std::min(*min, std::atoi(s.c_str()) );
|
||||
if (n < 0)
|
||||
n = s.size();
|
||||
else if ( n != (int) s.size() ) {
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( n < 1 )
|
||||
return std::string();
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
std::string pattern = std::string(s0.cbegin(), _end);
|
||||
pattern += "%0" + std::to_string(n) + "d";
|
||||
pattern += suffix;
|
||||
return pattern;
|
||||
}
|
||||
Reference in New Issue
Block a user