/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2019, Google Inc. * * utils.cpp - Miscellaneous utility functions */ #include #include #include #include #include #include /** * \file base/utils.h * \brief Miscellaneous utility functions */ namespace libcamera { namespace utils { /** * \brief Strip the directory prefix from the path * \param[in] path The path to process * * basename is implemented differently across different C libraries. This * implementation matches the one provided by the GNU libc, and does not * modify its input parameter. * * \return A pointer within the given path without any leading directory * components. */ const char *basename(const char *path) { const char *base = strrchr(path, '/'); return base ? base + 1 : path; } /** * \brief Get an environment variable * \param[in] name The name of the variable to return * * The environment list is searched to find the variable 'name', and the * corresponding string is returned. * * If 'secure execution' is required then this function always returns NULL to * avoid vulnerabilities that could occur if set-user-ID or set-group-ID * programs accidentally trust the environment. * * \note Not all platforms may support the features required to implement the * secure execution check, in which case this function behaves as getenv(). A * notable example of this is Android. * * \return A pointer to the value in the environment or NULL if the requested * environment variable doesn't exist or if secure execution is required. */ char *secure_getenv(const char *name) { #if HAVE_SECURE_GETENV return ::secure_getenv(name); #else #if HAVE_ISSETUGID if (issetugid()) return NULL; #endif return getenv(name); #endif } /** * \brief Identify the dirname portion of a path * \param[in] path The full path to parse * * This function conforms with the behaviour of the %dirname() function as * defined by POSIX. * * \return A string of the directory component of the path */ std::string dirname(const std::string &path) { if (path.empty()) return "."; /* * Skip all trailing slashes. If the path is only made of slashes, * return "/". */ size_t pos = path.size() - 1; while (path[pos] == '/') { if (!pos) return "/"; pos--; } /* * Find the previous slash. If the path contains no non-trailing slash, * return ".". */ while (path[pos] != '/') { if (!pos) return "."; pos--; } /* * Return the directory name up to (but not including) any trailing * slash. If this would result in an empty string, return "/". */ while (path[pos] == '/') { if (!pos) return "/"; pos--; } return path.substr(0, pos + 1); } /** * \fn std::vector map_keys(const T &map) * \brief Retrieve the keys of a std::map<> * \param[in] map The map whose keys to retrieve * \return A std::vector<> containing the keys of \a map */ /** * \fn libcamera::utils::set_overlap(InputIt1 first1, InputIt1 last1, * InputIt2 first2, InputIt2 last2) * \brief Count the number of elements in the intersection of two ranges * * Count the number of elements in the intersection of the sorted ranges [\a * first1, \a last1) and [\a first1, \a last2). Elements are compared using * operator< and the ranges must be sorted with respect to the same. * * \return The number of elements in the intersection of the two ranges */ /** * \typedef clock * \brief The libcamera clock (monotonic) */ /** * \typedef duration * \brief The libcamera duration related to libcamera::utils::clock */ /** * \typedef time_point * \brief The libcamera time point related to libcamera::utils::clock */ /** * \brief Convert a duration to a timespec * \param[in] value The duration * \return A timespec expressing the duration */ struct timespec duration_to_timespec(const duration &value) { uint64_t nsecs = std::chrono::duration_cast(value).count(); struct timespec ts; ts.tv_sec = nsecs / 1000000000ULL; ts.tv_nsec = nsecs % 1000000000ULL; return ts; } /** * \brief Convert a time point to a string representation * \param[in] time The time point * \return A string representing the time point in hh:mm:ss.nanoseconds format */ std::string time_point_to_string(const time_point &time) { uint64_t nsecs = std::chrono::duration_cast(time.time_since_epoch()).count(); unsigned int secs = nsecs / 1000000000ULL; std::ostringstream ossTimestamp; ossTimestamp.fill('0'); ossTimestamp << secs / (60 * 60) << ":" << std::setw(2) << (secs / 60) % 60 << ":" << std::setw(2) << secs % 60 << "." << std::setw(9) << nsecs % 1000000000ULL; return ossTimestamp.str(); } std::basic_ostream> & operator<<(std::basic_ostream> &stream, const _hex &h) { stream << "0x"; std::ostream::fmtflags flags = stream.setf(std::ios_base::hex, std::ios_base::basefield); std::streamsize width = stream.width(h.w); char fill = stream.fill('0'); stream << h.v; stream.flags(flags); stream.width(width); stream.fill(fill); return stream; } /** * \fn hex(T value, unsigned int width) * \brief Write an hexadecimal value to an output string * \param value The value * \param width The width * * Return an object of unspecified type such that, if \a os is the name of an * output stream of type std::ostream, and T is an integer type, then the * expression * * \code{.cpp} * os << utils::hex(value) * \endcode * * will output the \a value to the stream in hexadecimal form with the base * prefix and the filling character set to '0'. The field width is set to \a * width if specified to a non-zero value, or to the native width of type T * otherwise. The \a os stream configuration is not modified. */ /** * \brief Copy a string with a size limit * \param[in] dst The destination string * \param[in] src The source string * \param[in] size The size of the destination string * * This function copies the null-terminated string \a src to \a dst with a limit * of \a size - 1 characters, and null-terminates the result if \a size is * larger than 0. If \a src is larger than \a size - 1, \a dst is truncated. * * \return The size of \a src */ size_t strlcpy(char *dst, const char *src, size_t size) { if (size) { strncpy(dst, src, size); dst[size - 1] = '\0'; } return strlen(src); } details::StringSplitter::StringSplitter(const std::string &str, const std::string &delim) : str_(str), delim_(delim) { } details::StringSplitter::iterator::iterator(const details::StringSplitter *ss, std::string::size_type pos) : ss_(ss), pos_(pos) { next_ = ss_->str_.find(ss_->delim_, pos_); } details::StringSplitter::iterator &details::StringSplitter::iterator::operator++() { pos_ = next_; if (pos_ != std::string::npos) { pos_ += ss_->delim_.length(); next_ = ss_->str_.find(ss_->delim_, pos_); } return *this; } std::string details::StringSplitter::iterator::operator*() const { std::string::size_type count; count = next_ != std::string::npos ? next_ - pos_ : next_; return ss_->str_.substr(pos_, count); } bool details::StringSplitter::iterator::operator!=(const details::StringSplitter::iterator &other) const { return pos_ != other.pos_; } details::StringSplitter::iterator details::StringSplitter::begin() const { return iterator(this, 0); } details::StringSplitter::iterator details::StringSplitter::end() const { return iterator(this, std::string::npos); } /** * \fn template \ * std::string utils::join(const Container &items, const std::string &sep, UnaryOp op) * \brief Join elements of a container in a string with a separator * \param[in] items The container * \param[in] sep The separator to add between elements * \param[in] op A function that converts individual elements to strings * * This function joins all elements in the \a items container into a string and * returns it. The \a sep separator is added between elements. If the container * elements are not implicitly convertible to std::string, the \a op function * shall be provided to perform conversion of elements to std::string. * * \return A string that concatenates all elements in the container */ /** * \fn split(const std::string &str, const std::string &delim) * \brief Split a string based on a delimiter * \param[in] str The string to split * \param[in] delim The delimiter string * * This function splits the string \a str into substrings based on the * delimiter \a delim. It returns an object of unspecified type that can be * used in a range-based for loop and yields the substrings in sequence. * * \return An object that can be used in a range-based for loop to iterate over * the substrings */ details::StringSplitter split(const std::string &str, const std::string &delim) { /** \todo Try to avoid copies of str and delim */ return details::StringSplitter(str, delim); } /** * \brief Remove any non-ASCII characters from a string * \param[in] str The string to strip * * Remove all non-ASCII characters from a string. * * \return A string equal to \a str stripped out of all non-ASCII characters */ std::string toAscii(const std::string &str) { std::string ret; for (const char &c : str) if (!(c & 0x80)) ret += c; return ret; } /** * \fn alignDown(unsigned int value, unsigned int alignment) * \brief Align \a value down to \a alignment * \param[in] value The value to align * \param[in] alignment The alignment * \return The value rounded down to the nearest multiple of \a alignment */ /** * \fn alignUp(unsigned int value, unsigned int alignment) * \brief Align \a value up to \a alignment * \param[in] value The value to align * \param[in] alignment The alignment * \return The value rounded up to the nearest multiple of \a alignment */ /** * \fn reverse(T &&iterable) * \brief Wrap an iterable to reverse iteration in a range-based loop * \param[in] iterable The iterable * \return A value of unspecified type that, when used in a range-based for * loop, will cause the loop to iterate over the \a iterable in reverse order */ /** * \fn enumerate(T &iterable) * \brief Wrap an iterable to enumerate index and value in a range-based loop * \param[in] iterable The iterable * * Range-based for loops are handy and widely preferred in C++, but are limited * in their ability to replace for loops that require access to a loop counter. * The enumerate() function solves this problem by wrapping the \a iterable in * an adapter that, when used as a range-expression, will provide iterators * whose value_type is a pair of index and value reference. * * The iterable must support std::begin() and std::end(). This includes all * containers provided by the standard C++ library, as well as C-style arrays. * * A typical usage pattern would use structured binding to store the index and * value in two separate variables: * * \code{.cpp} * std::vector values = ...; * * for (auto [index, value] : utils::enumerate(values)) { * ... * } * \endcode * * Note that the argument to enumerate() has to be an lvalue, as the lifetime * of any rvalue would not be extended to the whole for loop. The compiler will * complain if an rvalue is passed to the function, in which case it should be * stored in a local variable before the loop. * * \return A value of unspecified type that, when used in a range-based for * loop, iterates over an indexed view of the \a iterable */ /** * \class Duration * \brief Helper class from std::chrono::duration that represents a time * duration in nanoseconds with double precision */ /** * \fn Duration::Duration(const std::chrono::duration &d) * \brief Construct a Duration by converting an arbitrary std::chrono::duration * \param[in] d The std::chrono::duration object to convert from * * The constructed \a Duration object is internally represented in double * precision with nanoseconds ticks. */ /** * \fn Duration::get() * \brief Retrieve the tick count, converted to the timebase provided by the * template argument Period of type \a std::ratio * * A typical usage example is given below: * * \code{.cpp} * utils::Duration d = 5s; * double d_in_ms = d.get(); * \endcode * * \return The tick count of the Duration expressed in \a Period */ /** * \fn Duration::operator bool() * \brief Boolean operator to test if a \a Duration holds a non-zero time value * * \return True if \a Duration is a non-zero time value, False otherwise */ } /* namespace utils */ #ifndef __DOXYGEN__ template std::basic_ostream &operator<<(std::basic_ostream &os, const utils::Duration &d) { std::basic_ostringstream s; s.flags(os.flags()); s.imbue(os.getloc()); s.setf(std::ios_base::fixed, std::ios_base::floatfield); s.precision(2); s << d.get() << "us"; return os << s.str(); } template std::basic_ostream> & operator<< >(std::basic_ostream> &os, const utils::Duration &d); #endif } /* namespace libcamera */