OEMCrypto and OPK v20 prerelease initial commit

This commit is contained in:
Matt Feddersen
2025-05-20 20:30:59 -07:00
parent 98dfef4389
commit a2b9e085e9
193 changed files with 22480 additions and 3275 deletions

View File

@@ -0,0 +1,78 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_BUFFER_READER_H_
#define WVCDM_UTIL_BUFFER_READER_H_
#include <inttypes.h>
#include <string>
#include <vector>
#include "wv_class_utils.h"
namespace wvutil {
// Annotate a function indicating the caller must examine the return value.
// Use like:
// int foo() WARN_UNUSED_RESULT;
// To explicitly ignore a result, see |ignore_result()| in <base/basictypes.h>.
#if defined(COMPILER_GCC)
# define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
# define WARN_UNUSED_RESULT
#endif
class BufferReader {
public:
BufferReader() = delete;
WVCDM_DISALLOW_COPY_AND_MOVE(BufferReader);
BufferReader(const uint8_t* buf, size_t size)
: buf_(buf), size_(buf != nullptr ? size : 0) {}
// == Accessors ==
constexpr const uint8_t* data() const { return buf_; }
constexpr size_t size() const { return size_; }
constexpr size_t pos() const { return pos_; }
constexpr size_t BytesRemaining() const {
return size_ >= pos_ ? size_ - pos_ : 0;
}
constexpr bool HasBytes(size_t count) const {
return count <= BytesRemaining();
}
constexpr bool IsEof() const { return pos_ >= size_; }
// Read a value from the stream, performing endian correction,
// and advance the stream pointer.
bool Read1(uint8_t* v) WARN_UNUSED_RESULT;
bool Read2(uint16_t* v) WARN_UNUSED_RESULT;
bool Read2s(int16_t* v) WARN_UNUSED_RESULT;
bool Read4(uint32_t* v) WARN_UNUSED_RESULT;
bool Read4s(int32_t* v) WARN_UNUSED_RESULT;
bool Read8(uint64_t* v) WARN_UNUSED_RESULT;
bool Read8s(int64_t* v) WARN_UNUSED_RESULT;
bool ReadString(std::string* str, size_t count) WARN_UNUSED_RESULT;
bool ReadVec(std::vector<uint8_t>* t, size_t count) WARN_UNUSED_RESULT;
// These variants read a 4-byte integer of the corresponding signedness and
// store it in the 8-byte return type.
bool Read4Into8(uint64_t* v) WARN_UNUSED_RESULT;
bool Read4sInto8s(int64_t* v) WARN_UNUSED_RESULT;
// Advance the stream by this many bytes.
bool SkipBytes(size_t count) WARN_UNUSED_RESULT;
private:
const uint8_t* buf_ = nullptr;
size_t size_ = 0;
size_t pos_ = 0;
template <typename T>
bool Read(T* t) WARN_UNUSED_RESULT;
}; // class BufferReader
} // namespace wvutil
#endif // WVCDM_UTIL_BUFFER_READER_H_

View File

