| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COBALT_MEDIA_CAST_COMMON_EXPANDED_VALUE_BASE_H_ |
| #define COBALT_MEDIA_CAST_COMMON_EXPANDED_VALUE_BASE_H_ |
| |
| #include <limits> |
| |
| #include "base/logging.h" |
| #include "starboard/types.h" |
| |
| namespace cobalt { |
| namespace media { |
| namespace cast { |
| |
| // Abstract base template class for common "sequence value" data types such as |
| // RtpTimeTicks, FrameId, or PacketId which generally increment/decrement in |
| // predictable amounts as media is streamed, and which often need to be reliably |
| // truncated and re-expanded for over-the-wire transmission. |
| // |
| // FullWidthInteger should be a signed integer POD type that is of sufficiently |
| // high width (in bits) such that it is never expected to under/overflow during |
| // the longest reasonable length of continuous system operation. Subclass is |
| // the class inheriting the common functionality provided in this template, and |
| // is used to provide operator overloads. The Subclass must friend this class |
| // to enable these operator overloads. |
| // |
| // Please see RtpTimeTicks and unit test code for examples of how to define |
| // Subclasses and add features specific to their concrete data type, and how to |
| // use data types derived from ExpandedValueBase. For example, a RtpTimeTicks |
| // adds math operators consisting of the meaningful and valid set of operations |
| // allowed for doing "time math." On the other hand, FrameId only adds math |
| // operators for incrementing/decrementing since multiplication and division are |
| // meaningless. |
| template <typename FullWidthInteger, class Subclass> |
| class ExpandedValueBase { |
| static_assert(std::numeric_limits<FullWidthInteger>::is_signed, |
| "FullWidthInteger must be a signed integer."); |
| static_assert(std::numeric_limits<FullWidthInteger>::is_integer, |
| "FullWidthInteger must be a signed integer."); |
| |
| public: |
| // Methods that return the lower bits of this value. This should only be used |
| // for serializing/wire-formatting, and not to subvert the restricted set of |
| // operators allowed on this data type. |
| uint8_t lower_8_bits() const { return static_cast<uint8_t>(value_); } |
| uint16_t lower_16_bits() const { return static_cast<uint16_t>(value_); } |
| uint32_t lower_32_bits() const { return static_cast<uint32_t>(value_); } |
| |
| // Compute the greatest value less than or equal to |this| value whose lower |
| // bits are those of |x|. The purpose of this method is to re-instantiate an |
| // original value from its truncated form, usually when deserializing |
| // off-the-wire, when |this| value is known to be the greatest possible valid |
| // value. |
| // |
| // Use case example: Start with an original 32-bit value of 0x000001fe (510 |
| // decimal) and truncate, throwing away its upper 24 bits: 0xfe. Now, send |
| // this truncated value over-the-wire to a peer who needs to expand it back to |
| // the original 32-bit value. The peer knows that the greatest possible valid |
| // value is 0x00000202 (514 decimal). This method will initially attempt to |
| // just concatenate the upper 24 bits of |this->value_| with |x| (the 8-bit |
| // value), and get a result of 0x000002fe (766 decimal). However, this is |
| // greater than |this->value_|, so the upper 24 bits are subtracted by one to |
| // get 0x000001fe, which is the original value. |
| template <typename ShortUnsigned> |
| Subclass ExpandLessThanOrEqual(ShortUnsigned x) const { |
| static_assert(!std::numeric_limits<ShortUnsigned>::is_signed, |
| "|x| must be an unsigned integer."); |
| static_assert(std::numeric_limits<ShortUnsigned>::is_integer, |
| "|x| must be an unsigned integer."); |
| static_assert(sizeof(ShortUnsigned) <= sizeof(FullWidthInteger), |
| "|x| must fit within the FullWidthInteger."); |
| |
| if (sizeof(ShortUnsigned) < sizeof(FullWidthInteger)) { |
| // Initially, the |result| is composed of upper bits from |value_| and |
| // lower bits from |x|. |
| const FullWidthInteger short_max = |
| std::numeric_limits<ShortUnsigned>::max(); |
| FullWidthInteger result = (value_ & ~short_max) | x; |
| |
| // If the |result| is larger than |value_|, decrement the upper bits by |
| // one. In other words, |x| must always be interpreted as a truncated |
| // version of a value less than or equal to |value_|. |
| if (result > value_) result -= short_max + 1; |
| |
| return Subclass(result); |
| } else { |
| // Debug builds: Ensure the highest bit is not set (which would cause |
| // overflow when casting to the signed integer). |
| DCHECK_EQ(static_cast<ShortUnsigned>(0), |
| x & (static_cast<ShortUnsigned>(1) << ((sizeof(x) * 8) - 1))); |
| return Subclass(x); |
| } |
| } |
| |
| // Compute the value closest to |this| value whose lower bits are those of |
| // |x|. The result is always within |max_distance_for_expansion()| of |this| |
| // value. The purpose of this method is to re-instantiate an original value |
| // from its truncated form, usually when deserializing off-the-wire. See |
| // comments for ExpandLessThanOrEqual() above for further explanation. |
| template <typename ShortUnsigned> |
| Subclass Expand(ShortUnsigned x) const { |
| const Subclass maximum_possible_result( |
| value_ + max_distance_for_expansion<ShortUnsigned>()); |
| return maximum_possible_result.ExpandLessThanOrEqual(x); |
| } |
| |
| // Comparison operators. |
| bool operator==(Subclass rhs) const { return value_ == rhs.value_; } |
| bool operator!=(Subclass rhs) const { return value_ != rhs.value_; } |
| bool operator<(Subclass rhs) const { return value_ < rhs.value_; } |
| bool operator>(Subclass rhs) const { return value_ > rhs.value_; } |
| bool operator<=(Subclass rhs) const { return value_ <= rhs.value_; } |
| bool operator>=(Subclass rhs) const { return value_ >= rhs.value_; } |
| |
| // (De)Serialize for transmission over IPC. Do not use these to subvert the |
| // valid set of operators allowed by this class or its Subclass. |
| uint64_t SerializeForIPC() const { |
| static_assert(sizeof(uint64_t) >= sizeof(FullWidthInteger), |
| "Cannot serialize FullWidthInteger into an uint64_t."); |
| return static_cast<uint64_t>(value_); |
| } |
| static Subclass DeserializeForIPC(uint64_t serialized) { |
| return Subclass(static_cast<FullWidthInteger>(serialized)); |
| } |
| |
| // Design limit: Values that are truncated to the ShortUnsigned type must be |
| // no more than this maximum distance from each other in order to ensure the |
| // original value can be determined correctly. |
| template <typename ShortUnsigned> |
| static constexpr FullWidthInteger max_distance_for_expansion() { |
| return std::numeric_limits<ShortUnsigned>::max() / 2; |
| } |
| |
| protected: |
| // Only subclasses are permitted to instantiate directly. |
| explicit ExpandedValueBase(FullWidthInteger value) : value_(value) {} |
| |
| FullWidthInteger value_; |
| }; |
| |
| } // namespace cast |
| } // namespace media |
| } // namespace cobalt |
| |
| #endif // COBALT_MEDIA_CAST_COMMON_EXPANDED_VALUE_BASE_H_ |