Loading media/utils/TimeCheck.cpp +8 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <android-base/logging.h> #include <audio_utils/clock.h> #include <mediautils/EventLog.h> #include <mediautils/FixedString.h> #include <mediautils/MethodStatistics.h> #include <mediautils/TimeCheck.h> #include <utils/Log.h> Loading Loading @@ -138,11 +139,11 @@ std::string TimeCheck::toString() { return getTimeCheckThread().toString(); } TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs, TimeCheck::TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, uint32_t timeoutMs, bool crashOnTimeout) : mTimeCheckHandler(new TimeCheckHandler{ std::move(tag), std::move(onTimer), crashOnTimeout, std::chrono::system_clock::now(), gettid()}) : mTimeCheckHandler{ std::make_shared<TimeCheckHandler>( tag, std::move(onTimer), crashOnTimeout, std::chrono::system_clock::now(), gettid()) } , mTimerHandle(timeoutMs == 0 ? getTimeCheckThread().trackTask(mTimeCheckHandler->tag) : getTimeCheckThread().scheduleTask( Loading Loading @@ -231,14 +232,14 @@ mediautils::TimeCheck makeTimeCheckStatsForClassMethod( mediautils::getStatisticsForClass(className); if (!statistics) return {}; // empty TimeCheck. return mediautils::TimeCheck( std::string(className).append("::").append(methodName), [ clazz = std::string(className), method = std::string(methodName), FixedString62(className).append("::").append(methodName), [ safeMethodName = FixedString30(methodName), stats = std::move(statistics) ] (bool timeout, float elapsedMs) { if (timeout) { ; // ignored, there is no timeout value. } else { stats->event(method, elapsedMs); stats->event(safeMethodName.asStringView(), elapsedMs); } }, 0 /* timeoutMs */); } Loading media/utils/TimerThread.cpp +6 −8 Original line number Diff line number Diff line Loading @@ -31,17 +31,15 @@ extern std::string formatTime(std::chrono::system_clock::time_point t); extern std::string_view timeSuffix(std::string_view time1, std::string_view time2); TimerThread::Handle TimerThread::scheduleTask( std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout) { std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout) { const auto now = std::chrono::system_clock::now(); std::shared_ptr<const Request> request{ new Request{ now, now + timeout, gettid(), std::move(tag) }}; auto request = std::make_shared<const Request>(now, now + timeout, gettid(), tag); return mMonitorThread.add(std::move(request), std::move(func), timeout); } TimerThread::Handle TimerThread::trackTask(std::string tag) { TimerThread::Handle TimerThread::trackTask(std::string_view tag) { const auto now = std::chrono::system_clock::now(); std::shared_ptr<const Request> request{ new Request{ now, now, gettid(), std::move(tag) }}; auto request = std::make_shared<const Request>(now, now, gettid(), tag); return mNoTimeoutMap.add(std::move(request)); } Loading Loading @@ -106,10 +104,10 @@ std::string TimerThread::toString(size_t retiredCount) const { // /* static */ bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) { const size_t hidlPos = request->tag.find("Hidl"); const size_t hidlPos = request->tag.asStringView().find("Hidl"); if (hidlPos == std::string::npos) return false; // should be a separator afterwards Hidl which indicates the string was in the class. const size_t separatorPos = request->tag.find("::", hidlPos); const size_t separatorPos = request->tag.asStringView().find("::", hidlPos); return separatorPos != std::string::npos; } Loading media/utils/include/mediautils/FixedString.h 0 → 100644 +282 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <algorithm> #include <string> #include <string_view> namespace android::mediautils { /* * FixedString is a stack allocatable string buffer that supports * simple appending of other strings and string_views. * * It is designed for no-malloc operation when std::string * small buffer optimization is insufficient. * * To keep code small, use asStringView() for operations on this. * * Notes: * 1) Appending beyond the internal buffer size results in truncation. * * Alternatives: * 1) If you want a sharable copy-on-write string implementation, * consider using the legacy android::String8(). * 2) Using std::string with a fixed stack allocator may suit your needs, * but exception avoidance is tricky. * 3) Using C++20 ranges https://en.cppreference.com/w/cpp/ranges if you don't * need backing store. Be careful about allocation with ranges. * * Good small sizes are multiples of 16 minus 2, e.g. 14, 30, 46, 62. * * Implementation Notes: * 1) No iterators or [] for FixedString - please convert to std::string_view. * 2) For small N (e.g. less than 62), consider a change to always zero fill and * potentially prevent always zero terminating (if one only does append). * * Possible arguments to create/append: * 1) A FixedString. * 2) A single char. * 3) A char * pointer. * 4) A std::string. * 5) A std::string_view (or something convertible to it). * * Example: * * FixedString s1(std::string("a")); // ctor * s1 << "bc" << 'd' << '\n'; // streaming append * s1 += "hello"; // += append * ASSERT_EQ(s1, "abcd\nhello"); */ template <uint32_t N> struct FixedString { // Find the best size type. using strsize_t = std::conditional_t<(N > 255), uint32_t, uint8_t>; // constructors FixedString() { // override default buffer_[0] = '\0'; } FixedString(const FixedString& other) { // override default. copyFrom<N>(other); } // The following constructor is not explicit to allow // FixedString<8> s = "abcd"; template <typename ...Types> FixedString(Types&&... args) { append(std::forward<Types>(args)...); } // copy assign (copyFrom checks for equality and returns *this). FixedString& operator=(const FixedString& other) { // override default. return copyFrom<N>(other); } template <typename ...Types> FixedString& operator=(Types&&... args) { size_ = 0; return append(std::forward<Types>(args)...); } // operator equals bool operator==(const char *s) const { return strncmp(c_str(), s, capacity() + 1) == 0; } bool operator==(std::string_view s) const { return size() == s.size() && memcmp(data(), s.data(), size()) == 0; } // operator not-equals template <typename T> bool operator!=(const T& other) const { return !operator==(other); } // operator += template <typename ...Types> FixedString& operator+=(Types&&... args) { return append(std::forward<Types>(args)...); } // conversion to std::string_view. operator std::string_view() const { return asStringView(); } // basic observers size_t buffer_offset() const { return offsetof(std::decay_t<decltype(*this)>, buffer_); } static constexpr uint32_t capacity() { return N; } uint32_t size() const { return size_; } uint32_t remaining() const { return size_ >= N ? 0 : N - size_; } bool empty() const { return size_ == 0; } bool full() const { return size_ == N; } // when full, possible truncation risk. char * data() { return buffer_; } const char * data() const { return buffer_; } const char * c_str() const { return buffer_; } inline std::string_view asStringView() const { return { buffer_, static_cast<size_t>(size_) }; } inline std::string asString() const { return { buffer_, static_cast<size_t>(size_) }; } void clear() { size_ = 0; buffer_[0] = 0; } // Implementation of append - using templates // to guarantee precedence in the presence of ambiguity. // // Consider C++20 template overloading through constraints and concepts. template <typename T> FixedString& append(const T& t) { using decayT = std::decay_t<T>; if constexpr (is_specialization_v<decayT, FixedString>) { // A FixedString<U> if (size_ == 0) { // optimization to erase everything. return copyFrom(t); } else { return appendStringView({t.data(), t.size()}); } } else if constexpr(std::is_same_v<decayT, char>) { if (size_ < N) { buffer_[size_++] = t; buffer_[size_] = '\0'; } return *this; } else if constexpr(std::is_same_v<decayT, char *>) { // Some char* ptr. return appendString(t); } else if constexpr (std::is_convertible_v<decayT, std::string_view>) { // std::string_view, std::string, or some other convertible type. return appendStringView(t); } else /* constexpr */ { static_assert(dependent_false_v<T>, "non-matching append type"); } } FixedString& appendStringView(std::string_view s) { uint32_t total = std::min(static_cast<size_t>(N - size_), s.size()); memcpy(buffer_ + size_, s.data(), total); size_ += total; buffer_[size_] = '\0'; return *this; } FixedString& appendString(const char *s) { // strncpy zero pads to the end, // strlcpy returns total expected length, // we don't have strncpy_s in Bionic, // so we write our own here. while (size_ < N && *s != '\0') { buffer_[size_++] = *s++; } buffer_[size_] = '\0'; return *this; } // Copy initialize the struct. // Note: We are POD but customize the copying for acceleration // of moving small strings embedded in a large buffers. template <uint32_t U> FixedString& copyFrom(const FixedString<U>& other) { if ((void*)this != (void*)&other) { // not a self-assignment if (other.size() == 0) { size_ = 0; buffer_[0] = '\0'; return *this; } constexpr size_t kSizeToCopyWhole = 64; if constexpr (N == U && sizeof(*this) == sizeof(other) && sizeof(*this) <= kSizeToCopyWhole) { // As we have the same str size type, we can just // memcpy with fixed size, which can be easily optimized. memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this)); return *this; } if constexpr (std::is_same_v<strsize_t, typename FixedString<U>::strsize_t>) { constexpr size_t kAlign = 8; // align to a multiple of 8. static_assert((kAlign & (kAlign - 1)) == 0); // power of 2. // we check any alignment issues. if (buffer_offset() == other.buffer_offset() && other.size() <= capacity()) { // improve on standard POD copying by reducing size. const size_t mincpy = buffer_offset() + other.size() + 1 /* nul */; const size_t maxcpy = std::min(sizeof(*this), sizeof(other)); const size_t cpysize = std::min(mincpy + kAlign - 1 & ~(kAlign - 1), maxcpy); memcpy(static_cast<void*>(this), static_cast<const void*>(&other), cpysize); return *this; } } size_ = std::min(other.size(), capacity()); memcpy(buffer_, other.data(), size_); buffer_[size_] = '\0'; // zero terminate. } return *this; } private: // Template helper methods template <typename Test, template <uint32_t> class Ref> struct is_specialization : std::false_type {}; template <template <uint32_t> class Ref, uint32_t UU> struct is_specialization<Ref<UU>, Ref>: std::true_type {}; template <typename Test, template <uint32_t> class Ref> static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value; // For static assert(false) we need a template version to avoid early failure. template <typename T> static inline constexpr bool dependent_false_v = false; // POD variables strsize_t size_ = 0; char buffer_[N + 1 /* allow zero termination */]; }; // Stream operator syntactic sugar. // Example: // s << 'b' << "c" << "d" << '\n'; template <uint32_t N, typename ...Types> FixedString<N>& operator<<(FixedString<N>& fs, Types&&... args) { return fs.append(std::forward<Types>(args)...); } // We do not use a default size for fixed string as changing // the default size would lead to different behavior - we want the // size to be explicitly known. // FixedString62 of 62 chars fits in one typical cache line. using FixedString62 = FixedString<62>; // Slightly smaller using FixedString30 = FixedString<30>; // Since we have added copy and assignment optimizations, // we are no longer trivially assignable and copyable. // But we check standard layout here to prevent inclusion of unacceptable members or virtuals. static_assert(std::is_standard_layout_v<FixedString62>); static_assert(std::is_standard_layout_v<FixedString30>); } // namespace android::mediautils media/utils/include/mediautils/TimeCheck.h +12 −2 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ class TimeCheck { * the TimeCheck object is destroyed or leaves scope. * \param crashOnTimeout true if the object issues an abort on timeout. */ explicit TimeCheck(std::string tag, OnTimerFunc&& onTimer = {}, explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer = {}, uint32_t timeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true); TimeCheck() = default; Loading @@ -79,7 +79,17 @@ class TimeCheck { // The usage here is const safe. class TimeCheckHandler { public: const std::string tag; template <typename S, typename F> TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout, const std::chrono::system_clock::time_point& _startTime, pid_t _tid) : tag(std::forward<S>(_tag)) , onTimer(std::forward<F>(_onTimer)) , crashOnTimeout(_crashOnTimeout) , startTime(_startTime) , tid(_tid) {} const FixedString62 tag; const OnTimerFunc onTimer; const bool crashOnTimeout; const std::chrono::system_clock::time_point startTime; Loading media/utils/include/mediautils/TimerThread.h +15 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ #include <android-base/thread_annotations.h> #include <mediautils/FixedString.h> namespace android::mediautils { /** Loading Loading @@ -54,7 +56,7 @@ class TimerThread { * \returns a handle that can be used for cancellation. */ Handle scheduleTask( std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout); std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout); /** * Tracks a task that shows up on toString() until cancelled. Loading @@ -62,7 +64,7 @@ class TimerThread { * \param tag string associated with the task. * \returns a handle that can be used for cancellation. */ Handle trackTask(std::string tag); Handle trackTask(std::string_view tag); /** * Cancels a task previously scheduled with scheduleTask() Loading Loading @@ -129,12 +131,22 @@ class TimerThread { // To minimize movement of data, we pass around shared_ptrs to Requests. // These are allocated and deallocated outside of the lock. struct Request { Request(const std::chrono::system_clock::time_point& _scheduled, const std::chrono::system_clock::time_point& _deadline, pid_t _tid, std::string_view _tag) : scheduled(_scheduled) , deadline(_deadline) , tid(_tid) , tag(_tag) {} const std::chrono::system_clock::time_point scheduled; const std::chrono::system_clock::time_point deadline; // deadline := scheduled + timeout // if deadline == scheduled, no // timeout, task not executed. const pid_t tid; const std::string tag; const FixedString62 tag; std::string toString() const; }; Loading Loading
media/utils/TimeCheck.cpp +8 −7 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <android-base/logging.h> #include <audio_utils/clock.h> #include <mediautils/EventLog.h> #include <mediautils/FixedString.h> #include <mediautils/MethodStatistics.h> #include <mediautils/TimeCheck.h> #include <utils/Log.h> Loading Loading @@ -138,11 +139,11 @@ std::string TimeCheck::toString() { return getTimeCheckThread().toString(); } TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs, TimeCheck::TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, uint32_t timeoutMs, bool crashOnTimeout) : mTimeCheckHandler(new TimeCheckHandler{ std::move(tag), std::move(onTimer), crashOnTimeout, std::chrono::system_clock::now(), gettid()}) : mTimeCheckHandler{ std::make_shared<TimeCheckHandler>( tag, std::move(onTimer), crashOnTimeout, std::chrono::system_clock::now(), gettid()) } , mTimerHandle(timeoutMs == 0 ? getTimeCheckThread().trackTask(mTimeCheckHandler->tag) : getTimeCheckThread().scheduleTask( Loading Loading @@ -231,14 +232,14 @@ mediautils::TimeCheck makeTimeCheckStatsForClassMethod( mediautils::getStatisticsForClass(className); if (!statistics) return {}; // empty TimeCheck. return mediautils::TimeCheck( std::string(className).append("::").append(methodName), [ clazz = std::string(className), method = std::string(methodName), FixedString62(className).append("::").append(methodName), [ safeMethodName = FixedString30(methodName), stats = std::move(statistics) ] (bool timeout, float elapsedMs) { if (timeout) { ; // ignored, there is no timeout value. } else { stats->event(method, elapsedMs); stats->event(safeMethodName.asStringView(), elapsedMs); } }, 0 /* timeoutMs */); } Loading
media/utils/TimerThread.cpp +6 −8 Original line number Diff line number Diff line Loading @@ -31,17 +31,15 @@ extern std::string formatTime(std::chrono::system_clock::time_point t); extern std::string_view timeSuffix(std::string_view time1, std::string_view time2); TimerThread::Handle TimerThread::scheduleTask( std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout) { std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout) { const auto now = std::chrono::system_clock::now(); std::shared_ptr<const Request> request{ new Request{ now, now + timeout, gettid(), std::move(tag) }}; auto request = std::make_shared<const Request>(now, now + timeout, gettid(), tag); return mMonitorThread.add(std::move(request), std::move(func), timeout); } TimerThread::Handle TimerThread::trackTask(std::string tag) { TimerThread::Handle TimerThread::trackTask(std::string_view tag) { const auto now = std::chrono::system_clock::now(); std::shared_ptr<const Request> request{ new Request{ now, now, gettid(), std::move(tag) }}; auto request = std::make_shared<const Request>(now, now, gettid(), tag); return mNoTimeoutMap.add(std::move(request)); } Loading Loading @@ -106,10 +104,10 @@ std::string TimerThread::toString(size_t retiredCount) const { // /* static */ bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) { const size_t hidlPos = request->tag.find("Hidl"); const size_t hidlPos = request->tag.asStringView().find("Hidl"); if (hidlPos == std::string::npos) return false; // should be a separator afterwards Hidl which indicates the string was in the class. const size_t separatorPos = request->tag.find("::", hidlPos); const size_t separatorPos = request->tag.asStringView().find("::", hidlPos); return separatorPos != std::string::npos; } Loading
media/utils/include/mediautils/FixedString.h 0 → 100644 +282 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <algorithm> #include <string> #include <string_view> namespace android::mediautils { /* * FixedString is a stack allocatable string buffer that supports * simple appending of other strings and string_views. * * It is designed for no-malloc operation when std::string * small buffer optimization is insufficient. * * To keep code small, use asStringView() for operations on this. * * Notes: * 1) Appending beyond the internal buffer size results in truncation. * * Alternatives: * 1) If you want a sharable copy-on-write string implementation, * consider using the legacy android::String8(). * 2) Using std::string with a fixed stack allocator may suit your needs, * but exception avoidance is tricky. * 3) Using C++20 ranges https://en.cppreference.com/w/cpp/ranges if you don't * need backing store. Be careful about allocation with ranges. * * Good small sizes are multiples of 16 minus 2, e.g. 14, 30, 46, 62. * * Implementation Notes: * 1) No iterators or [] for FixedString - please convert to std::string_view. * 2) For small N (e.g. less than 62), consider a change to always zero fill and * potentially prevent always zero terminating (if one only does append). * * Possible arguments to create/append: * 1) A FixedString. * 2) A single char. * 3) A char * pointer. * 4) A std::string. * 5) A std::string_view (or something convertible to it). * * Example: * * FixedString s1(std::string("a")); // ctor * s1 << "bc" << 'd' << '\n'; // streaming append * s1 += "hello"; // += append * ASSERT_EQ(s1, "abcd\nhello"); */ template <uint32_t N> struct FixedString { // Find the best size type. using strsize_t = std::conditional_t<(N > 255), uint32_t, uint8_t>; // constructors FixedString() { // override default buffer_[0] = '\0'; } FixedString(const FixedString& other) { // override default. copyFrom<N>(other); } // The following constructor is not explicit to allow // FixedString<8> s = "abcd"; template <typename ...Types> FixedString(Types&&... args) { append(std::forward<Types>(args)...); } // copy assign (copyFrom checks for equality and returns *this). FixedString& operator=(const FixedString& other) { // override default. return copyFrom<N>(other); } template <typename ...Types> FixedString& operator=(Types&&... args) { size_ = 0; return append(std::forward<Types>(args)...); } // operator equals bool operator==(const char *s) const { return strncmp(c_str(), s, capacity() + 1) == 0; } bool operator==(std::string_view s) const { return size() == s.size() && memcmp(data(), s.data(), size()) == 0; } // operator not-equals template <typename T> bool operator!=(const T& other) const { return !operator==(other); } // operator += template <typename ...Types> FixedString& operator+=(Types&&... args) { return append(std::forward<Types>(args)...); } // conversion to std::string_view. operator std::string_view() const { return asStringView(); } // basic observers size_t buffer_offset() const { return offsetof(std::decay_t<decltype(*this)>, buffer_); } static constexpr uint32_t capacity() { return N; } uint32_t size() const { return size_; } uint32_t remaining() const { return size_ >= N ? 0 : N - size_; } bool empty() const { return size_ == 0; } bool full() const { return size_ == N; } // when full, possible truncation risk. char * data() { return buffer_; } const char * data() const { return buffer_; } const char * c_str() const { return buffer_; } inline std::string_view asStringView() const { return { buffer_, static_cast<size_t>(size_) }; } inline std::string asString() const { return { buffer_, static_cast<size_t>(size_) }; } void clear() { size_ = 0; buffer_[0] = 0; } // Implementation of append - using templates // to guarantee precedence in the presence of ambiguity. // // Consider C++20 template overloading through constraints and concepts. template <typename T> FixedString& append(const T& t) { using decayT = std::decay_t<T>; if constexpr (is_specialization_v<decayT, FixedString>) { // A FixedString<U> if (size_ == 0) { // optimization to erase everything. return copyFrom(t); } else { return appendStringView({t.data(), t.size()}); } } else if constexpr(std::is_same_v<decayT, char>) { if (size_ < N) { buffer_[size_++] = t; buffer_[size_] = '\0'; } return *this; } else if constexpr(std::is_same_v<decayT, char *>) { // Some char* ptr. return appendString(t); } else if constexpr (std::is_convertible_v<decayT, std::string_view>) { // std::string_view, std::string, or some other convertible type. return appendStringView(t); } else /* constexpr */ { static_assert(dependent_false_v<T>, "non-matching append type"); } } FixedString& appendStringView(std::string_view s) { uint32_t total = std::min(static_cast<size_t>(N - size_), s.size()); memcpy(buffer_ + size_, s.data(), total); size_ += total; buffer_[size_] = '\0'; return *this; } FixedString& appendString(const char *s) { // strncpy zero pads to the end, // strlcpy returns total expected length, // we don't have strncpy_s in Bionic, // so we write our own here. while (size_ < N && *s != '\0') { buffer_[size_++] = *s++; } buffer_[size_] = '\0'; return *this; } // Copy initialize the struct. // Note: We are POD but customize the copying for acceleration // of moving small strings embedded in a large buffers. template <uint32_t U> FixedString& copyFrom(const FixedString<U>& other) { if ((void*)this != (void*)&other) { // not a self-assignment if (other.size() == 0) { size_ = 0; buffer_[0] = '\0'; return *this; } constexpr size_t kSizeToCopyWhole = 64; if constexpr (N == U && sizeof(*this) == sizeof(other) && sizeof(*this) <= kSizeToCopyWhole) { // As we have the same str size type, we can just // memcpy with fixed size, which can be easily optimized. memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this)); return *this; } if constexpr (std::is_same_v<strsize_t, typename FixedString<U>::strsize_t>) { constexpr size_t kAlign = 8; // align to a multiple of 8. static_assert((kAlign & (kAlign - 1)) == 0); // power of 2. // we check any alignment issues. if (buffer_offset() == other.buffer_offset() && other.size() <= capacity()) { // improve on standard POD copying by reducing size. const size_t mincpy = buffer_offset() + other.size() + 1 /* nul */; const size_t maxcpy = std::min(sizeof(*this), sizeof(other)); const size_t cpysize = std::min(mincpy + kAlign - 1 & ~(kAlign - 1), maxcpy); memcpy(static_cast<void*>(this), static_cast<const void*>(&other), cpysize); return *this; } } size_ = std::min(other.size(), capacity()); memcpy(buffer_, other.data(), size_); buffer_[size_] = '\0'; // zero terminate. } return *this; } private: // Template helper methods template <typename Test, template <uint32_t> class Ref> struct is_specialization : std::false_type {}; template <template <uint32_t> class Ref, uint32_t UU> struct is_specialization<Ref<UU>, Ref>: std::true_type {}; template <typename Test, template <uint32_t> class Ref> static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value; // For static assert(false) we need a template version to avoid early failure. template <typename T> static inline constexpr bool dependent_false_v = false; // POD variables strsize_t size_ = 0; char buffer_[N + 1 /* allow zero termination */]; }; // Stream operator syntactic sugar. // Example: // s << 'b' << "c" << "d" << '\n'; template <uint32_t N, typename ...Types> FixedString<N>& operator<<(FixedString<N>& fs, Types&&... args) { return fs.append(std::forward<Types>(args)...); } // We do not use a default size for fixed string as changing // the default size would lead to different behavior - we want the // size to be explicitly known. // FixedString62 of 62 chars fits in one typical cache line. using FixedString62 = FixedString<62>; // Slightly smaller using FixedString30 = FixedString<30>; // Since we have added copy and assignment optimizations, // we are no longer trivially assignable and copyable. // But we check standard layout here to prevent inclusion of unacceptable members or virtuals. static_assert(std::is_standard_layout_v<FixedString62>); static_assert(std::is_standard_layout_v<FixedString30>); } // namespace android::mediautils
media/utils/include/mediautils/TimeCheck.h +12 −2 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ class TimeCheck { * the TimeCheck object is destroyed or leaves scope. * \param crashOnTimeout true if the object issues an abort on timeout. */ explicit TimeCheck(std::string tag, OnTimerFunc&& onTimer = {}, explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer = {}, uint32_t timeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true); TimeCheck() = default; Loading @@ -79,7 +79,17 @@ class TimeCheck { // The usage here is const safe. class TimeCheckHandler { public: const std::string tag; template <typename S, typename F> TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout, const std::chrono::system_clock::time_point& _startTime, pid_t _tid) : tag(std::forward<S>(_tag)) , onTimer(std::forward<F>(_onTimer)) , crashOnTimeout(_crashOnTimeout) , startTime(_startTime) , tid(_tid) {} const FixedString62 tag; const OnTimerFunc onTimer; const bool crashOnTimeout; const std::chrono::system_clock::time_point startTime; Loading
media/utils/include/mediautils/TimerThread.h +15 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ #include <android-base/thread_annotations.h> #include <mediautils/FixedString.h> namespace android::mediautils { /** Loading Loading @@ -54,7 +56,7 @@ class TimerThread { * \returns a handle that can be used for cancellation. */ Handle scheduleTask( std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout); std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout); /** * Tracks a task that shows up on toString() until cancelled. Loading @@ -62,7 +64,7 @@ class TimerThread { * \param tag string associated with the task. * \returns a handle that can be used for cancellation. */ Handle trackTask(std::string tag); Handle trackTask(std::string_view tag); /** * Cancels a task previously scheduled with scheduleTask() Loading Loading @@ -129,12 +131,22 @@ class TimerThread { // To minimize movement of data, we pass around shared_ptrs to Requests. // These are allocated and deallocated outside of the lock. struct Request { Request(const std::chrono::system_clock::time_point& _scheduled, const std::chrono::system_clock::time_point& _deadline, pid_t _tid, std::string_view _tag) : scheduled(_scheduled) , deadline(_deadline) , tid(_tid) , tag(_tag) {} const std::chrono::system_clock::time_point scheduled; const std::chrono::system_clock::time_point deadline; // deadline := scheduled + timeout // if deadline == scheduled, no // timeout, task not executed. const pid_t tid; const std::string tag; const FixedString62 tag; std::string toString() const; }; Loading