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