Files
vimix/BaseToolkit.cpp
Bruno Herbelin a612154123 Initial implementation of Control manager
Control manager will handle control actions, recorded or from OSC. Here skeleton for receiving OSC messages is in place. Cleanup of includes for NetworkToolkit. Touched a bit the BaseToolkit.
2021-12-18 16:02:37 +01:00

289 lines
9.1 KiB
C++

/*
* This file is part of vimix - video live mixer
*
* **Copyright** (C) 2020-2021 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)
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 dictionnary of already
// transliterated texts to be faster during repeated calls (update of user interface)
static std::map<std::string, std::string> dictionnary_;
std::map<std::string, std::string>::const_iterator existingentry = dictionnary_.find(input);
if (existingentry == dictionnary_.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
dictionnary_[input] = output.str();
}
// return remembered transliterated text
return dictionnary_[input];
}
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;
size_t end = 0;
while ((start = str.find_first_not_of(delim, end)) != std::string::npos) {
end = str.find(delim, start);
strings.push_back(str.substr(start, end - start));
}
return strings;
}
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;
}