You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
7.3 KiB
165 lines
7.3 KiB
// 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_
|