1*3f982cf4SFabien Sanglard // Copyright 2015 The Chromium Authors. All rights reserved. 2*3f982cf4SFabien Sanglard // Use of this source code is governed by a BSD-style license that can be 3*3f982cf4SFabien Sanglard // found in the LICENSE file. 4*3f982cf4SFabien Sanglard 5*3f982cf4SFabien Sanglard #ifndef CAST_STREAMING_EXPANDED_VALUE_BASE_H_ 6*3f982cf4SFabien Sanglard #define CAST_STREAMING_EXPANDED_VALUE_BASE_H_ 7*3f982cf4SFabien Sanglard 8*3f982cf4SFabien Sanglard #include <stdint.h> 9*3f982cf4SFabien Sanglard 10*3f982cf4SFabien Sanglard #include <limits> 11*3f982cf4SFabien Sanglard 12*3f982cf4SFabien Sanglard #include "util/osp_logging.h" 13*3f982cf4SFabien Sanglard 14*3f982cf4SFabien Sanglard namespace openscreen { 15*3f982cf4SFabien Sanglard namespace cast { 16*3f982cf4SFabien Sanglard 17*3f982cf4SFabien Sanglard // Abstract base template class for common "sequence value" data types such as 18*3f982cf4SFabien Sanglard // RtpTimeTicks, FrameId, or PacketId which generally increment/decrement in 19*3f982cf4SFabien Sanglard // predictable amounts as media is streamed, and which often need to be reliably 20*3f982cf4SFabien Sanglard // truncated and re-expanded for over-the-wire transmission. 21*3f982cf4SFabien Sanglard // 22*3f982cf4SFabien Sanglard // FullWidthInteger should be a signed integer POD type that is of sufficiently 23*3f982cf4SFabien Sanglard // high width (in bits) such that it is never expected to under/overflow during 24*3f982cf4SFabien Sanglard // the longest reasonable length of continuous system operation. Subclass is 25*3f982cf4SFabien Sanglard // the class inheriting the common functionality provided in this template, and 26*3f982cf4SFabien Sanglard // is used to provide operator overloads. The Subclass must friend this class 27*3f982cf4SFabien Sanglard // to enable these operator overloads. 28*3f982cf4SFabien Sanglard // 29*3f982cf4SFabien Sanglard // Please see RtpTimeTicks and unit test code for examples of how to define 30*3f982cf4SFabien Sanglard // Subclasses and add features specific to their concrete data type, and how to 31*3f982cf4SFabien Sanglard // use data types derived from ExpandedValueBase. For example, a RtpTimeTicks 32*3f982cf4SFabien Sanglard // adds math operators consisting of the meaningful and valid set of operations 33*3f982cf4SFabien Sanglard // allowed for doing "time math." On the other hand, FrameId only adds math 34*3f982cf4SFabien Sanglard // operators for incrementing/decrementing since multiplication and division are 35*3f982cf4SFabien Sanglard // meaningless. 36*3f982cf4SFabien Sanglard template <typename FullWidthInteger, class Subclass> 37*3f982cf4SFabien Sanglard class ExpandedValueBase { 38*3f982cf4SFabien Sanglard static_assert(std::numeric_limits<FullWidthInteger>::is_signed, 39*3f982cf4SFabien Sanglard "FullWidthInteger must be a signed integer."); 40*3f982cf4SFabien Sanglard static_assert(std::numeric_limits<FullWidthInteger>::is_integer, 41*3f982cf4SFabien Sanglard "FullWidthInteger must be a signed integer."); 42*3f982cf4SFabien Sanglard 43*3f982cf4SFabien Sanglard public: 44*3f982cf4SFabien Sanglard // Methods that return the lower bits of this value. This should only be used 45*3f982cf4SFabien Sanglard // for serializing/wire-formatting, and not to subvert the restricted set of 46*3f982cf4SFabien Sanglard // operators allowed on this data type. lower_8_bits()47*3f982cf4SFabien Sanglard constexpr uint8_t lower_8_bits() const { 48*3f982cf4SFabien Sanglard return static_cast<uint8_t>(value_); 49*3f982cf4SFabien Sanglard } lower_16_bits()50*3f982cf4SFabien Sanglard constexpr uint16_t lower_16_bits() const { 51*3f982cf4SFabien Sanglard return static_cast<uint16_t>(value_); 52*3f982cf4SFabien Sanglard } lower_32_bits()53*3f982cf4SFabien Sanglard constexpr uint32_t lower_32_bits() const { 54*3f982cf4SFabien Sanglard return static_cast<uint32_t>(value_); 55*3f982cf4SFabien Sanglard } 56*3f982cf4SFabien Sanglard 57*3f982cf4SFabien Sanglard // Compute the greatest value less than or equal to |this| value whose lower 58*3f982cf4SFabien Sanglard // bits are those of |x|. The purpose of this method is to re-instantiate an 59*3f982cf4SFabien Sanglard // original value from its truncated form, usually when deserializing 60*3f982cf4SFabien Sanglard // off-the-wire, when |this| value is known to be the greatest possible valid 61*3f982cf4SFabien Sanglard // value. 62*3f982cf4SFabien Sanglard // 63*3f982cf4SFabien Sanglard // Use case example: Start with an original 32-bit value of 0x000001fe (510 64*3f982cf4SFabien Sanglard // decimal) and truncate, throwing away its upper 24 bits: 0xfe. Now, send 65*3f982cf4SFabien Sanglard // this truncated value over-the-wire to a peer who needs to expand it back to 66*3f982cf4SFabien Sanglard // the original 32-bit value. The peer knows that the greatest possible valid 67*3f982cf4SFabien Sanglard // value is 0x00000202 (514 decimal). This method will initially attempt to 68*3f982cf4SFabien Sanglard // just concatenate the upper 24 bits of |this->value_| with |x| (the 8-bit 69*3f982cf4SFabien Sanglard // value), and get a result of 0x000002fe (766 decimal). However, this is 70*3f982cf4SFabien Sanglard // greater than |this->value_|, so the upper 24 bits are subtracted by one to 71*3f982cf4SFabien Sanglard // get 0x000001fe, which is the original value. 72*3f982cf4SFabien Sanglard template <typename ShortUnsigned> ExpandLessThanOrEqual(ShortUnsigned x)73*3f982cf4SFabien Sanglard Subclass ExpandLessThanOrEqual(ShortUnsigned x) const { 74*3f982cf4SFabien Sanglard static_assert(!std::numeric_limits<ShortUnsigned>::is_signed, 75*3f982cf4SFabien Sanglard "|x| must be an unsigned integer."); 76*3f982cf4SFabien Sanglard static_assert(std::numeric_limits<ShortUnsigned>::is_integer, 77*3f982cf4SFabien Sanglard "|x| must be an unsigned integer."); 78*3f982cf4SFabien Sanglard static_assert(sizeof(ShortUnsigned) <= sizeof(FullWidthInteger), 79*3f982cf4SFabien Sanglard "|x| must fit within the FullWidthInteger."); 80*3f982cf4SFabien Sanglard 81*3f982cf4SFabien Sanglard if (sizeof(ShortUnsigned) < sizeof(FullWidthInteger)) { 82*3f982cf4SFabien Sanglard // Initially, the |result| is composed of upper bits from |value_| and 83*3f982cf4SFabien Sanglard // lower bits from |x|. 84*3f982cf4SFabien Sanglard const FullWidthInteger short_max = 85*3f982cf4SFabien Sanglard std::numeric_limits<ShortUnsigned>::max(); 86*3f982cf4SFabien Sanglard FullWidthInteger result = (value_ & ~short_max) | x; 87*3f982cf4SFabien Sanglard 88*3f982cf4SFabien Sanglard // If the |result| is larger than |value_|, decrement the upper bits by 89*3f982cf4SFabien Sanglard // one. In other words, |x| must always be interpreted as a truncated 90*3f982cf4SFabien Sanglard // version of a value less than or equal to |value_|. 91*3f982cf4SFabien Sanglard if (result > value_) 92*3f982cf4SFabien Sanglard result -= short_max + 1; 93*3f982cf4SFabien Sanglard 94*3f982cf4SFabien Sanglard return Subclass(result); 95*3f982cf4SFabien Sanglard } else { 96*3f982cf4SFabien Sanglard // Debug builds: Ensure the highest bit is not set (which would cause 97*3f982cf4SFabien Sanglard // overflow when casting to the signed integer). 98*3f982cf4SFabien Sanglard OSP_DCHECK_EQ( 99*3f982cf4SFabien Sanglard static_cast<ShortUnsigned>(0), 100*3f982cf4SFabien Sanglard x & (static_cast<ShortUnsigned>(1) << ((sizeof(x) * 8) - 1))); 101*3f982cf4SFabien Sanglard return Subclass(x); 102*3f982cf4SFabien Sanglard } 103*3f982cf4SFabien Sanglard } 104*3f982cf4SFabien Sanglard 105*3f982cf4SFabien Sanglard // Compute the smallest value greater than |this| value whose lower bits are 106*3f982cf4SFabien Sanglard // those of |x|. 107*3f982cf4SFabien Sanglard template <typename ShortUnsigned> ExpandGreaterThan(ShortUnsigned x)108*3f982cf4SFabien Sanglard Subclass ExpandGreaterThan(ShortUnsigned x) const { 109*3f982cf4SFabien Sanglard const Subclass maximum_possible_result( 110*3f982cf4SFabien Sanglard value_ + std::numeric_limits<ShortUnsigned>::max() + 1); 111*3f982cf4SFabien Sanglard return maximum_possible_result.ExpandLessThanOrEqual(x); 112*3f982cf4SFabien Sanglard } 113*3f982cf4SFabien Sanglard 114*3f982cf4SFabien Sanglard // Compute the value closest to |this| value whose lower bits are those of 115*3f982cf4SFabien Sanglard // |x|. The result is always within |max_distance_for_expansion()| of |this| 116*3f982cf4SFabien Sanglard // value. The purpose of this method is to re-instantiate an original value 117*3f982cf4SFabien Sanglard // from its truncated form, usually when deserializing off-the-wire. See 118*3f982cf4SFabien Sanglard // comments for ExpandLessThanOrEqual() above for further explanation. 119*3f982cf4SFabien Sanglard template <typename ShortUnsigned> Expand(ShortUnsigned x)120*3f982cf4SFabien Sanglard Subclass Expand(ShortUnsigned x) const { 121*3f982cf4SFabien Sanglard const Subclass maximum_possible_result( 122*3f982cf4SFabien Sanglard value_ + max_distance_for_expansion<ShortUnsigned>()); 123*3f982cf4SFabien Sanglard return maximum_possible_result.ExpandLessThanOrEqual(x); 124*3f982cf4SFabien Sanglard } 125*3f982cf4SFabien Sanglard 126*3f982cf4SFabien Sanglard // Comparison operators. 127*3f982cf4SFabien Sanglard constexpr bool operator==(Subclass rhs) const { return value_ == rhs.value_; } 128*3f982cf4SFabien Sanglard constexpr bool operator!=(Subclass rhs) const { return value_ != rhs.value_; } 129*3f982cf4SFabien Sanglard constexpr bool operator<(Subclass rhs) const { return value_ < rhs.value_; } 130*3f982cf4SFabien Sanglard constexpr bool operator>(Subclass rhs) const { return value_ > rhs.value_; } 131*3f982cf4SFabien Sanglard constexpr bool operator<=(Subclass rhs) const { return value_ <= rhs.value_; } 132*3f982cf4SFabien Sanglard constexpr bool operator>=(Subclass rhs) const { return value_ >= rhs.value_; } 133*3f982cf4SFabien Sanglard 134*3f982cf4SFabien Sanglard // (De)Serialize for transmission over IPC. Do not use these to subvert the 135*3f982cf4SFabien Sanglard // valid set of operators allowed by this class or its Subclass. SerializeForIPC()136*3f982cf4SFabien Sanglard uint64_t SerializeForIPC() const { 137*3f982cf4SFabien Sanglard static_assert(sizeof(uint64_t) >= sizeof(FullWidthInteger), 138*3f982cf4SFabien Sanglard "Cannot serialize FullWidthInteger into an uint64_t."); 139*3f982cf4SFabien Sanglard return static_cast<uint64_t>(value_); 140*3f982cf4SFabien Sanglard } DeserializeForIPC(uint64_t serialized)141*3f982cf4SFabien Sanglard static Subclass DeserializeForIPC(uint64_t serialized) { 142*3f982cf4SFabien Sanglard return Subclass(static_cast<FullWidthInteger>(serialized)); 143*3f982cf4SFabien Sanglard } 144*3f982cf4SFabien Sanglard 145*3f982cf4SFabien Sanglard // Design limit: Values that are truncated to the ShortUnsigned type must be 146*3f982cf4SFabien Sanglard // no more than this maximum distance from each other in order to ensure the 147*3f982cf4SFabien Sanglard // original value can be determined correctly. 148*3f982cf4SFabien Sanglard template <typename ShortUnsigned> max_distance_for_expansion()149*3f982cf4SFabien Sanglard static constexpr FullWidthInteger max_distance_for_expansion() { 150*3f982cf4SFabien Sanglard return std::numeric_limits<ShortUnsigned>::max() / 2; 151*3f982cf4SFabien Sanglard } 152*3f982cf4SFabien Sanglard 153*3f982cf4SFabien Sanglard protected: 154*3f982cf4SFabien Sanglard // Only subclasses are permitted to instantiate directly. ExpandedValueBase(FullWidthInteger value)155*3f982cf4SFabien Sanglard constexpr explicit ExpandedValueBase(FullWidthInteger value) 156*3f982cf4SFabien Sanglard : value_(value) {} 157*3f982cf4SFabien Sanglard 158*3f982cf4SFabien Sanglard FullWidthInteger value_; 159*3f982cf4SFabien Sanglard }; 160*3f982cf4SFabien Sanglard 161*3f982cf4SFabien Sanglard } // namespace cast 162*3f982cf4SFabien Sanglard } // namespace openscreen 163*3f982cf4SFabien Sanglard 164*3f982cf4SFabien Sanglard #endif // CAST_STREAMING_EXPANDED_VALUE_BASE_H_ 165