aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/expanded_value_base.h
blob: d418df647dfe83f3e8aee5eb9339922109faf092 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// 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 CAST_STREAMING_EXPANDED_VALUE_BASE_H_
#define CAST_STREAMING_EXPANDED_VALUE_BASE_H_

#include <stdint.h>

#include <limits>

#include "util/logging.h"

namespace openscreen {
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.
  constexpr uint8_t lower_8_bits() const {
    return static_cast<uint8_t>(value_);
  }
  constexpr uint16_t lower_16_bits() const {
    return static_cast<uint16_t>(value_);
  }
  constexpr 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).
      OSP_DCHECK_EQ(
          static_cast<ShortUnsigned>(0),
          x & (static_cast<ShortUnsigned>(1) << ((sizeof(x) * 8) - 1)));
      return Subclass(x);
    }
  }

  // Compute the smallest value greater than |this| value whose lower bits are
  // those of |x|.
  template <typename ShortUnsigned>
  Subclass ExpandGreaterThan(ShortUnsigned x) const {
    const Subclass maximum_possible_result(
        value_ + std::numeric_limits<ShortUnsigned>::max() + 1);
    return maximum_possible_result.ExpandLessThanOrEqual(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.
  constexpr bool operator==(Subclass rhs) const { return value_ == rhs.value_; }
  constexpr bool operator!=(Subclass rhs) const { return value_ != rhs.value_; }
  constexpr bool operator<(Subclass rhs) const { return value_ < rhs.value_; }
  constexpr bool operator>(Subclass rhs) const { return value_ > rhs.value_; }
  constexpr bool operator<=(Subclass rhs) const { return value_ <= rhs.value_; }
  constexpr 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.
  constexpr explicit ExpandedValueBase(FullWidthInteger value)
      : value_(value) {}

  FullWidthInteger value_;
};

}  // namespace cast
}  // namespace openscreen

#endif  // CAST_STREAMING_EXPANDED_VALUE_BASE_H_