From 62a875e68ba80fbe99a05a0ecff744b2199e089b Mon Sep 17 00:00:00 2001 From: Markus Ehrnsperger <> Date: Thu, 23 Nov 2023 08:08:54 +0100 Subject: [PATCH] recs_level_0 --- pages/recordings.ecpp | 41 ++---- recman.cpp | 64 +++++++-- recman.h | 8 +- stringhelpers.h | 293 +++++------------------------------------- 4 files changed, 105 insertions(+), 301 deletions(-) diff --git a/pages/recordings.ecpp b/pages/recordings.ecpp index 1a35167e..d8270bcf 100644 --- a/pages/recordings.ecpp +++ b/pages/recordings.ecpp @@ -123,7 +123,7 @@ std::vector available_recs; std::string openTreeNodes; if (request.hasCookie("VDR-Live-Recordings-Tree-Open-Nodes")) { openTreeNodes = request.getCookie("VDR-Live-Recordings-Tree-Open-Nodes"); - isyslog("live: VDR-Live-Recordings-Tree-Open-Nodes = %s", openTreeNodes.c_str()); +// isyslog("live: VDR-Live-Recordings-Tree-Open-Nodes = %s", openTreeNodes.c_str()); } cLargeString recoring_item("recoring_item", 25000); @@ -456,6 +456,17 @@ if (currentFlat != "true") { addDuplicateRecordingsNoSd(recItems, recordingsTree); recItemsC = &recItems; } +} +// do we have to get/write the recording details? +// we have to, if currentFlat == true || level == 0 || this folder is open (cookie) +bool write_recs = level == 0 || currentFlat == "true"; +if (!write_recs) { +// check: is this folder open (cookie)? + auto equal_fldf = [parentIdHash](cSv f) { return f.compare(5, std::string_view::npos, parentIdHash) == 0; }; + cSplit splitOpenTreeNodes(openTreeNodes, ','); + write_recs = std::find_if(splitOpenTreeNodes.begin(), splitOpenTreeNodes.end(), equal_fldf) != splitOpenTreeNodes.end(); +} +if (write_recs) { char buffer_i[21]; buffer_i[20] = 0; @@ -466,9 +477,7 @@ if (currentFlat != "true") { rPtr->AppendAsJSArray(*recoring_item, true); recs[<$concat::addCharsIbe(buffer_i+20, rPtr->IdI())$>]=[<$$ recoring_item->c_str() $>] - <%cpp> - } - +% } <%cpp> } @@ -489,29 +498,7 @@ if (currentSort != "duplicates" || currentFlat == "true") { document.write(rec_string_d([<$level$>, <$displayFolder$>, [ <$$ recoring_item->c_str() $> ]])) % } else { rec_ids["fldr_<$ parentIdHash $>"] = [<$level$>, <$displayFolder$>, [ <$$ recoring_item->c_str() $> ]] - <%cpp> -// if this folder is open, also add the recs -// check: is this folder open? - auto equal_fldf = [parentIdHash](cSv f) { return f.compare(5, std::string_view::npos, parentIdHash) == 0; }; - cSplit splitOpenTreeNodes(openTreeNodes, ','); - if (std::find_if(splitOpenTreeNodes.begin(), splitOpenTreeNodes.end(), equal_fldf) != splitOpenTreeNodes.end()) { -// this folder is open -> write the missing recs - char buffer_i[21]; - buffer_i[20] = 0; - for (const RecordingsItemPtr &rPtr: *recItemsC) { - if (std::find(available_recs.begin(), available_recs.end(), rPtr->IdI() ) == available_recs.end() ) { -// this rPtr->IdI() was not provided before - recoring_item->clear(); - rPtr->AppendAsJSArray(*recoring_item, true); - -recs[<$concat::addCharsIbe(buffer_i+20, rPtr->IdI())$>]=[<$$ recoring_item->c_str() $>] - <%cpp> - available_recs.push_back(rPtr->IdI()); - } - } - } - } - +% } <%cpp> } diff --git a/recman.cpp b/recman.cpp index 7b101898..a982f048 100644 --- a/recman.cpp +++ b/recman.cpp @@ -65,7 +65,11 @@ namespace vdrlive { std::string RecordingsManager::Md5Hash(cRecording const * recording) const { - return "recording_" + MD5Hash(recording->FileName()); + m_timeMd5Hash.start(); + std::string result = "recording_" + MD5Hash(recording->FileName()); + m_timeMd5Hash.stop(); + return result; +// return "recording_" + MD5Hash(recording->FileName()); } cRecording const * RecordingsManager::GetByMd5Hash(cSv hash) const @@ -580,16 +584,27 @@ bool searchNameDesc(RecordingsItemPtr &RecItem, const std::vectorstart(); + bool scraper_available = getScraperVideo.call(LiveSetup().GetPluginScraper()); + if (m_timeIdentify) m_timeIdentify->stop(); + if (scraper_available) { m_s_videoType = getScraperVideo.m_scraperVideo->getVideoType(); m_s_dbid = getScraperVideo.m_scraperVideo->getDbId(); - getScraperVideo.m_scraperVideo->getOverview(&m_s_title, &m_s_episode_name, &m_s_release_date, &m_s_runtime, &m_s_IMDB_ID, &m_s_collection_id, collectionName); - m_s_episode_number = getScraperVideo.m_scraperVideo->getEpisodeNumber(); - m_s_season_number = getScraperVideo.m_scraperVideo->getSeasonNumber(); + if (m_s_videoType == tSeries || m_s_videoType == tMovie) { + if (m_timeOverview) m_timeOverview->start(); + getScraperVideo.m_scraperVideo->getOverview(&m_s_title, &m_s_episode_name, &m_s_release_date, &m_s_runtime, &m_s_IMDB_ID, &m_s_collection_id, collectionName); + if (m_timeOverview) m_timeOverview->stop(); + m_s_episode_number = getScraperVideo.m_scraperVideo->getEpisodeNumber(); + m_s_season_number = getScraperVideo.m_scraperVideo->getSeasonNumber(); + if (m_timeDurationDeviation) m_timeDurationDeviation->start(); + m_duration_deviation = getScraperVideo.m_scraperVideo->getDurationDeviation(); + if (m_timeDurationDeviation) m_timeDurationDeviation->stop(); + m_language = getScraperVideo.m_scraperVideo->getLanguage(); + m_video_SD_HD = getScraperVideo.m_scraperVideo->getHD(); + } + if (m_timeImage) m_timeImage->start(); m_s_image = getScraperVideo.m_scraperVideo->getImage(imageLevels, cOrientations(eOrientation::landscape, eOrientation::portrait, eOrientation::banner), false); - m_duration_deviation = getScraperVideo.m_scraperVideo->getDurationDeviation(); - m_language = getScraperVideo.m_scraperVideo->getLanguage(); - m_video_SD_HD = getScraperVideo.m_scraperVideo->getHD(); + if (m_timeImage) m_timeImage->stop(); } else { // service "GetScraperVideo" is not available, just get the image m_s_videoType = tNone; @@ -719,7 +734,7 @@ bool searchNameDesc(RecordingsItemPtr &RecItem, const std::vector %s", name.c_str(), parent->Name().c_str()); m_idI = idI; + m_timeIdentify = timeIdentify; + m_timeOverview = timeOverview; + m_timeImage = timeImage; + m_timeDurationDeviation = timeDurationDeviation; + getScraperData(recording, cImageLevels(eImageLevel::episodeMovie, eImageLevel::seasonMovie, eImageLevel::tvShowCollection, eImageLevel::anySeasonCollection)); // find our number of ts files @@ -986,6 +1006,14 @@ void RecordingsItemRec::AppendAsJSArray(cLargeString &target, std::vectorm_timeMd5Hash.reset(); + cMeasureTime timeRecs, timeIdentify, timeOverview, timeImage, timeDurationDeviation; + timeRecs.reset(); + timeIdentify.reset(); + timeOverview.reset(); + timeImage.reset(); + timeDurationDeviation.reset(); + std::chrono::time_point begin = std::chrono::high_resolution_clock::now(); // check availability of scraper data m_creation_timestamp = time(0); @@ -1015,7 +1043,7 @@ void RecordingsItemRec::AppendAsJSArray(cLargeString &target, std::vectorHierarchyLevels() ); RecordingsItemPtr dir = m_rootFileSystem; - std::string_view name(recording->Name()); + cSv name(recording->Name()); size_t index = 0; size_t pos = 0; @@ -1023,13 +1051,15 @@ void RecordingsItemRec::AppendAsJSArray(cLargeString &target, std::vectoraddDirIfNotExists(name.substr(index, pos - index) ); + dir = dir->addDirIfNotExists(name.substr_csv(index, pos - index) ); index = pos + 1; // esyslog("live: DH: current dir: '%s'", dir->Name().c_str()); } else { - std::string_view recName(name.substr(index, name.length() - index)); - RecordingsItemPtr recPtr (new RecordingsItemRec(recording->Id(), recMan->Md5Hash(recording), recName, recording)); + cSv recName = name.substr_csv(index, name.length() - index); + timeRecs.start(); + RecordingsItemPtr recPtr (new RecordingsItemRec(recording->Id(), recMan->Md5Hash(recording), recName, recording, &timeIdentify, &timeOverview, &timeImage, &timeDurationDeviation)); + timeRecs.stop(); dir->m_entries.push_back(recPtr); m_allRecordings.push_back(recPtr); // esyslog("live: DH: added rec: '%.*s'", (int)recName.length(), recName.data()); @@ -1068,6 +1098,14 @@ m_allRecordings.push_back(recPtr); m_root->finishRecordingsTree(); std::chrono::duration timeNeeded = std::chrono::high_resolution_clock::now() - begin; esyslog("live: DH: ------ RecordingsTree::RecordingsTree() --------, required time: %9.5f", timeNeeded.count() ); +/* + recMan->m_timeMd5Hash.print("live: timeMd5Hash"); + timeRecs.print("live: timeRecs "); + timeIdentify.print("live: Identify "); + timeOverview.print("live: Overview "); + timeImage.print("live: Image "); + timeDurationDeviation.print("live: DurDev "); +*/ } RecordingsTree::~RecordingsTree() diff --git a/recman.h b/recman.h index 803a0b51..5363b5c6 100644 --- a/recman.h +++ b/recman.h @@ -73,6 +73,7 @@ namespace vdrlive { * to reidentify a recording. */ std::string Md5Hash(cRecording const * recording) const; + mutable cMeasureTime m_timeMd5Hash; /** * fetches a cRecording from VDR's Recordings collection. Returns @@ -243,6 +244,11 @@ template bool recEntriesSorted() { return m_cmp_rec != NULL; } bool dirEntriesSorted() { return m_cmp_dir != NULL; } + mutable cMeasureTime *m_timeIdentify = nullptr; + mutable cMeasureTime *m_timeOverview = nullptr; + mutable cMeasureTime *m_timeImage = nullptr; + mutable cMeasureTime *m_timeDurationDeviation = nullptr; + private: std::string GetNameForSearch(cSv name); protected: @@ -333,7 +339,7 @@ template class RecordingsItemRec : public RecordingsItem { public: - RecordingsItemRec(int idI, cSv id, cSv name, const cRecording* recording); + RecordingsItemRec(int idI, cSv id, cSv name, const cRecording* recording, cMeasureTime *timeIdentify, cMeasureTime *timeOverview, cMeasureTime *timeImage, cMeasureTime *timeDurationDeviation); virtual ~RecordingsItemRec(); diff --git a/stringhelpers.h b/stringhelpers.h index b39fc709..857788e4 100644 --- a/stringhelpers.h +++ b/stringhelpers.h @@ -1,6 +1,7 @@ /* * version 0.9.2 * general stringhelper functions + * Note: currently, most up to date Version is in live! * * only depends on g++ -std=c++17 std:: standard headers and on esyslog (from VDR) * no other dependencies, so it can be easily included in any other header @@ -120,17 +121,11 @@ SELECT( CONCATENATE_END, CV_VA_NUM_ARGS(__VA_ARGS__) )(result, __VA_ARGS__) \ // ========================================================= // methods for char *s, make sure that s==NULL is just an empty string // ========================================================= -inline std::string charPointerToString(const unsigned char *s) { - return s?reinterpret_cast(s):""; -} -inline std::string_view charPointerToStringView(const unsigned char *s) { - return s?reinterpret_cast(s):std::string_view(); -} -inline std::string_view charPointerToStringView(const char *s) { - return s?s:std::string_view(); -} inline std::string charPointerToString(const char *s) { - return s?s:""; + return s?s:std::string(); +} +inline std::string charPointerToString(const unsigned char *s) { + return s?reinterpret_cast(s):std::string(); } // challange: // method with importing parameter cSv called with const char * = nullptr @@ -145,20 +140,17 @@ class cSv: public std::string_view { public: cSv(): std::string_view() {} cSv(const char *s): std::string_view(charPointerToStringView(s)) {} + cSv(const unsigned char *s): std::string_view(charPointerToStringView(reinterpret_cast(s))) {} cSv(const char *s, size_t l): std::string_view(s, l) {} cSv(std::string_view sv): std::string_view(sv) {} cSv(const std::string &s): std::string_view(s) {} cSv substr_csv(size_t pos = 0) { return (length() > pos)?cSv(data() + pos, length() - pos):cSv(); } cSv substr_csv(size_t pos, size_t count) { return (length() > pos)?cSv(data() + pos, std::min(length() - pos, count) ):cSv(); } + private: + static std::string_view charPointerToStringView(const char *s) { + return s?std::string_view(s, strlen(s)):std::string_view(); + } }; -inline bool stringEqual(const char *s1, const char *s2) { -// return true if texts are identical (or both texts are NULL) - if (s1 && s2) return strcmp(s1, s2) == 0; - if (!s1 && !s2 ) return true; - if (!s1 && !*s2 ) return true; - if (!*s1 && !s2 ) return true; - return false; -} // ========================================================= // ========================================================= @@ -418,16 +410,6 @@ inline const char *strstr_word (const char *haystack, const char *needle, size_t return NULL; } -inline cSv textAttributeValue(const char *text, const char *attributeName) { - if (!text || !attributeName) return cSv(); - const char *found = strstr(text, attributeName); - if (!found) return cSv(); - const char *avs = found + strlen(attributeName); - const char *ave = strchr(avs, '\n'); - if (!ave) return cSv(); - return cSv(avs, ave-avs); -} - inline bool splitString(cSv str, cSv delim, size_t minLengh, cSv &first, cSv &second) { // true if delim is part of str, and length of first & second >= minLengh std::size_t found = str.find(delim); @@ -449,25 +431,18 @@ inline bool splitString(cSv str, cSv delim, size_t minLengh, cSv &first, cSv &se return true; } -inline bool splitString(cSv str, char delimiter, size_t minLengh, cSv &first, cSv &second) { - using namespace std::literals::string_view_literals; - if (delimiter == '-') return splitString(str, " - "sv, minLengh, first, second); - if (delimiter == ':') return splitString(str, ": "sv, minLengh, first, second); - std::string delim(1, delimiter); - return splitString(str, delim, minLengh, first, second); -} - inline cSv SecondPart(cSv str, cSv delim, size_t minLengh) { // return second part of split string if delim is part of str, and length of first & second >= minLengh // otherwise, return "" cSv first, second; if (splitString(str, delim, minLengh, first, second)) return second; - else return ""; + else return cSv(); } inline cSv SecondPart(cSv str, cSv delim) { -// Return part of str after first occurence of delim // if delim is not in str, return "" +// Otherwise, return part of str after first occurence of delim +// remove leading blanks from result size_t found = str.find(delim); if (found == std::string::npos) return cSv(); std::size_t ssnd; @@ -475,115 +450,23 @@ inline cSv SecondPart(cSv str, cSv delim) { return str.substr(ssnd); } -inline int StringRemoveLastPartWithP(const char *str, int len) { -// remove part with (...) -// return -1 if nothing can be removed -// otherwise length of string without () - len = StringRemoveTrailingWhitespace(str, len); - if (len < 3) return -1; - if (str[len -1] != ')') return -1; - for (int i = len -2; i; i--) { - if (!isdigit(str[i]) && str[i] != '/') { - if (str[i] != '(') return -1; - int len2 = StringRemoveLastPartWithP(str, i); - if (len2 == -1 ) return StringRemoveTrailingWhitespace(str, i); - return len2; - } - } - return -1; -} - -inline bool StringRemoveLastPartWithP(std::string &str) { -// remove part with (...) - int len = StringRemoveLastPartWithP(str.c_str(), str.length() ); - if (len < 0) return false; - str.erase(len); - return true; -} -inline cSv removeLastPartWithP(cSv str) { - int l = StringRemoveLastPartWithP(str.data(), str.length() ); - if (l < 0) return str; - return cSv(str.data(), l); -} - -inline int NumberInLastPartWithPS(cSv str) { -// return number in last part with (./.), 0 if not found / invalid - if (str.length() < 3 ) return 0; - if (str[str.length() - 1] != ')') return 0; - std::size_t found = str.find_last_of("("); - if (found == std::string::npos) return 0; - for (std::size_t i = found + 1; i < str.length() - 1; i ++) { - if (!isdigit(str[i]) && str[i] != '/') return 0; // we ignore (asw), and return only number with digits only - } - return parse_unsigned_internal(str.substr_csv(found + 1)); -} -inline int NumberInLastPartWithP(cSv str) { -// return number in last part with (...), 0 if not found / invalid - if (str.length() < 3 ) return 0; - if (str[str.length() - 1] != ')') return 0; - std::size_t found = str.find_last_of("("); - if (found == std::string::npos) return 0; - for (std::size_t i = found + 1; i < str.length() - 1; i ++) { - if (!isdigit(str[i])) return 0; // we ignore (asw), and return only number with digits only - } - return parse_unsigned_internal(str.substr_csv(found + 1)); -} - -inline int seasonS(cSv description_part, const char *S) { -// return season, if found at the beginning of description_part -// otherwise, return -1 -// std::cout << "seasonS " << description_part << "\n"; - size_t s_len = strlen(S); - if (description_part.length() <= s_len || - !isdigit(description_part[s_len]) || - description_part.compare(0, s_len, S) != 0) return -1; - return parse_unsigned(description_part.substr(s_len)); -} -inline bool episodeSEp(int &season, int &episode, cSv description, const char *S, const char *Ep) { -// search pattern S Ep -// return true if episode was found. -// set season = -1 if season was not found -// set episode = 0 if episode was not found -// find Ep[digit] - season = -1; - episode = 0; - size_t Ep_len = strlen(Ep); - size_t ep_pos = 0; - do { - ep_pos = description.find(Ep, ep_pos); - if (ep_pos == std::string_view::npos || ep_pos + Ep_len >= description.length() ) return false; // no Ep[digit] - ep_pos += Ep_len; -// std::cout << "ep_pos = " << ep_pos << "\n"; - } while (!isdigit(description[ep_pos])); -// Ep[digit] found -// std::cout << "ep found at " << description.substr(ep_pos) << "\n"; - episode = parse_unsigned(description.substr(ep_pos)); - if (ep_pos - Ep_len >= 3) season = seasonS(description.substr(ep_pos - Ep_len - 3), S); - if (season < 0 && ep_pos - Ep_len >= 4) season = seasonS(description.substr(ep_pos - Ep_len - 4), S); - return true; -} - // ========================================================= // ========================================================= // Chapter 2: change string: mainly: append to string // ========================================================= // ========================================================= -// ========================================================= -// some performance improvemnt, to get string presentation for channel -// you can also use channelID.ToString() -// ========================================================= - inline int stringAppendAllASCIICharacters(std::string &target, const char *str) { // append all characters > 31 (signed !!!!). Unsigned: 31 < character < 128 // return number of appended characters int i = 0; -// for (const signed char *strs = reinterpret_cast(str); strs[i] > 31; i++); for (; reinterpret_cast(str)[i] > 31; i++); target.append(str, i); return i; } inline void stringAppendRemoveControlCharacters(std::string &target, const char *str) { +// we replace control characters with " " and invalid UTF8 with "?" +// and remove trailing whitespace for(;;) { str += stringAppendAllASCIICharacters(target, str); wint_t cp = getNextUtfCodepoint(str); @@ -624,26 +507,13 @@ inline std::string concatenate(cSv s1, cSv s2, cSv s3) { return result; } -inline std::string concatenate(const char *s1, const char *s2) { - if (!s1 && !s2) return std::string(); - if (!s1) return s2; - if (!s2) return s1; +inline std::string concatenate(cSv s1, cSv s2, cSv s3, cSv s4) { std::string result; - result.reserve(strlen(s1) + strlen(s2)); - result.append(s1); - result.append(s2); - return result; -} - -inline std::string concatenate(const char *s1, const char *s2, const char *s3) { - if (!s1) return concatenate(s2, s3); - if (!s2) return concatenate(s1, s3); - if (!s3) return concatenate(s1, s2); - std::string result; - result.reserve(strlen(s1) + strlen(s2) + strlen(s3) ); + result.reserve(s1.length() + s2.length() + s3.length() + s4.length() ); result.append(s1); result.append(s2); result.append(s3); + result.append(s4); return result; } @@ -654,11 +524,6 @@ namespace concat { for (numChars = 0; i; i /= 10) numChars++; return numChars; } - template inline int numCharsU(T i) { -// note: i must be >= 0!!!! - if (i < 10) return 1; - return numCharsUg0(i); - } inline int numChars(cSv s) { return s.length(); } inline int numChars(std::string_view s) { return s.length(); } inline int numChars(const std::string &s) { return s.length(); } @@ -707,11 +572,12 @@ namespace concat { char *result = concat::addCharsIbe(be, i); return cSv(result, be - result); } + inline void addChars(char *b, int l, int i) { addCharsIbe(b+l, i); } inline void addChars(char *b, int l, const std::string_view &s) { memcpy(b, s.data(), l); } inline void addChars(char *b, int l, const cSv &s) { memcpy(b, s.data(), l); } inline void addChars(char *b, int l, const std::string &s) { memcpy(b, s.data(), l); } - inline void addChars(char *b, int l, const char *s) { memcpy(b, s, l); } + inline void addChars(char *b, int l, const char *s) { if(s) memcpy(b, s, l); } // methods to append to std::strings ======================== template @@ -732,6 +598,10 @@ namespace concat { str.append(bufs, bufe-bufs); } } +template inline std::string toStringI(T i) { + char buffer[20]; + return std::string(concat::addCharsIbeSc(buffer+20, i)); +} inline void stringAppend(std::string &str, unsigned int i) { concat::stringAppendU(str, i); } inline void stringAppend(std::string &str, unsigned long int i) { concat::stringAppendU(str, i); } inline void stringAppend(std::string &str, unsigned long long int i) { concat::stringAppendU(str, i); } @@ -797,103 +667,10 @@ class cConcatenate // ========================================================= // ========================================================= -template -void push_back_new(std::vector &vec, const T &str) { -// add str to vec, but only if str is not yet in vec and if str is not empty - if (str.empty() ) return; - if (find (vec.begin(), vec.end(), str) != vec.end() ) return; - vec.push_back(str); -} - -template -void stringToVector(std::vector &vec, const char *str) { -// if str does not start with '|', don't split, just add str to vec -// otherwise, split str at '|', and add each part to vec - if (!str || !*str) return; - if (str[0] != '|') { vec.push_back(str); return; } - const char *lDelimPos = str; - for (const char *rDelimPos = strchr(lDelimPos + 1, '|'); rDelimPos != NULL; rDelimPos = strchr(lDelimPos + 1, '|') ) { - push_back_new(vec, T(lDelimPos + 1, rDelimPos - lDelimPos - 1)); - lDelimPos = rDelimPos; - } -} - -inline std::string vectorToString(const std::vector &vec) { - if (vec.size() == 0) return ""; - if (vec.size() == 1) return vec[0]; - std::string result("|"); - for (const std::string &str: vec) { result.append(str); result.append("|"); } - return result; -} - -inline std::string objToString(const int &i) { return std::to_string(i); } -inline std::string objToString(const std::string &i) { return i; } - -template> -std::string getStringFromSet(const C &in, char delim = ';') { - if (in.size() == 0) return ""; - std::string result; - if (delim == '|') result.append(1, delim); - for (const T &i: in) { - result.append(objToString(i)); - result.append(1, delim); - } - return result; -} - -template T stringToObj(const char *s, size_t len) { - esyslog("tvscraper: ERROR: template T stringToObj called"); - return 5; } -template<> inline int stringToObj(const char *s, size_t len) { return atoi(s); } -template<> inline std::string stringToObj(const char *s, size_t len) { return std::string(s, len); } -template<> inline cSv stringToObj(const char *s, size_t len) { return cSv(s, len); } - -template void insertObject(std::vector &cont, const T &obj) { cont.push_back(obj); } -template void insertObject(std::set &cont, const T &obj) { cont.insert(obj); } - -template> -C getSetFromString(const char *str, char delim = ';') { -// split str at delim';', and add each part to result - C result; - if (!str || !*str) return result; - const char *lStartPos = str; - if (delim == '|' && lStartPos[0] == delim) lStartPos++; - for (const char *rDelimPos = strchr(lStartPos, delim); rDelimPos != NULL; rDelimPos = strchr(lStartPos, delim) ) { - insertObject(result, stringToObj(lStartPos, rDelimPos - lStartPos)); - lStartPos = rDelimPos + 1; - } - const char *rDelimPos = strchr(lStartPos, 0); - if (rDelimPos != lStartPos) insertObject(result, stringToObj(lStartPos, rDelimPos - lStartPos)); - return result; -} - -inline const char *strchr_se(const char *ss, const char *se, char ch) { - for (const char *sc = ss; sc < se; sc++) if (*sc == ch) return sc; - return NULL; -} - -template> -C getSetFromString(cSv str, char delim, const std::set &ignoreWords, int max_len = 0) { -// split str at delim, and add each part to result - C result; - if (str.empty() ) return result; - const char *lCurrentPos = str.data(); - const char *lEndPos = str.data() + str.length(); - const char *lSoftEndPos = max_len == 0?lEndPos:str.data() + max_len; - for (const char *rDelimPos = strchr_se(lCurrentPos, lEndPos, delim); rDelimPos != NULL; rDelimPos = strchr_se(lCurrentPos, lEndPos, delim) ) { - if (ignoreWords.find(cSv(lCurrentPos, rDelimPos - lCurrentPos)) == ignoreWords.end() ) - insertObject(result, stringToObj(lCurrentPos, rDelimPos - lCurrentPos)); - lCurrentPos = rDelimPos + 1; - if (lCurrentPos >= lSoftEndPos) break; - } - if (lCurrentPos < lEndPos && lCurrentPos < lSoftEndPos) insertObject(result, stringToObj(lCurrentPos, lEndPos - lCurrentPos)); - return result; -} - class cSplit { public: cSplit(cSv sv, char delim): m_sv(sv), m_delim(delim), m_end(cSv(), m_delim) {} - cSplit(const char *s, char delim): m_sv(charPointerToStringView(s)), m_delim(delim), m_end(cSv(), m_delim) {} +// sv can start with delim (optional) cSplit(const cSplit&) = delete; cSplit &operator= (const cSplit &) = delete; class iterator { @@ -943,16 +720,15 @@ class cSplit { class cContainer { public: cContainer(char delim = '|'): m_delim(delim) { } +// start with delimiter. This allows 'empty' items cContainer(const cContainer&) = delete; cContainer &operator= (const cContainer &) = delete; bool find(cSv sv) { - if (!sv.empty() ) { - size_t f = m_buffer.find(sv); - if (f == std::string_view::npos || f== 0 || f + sv.length() == m_buffer.length() ) return false; - if (m_buffer[f-1] == m_delim && m_buffer[f+sv.length()] == m_delim) return true; - } - CONCATENATE(ns, "|", sv, "|"); - size_t f = m_buffer.find(ns); + char ns[sv.length() + 2]; + ns[0] = m_delim; + ns[sv.length() + 1] = m_delim; + memcpy(ns + 1, sv.data(), sv.length()); + size_t f = m_buffer.find(ns, 0, sv.length()+2); return f != std::string_view::npos; } bool insert(cSv sv) { @@ -966,10 +742,6 @@ class cContainer { m_buffer.append(1, m_delim); return false; } - bool insert(const char *s) { - if (!s) return true; - return insert(cSv(s)); - } std::string moveBuffer() { return std::move(m_buffer); } const std::string &getBufferRef() { return m_buffer; } private: @@ -1002,8 +774,9 @@ class cMeasureTime { void print(const char *context) const { if (numCalls == 0) return; if (!context) context = "cMeasureTime"; - esyslog("tvscraper: %s num = %5i, time = %9.5f, average %f, max = %f", context, numCalls, sumT.count(), sumT.count()/numCalls, maxT.count()); + esyslog("%s num = %5i, time = %9.5f, average %f, max = %f", context, numCalls, sumT.count(), sumT.count()/numCalls, maxT.count()); } + int getNumCalls() const { return numCalls; } private: int numCalls = 0;