dcsctp: Add bounded byte reader and writer
Packets, chunks, parameters and error causes - the SCTP entities that are sent on the wire - are buffers with fields that are stored in big endian and that generally consist of a fixed header size, and a variable sized part, that can e.g. be encoded sub-fields or serialized strings. The BoundedByteReader and BoundedByteWriter utilities make it easy to read those fields with as much aid from the compiler as possible, by having compile-time assertions that fields are not accessed outside the buffer's span. There are some byte reading functionality already in modules/rtp_rtcp, but that module would be a bit unfortunate to depend on, and doesn't have the compile time bounds checking that is the biggest feature of this abstraction of an rtc::ArrayView. Bug: webrtc:12614 Change-Id: I9fc641aff22221018dda9add4e2c44853c0f64f0 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/212967 Commit-Queue: Victor Boivie <boivie@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Tommi <tommi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#33597}
This commit is contained in:
committed by
Commit Bot
parent
ff0fb4a5fa
commit
7d3c49a171
1
BUILD.gn
1
BUILD.gn
@ -55,6 +55,7 @@ if (!build_with_chromium) {
|
||||
"modules/remote_bitrate_estimator:rtp_to_text",
|
||||
"modules/rtp_rtcp:test_packet_masks_metrics",
|
||||
"modules/video_capture:video_capture_internal_impl",
|
||||
"net/dcsctp:dcsctp_unittests",
|
||||
"pc:peerconnection_unittests",
|
||||
"pc:rtc_pc_unittests",
|
||||
"rtc_tools:rtp_generator",
|
||||
|
||||
1
api/DEPS
1
api/DEPS
@ -16,6 +16,7 @@ include_rules = [
|
||||
"-infra",
|
||||
"-logging",
|
||||
"-media",
|
||||
"-net",
|
||||
"-modules",
|
||||
"-out",
|
||||
"-p2p",
|
||||
|
||||
19
net/dcsctp/BUILD.gn
Normal file
19
net/dcsctp/BUILD.gn
Normal file
@ -0,0 +1,19 @@
|
||||
# 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")
|
||||
|
||||
if (rtc_include_tests) {
|
||||
rtc_test("dcsctp_unittests") {
|
||||
testonly = true
|
||||
deps = [
|
||||
"../../test:test_main",
|
||||
"packet:dcsctp_packet_unittests",
|
||||
]
|
||||
}
|
||||
}
|
||||
45
net/dcsctp/packet/BUILD.gn
Normal file
45
net/dcsctp/packet/BUILD.gn
Normal file
@ -0,0 +1,45 @@
|
||||
# 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")
|
||||
|
||||
group("packet") {
|
||||
deps = [ ":bounded_io" ]
|
||||
}
|
||||
|
||||
rtc_source_set("bounded_io") {
|
||||
deps = [
|
||||
"../../../api:array_view",
|
||||
"../../../rtc_base",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
]
|
||||
sources = [
|
||||
"bounded_byte_reader.h",
|
||||
"bounded_byte_writer.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (rtc_include_tests) {
|
||||
rtc_library("dcsctp_packet_unittests") {
|
||||
testonly = true
|
||||
|
||||
deps = [
|
||||
":bounded_io",
|
||||
"../../../api:array_view",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:gunit_helpers",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
"../../../test:test_support",
|
||||
]
|
||||
sources = [
|
||||
"bounded_byte_reader_test.cc",
|
||||
"bounded_byte_writer_test.cc",
|
||||
]
|
||||
}
|
||||
}
|
||||
99
net/dcsctp/packet/bounded_byte_reader.h
Normal file
99
net/dcsctp/packet/bounded_byte_reader.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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_PACKET_BOUNDED_BYTE_READER_H_
|
||||
#define NET_DCSCTP_PACKET_BOUNDED_BYTE_READER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "api/array_view.h"
|
||||
|
||||
namespace dcsctp {
|
||||
|
||||
// TODO(boivie): These generic functions - and possibly this entire class -
|
||||
// could be a candidate to have added to rtc_base/. They should use compiler
|
||||
// intrinsics as well.
|
||||
namespace internal {
|
||||
// Loads a 8-bit unsigned word at `data`.
|
||||
inline uint8_t LoadBigEndian8(const uint8_t* data) {
|
||||
return data[0];
|
||||
}
|
||||
|
||||
// Loads a 16-bit unsigned word at `data`.
|
||||
inline uint16_t LoadBigEndian16(const uint8_t* data) {
|
||||
return (data[0] << 8) | data[1];
|
||||
}
|
||||
|
||||
// Loads a 32-bit unsigned word at `data`.
|
||||
inline uint32_t LoadBigEndian32(const uint8_t* data) {
|
||||
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
// BoundedByteReader wraps an ArrayView and divides it into two parts; A fixed
|
||||
// size - which is the template parameter - and a variable size, which is what
|
||||
// remains in `data` after the `FixedSize`.
|
||||
//
|
||||
// The BoundedByteReader provides methods to load/read big endian numbers from
|
||||
// the FixedSize portion of the buffer, and these are read with static bounds
|
||||
// checking, to avoid out-of-bounds accesses without a run-time penalty.
|
||||
//
|
||||
// The variable sized portion can either be used to create sub-readers, which
|
||||
// themselves would provide compile-time bounds-checking, or the entire variable
|
||||
// sized portion can be retrieved as an ArrayView.
|
||||
template <int FixedSize>
|
||||
class BoundedByteReader {
|
||||
public:
|
||||
explicit BoundedByteReader(rtc::ArrayView<const uint8_t> data) : data_(data) {
|
||||
RTC_DCHECK(data.size() >= FixedSize);
|
||||
}
|
||||
|
||||
template <size_t offset>
|
||||
uint8_t Load8() const {
|
||||
static_assert(offset + sizeof(uint8_t) <= FixedSize, "Out-of-bounds");
|
||||
return internal::LoadBigEndian8(&data_[offset]);
|
||||
}
|
||||
|
||||
template <size_t offset>
|
||||
uint16_t Load16() const {
|
||||
static_assert(offset + sizeof(uint16_t) <= FixedSize, "Out-of-bounds");
|
||||
static_assert((offset % sizeof(uint16_t)) == 0, "Unaligned access");
|
||||
return internal::LoadBigEndian16(&data_[offset]);
|
||||
}
|
||||
|
||||
template <size_t offset>
|
||||
uint32_t Load32() const {
|
||||
static_assert(offset + sizeof(uint32_t) <= FixedSize, "Out-of-bounds");
|
||||
static_assert((offset % sizeof(uint32_t)) == 0, "Unaligned access");
|
||||
return internal::LoadBigEndian32(&data_[offset]);
|
||||
}
|
||||
|
||||
template <size_t SubSize>
|
||||
BoundedByteReader<SubSize> sub_reader(size_t variable_offset) const {
|
||||
RTC_DCHECK(FixedSize + variable_offset + SubSize <= data_.size());
|
||||
|
||||
rtc::ArrayView<const uint8_t> sub_span =
|
||||
data_.subview(FixedSize + variable_offset, SubSize);
|
||||
return BoundedByteReader<SubSize>(sub_span);
|
||||
}
|
||||
|
||||
size_t variable_data_size() const { return data_.size() - FixedSize; }
|
||||
|
||||
rtc::ArrayView<const uint8_t> variable_data() const {
|
||||
return data_.subview(FixedSize, data_.size() - FixedSize);
|
||||
}
|
||||
|
||||
private:
|
||||
const rtc::ArrayView<const uint8_t> data_;
|
||||
};
|
||||
|
||||
} // namespace dcsctp
|
||||
|
||||
#endif // NET_DCSCTP_PACKET_BOUNDED_BYTE_READER_H_
|
||||
43
net/dcsctp/packet/bounded_byte_reader_test.cc
Normal file
43
net/dcsctp/packet/bounded_byte_reader_test.cc
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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/packet/bounded_byte_reader.h"
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace dcsctp {
|
||||
namespace {
|
||||
using ::testing::ElementsAre;
|
||||
|
||||
TEST(BoundedByteReaderTest, CanLoadData) {
|
||||
uint8_t data[14] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4};
|
||||
|
||||
BoundedByteReader<8> reader(data);
|
||||
EXPECT_EQ(reader.variable_data_size(), 6U);
|
||||
EXPECT_EQ(reader.Load32<0>(), 0x01020304U);
|
||||
EXPECT_EQ(reader.Load32<4>(), 0x05060708U);
|
||||
EXPECT_EQ(reader.Load16<4>(), 0x0506U);
|
||||
EXPECT_EQ(reader.Load8<4>(), 0x05U);
|
||||
EXPECT_EQ(reader.Load8<5>(), 0x06U);
|
||||
|
||||
BoundedByteReader<6> sub = reader.sub_reader<6>(0);
|
||||
EXPECT_EQ(sub.Load16<0>(), 0x0900U);
|
||||
EXPECT_EQ(sub.Load32<0>(), 0x09000102U);
|
||||
EXPECT_EQ(sub.Load16<4>(), 0x0304U);
|
||||
|
||||
EXPECT_THAT(reader.variable_data(), ElementsAre(9, 0, 1, 2, 3, 4));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace dcsctp
|
||||
100
net/dcsctp/packet/bounded_byte_writer.h
Normal file
100
net/dcsctp/packet/bounded_byte_writer.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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_PACKET_BOUNDED_BYTE_WRITER_H_
|
||||
#define NET_DCSCTP_PACKET_BOUNDED_BYTE_WRITER_H_
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "api/array_view.h"
|
||||
|
||||
namespace dcsctp {
|
||||
|
||||
// TODO(boivie): These generic functions - and possibly this entire class -
|
||||
// could be a candidate to have added to rtc_base/. They should use compiler
|
||||
// intrinsics as well.
|
||||
namespace internal {
|
||||
// Stores a 8-bit unsigned word at `data`.
|
||||
inline void StoreBigEndian8(uint8_t* data, uint8_t val) {
|
||||
data[0] = val;
|
||||
}
|
||||
|
||||
// Stores a 16-bit unsigned word at `data`.
|
||||
inline void StoreBigEndian16(uint8_t* data, uint16_t val) {
|
||||
data[0] = val >> 8;
|
||||
data[1] = val;
|
||||
}
|
||||
|
||||
// Stores a 32-bit unsigned word at `data`.
|
||||
inline void StoreBigEndian32(uint8_t* data, uint32_t val) {
|
||||
data[0] = val >> 24;
|
||||
data[1] = val >> 16;
|
||||
data[2] = val >> 8;
|
||||
data[3] = val;
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
// BoundedByteWriter wraps an ArrayView and divides it into two parts; A fixed
|
||||
// size - which is the template parameter - and a variable size, which is what
|
||||
// remains in `data` after the `FixedSize`.
|
||||
//
|
||||
// The BoundedByteWriter provides methods to write big endian numbers to the
|
||||
// FixedSize portion of the buffer, and these are written with static bounds
|
||||
// checking, to avoid out-of-bounds accesses without a run-time penalty.
|
||||
//
|
||||
// The variable sized portion can either be used to create sub-writers, which
|
||||
// themselves would provide compile-time bounds-checking, or data can be copied
|
||||
// to it.
|
||||
template <int FixedSize>
|
||||
class BoundedByteWriter {
|
||||
public:
|
||||
explicit BoundedByteWriter(rtc::ArrayView<uint8_t> data) : data_(data) {
|
||||
RTC_DCHECK(data.size() >= FixedSize);
|
||||
}
|
||||
|
||||
template <size_t offset>
|
||||
void Store8(uint8_t value) {
|
||||
static_assert(offset + sizeof(uint8_t) <= FixedSize, "Out-of-bounds");
|
||||
internal::StoreBigEndian8(&data_[offset], value);
|
||||
}
|
||||
|
||||
template <size_t offset>
|
||||
void Store16(uint16_t value) {
|
||||
static_assert(offset + sizeof(uint16_t) <= FixedSize, "Out-of-bounds");
|
||||
static_assert((offset % sizeof(uint16_t)) == 0, "Unaligned access");
|
||||
internal::StoreBigEndian16(&data_[offset], value);
|
||||
}
|
||||
|
||||
template <size_t offset>
|
||||
void Store32(uint32_t value) {
|
||||
static_assert(offset + sizeof(uint32_t) <= FixedSize, "Out-of-bounds");
|
||||
static_assert((offset % sizeof(uint32_t)) == 0, "Unaligned access");
|
||||
internal::StoreBigEndian32(&data_[offset], value);
|
||||
}
|
||||
|
||||
template <size_t SubSize>
|
||||
BoundedByteWriter<SubSize> sub_writer(size_t variable_offset) {
|
||||
RTC_DCHECK(FixedSize + variable_offset + SubSize <= data_.size());
|
||||
|
||||
return BoundedByteWriter<SubSize>(
|
||||
data_.subview(FixedSize + variable_offset, SubSize));
|
||||
}
|
||||
|
||||
void CopyToVariableData(rtc::ArrayView<const uint8_t> source) {
|
||||
memcpy(data_.data() + FixedSize, source.data(),
|
||||
std::min(source.size(), data_.size() - FixedSize));
|
||||
}
|
||||
|
||||
private:
|
||||
rtc::ArrayView<uint8_t> data_;
|
||||
};
|
||||
} // namespace dcsctp
|
||||
|
||||
#endif // NET_DCSCTP_PACKET_BOUNDED_BYTE_WRITER_H_
|
||||
48
net/dcsctp/packet/bounded_byte_writer_test.cc
Normal file
48
net/dcsctp/packet/bounded_byte_writer_test.cc
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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/packet/bounded_byte_writer.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace dcsctp {
|
||||
namespace {
|
||||
using ::testing::ElementsAre;
|
||||
|
||||
TEST(BoundedByteWriterTest, CanWriteData) {
|
||||
std::vector<uint8_t> data(14);
|
||||
|
||||
BoundedByteWriter<8> writer(data);
|
||||
writer.Store32<0>(0x01020304);
|
||||
writer.Store16<4>(0x0506);
|
||||
writer.Store8<6>(0x07);
|
||||
writer.Store8<7>(0x08);
|
||||
|
||||
uint8_t variable_data[] = {0, 0, 0, 0, 3, 0};
|
||||
writer.CopyToVariableData(variable_data);
|
||||
|
||||
BoundedByteWriter<6> sub = writer.sub_writer<6>(0);
|
||||
sub.Store32<0>(0x09000000);
|
||||
sub.Store16<2>(0x0102);
|
||||
|
||||
BoundedByteWriter<2> sub2 = writer.sub_writer<2>(4);
|
||||
sub2.Store8<1>(0x04);
|
||||
|
||||
EXPECT_THAT(data, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace dcsctp
|
||||
Reference in New Issue
Block a user