@@ -9,18 +9,19 @@
#include <stdint.h>
namespace wvutil {
#include "wv_timestamp.h"
namespace wvutil {
// Provides time related information. The implementation is platform dependent.
class Clock {
public:
Clock() {}
virtual ~Clock() {}
Clock() = default;
virtual ~Clock() = default;
// Provides the number of seconds since an epoch - 01/01/1970 00:00 UTC
virtual int64_t GetCurrentTime();
virtual Timestamp GetCurrentTimestamp();
};
} // namespace wvutil
#endif // WVCDM_UTIL_CLOCK_H_

View File

@@ -0,0 +1,211 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_HLS_ATTRIBUTE_LIST_H_
#define WVCDM_UTIL_HLS_ATTRIBUTE_LIST_H_
#include <inttypes.h>
#include <map>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "wv_class_utils.h"
namespace wvutil {
// An HLS Attribute List is a loosely defined HLS tag value
// type representing a dictionary of attribute name-value pairs.
// No attribute name may appear twice in a valid HLS Attribute
// List.
//
// When serialized, an HLS attribute list appears as a comma
// separated list of <name>=<value> pairs, without any line breaks,
// and whitespace only within quoted string attribute values.
//
// The HLS specification defines them as context sensitive-types,
// as the format of certain value types are ambiguous with other
// value types (ex. something that looks like a hex sequence could
// actually be an enum string). The exact value types of attributes
// depends on the HLS tag.
//
// This implementation is intended to be context free when parsing.
// Internally, value will be assigned a type based on their
// appearance to the most restrictive type; however, accessing as a
// particular type will be allowed so long as they match the format
// that type. The only exception to this is quoted strings, which
// are fully unambiguous with all other types.
//
// The standard HLS attribute list allows for UTF-8 encoded unicode
// characters; however, for Widevine's use case, we only allow
// basic ASCII.
//
// This class is based on RFC 8216 section 4.2.
class HlsAttributeList {
public:
enum ValueType {
kUnsetType = 0,
// HLS integers are a sequence of 1 to 20 base10 digits
// values, which must fit into an unsigned 64-bit integer.
// Note:
// All integers can appear as an enum string.
// All integers can appear as floats; however, precision
// restrictions may limit ability to parse accurately.
kIntegerType,
// HLS hex sequence are a sequence of 1 or more upper case
// hexadecimal digits, with the prefix "0x" or "0X".
// Note:
// All hex sequences can appear as an enum string.
// Certain hex sequences can appear as a resolution.
kHexSequenceType,
// HLS float and sign float are a sequence of base10 digits
// with a possible leading negative sign ('-') and at most one
// decimal point ('.').
// Note:
// All floats can appear as an enum string.
// Certain floats can appear are an integer.
kFloatType,
// For simplicity, signed and unsigned floats are internally
// treated as the same.
kSignedFloatType = kFloatType, // Aliasing
// HLS quoted strings are a sequence of printable characters
// (except a double quote), or space characters, contained within
// a pair of double quotes.
// Note: Quote string are unambiguous with all other types.
kQuotedStringType,
// HLS enum strings are a sequence of any printable characters
// (except quotes or commas) and do not contain whitespace.
// Note:
// Certain enum strings can appear as integers, floats, hex
// sequences or resolutions.
kEnumStringType,
// HLS resolutions are a pair of base10 integers (similar to
// integer types) separated by an 'x' character.
// Note:
// All resolutions can appear as enum strings
// Certain resolutions can appear as hex sequences.
kResolutionType,
};
static const char* ValueTypeToString(ValueType type);
// Checks if the provided |name| is valid HLS attribute name.
static bool IsValidName(const std::string& name);
// Checks if the provided |value| is a valid HLS enumerated
// string.
static bool IsValidEnumStringValue(const std::string& value);
// Checks if the provided |value| is an allowed content
// of a quote string (i.e., |value| is the portion that is
// to be contained within the double quotes).
static bool IsValidQuotedStringValue(const std::string& value);
// Validator for an HLS unsigned integer representation.
// Checks that the value representation |value_rep| conforms
// to HLS requirements for a serialized integer.
static bool IsValidIntegerRep(const std::string& value_rep);
// Parses the provided |integer_rep| as an HLS integer, assign
// the parsed integer to |value|.
static bool ParseInteger(const std::string& integer_rep, uint64_t* value);
HlsAttributeList() = default;
WVCDM_DEFAULT_COPY_AND_MOVE(HlsAttributeList);
// == Basic Accessors ==
bool IsEmpty() const { return members_.empty(); }
size_t Count() const { return members_.size(); }
void Clear() { members_.clear(); }
// == Value Getters ==
// Returns a list of attribute names.
std::vector<std::string> GetNames() const;
// Returns true if the provided attribute |name| is contained
// within list.
bool Contains(const std::string& name) const;
// Checks if the provided attribute |name| could be the
// specified |type|.
// Certain value types are ambiguous; so they may be marked
// internally as one type, but are still valid forms of a
// different type.
bool IsType(const std::string& name, ValueType type) const;
// Gets the attribute value for the provided attribute |name|,
// assigning the deserialized value to the output parameter(s).
//
// If a type is internally stored as a different type, but the format
// matches the requested type, it will be allowed.
//
// Returns true if the attribute value was successfully obtained;
// false otherwise (attribute does not exist, or the value could
// not be parsed as the specified type).
bool GetEnumString(const std::string& name, std::string* value) const;
bool GetQuotedString(const std::string& name, std::string* value) const;
bool GetHexSequence(const std::string& name, std::string* value) const;
bool GetHexSequence(const std::string& name,
std::vector<uint8_t>* value) const;
bool GetInteger(const std::string& name, uint64_t* value) const;
bool GetFloat(const std::string& name, double* value) const;
bool GetResolution(const std::string& name, uint64_t* width,
uint64_t* height) const;
// == Value Setters ==
// The value setters attempt to serializes the provided value
// into an HLS attribute value format of the type indicated
// by the method name. These methods will overwrite any existing
// attribute of the same name.
// Setting will be rejected if |name| is not a valid HLS attribute
// name, or if the provided |value| is valid (only applicable to
// certain types).
bool SetEnumString(const std::string& name, const std::string& value);
bool SetQuotedString(const std::string& name, const std::string& value);
// Note: This implementation will use lower "0x" prefix for
// hex sequences.
bool SetHexSequence(const std::string& name, const std::string& value);
bool SetHexSequence(const std::string& name,
const std::vector<uint8_t>& value);
bool SetInteger(const std::string& name, uint64_t value);
bool SetFloat(const std::string& name, double value);
bool SetResolution(const std::string& name, uint64_t width, uint64_t height);
// Removes the specified attribute. Returns true if the attribute
// was removed; false otherwise.
bool Remove(const std::string& name);
// == Parsing / Serialization ==
// Attempts to parse the provided HLS Attribute List.
// Always clears the existing contents, even if the attribute
// list cannot be parsed.
//
// Internally, values are not parsed, but checked to see which
// type they resemble. This does not restrict accessing them
// as different types.
//
// Returns true if successfully parsed; false otherwise.
bool Parse(const std::string& hls_attr_list_rep);
// Serializes the contents of the HLS attribute list into a
// valid HLS attribute list form.
std::string Serialize() const;
bool SerializeToStream(std::ostream* out) const;
// Internally, values are stored in their serialized form
// in case their value is ambiguous between two or more types.
// The assigned ValueType is intended to be a hint for improve
// access speeds.
using ValueInfo =
std::pair</* type hint */ ValueType, /* value rep */ std::string>;
private:
// Internal utility to obtain the value info.
// Silently returns null if the name does not exist.
const ValueInfo* GetInfo(const std::string& name) const;
// Values in |members_| will always be validated before assigned.
std::map</* name */ std::string, ValueInfo> members_;
}; // class HlsAttributeList
} // namespace wvutil
#endif // WVCDM_UTIL_HLS_ATTRIBUTE_LIST_H_

View File

@@ -1,65 +0,0 @@
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_RW_LOCK_H_
#define WVCDM_UTIL_RW_LOCK_H_
#include <stdint.h>
#include <condition_variable>
#include <mutex>
#include "disallow_copy_and_assign.h"
#include "util_common.h"
namespace wvutil {
// A simple reader-writer mutex implementation that mimics the one from C++17
class shared_mutex {
public:
shared_mutex() : reader_count_(0), has_writer_(false) {}
~shared_mutex();
// These methods take the mutex as a reader. They do not fulfill the
// SharedMutex requirement from the C++14 STL, but they fulfill enough of it
// to be used with |shared_lock| below.
void lock_shared();
void unlock_shared();
// These methods take the mutex as a writer. They fulfill the Mutex
// requirement from the C++11 STL so that this mutex can be used with
// |std::unique_lock|.
void lock() { lock_implementation(false); }
bool try_lock() { return lock_implementation(true); }
void unlock();
private:
bool lock_implementation(bool abort_if_unavailable);
uint32_t reader_count_;
bool has_writer_;
std::mutex mutex_;
std::condition_variable condition_variable_;
CORE_DISALLOW_COPY_AND_ASSIGN(shared_mutex);
};
// A simple reader lock implementation that mimics the one from C++14
template <typename Mutex>
class shared_lock {
public:
explicit shared_lock(Mutex& lock) : lock_(&lock) { lock_->lock_shared(); }
explicit shared_lock(Mutex* lock) : lock_(lock) { lock_->lock_shared(); }
~shared_lock() { lock_->unlock_shared(); }
private:
Mutex* lock_;
CORE_DISALLOW_COPY_AND_ASSIGN(shared_lock);
};
} // namespace wvutil
#endif // WVCDM_UTIL_RW_LOCK_H_

View File

@@ -0,0 +1,89 @@
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_STRING_UTILS_H_
#define WVCDM_UTIL_STRING_UTILS_H_
#include <string>
#include <vector>
// Small set of simple string utilities.
//
// It is inspired by Python strings and Abseil. The CDM tends to not
// be too fancy with its use of templated containers; none of the
// Abseil container magic is not provided, nor are the fancy templated
// configurations.
namespace wvutil {
// Splits a string into several substrings based on the provided
// delimiter.
//
// Special cases:
// - An empty delimiter will split the string into each character.
std::vector<std::string> StringSplit(const std::string& s, char delim);
std::vector<std::string> StringSplit(const std::string& s,
const std::string& delim);
std::vector<std::string> StringSplit(const std::string& s, const char* delim);
// Joins a list of strings into a single string, using the specified
// "glue" character(s) between tokens. Note: |glue| can be empty.
std::string StringJoin(const std::vector<std::string>& tokens, char glue);
std::string StringJoin(const std::vector<std::string>& tokens,
const std::string& glue = "");
std::string StringJoin(const std::vector<std::string>& tokens,
const char* glue);
// Counts the number of instances of |needle| sequences found in the
// |haystack|.
//
// Special cases:
// - An empty needle will return the length of |haystack| plus 1,
// including for an empty string.
// Note: This is a convention used by many string utility
// libraries.
size_t StringCount(const std::string& haystack, char needle);
size_t StringCount(const std::string& haystack, const std::string& needle);
size_t StringCount(const std::string& haystack, const char* needle);
// Checks if any instances of |needle| sequences found in the |haystack|.
//
// Special cases:
// - An empty |needle| is always present, even if |haystack| is empty.
// Note: This is a convention used by many string utility
// libraries.
bool StringContains(const std::string& haystack, char needle);
bool StringContains(const std::string& haystack, const std::string& needle);
bool StringContains(const std::string& haystack, const char* needle);
// Checks if the |needle| sequences found at the beginning of |haystack|.
//
// Special cases:
// - An empty |needle| is always present, even if |haystack| is empty.
// Note: This is a convention used by many string utility
// libraries.
bool StringStartsWith(const std::string& haystack, char needle);
bool StringStartsWith(const std::string& haystack, const std::string& needle);
bool StringStartsWith(const std::string& haystack, const char* needle);
// Checks if the |needle| sequences found at the end of |haystack|.
//
// Special cases:
// - An empty |needle| is always present, even if |haystack| is empty.
// Note: This is a convention used by many string utility
// libraries.
bool StringEndsWith(const std::string& haystack, char needle);
bool StringEndsWith(const std::string& haystack, const std::string& needle);
bool StringEndsWith(const std::string& haystack, const char* needle);
// Removes any leading or trailing white space from the provided string.
std::string StringTrim(const std::string& s);
// Checks if the vector of strings contains any instance of the
// specified |needle|.
//
// Note: Unlike the other utilities, an empty |needle| is treated
// as a value.
bool StringVecContains(const std::vector<std::string>& haystack,
const std::string& needle);
} // namespace wvutil
#endif // WVCDM_UTIL_STRING_UTILS_H_

223
util/include/wv_date_time.h Normal file
View File

@@ -0,0 +1,223 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_DATE_TIME_H_
#define WVCDM_UTIL_DATE_TIME_H_
#include <stdint.h>
#include <chrono>
#include <optional>
#include <ostream>
#include <string>
#include "wv_class_utils.h"
#include "wv_duration.h"
#include "wv_timestamp.h"
namespace wvutil {
// The DateTime class represents a time point measured in UTC
// Gregorian calendar dates and 24-hour timekeeping system.
//
// Internally, the time is measured in Unix Time (milliseconds
// since January 1st, 1970 (epoch)).
//
// For the CDM, we are not concerned about time points before
// January 1st, 1970; this class does not support negative epoch
// seconds. In addition, zero is treated as a special value
// which indicates an uninitialized time point.
//
// Min DateTime: 1970-01-01 00:00:00.001 (epoch ms = 1)
// Max DateTime: 9999-12-31 23:59:59.999 (epoch ms = 253402300799999)
//
// Converting from Unix Time to datetime components is a potentially
// expensive operation for their amount of utility; to reduce this
// overhead date components are calculated once per change, and stored
// individually.
//
// For testing, use the DateTime::Builder class for create DateTime
// instances from their individual components.
//
// The DateTime class provides a PrintTo()/ToString() method which
// returns an initialized DateTime in ISO 8601 format without timezone
// indicator and with milliseconds only being printed if non-zero.
// Use DateTime::Formatter for specific formatting needs.
class DateTime {
public:
constexpr DateTime() = default; // Uninitialized DateTime.
WVCDM_CONSTEXPR_DEFAULT_COPY_AND_MOVE(DateTime);
private:
// Note: This private section is needed here.
// clang++ with -Wundefined-inline will complain about certain
// inline members not being declared before use in other inline
// methods.
template <class ToDuration>
constexpr ToDuration EpochFloor() const {
return std::chrono::floor<ToDuration>(epoch_milliseconds());
}
public:
static DateTime FromTimestamp(const Timestamp& timestamp);
// Create a DateTime instance from Unix Time in epoch seconds.
// Optionally allowed to include |millisecond| (0 to 999).
// If |epoch_seconds| or |millisecond| are invalid values, then an
// uninitlaized DateTime instance is returned.
static DateTime FromUnixSeconds(uint64_t epoch_seconds,
uint32_t milliseconds = 0) {
return FromTimestamp(
Timestamp::FromUnixSeconds(epoch_seconds, milliseconds));
}
static DateTime FromUnixSeconds(const Seconds& epoch_seconds,
uint32_t milliseconds = 0) {
return FromTimestamp(
Timestamp::FromUnixSeconds(epoch_seconds, milliseconds));
}
// Create a DateTime instance from Unix Time in epoch milliseconds.
// If |epoch_milliseconds| is an invalid value (zero), then an
// uninitlaized DateTime instance is returned.
static DateTime FromUnixMilliseconds(uint64_t epoch_milliseconds) {
return FromTimestamp(Timestamp::FromUnixMilliseconds(epoch_milliseconds));
}
static DateTime FromUnixMilliseconds(const Milliseconds& epoch_milliseconds) {
return FromTimestamp(Timestamp::FromUnixMilliseconds(epoch_milliseconds));
}
// Obtain the minimum and maximum representable DateTime.
static DateTime Min() { return FromTimestamp(Timestamp::Min()); }
static DateTime Max() { return FromTimestamp(Timestamp::Max()); }
constexpr const Timestamp& timestamp() const { return timestamp_; }
// == Epoch Accessors ==
constexpr Seconds epoch_seconds() const { return timestamp_.epoch_seconds(); }
constexpr Milliseconds epoch_milliseconds() const {
return timestamp_.epoch_milliseconds();
}
// == Component Accessors ==
// Year on Gregorian calendar (1970 - 9999; 0 if unset)
constexpr uint32_t year() const { return year_; }
// Month of the year (January = 1, December = 12; 0 if unset)
constexpr uint32_t month() const { return month_; }
// Day of the month (1 - 28/29/30/31; 0 if unset)
constexpr uint32_t day() const { return day_; }
// Day of the year (1 - 365/366; 0 if unset)
constexpr uint32_t day_of_year() const { return day_of_year_; }
// Day of the week (1 (Sunday) - 7 (Saturday); 0 if unset)
constexpr uint32_t day_of_week() const { return day_of_week_; }
// Time component accessors.
// Hour of day (0 - 23; 0 if unset)
constexpr uint32_t hour() const {
if (!IsSet()) return 0;
return static_cast<uint32_t>(
(EpochFloor<Hours>() - EpochFloor<Days>()).count());
}
// Minute of hour (0 - 59; 0 if unset)
constexpr uint32_t minute() const {
if (!IsSet()) return 0;
return static_cast<uint32_t>(
(EpochFloor<Minutes>() - EpochFloor<Hours>()).count());
}
// Second of minute (0 - 59; 0 if unset)
constexpr uint32_t second() const {
if (!IsSet()) return 0;
return static_cast<uint32_t>(
(EpochFloor<Seconds>() - EpochFloor<Minutes>()).count());
}
// Millisecond of second (0 - 999; 0 if unset)
constexpr uint32_t millisecond() const { return timestamp_.milliseconds(); }
// Checks if the DateTime instance is set.
constexpr bool IsSet() const { return timestamp_.IsSet(); }
constexpr explicit operator bool() const { return IsSet(); }
constexpr void Clear() {
timestamp_.Clear();
ClearComponents();
}
// == Comparison Operators ==
constexpr bool IsEqualTo(const DateTime& other) const {
return timestamp_.IsEqualTo(other.timestamp_);
}
constexpr int64_t CompareTo(const DateTime& other) const {
return timestamp_.CompareTo(other.timestamp_);
}
WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(DateTime);
constexpr bool IsEqualTo(const Timestamp& other) const {
return timestamp_.IsEqualTo(other);
}
constexpr int64_t CompareTo(const Timestamp& other) const {
return timestamp_.CompareTo(other);
}
WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(Timestamp);
// == Duration Operators ==
// Duration-based addition/subtraction operations.
// Returns a new DateTime instance with time point adjusted by
// the provided Duration.
// If current DateTime instance is unset, or if result value
// is outside the range of valid DateTime values, then an unset
// DateTime is returned.
DateTime operator+(const Duration& duration) const;
DateTime operator-(const Duration& duration) const;
// DateTime difference.
// Returns the duration between the two provided DateTime
// instances.
// If either DateTime instance is unset, then the resulting
// Duration is zero.
Duration operator-(const DateTime& other) const;
// Duration-based increment/decrement operators.
// Increment or decrement the DateTime by the provided Duration
// amount.
// If current DateTime instance is unset, then no action will
// occur. If result value is outside the range of valid DateTime
// values, then DateTime will be unset.
DateTime& operator+=(const Duration& duration);
DateTime& operator-=(const Duration& duration);
// For initialized DateTime instances, returns the datetime
// in ISO 8601 format. Milliseconds are only printed
// if non-zero.
// An uninitialized DateTime will return an empty string.
// Ex:
// Without milli: 2024-01-19T14:49:13Z
// With milli: 2024-01-19T14:49:13.507Z
// Use DateTime::Formatter for more formatting options.
bool PrintTo(std::ostream* out) const;
std::string ToString() const;
private:
// Special constructor.
explicit DateTime(const Timestamp& timestamp);
// Attempts to calculate subcomponents.
// Failure to calculate subcomponents will clear |timestamp_|.
void UpdateTimestamp(const Timestamp& new_timestamp);
constexpr void ClearComponents() {
year_ = month_ = day_ = day_of_year_ = day_of_week_ = 0;
}
// Source of truth for DateTime class.
Timestamp timestamp_;
// Components (derived from |timestamp_|).
uint16_t year_ = 0;
uint8_t month_ = 0;
uint8_t day_ = 0;
uint16_t day_of_year_ = 0;
uint8_t day_of_week_ = 0;
}; // class DateTime
// == GTest Printer ==
void PrintTo(const DateTime& date_time, std::ostream* out);
} // namespace wvutil
#endif // WVCDM_UTIL_DATE_TIME_H_

286
util/include/wv_duration.h Normal file
View File

@@ -0,0 +1,286 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_WV_DURATION_H_
#define WVCDM_UTIL_WV_DURATION_H_
#include <inttypes.h>
#include <chrono>
#include <ostream>
#include <ratio>
#include <string>
#include "wv_class_utils.h"
namespace wvutil {
// Wrappers around various std::chono::duration units.
using Nanoseconds = std::chrono::nanoseconds;
using Microseconds = std::chrono::microseconds;
using Milliseconds = std::chrono::milliseconds;
using Seconds = std::chrono::seconds;
using Minutes = std::chrono::minutes;
using Hours = std::chrono::hours;
#if __cplusplus >= 202002L // C++20
using Days = std::chrono::days;
#else
// Days is not declared in C++17, the standard C++ library
// uses the following implementation for C++20.
using Days = std::chrono::duration<int64_t, std::ratio<86400> >;
#endif
// A high-level duration struct for storing a duration and
// easily accessing the components.
// A duration measures a concrete amount of time between two
// different points in time.
//
// Precision of Duration is in milliseconds.
class Duration final {
public:
constexpr Duration() = default;
// Initialize duration from some kind of chrono duration type that
// can easily convert to milliseconds.
// Compiler will not allow irreconcilable duration types to be used
// without caller explicitly chrono::duration_cast<milliseconds>.
template <class Rep, class Period>
constexpr Duration(const std::chrono::duration<Rep, Period>& total_duration)
: total_milliseconds_(total_duration) {}
WVCDM_CONSTEXPR_DEFAULT_COPY_AND_MOVE(Duration);
private:
// Note: This private section is needed here.
// clang++ with -Wundefined-inline will complain about certain
// inline members not being declared before use in other inline
// methods.
// Helper function for performing truncate (round towards zero)
// operation on chrono::duration types.
// The standard C++ chrono library does not provide such a function.
template <class ToDuration, class Rep, class Period>
static constexpr ToDuration DurationTruncate(
const std::chrono::duration<Rep, Period>& duration) {
return duration >= std::chrono::duration<Rep, Period>::zero()
? std::chrono::floor<ToDuration>(duration)
: std::chrono::ceil<ToDuration>(duration);
}
// Helper function for performing truncate on Duration class's
// |total_milliseconds_|, returning it in a chrono::duration type
// which is specified by template parameter |ToDuration|.
template <class ToDuration>
constexpr Duration GetTruncateInternal() const {
return Duration(DurationTruncate<ToDuration>(total_milliseconds_));
}
public:
// == Special Initializers ==
static constexpr Duration Zero() { return Duration(); }
static constexpr Duration FromMilliseconds(int64_t milliseconds) {
return Duration(Milliseconds(milliseconds));
}
static constexpr Duration FromSeconds(int64_t seconds) {
return Duration(Seconds(seconds));
}
constexpr bool IsZero() const {
return total_milliseconds_ == Milliseconds::zero();
}
constexpr bool IsNegative() const {
return total_milliseconds_ < Milliseconds::zero();
}
constexpr bool IsPositive() const {
return total_milliseconds_ > Milliseconds::zero();
}
constexpr bool IsNonNegative() const {
return total_milliseconds_ >= Milliseconds::zero();
}
// Basic accessor and conversions (rounds down).
constexpr Milliseconds total_milliseconds() const {
return total_milliseconds_;
}
constexpr Seconds total_seconds() const {
return DurationTruncate<Seconds>(total_milliseconds_);
}
constexpr Minutes total_minutes() const {
return DurationTruncate<Minutes>(total_milliseconds_);
}
constexpr Hours total_hours() const {
return DurationTruncate<Hours>(total_milliseconds_);
}
constexpr Days total_days() const {
return DurationTruncate<Days>(total_milliseconds_);
}
// Access duration absolute components bounded within a
// particular unit.
// Milliseconds modulo milliseconds per-second (0-999)
constexpr uint32_t milliseconds() const {
if (IsNegative()) return GetAbsolute().milliseconds();
return static_cast<uint32_t>(
(total_milliseconds() - total_seconds()).count());
}
// Seconds modulo seconds per-minute (0-59)
constexpr uint32_t seconds() const {
if (IsNegative()) return GetAbsolute().seconds();
return static_cast<uint32_t>((total_seconds() - total_minutes()).count());
}
// Minutes modulo minutes per-hour (0-59).
constexpr uint32_t minutes() const {
if (IsNegative()) return GetAbsolute().minutes();
return static_cast<uint32_t>((total_minutes() - total_hours()).count());
}
// Hours modulo hours per-day (0-23)
constexpr uint32_t hours() const {
if (IsNegative()) return GetAbsolute().hours();
return static_cast<uint32_t>((total_hours() - total_days()).count());
}
// Days (total).
constexpr uint32_t days() const {
if (IsNegative()) return GetAbsolute().days();
return static_cast<uint32_t>(total_days().count());
}
// Obtain the absolute value.
constexpr Duration GetAbsolute() const {
return Duration(std::chrono::abs(total_milliseconds_));
}
// Obtain the truncated (math term for rounding towards zero)
// value by units.
constexpr Duration GetTruncateBySeconds() const {
return GetTruncateInternal<Seconds>();
}
constexpr Duration GetTruncateByMinutes() const {
return GetTruncateInternal<Minutes>();
}
constexpr Duration GetTruncateByHours() const {
return GetTruncateInternal<Hours>();
}
constexpr Duration GetTruncateByDays() const {
return GetTruncateInternal<Days>();
}
// Comparison operators (between another Duration class)
constexpr bool IsEqualTo(const Duration& other) const {
return total_milliseconds_ == other.total_milliseconds_;
}
constexpr int64_t CompareTo(const Duration& other) const {
return static_cast<int64_t>(total_milliseconds_.count() -
other.total_milliseconds_.count());
}
WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(Duration);
// Comparison operators (between other duration types)
template <class Rep, class Period>
constexpr bool operator==(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ == other;
}
template <class Rep, class Period>
constexpr bool operator!=(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ != other;
}
template <class Rep, class Period>
constexpr bool operator<=(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ <= other;
}
template <class Rep, class Period>
constexpr bool operator<(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ < other;
}
template <class Rep, class Period>
constexpr bool operator>=(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ >= other;
}
template <class Rep, class Period>
constexpr bool operator>(
const std::chrono::duration<Rep, Period>& other) const {
return total_milliseconds_ > other;
}
// Unary plus/minus operators
constexpr Duration operator+() const { return Duration(total_milliseconds_); }
constexpr Duration operator-() const {
return Duration(-total_milliseconds_);
}
// Add/subtract operators (between another Duration class)
constexpr Duration operator+(const Duration& other) const {
return Duration(total_milliseconds_ + other.total_milliseconds_);
}
constexpr Duration operator-(const Duration& other) const {
return Duration(total_milliseconds_ - other.total_milliseconds_);
}
// Add/subtract operators (between other duration types)
// Note 1: This is intentionally implemented as a member function to
// force the caller the put the Duration class first in the
// expression.
// Note 2: These operators are not defined for duration types that
// smaller than chrono::milliseconds. This could cause
// unexpected behavior as operations would be truncated.
// Ex: (Duration(5s) + 500us + 500us) = Duration(5s)
// Compiler will not allow such operations.
template <class Rep, class Period>
constexpr Duration operator+(
const std::chrono::duration<Rep, Period>& other) const {
return Duration(total_milliseconds_ + other);
}
template <class Rep, class Period>
constexpr Duration operator-(
const std::chrono::duration<Rep, Period>& other) const {
return Duration(total_milliseconds_ - other);
}
// Increment/decrement operators (between another Duration class)
constexpr Duration& operator+=(const Duration& other) {
total_milliseconds_ += other.total_milliseconds_;
return *this;
}
constexpr Duration& operator-=(const Duration& other) {
total_milliseconds_ -= other.total_milliseconds_;
return *this;
}
// Increment/decrement operators (between another duration types)
// Note: These operators are not defined for duration types that
// smaller than chrono::milliseconds (see '+' operator
// comment for details).
template <class Rep, class Period>
constexpr Duration& operator+=(
const std::chrono::duration<Rep, Period>& other) {
total_milliseconds_ += other;
return *this;
}
template <class Rep, class Period>
constexpr Duration& operator-=(
const std::chrono::duration<Rep, Period>& other) {
total_milliseconds_ -= other;
return *this;
}
// To string operator.
// Converts the Duration to its string representation.
// Examples:
// Duration::FromMilliseconds(4000123).ToString()
// ==> "1h6m40s123ms"
// Duration::FromSeconds(4000).ToString()
// ==> "1h6m40s"
bool PrintTo(std::ostream* out) const;
std::string ToString() const;
private:
// Internally, the Duration class stores durations in milliseconds.
Milliseconds total_milliseconds_ = Milliseconds::zero();
}; // class Duration
// == GTest Printer ==
void PrintTo(const Duration& duration, std::ostream* out);
} // namespace wvutil
#endif // WVCDM_UTIL_WV_DURATION_H_

170
util/include/wv_timestamp.h Normal file
View File

@@ -0,0 +1,170 @@
// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_UTIL_WV_TIMESTAMP_H_
#define WVCDM_UTIL_WV_TIMESTAMP_H_
#include <inttypes.h>
#include <chrono>
#include "wv_class_utils.h"
#include "wv_duration.h"
namespace wvutil {
// The Timestamp class is a light-weight representation of a
// time point.
//
// Internally, the time is measured in Unix Time (milliseconds
// since January 1st, 1970 UTC (epoch)).
//
// For this library, we are not concerned about time points before
// January 1st, 1970; this class does not support negative epoch
// seconds. In addition, zero is treated as a special value
// which indicates an uninitialized time point.
//
// Min Timestamp: 1970-01-01 00:00:00.001 (epoch ms = 1)
// Max Timestamp: 9999-12-31 23:59:59.999 (epoch ms = 253402300799999)
class Timestamp {
public:
constexpr Timestamp() = default; // Defaults to "unset".
WVCDM_CONSTEXPR_DEFAULT_COPY_AND_MOVE(Timestamp);
// Create a Timestamp instance from Unix Time in epoch seconds.
// Optionally allowed to include |millisecond| (0 to 999).
// If |epoch_seconds| or |millisecond| are invalid values, then an
// uninitlaized Timestamp instance is returned.
static constexpr Timestamp FromUnixSeconds(uint64_t epoch_seconds,
uint32_t milliseconds = 0) {
if (milliseconds > 999u) return Timestamp();
return Timestamp(Seconds(epoch_seconds) + Milliseconds(milliseconds));
}
static constexpr Timestamp FromUnixSeconds(const Seconds& epoch_seconds,
uint32_t milliseconds = 0) {
if (milliseconds > 999u) return Timestamp();
return Timestamp(epoch_seconds + Milliseconds(milliseconds));
}
// Create a Timestamp instance from Unix Time in epoch milliseconds.
// If |epoch_milliseconds| is an invalid value (zero), then an
// uninitlaized Timestamp instance is returned.
static constexpr Timestamp FromUnixMilliseconds(uint64_t epoch_milliseconds) {
return Timestamp(Milliseconds(epoch_milliseconds));
}
static constexpr Timestamp FromUnixMilliseconds(
const Milliseconds& epoch_milliseconds) {
return Timestamp(epoch_milliseconds);
}
// Obtain the minimum and maximum representable Timestamp.
static constexpr Timestamp Min() { return Timestamp(kMinMsDuration); }
static constexpr Timestamp Max() { return Timestamp(kMaxMsDuration); }
// == General Accessors ==
// Get the epoch seconds duration (rounding down milliseconds).
constexpr Seconds epoch_seconds() const {
return std::chrono::floor<Seconds>(epoch_milliseconds_);
}
// Get the milliseconds fraction of the epoch time (0-999).
constexpr uint32_t milliseconds() const {
return static_cast<uint32_t>(
(epoch_milliseconds_ - epoch_seconds()).count());
}
constexpr Milliseconds epoch_milliseconds() const {
return epoch_milliseconds_;
}
// As noted, the timestamp is considered "unset" if zero.
constexpr bool IsSet() const {
return epoch_milliseconds_ > Milliseconds::zero();
}
explicit constexpr operator bool() const { return IsSet(); }
constexpr void Clear() { epoch_milliseconds_ = kUnsetMsDuration; }
// == Comparisons ==
constexpr bool IsEqualTo(const Timestamp& other) const {
return epoch_milliseconds_ == other.epoch_milliseconds_;
}
constexpr int64_t CompareTo(const Timestamp& other) const {
return static_cast<int64_t>(epoch_milliseconds_.count() -
other.epoch_milliseconds_.count());
}
WVCDM_DEFINE_CONSTEXPR_EQ_AND_CMP_OPERATORS(Timestamp);
// == Arithmetic Operations ==
// Duration-based addition/subtraction operations.
// Returns a new Timestamp instance with time point adjusted by
// the provided Duration.
// If current Timestamp instance is unset, or if result value
// is outside the range of valid Timestamp values, then an unset
// Timestamp is returned.
constexpr Timestamp operator+(const Duration& duration) const {
if (!IsSet()) return Timestamp();
return Timestamp(epoch_milliseconds_ + duration.total_milliseconds());
}
constexpr Timestamp operator-(const Duration& duration) const {
if (!IsSet()) return Timestamp();
return Timestamp(epoch_milliseconds_ - duration.total_milliseconds());
}
// Duration-based increment/decrement operators.
// Increment or decrement the Timestamp by the provided Duration
// amount.
// If current Timestamp instance is unset, then no action will
// occur. If result value is outside the range of valid Timestamp
// values, then Timestamp will be unset.
constexpr Timestamp& operator+=(const Duration& duration) {
if (!IsSet()) return *this;
epoch_milliseconds_ =
Normalize(epoch_milliseconds_ + duration.total_milliseconds());
return *this;
}
constexpr Timestamp& operator-=(const Duration& duration) {
if (!IsSet()) return *this;
epoch_milliseconds_ =
Normalize(epoch_milliseconds_ - duration.total_milliseconds());
return *this;
}
// Timestamp difference.
// Returns the duration between the two provided Timestamp
// instances.
// If either Timestamp instance is unset, then the resulting
// Duration is zero.
constexpr Duration operator-(const Timestamp& other) const {
if (!IsSet() || !other.IsSet()) return Duration();
return Duration(epoch_milliseconds_ - other.epoch_milliseconds_);
}
private:
static constexpr const uint64_t kMinMs = 1;
static constexpr const uint64_t kMaxMs = 253402300799999;
static constexpr const Milliseconds kMinMsDuration = Milliseconds(kMinMs);
static constexpr const Milliseconds kMaxMsDuration = Milliseconds(kMaxMs);
static constexpr const Milliseconds kUnsetMsDuration = Milliseconds::zero();
static constexpr bool IsInRange(const Milliseconds& epoch_milliseconds) {
return epoch_milliseconds >= kMinMsDuration &&
epoch_milliseconds <= kMaxMsDuration;
}
// Ensures the provided |epoch_milliseconds| is in range of the
// Timestamp class; otherwise returns an unset duration value.
static constexpr Milliseconds Normalize(
const Milliseconds& epoch_milliseconds) {
return IsInRange(epoch_milliseconds) ? epoch_milliseconds
: kUnsetMsDuration;
}
constexpr Timestamp(const Milliseconds& epoch_milliseconds)
: epoch_milliseconds_(Normalize(epoch_milliseconds)) {}
Milliseconds epoch_milliseconds_ = kUnsetMsDuration;
}; // class Timestamp
} // namespace wvutil
#endif // WVCDM_UTIL_WV_TIMESTAMP_H_