aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/expanded_value_base.h
diff options
context:
space:
mode:
Diffstat (limited to 'cast/streaming/expanded_value_base.h')
-rw-r--r--cast/streaming/expanded_value_base.h164
1 files changed, 164 insertions, 0 deletions
diff --git a/cast/streaming/expanded_value_base.h b/cast/streaming/expanded_value_base.h
new file mode 100644
index 00000000..0ac0dbeb
--- /dev/null
+++ b/cast/streaming/expanded_value_base.h
@@ -0,0 +1,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 cast {
+namespace streaming {
+
+// 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 streaming
+} // namespace cast
+
+#endif // CAST_STREAMING_EXPANDED_VALUE_BASE_H_