diff --git a/net/dcsctp/BUILD.gn b/net/dcsctp/BUILD.gn index ee15a45361..8510f42d32 100644 --- a/net/dcsctp/BUILD.gn +++ b/net/dcsctp/BUILD.gn @@ -15,6 +15,7 @@ if (rtc_include_tests) { "../../test:test_main", "common:dcsctp_common_unittests", "packet:dcsctp_packet_unittests", + "public:dcsctp_public_unittests", ] } } diff --git a/net/dcsctp/common/BUILD.gn b/net/dcsctp/common/BUILD.gn index 55fa86b984..374eb7f0e8 100644 --- a/net/dcsctp/common/BUILD.gn +++ b/net/dcsctp/common/BUILD.gn @@ -6,9 +6,12 @@ # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. -import("//build/config/linux/pkg_config.gni") import("../../../webrtc.gni") +rtc_source_set("internal_types") { + sources = [ "internal_types.h" ] +} + rtc_source_set("math") { deps = [] sources = [ "math.h" ] diff --git a/net/dcsctp/common/internal_types.h b/net/dcsctp/common/internal_types.h new file mode 100644 index 0000000000..fcb88f8bba --- /dev/null +++ b/net/dcsctp/common/internal_types.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef NET_DCSCTP_COMMON_INTERNAL_TYPES_H_ +#define NET_DCSCTP_COMMON_INTERNAL_TYPES_H_ + +#include "net/dcsctp/public/strong_alias.h" + +namespace dcsctp { + +// Stream Sequence Number (SSN) +using SSN = StrongAlias; + +// Message Identifier (MID) +using MID = StrongAlias; + +// Fragment Sequence Number (FSN) +using FSN = StrongAlias; + +// Transmission Sequence Number (TSN) +using TSN = StrongAlias; + +// Reconfiguration Request Sequence Number +using ReconfigRequestSN = StrongAlias; + +// Reconfiguration Response Sequence Number +using ReconfigResponseSN = StrongAlias; + +// Verification Tag, used for packet validation. +using VerificationTag = StrongAlias; + +} // namespace dcsctp +#endif // NET_DCSCTP_COMMON_INTERNAL_TYPES_H_ diff --git a/net/dcsctp/common/sequence_numbers.h b/net/dcsctp/common/sequence_numbers.h index f60433757c..69e33b331f 100644 --- a/net/dcsctp/common/sequence_numbers.h +++ b/net/dcsctp/common/sequence_numbers.h @@ -136,14 +136,14 @@ class UnwrappedSequenceNumber { int64_t value_; }; -// Transmission Sequence Numbers (TSN) -using TSN = UnwrappedSequenceNumber; +// Unwrapped Transmission Sequence Numbers (TSN) +using UnwrappedTSN = UnwrappedSequenceNumber; -// Stream Sequence Numbers (SSN) -using SSN = UnwrappedSequenceNumber; +// Unwrapped Stream Sequence Numbers (SSN) +using UnwrappedSSN = UnwrappedSequenceNumber; -// Message Identifier (MID) -using MID = UnwrappedSequenceNumber; +// Unwrapped Message Identifier (MID) +using UnwrappedMID = UnwrappedSequenceNumber; } // namespace dcsctp diff --git a/net/dcsctp/packet/data.h b/net/dcsctp/packet/data.h index 50f4552182..23a5aa4616 100644 --- a/net/dcsctp/packet/data.h +++ b/net/dcsctp/packet/data.h @@ -14,6 +14,9 @@ #include #include +#include "net/dcsctp/common/internal_types.h" +#include "net/dcsctp/public/types.h" + namespace dcsctp { // Represents data that is either received and extracted from a DATA/I-DATA @@ -29,15 +32,23 @@ namespace dcsctp { // are wrapped numbers, and within the library, unwrapped sequence numbers are // preferably used. struct Data { - Data(uint16_t stream_id, - uint16_t ssn, - uint32_t message_id, - uint32_t fsn, - uint32_t ppid, + // Indicates if a chunk is the first in a fragmented message and maps to the + // "beginning" flag in DATA/I-DATA chunk. + using IsBeginning = StrongAlias; + + // Indicates if a chunk is the last in a fragmented message and maps to the + // "end" flag in DATA/I-DATA chunk. + using IsEnd = StrongAlias; + + Data(StreamID stream_id, + SSN ssn, + MID message_id, + FSN fsn, + PPID ppid, std::vector payload, - bool is_beginning, - bool is_end, - bool is_unordered) + IsBeginning is_beginning, + IsEnd is_end, + IsUnordered is_unordered) : stream_id(stream_id), ssn(ssn), message_id(message_id), @@ -62,30 +73,30 @@ struct Data { size_t size() const { return payload.size(); } // Stream Identifier. - uint16_t stream_id; + StreamID stream_id; // Stream Sequence Number (SSN), per stream, for ordered chunks. Defined by // RFC4960 and used only in DATA chunks (not I-DATA). - uint16_t ssn; + SSN ssn; // Message Identifier (MID) per stream and ordered/unordered. Defined by // RFC8260, and used together with options.is_unordered and stream_id to // uniquely identify a message. Used only in I-DATA chunks (not DATA). - uint32_t message_id; + MID message_id; // Fragment Sequence Number (FSN) per stream and ordered/unordered, as above. - uint32_t fsn; + FSN fsn; // Payload Protocol Identifier (PPID). - uint32_t ppid; + PPID ppid; // The actual data payload. std::vector payload; // If this data represents the first, last or a middle chunk. - bool is_beginning; - bool is_end; + IsBeginning is_beginning; + IsEnd is_end; // If this data is sent/received unordered. - bool is_unordered; + IsUnordered is_unordered; }; } // namespace dcsctp diff --git a/net/dcsctp/public/BUILD.gn b/net/dcsctp/public/BUILD.gn new file mode 100644 index 0000000000..0bfab846e7 --- /dev/null +++ b/net/dcsctp/public/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("../../../webrtc.gni") + +rtc_source_set("strong_alias") { + sources = [ "strong_alias.h" ] +} + +rtc_source_set("types") { + deps = [ ":strong_alias" ] + sources = [ "types.h" ] +} + +if (rtc_include_tests) { + rtc_library("dcsctp_public_unittests") { + testonly = true + + deps = [ + ":strong_alias", + "../../../rtc_base:checks", + "../../../rtc_base:gunit_helpers", + "../../../rtc_base:rtc_base_approved", + "../../../test:test_support", + ] + sources = [ "strong_alias_test.cc" ] + } +} diff --git a/net/dcsctp/public/strong_alias.h b/net/dcsctp/public/strong_alias.h new file mode 100644 index 0000000000..efd447ab2f --- /dev/null +++ b/net/dcsctp/public/strong_alias.h @@ -0,0 +1,84 @@ +/* + * Copyright 2019 The Chromium Authors. All rights reserved. + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef NET_DCSCTP_PUBLIC_STRONG_ALIAS_H_ +#define NET_DCSCTP_PUBLIC_STRONG_ALIAS_H_ + +#include +#include + +namespace dcsctp { + +// This is a copy of +// https://source.chromium.org/chromium/chromium/src/+/master:base/types/strong_alias.h +// as the API (and internals) are using type-safe integral identifiers, but this +// library can't depend on that file. The ostream operator has been removed +// per WebRTC library conventions. + +template +class StrongAlias { + public: + constexpr StrongAlias() = default; + constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {} + constexpr explicit StrongAlias(UnderlyingType&& v) noexcept + : value_(std::move(v)) {} + + constexpr UnderlyingType* operator->() { return &value_; } + constexpr const UnderlyingType* operator->() const { return &value_; } + + constexpr UnderlyingType& operator*() & { return value_; } + constexpr const UnderlyingType& operator*() const& { return value_; } + constexpr UnderlyingType&& operator*() && { return std::move(value_); } + constexpr const UnderlyingType&& operator*() const&& { + return std::move(value_); + } + + constexpr UnderlyingType& value() & { return value_; } + constexpr const UnderlyingType& value() const& { return value_; } + constexpr UnderlyingType&& value() && { return std::move(value_); } + constexpr const UnderlyingType&& value() const&& { return std::move(value_); } + + constexpr explicit operator const UnderlyingType&() const& { return value_; } + + constexpr bool operator==(const StrongAlias& other) const { + return value_ == other.value_; + } + constexpr bool operator!=(const StrongAlias& other) const { + return value_ != other.value_; + } + constexpr bool operator<(const StrongAlias& other) const { + return value_ < other.value_; + } + constexpr bool operator<=(const StrongAlias& other) const { + return value_ <= other.value_; + } + constexpr bool operator>(const StrongAlias& other) const { + return value_ > other.value_; + } + constexpr bool operator>=(const StrongAlias& other) const { + return value_ >= other.value_; + } + + // Hasher to use in std::unordered_map, std::unordered_set, etc. + struct Hasher { + using argument_type = StrongAlias; + using result_type = std::size_t; + result_type operator()(const argument_type& id) const { + return std::hash()(id.value()); + } + }; + + protected: + UnderlyingType value_; +}; + +} // namespace dcsctp + +#endif // NET_DCSCTP_PUBLIC_STRONG_ALIAS_H_ diff --git a/net/dcsctp/public/strong_alias_test.cc b/net/dcsctp/public/strong_alias_test.cc new file mode 100644 index 0000000000..b0f97a73c2 --- /dev/null +++ b/net/dcsctp/public/strong_alias_test.cc @@ -0,0 +1,350 @@ +/* + * Copyright 2019 The Chromium Authors. All rights reserved. + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "net/dcsctp/public/strong_alias.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "rtc_base/gunit.h" +#include "test/gmock.h" + +// This is a copy of +// https://source.chromium.org/chromium/chromium/src/+/master:base/types/strong_alias_unittest.cc +// but adapted to use WebRTC's includes, remove unit tests that test the ostream +// operator (it's removed in this port) and other adaptations to pass lint. + +namespace dcsctp { +namespace { + +// For test correctnenss, it's important that these getters return lexically +// incrementing values as |index| grows. +template +T GetExampleValue(int index); + +template <> +int GetExampleValue(int index) { + return 5 + index; +} +template <> +uint64_t GetExampleValue(int index) { + return 500U + index; +} + +template <> +std::string GetExampleValue(int index) { + return std::string('a', index); +} + +} // namespace + +template +class StrongAliasTest : public ::testing::Test {}; + +using TestedTypes = ::testing::Types; +TYPED_TEST_SUITE(StrongAliasTest, TestedTypes); + +TYPED_TEST(StrongAliasTest, ValueAccessesUnderlyingValue) { + using FooAlias = StrongAlias; + + // Const value getter. + const FooAlias const_alias(GetExampleValue(1)); + EXPECT_EQ(GetExampleValue(1), const_alias.value()); + static_assert(std::is_const::type>::value, + "Reference returned by const value getter should be const."); +} + +TYPED_TEST(StrongAliasTest, ExplicitConversionToUnderlyingValue) { + using FooAlias = StrongAlias; + + const FooAlias const_alias(GetExampleValue(1)); + EXPECT_EQ(GetExampleValue(1), static_cast(const_alias)); +} + +TYPED_TEST(StrongAliasTest, CanBeCopyConstructed) { + using FooAlias = StrongAlias; + FooAlias alias(GetExampleValue(0)); + FooAlias copy_constructed = alias; + EXPECT_EQ(copy_constructed, alias); + + FooAlias copy_assigned; + copy_assigned = alias; + EXPECT_EQ(copy_assigned, alias); +} + +TYPED_TEST(StrongAliasTest, CanBeMoveConstructed) { + using FooAlias = StrongAlias; + FooAlias alias(GetExampleValue(0)); + FooAlias move_constructed = std::move(alias); + EXPECT_EQ(move_constructed, FooAlias(GetExampleValue(0))); + + FooAlias alias2(GetExampleValue(2)); + FooAlias move_assigned; + move_assigned = std::move(alias2); + EXPECT_EQ(move_assigned, FooAlias(GetExampleValue(2))); + + // Check that FooAlias is nothrow move constructible. This matters for + // performance when used in std::vectors. + static_assert(std::is_nothrow_move_constructible::value, + "Error: Alias is not nothow move constructible"); +} + +TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) { + // Note, using a move-only unique_ptr to T: + using FooAlias = StrongAlias>; + + FooAlias a(std::make_unique(GetExampleValue(0))); + EXPECT_EQ(*a.value(), GetExampleValue(0)); + + auto bare_value = std::make_unique(GetExampleValue(1)); + FooAlias b(std::move(bare_value)); + EXPECT_EQ(*b.value(), GetExampleValue(1)); +} + +TYPED_TEST(StrongAliasTest, MutableOperatorArrow) { + // Note, using a move-only unique_ptr to T: + using Ptr = std::unique_ptr; + using FooAlias = StrongAlias; + + FooAlias a(std::make_unique()); + EXPECT_TRUE(a.value()); + + // Check that `a` can be modified through the use of operator->. + a->reset(); + + EXPECT_FALSE(a.value()); +} + +TYPED_TEST(StrongAliasTest, MutableOperatorStar) { + // Note, using a move-only unique_ptr to T: + using Ptr = std::unique_ptr; + using FooAlias = StrongAlias; + + FooAlias a(std::make_unique()); + FooAlias b(std::make_unique()); + EXPECT_TRUE(*a); + EXPECT_TRUE(*b); + + // Check that both the mutable l-value and r-value overloads work and we can + // move out of the aliases. + { Ptr ignore(*std::move(a)); } + { Ptr ignore(std::move(*b)); } + + EXPECT_FALSE(a.value()); + EXPECT_FALSE(b.value()); +} + +TYPED_TEST(StrongAliasTest, MutableValue) { + // Note, using a move-only unique_ptr to T: + using Ptr = std::unique_ptr; + using FooAlias = StrongAlias; + + FooAlias a(std::make_unique()); + FooAlias b(std::make_unique()); + EXPECT_TRUE(a.value()); + EXPECT_TRUE(b.value()); + + // Check that both the mutable l-value and r-value overloads work and we can + // move out of the aliases. + { Ptr ignore(std::move(a).value()); } + { Ptr ignore(std::move(b.value())); } + + EXPECT_FALSE(a.value()); + EXPECT_FALSE(b.value()); +} + +TYPED_TEST(StrongAliasTest, SizeSameAsUnderlyingType) { + using FooAlias = StrongAlias; + static_assert(sizeof(FooAlias) == sizeof(TypeParam), + "StrongAlias should be as large as the underlying type."); +} + +TYPED_TEST(StrongAliasTest, IsDefaultConstructible) { + using FooAlias = StrongAlias; + static_assert(std::is_default_constructible::value, + "Should be possible to default-construct a StrongAlias."); + static_assert( + std::is_trivially_default_constructible::value == + std::is_trivially_default_constructible::value, + "Should be possible to trivially default-construct a StrongAlias iff the " + "underlying type is trivially default constructible."); +} + +TEST(StrongAliasTest, TrivialTypeAliasIsStandardLayout) { + using FooAlias = StrongAlias; + static_assert(std::is_standard_layout::value, + "int-based alias should have standard layout. "); + static_assert(std::is_trivially_copyable::value, + "int-based alias should be trivially copyable. "); +} + +TYPED_TEST(StrongAliasTest, CannotBeCreatedFromDifferentAlias) { + using FooAlias = StrongAlias; + using BarAlias = StrongAlias; + static_assert(!std::is_constructible::value, + "Should be impossible to construct FooAlias from a BarAlias."); + static_assert(!std::is_convertible::value, + "Should be impossible to convert a BarAlias into FooAlias."); +} + +TYPED_TEST(StrongAliasTest, CannotBeImplicitlyConverterToUnderlyingValue) { + using FooAlias = StrongAlias; + static_assert(!std::is_convertible::value, + "Should be impossible to implicitly convert a StrongAlias into " + "an underlying type."); +} + +TYPED_TEST(StrongAliasTest, ComparesEqualToSameValue) { + using FooAlias = StrongAlias; + // Comparison to self: + const FooAlias a = FooAlias(GetExampleValue(0)); + EXPECT_EQ(a, a); + EXPECT_FALSE(a != a); + EXPECT_TRUE(a >= a); + EXPECT_TRUE(a <= a); + EXPECT_FALSE(a > a); + EXPECT_FALSE(a < a); + // Comparison to other equal object: + const FooAlias b = FooAlias(GetExampleValue(0)); + EXPECT_EQ(a, b); + EXPECT_FALSE(a != b); + EXPECT_TRUE(a >= b); + EXPECT_TRUE(a <= b); + EXPECT_FALSE(a > b); + EXPECT_FALSE(a < b); +} + +TYPED_TEST(StrongAliasTest, ComparesCorrectlyToDifferentValue) { + using FooAlias = StrongAlias; + const FooAlias a = FooAlias(GetExampleValue(0)); + const FooAlias b = FooAlias(GetExampleValue(1)); + EXPECT_NE(a, b); + EXPECT_FALSE(a == b); + EXPECT_TRUE(b >= a); + EXPECT_TRUE(a <= b); + EXPECT_TRUE(b > a); + EXPECT_TRUE(a < b); +} + +TEST(StrongAliasTest, CanBeDerivedFrom) { + // Aliases can be enriched by custom operations or validations if needed. + // Ideally, one could go from a 'using' declaration to a derived class to add + // those methods without the need to change any other code. + class CountryCode : public StrongAlias { + public: + explicit CountryCode(const std::string& value) + : StrongAlias::StrongAlias(value) { + if (value_.length() != 2) { + // Country code invalid! + value_.clear(); // is_null() will return true. + } + } + + bool is_null() const { return value_.empty(); } + }; + + CountryCode valid("US"); + EXPECT_FALSE(valid.is_null()); + + CountryCode invalid("United States"); + EXPECT_TRUE(invalid.is_null()); +} + +TEST(StrongAliasTest, CanWrapComplexStructures) { + // A pair of strings implements odering and can, in principle, be used as + // a base of StrongAlias. + using PairOfStrings = std::pair; + using ComplexAlias = StrongAlias; + + ComplexAlias a1{std::make_pair("aaa", "bbb")}; + ComplexAlias a2{std::make_pair("ccc", "ddd")}; + EXPECT_TRUE(a1 < a2); + + EXPECT_TRUE(a1.value() == PairOfStrings("aaa", "bbb")); + + // Note a caveat, an std::pair doesn't have an overload of operator<<, and it + // cannot be easily added since ADL rules would require it to be in the std + // namespace. So we can't print ComplexAlias. +} + +TYPED_TEST(StrongAliasTest, CanBeKeysInStdUnorderedMap) { + using FooAlias = StrongAlias; + std::unordered_map map; + + FooAlias k1(GetExampleValue(0)); + FooAlias k2(GetExampleValue(1)); + + map[k1] = "value1"; + map[k2] = "value2"; + + EXPECT_EQ(map[k1], "value1"); + EXPECT_EQ(map[k2], "value2"); +} + +TYPED_TEST(StrongAliasTest, CanBeKeysInStdMap) { + using FooAlias = StrongAlias; + std::map map; + + FooAlias k1(GetExampleValue(0)); + FooAlias k2(GetExampleValue(1)); + + map[k1] = "value1"; + map[k2] = "value2"; + + EXPECT_EQ(map[k1], "value1"); + EXPECT_EQ(map[k2], "value2"); +} + +TYPED_TEST(StrongAliasTest, CanDifferentiateOverloads) { + using FooAlias = StrongAlias; + using BarAlias = StrongAlias; + class Scope { + public: + static std::string Overload(FooAlias) { return "FooAlias"; } + static std::string Overload(BarAlias) { return "BarAlias"; } + }; + EXPECT_EQ("FooAlias", Scope::Overload(FooAlias())); + EXPECT_EQ("BarAlias", Scope::Overload(BarAlias())); +} + +TEST(StrongAliasTest, EnsureConstexpr) { + using FooAlias = StrongAlias; + + // Check constructors. + static constexpr FooAlias kZero{}; + static constexpr FooAlias kOne(1); + + // Check operator*. + static_assert(*kZero == 0, ""); + static_assert(*kOne == 1, ""); + + // Check value(). + static_assert(kZero.value() == 0, ""); + static_assert(kOne.value() == 1, ""); + + // Check explicit conversions to underlying type. + static_assert(static_cast(kZero) == 0, ""); + static_assert(static_cast(kOne) == 1, ""); + + // Check comparison operations. + static_assert(kZero == kZero, ""); + static_assert(kZero != kOne, ""); + static_assert(kZero < kOne, ""); + static_assert(kZero <= kOne, ""); + static_assert(kOne > kZero, ""); + static_assert(kOne >= kZero, ""); +} +} // namespace dcsctp diff --git a/net/dcsctp/public/types.h b/net/dcsctp/public/types.h new file mode 100644 index 0000000000..16c3e28533 --- /dev/null +++ b/net/dcsctp/public/types.h @@ -0,0 +1,33 @@ +/* + * Copyright 2019 The Chromium Authors. All rights reserved. + * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#ifndef NET_DCSCTP_PUBLIC_TYPES_H_ +#define NET_DCSCTP_PUBLIC_TYPES_H_ + +#include "net/dcsctp/public/strong_alias.h" + +namespace dcsctp { + +// Stream Identifier +using StreamID = StrongAlias; + +// Payload Protocol Identifier (PPID) +using PPID = StrongAlias; + +// Timeout Identifier +using TimeoutID = StrongAlias; + +// Indicates if a message is allowed to be received out-of-order compared to +// other messages on the same stream. +using IsUnordered = StrongAlias; + +} // namespace dcsctp + +#endif // NET_DCSCTP_PUBLIC_TYPES_H_