Files
zoneminder/src/zm_utils.cpp
Peter Keresztes Schmidt 39a896f5b6 utils: cleanup StartsWith
2021-04-03 23:51:12 +02:00

450 lines
13 KiB
C++

//
// ZoneMinder General Utility Functions, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
//
// 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 2
// 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#include "zm_utils.h"
#include "zm_config.h"
#include "zm_logger.h"
#include <algorithm>
#include <array>
#include <cstring>
#include <fcntl.h> /* Definition of AT_* constants */
#include <sstream>
#include <sys/stat.h>
#if defined(__arm__)
#include <sys/auxv.h>
#endif
#ifdef HAVE_CURL_CURL_H
#include <curl/curl.h>
#endif
unsigned int sse_version = 0;
unsigned int neonversion = 0;
// Trim Both leading and trailing sets
std::string Trim(const std::string &str, const std::string &char_set) {
size_t start_pos = str.find_first_not_of(char_set);
size_t end_pos = str.find_last_not_of(char_set);
// if all spaces or empty return an empty string
if ((start_pos == std::string::npos) || (end_pos == std::string::npos))
return "";
return str.substr(start_pos, end_pos - start_pos + 1);
}
std::string ReplaceAll(std::string str, const std::string &old_value, const std::string &new_value) {
if (old_value.empty())
return str;
size_t start_pos = 0;
while ((start_pos = str.find(old_value, start_pos)) != std::string::npos) {
str.replace(start_pos, old_value.length(), new_value);
start_pos += new_value.length(); // In case 'new_value' contains 'old_value', like replacing 'x' with 'yx'
}
return str;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
std::stringstream ss(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(TrimSpaces(item));
}
return elems;
}
StringVector split(const std::string &string, const std::string &chars, int limit) {
StringVector stringVector;
std::string tempString = string;
std::string::size_type startIndex = 0;
std::string::size_type endIndex = 0;
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
do {
// Find delimiters
endIndex = string.find_first_of( chars, startIndex );
//Info( "Got endIndex at %d", endIndex );
if ( endIndex > 0 ) {
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
}
if ( endIndex == std::string::npos )
break;
// Find non-delimiters
startIndex = tempString.find_first_not_of( chars, endIndex );
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) ) {
stringVector.push_back( string.substr( startIndex ) );
break;
}
//Info( "Got new startIndex at %d", startIndex );
} while ( startIndex != std::string::npos );
//Info( "Finished with %d strings", stringVector.size() );
return stringVector;
}
const std::string join(const StringVector &v, const char * delim=",") {
std::stringstream ss;
for (size_t i = 0; i < v.size(); ++i) {
if ( i != 0 )
ss << delim;
ss << v[i];
}
return ss.str();
}
const std::string base64Encode(const std::string &inString) {
static char base64_table[64] = { '\0' };
if ( !base64_table[0] ) {
int i = 0;
for ( char c = 'A'; c <= 'Z'; c++ )
base64_table[i++] = c;
for ( char c = 'a'; c <= 'z'; c++ )
base64_table[i++] = c;
for ( char c = '0'; c <= '9'; c++ )
base64_table[i++] = c;
base64_table[i++] = '+';
base64_table[i++] = '/';
}
std::string outString;
outString.reserve(2 * inString.size());
const char *inPtr = inString.c_str();
while ( *inPtr ) {
unsigned char selection = *inPtr >> 2;
unsigned char remainder = (*inPtr++ & 0x03) << 4;
outString += base64_table[selection];
if ( *inPtr ) {
selection = remainder | (*inPtr >> 4);
remainder = (*inPtr++ & 0x0f) << 2;
outString += base64_table[selection];
if ( *inPtr ) {
selection = remainder | (*inPtr >> 6);
outString += base64_table[selection];
selection = (*inPtr++ & 0x3f);
outString += base64_table[selection];
} else {
outString += base64_table[remainder];
outString += '=';
}
} else {
outString += base64_table[remainder];
outString += '=';
outString += '=';
}
}
return outString;
}
int split(const char* string, const char delim, std::vector<std::string>& items) {
if ( string == nullptr )
return -1;
if ( string[0] == 0 )
return -2;
std::string str(string);
while ( true ) {
size_t pos = str.find(delim);
items.push_back(str.substr(0, pos));
str.erase(0, pos+1);
if ( pos == std::string::npos )
break;
}
return items.size();
}
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
if ( string == nullptr )
return -1;
if ( string[0] == 0 )
return -2;
std::string str(string);
size_t pos = str.find(delim);
if ( pos == std::string::npos || pos == 0 || pos >= str.length() )
return -3;
name = str.substr(0, pos);
value = str.substr(pos+1, std::string::npos);
return 0;
}
/* Detect special hardware features, such as SIMD instruction sets */
void hwcaps_detect() {
neonversion = 0;
sse_version = 0;
#if (defined(__i386__) || defined(__x86_64__))
__builtin_cpu_init();
if ( __builtin_cpu_supports("avx2") ) {
sse_version = 52; /* AVX2 */
Debug(1, "Detected a x86\\x86-64 processor with AVX2");
} else if ( __builtin_cpu_supports("avx") ) {
sse_version = 51; /* AVX */
Debug(1, "Detected a x86\\x86-64 processor with AVX");
} else if ( __builtin_cpu_supports("sse4.2") ) {
sse_version = 42; /* SSE4.2 */
Debug(1, "Detected a x86\\x86-64 processor with SSE4.2");
} else if ( __builtin_cpu_supports("sse4.1") ) {
sse_version = 41; /* SSE4.1 */
Debug(1, "Detected a x86\\x86-64 processor with SSE4.1");
} else if ( __builtin_cpu_supports("ssse3") ) {
sse_version = 35; /* SSSE3 */
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
} else if ( __builtin_cpu_supports("sse3") ) {
sse_version = 30; /* SSE3 */
Debug(1, "Detected a x86\\x86-64 processor with SSE3");
} else if ( __builtin_cpu_supports("sse2") ) {
sse_version = 20; /* SSE2 */
Debug(1, "Detected a x86\\x86-64 processor with SSE2");
} else if ( __builtin_cpu_supports("sse") ) {
sse_version = 10; /* SSE */
Debug(1, "Detected a x86\\x86-64 processor with SSE");
} else {
sse_version = 0;
Debug(1, "Detected a x86\\x86-64 processor");
}
#elif defined(__arm__)
// ARM processor in 32bit mode
// To see if it supports NEON, we need to get that information from the kernel
unsigned long auxval = getauxval(AT_HWCAP);
if (auxval & HWCAP_ARM_NEON) {
Debug(1,"Detected ARM (AArch32) processor with Neon");
neonversion = 1;
} else {
Debug(1,"Detected ARM (AArch32) processor");
}
#elif defined(__aarch64__)
// ARM processor in 64bit mode
// Neon is mandatory, no need to check for it
neonversion = 1;
Debug(1,"Detected ARM (AArch64) processor with Neon");
#else
// Unknown processor
Debug(1,"Detected unknown processor architecture");
#endif
}
/* SSE2 aligned memory copy. Useful for big copying of aligned memory like image buffers in ZM */
/* For platforms without SSE2 we will use standard x86 asm memcpy or glibc's memcpy() */
#if defined(__i386__) || defined(__x86_64__)
__attribute__((noinline,__target__("sse2")))
#endif
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
if(bytes > 128) {
unsigned int remainder = bytes % 128;
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
__asm__ __volatile__(
"sse2_copy_iter:\n\t"
"movdqa (%0),%%xmm0\n\t"
"movdqa 0x10(%0),%%xmm1\n\t"
"movdqa 0x20(%0),%%xmm2\n\t"
"movdqa 0x30(%0),%%xmm3\n\t"
"movdqa 0x40(%0),%%xmm4\n\t"
"movdqa 0x50(%0),%%xmm5\n\t"
"movdqa 0x60(%0),%%xmm6\n\t"
"movdqa 0x70(%0),%%xmm7\n\t"
"movntdq %%xmm0,(%1)\n\t"
"movntdq %%xmm1,0x10(%1)\n\t"
"movntdq %%xmm2,0x20(%1)\n\t"
"movntdq %%xmm3,0x30(%1)\n\t"
"movntdq %%xmm4,0x40(%1)\n\t"
"movntdq %%xmm5,0x50(%1)\n\t"
"movntdq %%xmm6,0x60(%1)\n\t"
"movntdq %%xmm7,0x70(%1)\n\t"
"add $0x80, %0\n\t"
"add $0x80, %1\n\t"
"cmp %2, %0\n\t"
"jb sse2_copy_iter\n\t"
"test %3, %3\n\t"
"jz sse2_copy_finish\n\t"
"cld\n\t"
"rep movsb\n\t"
"sse2_copy_finish:\n\t"
:
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
);
} else {
/* Standard memcpy */
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
}
#else
/* Non x86\x86-64 platform, use memcpy */
memcpy(dest,src,bytes);
#endif
return dest;
}
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
if (((end->tv_nsec)-(start->tv_nsec))<0) {
diff->tv_sec = end->tv_sec-start->tv_sec-1;
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
} else {
diff->tv_sec = end->tv_sec-start->tv_sec;
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
}
}
std::string TimevalToString(timeval tv) {
tm now = {};
std::array<char, 26> tm_buf = {};
localtime_r(&tv.tv_sec, &now);
size_t tm_buf_len = strftime(tm_buf.data(), tm_buf.size(), "%Y-%m-%d %H:%M:%S", &now);
if (tm_buf_len == 0) {
return "";
}
return stringtf("%s.%06ld", tm_buf.data(), tv.tv_usec);
}
std::string UriDecode( const std::string &encoded ) {
char a, b;
const char *src = encoded.c_str();
std::string retbuf;
retbuf.reserve(encoded.length());
while (*src) {
if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) {
if (a >= 'a')
a -= 'a'-'A';
if (a >= 'A')
a -= ('A' - 10);
else
a -= '0';
if (b >= 'a')
b -= 'a'-'A';
if (b >= 'A')
b -= ('A' - 10);
else
b -= '0';
retbuf.push_back(16*a+b);
src+=3;
} else if (*src == '+') {
retbuf.push_back(' ');
src++;
} else {
retbuf.push_back(*src++);
}
}
return retbuf;
}
void string_toupper( std::string& str) {
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}
void touch(const char *pathname) {
int fd = open(pathname,
O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
0666);
if ( fd < 0 ) {
// Couldn't open that path.
Error("Couldn't open() path %s in touch", pathname);
return;
}
int rc = utimensat(AT_FDCWD,
pathname,
nullptr,
0);
if ( rc ) {
Error("Couldn't utimensat() path %s in touch", pathname);
return;
}
}
QueryString::QueryString(std::istream &input) {
while (!input.eof() && input.peek() > 0) {
//Should eat "param1="
auto name = parseName(input);
//Should eat value1&
std::string value = parseValue(input);
auto foundItr = parameters_.find(name);
if (foundItr == parameters_.end()) {
auto newParam = ZM::make_unique<QueryParameter>(name);
if (value.size() > 0) {
newParam->addValue(value);
}
parameters_.emplace(name, std::move(newParam)).first;
} else {
foundItr->second->addValue(value);
}
} // end while not the end
}
std::vector<std::string> QueryString::names() const {
std::vector<std::string> names;
for (auto const& pair : parameters_)
names.push_back(pair.second->name());
return names;
}
const QueryParameter *QueryString::get(const std::string &name) const {
auto itr = parameters_.find(name);
return itr == parameters_.end() ? nullptr : itr->second.get();
};
std::string QueryString::parseName(std::istream &input) {
std::string name = "";
while (!input.eof() && input.peek() != '=')
name.push_back(input.get());
//Eat the '='
if (!input.eof()) input.get();
return name;
}
std::string QueryString::parseValue(std::istream &input) {
std::string urlEncodedValue;
int c = input.get();
while (c > 0 && c != '&') {
urlEncodedValue.push_back(c);
c = input.get();
}
if (urlEncodedValue.size() == 0)
return "";
return UriDecode(urlEncodedValue);
}