xref: /aosp_15_r20/external/openscreen/cast/streaming/expanded_value_base.h (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
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