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/osp_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_
|