[Open Screen] Replace ByteView with a generic Span type.
This replaces ByteView with a generic Span type that is generally
forward compatible with C++20 std::span. Span permits a view on both
const and non-const uint8_t data.
ByteView becomes an alias for Span<const uint8_t>. ByteBuffer is an
alias for Span<uint8_t> and is used for APIs that mutate their input.
Updates the public APIs for libcast to use the aliases and removes
remaining dependencies in public headers on absl::Span.
Removing absl::Span from the remainder of the codebase can be done as a
mechanical cleanup (to eliminate unnecessary conversions).
Bug: b/239435405
Change-Id: Ie5de1bcd0c2ec40450b154f5b36e26b3bde4579d
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/4215906
Reviewed-by: Jordan Bayles <jophba@chromium.org>
Commit-Queue: Mark Foltz <mfoltz@chromium.org>
Reviewed-by: Ryan Keane <rwkeane@google.com>
diff --git a/cast/standalone_receiver/decoder.cc b/cast/standalone_receiver/decoder.cc
index 96ad091..2fa3eb9 100644
--- a/cast/standalone_receiver/decoder.cc
+++ b/cast/standalone_receiver/decoder.cc
@@ -10,7 +10,7 @@
#include <sstream>
#include <thread>
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "util/osp_logging.h"
#include "util/std_util.h"
#include "util/trace_logging.h"
diff --git a/cast/standalone_receiver/decoder.h b/cast/standalone_receiver/decoder.h
index 5d41546..89d407c 100644
--- a/cast/standalone_receiver/decoder.h
+++ b/cast/standalone_receiver/decoder.h
@@ -13,7 +13,7 @@
#include "absl/types/span.h"
#include "cast/standalone_receiver/avcodec_glue.h"
#include "cast/streaming/frame_id.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
namespace openscreen {
namespace cast {
diff --git a/cast/standalone_receiver/dummy_player.cc b/cast/standalone_receiver/dummy_player.cc
index a66fcd7..f76940e 100644
--- a/cast/standalone_receiver/dummy_player.cc
+++ b/cast/standalone_receiver/dummy_player.cc
@@ -6,8 +6,8 @@
#include <chrono>
-#include "absl/types/span.h"
#include "cast/streaming/encoded_frame.h"
+#include "platform/base/span.h"
#include "platform/base/trivial_clock_traits.h"
#include "util/chrono_helpers.h"
#include "util/osp_logging.h"
@@ -29,8 +29,7 @@
void DummyPlayer::OnFramesReady(int buffer_size) {
// Consume the next frame.
buffer_.resize(buffer_size);
- const EncodedFrame frame =
- receiver_->ConsumeNextFrame(absl::Span<uint8_t>(buffer_));
+ const EncodedFrame frame = receiver_->ConsumeNextFrame(buffer_);
// Convert the RTP timestamp to a human-readable timestamp (in µs) and log
// some short information about the frame.
diff --git a/cast/standalone_sender/streaming_opus_encoder.cc b/cast/standalone_sender/streaming_opus_encoder.cc
index 54aeec4..3371816 100644
--- a/cast/standalone_sender/streaming_opus_encoder.cc
+++ b/cast/standalone_sender/streaming_opus_encoder.cc
@@ -10,7 +10,7 @@
#include <chrono>
#include <utility>
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "util/chrono_helpers.h"
namespace openscreen {
diff --git a/cast/standalone_sender/streaming_vpx_encoder.cc b/cast/standalone_sender/streaming_vpx_encoder.cc
index ef3f2b8..89da632 100644
--- a/cast/standalone_sender/streaming_vpx_encoder.cc
+++ b/cast/standalone_sender/streaming_vpx_encoder.cc
@@ -14,7 +14,7 @@
#include "cast/streaming/encoded_frame.h"
#include "cast/streaming/environment.h"
#include "cast/streaming/sender.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "util/chrono_helpers.h"
#include "util/osp_logging.h"
#include "util/saturate_cast.h"
@@ -403,7 +403,7 @@
}
frame.rtp_timestamp = results.rtp_timestamp;
frame.reference_time = results.reference_time;
- frame.data = ByteView(results.payload);
+ frame.data = results.payload;
if (sender_->EnqueueFrame(frame) != Sender::OK) {
// Since the frame will not be sent, the encoder's frame dependency chain
diff --git a/cast/streaming/encoded_frame.h b/cast/streaming/encoded_frame.h
index 755e000..c024aef 100644
--- a/cast/streaming/encoded_frame.h
+++ b/cast/streaming/encoded_frame.h
@@ -13,8 +13,8 @@
#include "cast/streaming/frame_id.h"
#include "cast/streaming/rtp_time.h"
#include "platform/api/time.h"
-#include "platform/base/byte_view.h"
#include "platform/base/macros.h"
+#include "platform/base/span.h"
namespace openscreen {
namespace cast {
diff --git a/cast/streaming/environment.h b/cast/streaming/environment.h
index ccc8447..5937a4a 100644
--- a/cast/streaming/environment.h
+++ b/cast/streaming/environment.h
@@ -13,8 +13,8 @@
#include "platform/api/time.h"
#include "platform/api/udp_socket.h"
-#include "platform/base/byte_view.h"
#include "platform/base/ip_address.h"
+#include "platform/base/span.h"
namespace openscreen {
namespace cast {
diff --git a/cast/streaming/frame_collector.cc b/cast/streaming/frame_collector.cc
index 9e0afb3..e959506 100644
--- a/cast/streaming/frame_collector.cc
+++ b/cast/streaming/frame_collector.cc
@@ -10,7 +10,7 @@
#include "cast/streaming/frame_id.h"
#include "cast/streaming/rtp_defines.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "util/osp_logging.h"
namespace openscreen {
@@ -139,7 +139,7 @@
frame_.owned_data_.insert(frame_.owned_data_.end(), chunk.payload.begin(),
chunk.payload.end());
}
- frame_.data = ByteView(frame_.owned_data_);
+ frame_.data = frame_.owned_data_;
}
return frame_;
diff --git a/cast/streaming/frame_crypto.cc b/cast/streaming/frame_crypto.cc
index 249c833..8c1db68 100644
--- a/cast/streaming/frame_crypto.cc
+++ b/cast/streaming/frame_crypto.cc
@@ -10,7 +10,7 @@
#include "openssl/crypto.h"
#include "openssl/err.h"
#include "openssl/rand.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "util/big_endian.h"
#include "util/crypto/openssl_util.h"
#include "util/crypto/random_bytes.h"
@@ -20,7 +20,7 @@
namespace cast {
EncryptedFrame::EncryptedFrame() {
- data = ByteView(owned_data_);
+ data = owned_data_;
}
EncryptedFrame::~EncryptedFrame() = default;
@@ -28,14 +28,14 @@
EncryptedFrame::EncryptedFrame(EncryptedFrame&& other) noexcept
: EncodedFrame(static_cast<EncodedFrame&&>(other)),
owned_data_(std::move(other.owned_data_)) {
- data = ByteView(owned_data_);
+ data = owned_data_;
other.data = ByteView();
}
EncryptedFrame& EncryptedFrame::operator=(EncryptedFrame&& other) {
this->EncodedFrame::operator=(static_cast<EncodedFrame&&>(other));
owned_data_ = std::move(other.owned_data_);
- data = ByteView(owned_data_);
+ data = owned_data_;
other.data = ByteView();
return *this;
}
@@ -65,14 +65,13 @@
EncryptedFrame result;
encoded_frame.CopyMetadataTo(&result);
result.owned_data_.resize(encoded_frame.data.size());
- result.data = ByteView(result.owned_data_);
- EncryptCommon(encoded_frame.frame_id, encoded_frame.data,
- absl::MakeSpan(result.owned_data_));
+ result.data = result.owned_data_;
+ EncryptCommon(encoded_frame.frame_id, encoded_frame.data, result.owned_data_);
return result;
}
void FrameCrypto::Decrypt(const EncryptedFrame& encrypted_frame,
- absl::Span<uint8_t> out) const {
+ ByteBuffer out) const {
// AES-CTC is symmetric. Thus, decryption back to the plaintext is the same as
// encrypting the ciphertext; and both are the same size.
OSP_DCHECK_EQ(encrypted_frame.data.size(), out.size());
@@ -80,8 +79,8 @@
}
void FrameCrypto::EncryptCommon(FrameId frame_id,
- absl::Span<const uint8_t> in,
- absl::Span<uint8_t> out) const {
+ ByteView in,
+ ByteBuffer out) const {
OSP_DCHECK(!frame_id.is_null());
OSP_DCHECK_EQ(in.size(), out.size());
diff --git a/cast/streaming/frame_crypto.h b/cast/streaming/frame_crypto.h
index af25bb2..a596412 100644
--- a/cast/streaming/frame_crypto.h
+++ b/cast/streaming/frame_crypto.h
@@ -11,10 +11,10 @@
#include <array>
#include <vector>
-#include "absl/types/span.h"
#include "cast/streaming/encoded_frame.h"
#include "openssl/aes.h"
#include "platform/base/macros.h"
+#include "platform/base/span.h"
namespace openscreen {
namespace cast {
@@ -59,8 +59,7 @@
// Decrypts `encrypted_frame` into `out`. `out` must have a sufficiently-sized
// data buffer (see GetPlaintextSize()).
- void Decrypt(const EncryptedFrame& encrypted_frame,
- absl::Span<uint8_t> out) const;
+ void Decrypt(const EncryptedFrame& encrypted_frame, ByteBuffer out) const;
// AES crypto inputs and outputs (for either encrypting or decrypting) are
// always the same size in bytes. The following are just "documentative code."
@@ -82,9 +81,7 @@
// AES-CTR is symmetric. Thus, the "meat" of both Encrypt() and Decrypt() is
// the same.
- void EncryptCommon(FrameId frame_id,
- absl::Span<const uint8_t> in,
- absl::Span<uint8_t> out) const;
+ void EncryptCommon(FrameId frame_id, ByteView in, ByteBuffer out) const;
};
} // namespace cast
diff --git a/cast/streaming/frame_crypto_unittest.cc b/cast/streaming/frame_crypto_unittest.cc
index 1c45229..7778a71 100644
--- a/cast/streaming/frame_crypto_unittest.cc
+++ b/cast/streaming/frame_crypto_unittest.cc
@@ -9,7 +9,7 @@
#include <vector>
#include "gtest/gtest.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "platform/test/byte_view_test_util.h"
#include "util/crypto/random_bytes.h"
@@ -26,7 +26,7 @@
std::vector<uint8_t> buffer(
reinterpret_cast<const uint8_t*>(kPayload),
reinterpret_cast<const uint8_t*>(kPayload) + sizeof(kPayload));
- frame0.data = ByteView(buffer);
+ frame0.data = buffer;
EncodedFrame frame1;
frame1.frame_id = frame0.frame_id + 1;
frame1.data = frame0.data;
@@ -55,19 +55,19 @@
// plaintext is retrieved.
std::vector<uint8_t> decrypted_frame0_buffer(
FrameCrypto::GetPlaintextSize(encrypted_frame0));
- crypto.Decrypt(encrypted_frame0, absl::MakeSpan(decrypted_frame0_buffer));
+ crypto.Decrypt(encrypted_frame0, decrypted_frame0_buffer);
EncodedFrame decrypted_frame0;
encrypted_frame0.CopyMetadataTo(&decrypted_frame0);
- decrypted_frame0.data = ByteView(decrypted_frame0_buffer);
+ decrypted_frame0.data = decrypted_frame0_buffer;
EXPECT_EQ(frame0.frame_id, decrypted_frame0.frame_id);
ExpectByteViewsHaveSameBytes(frame0.data, decrypted_frame0.data);
std::vector<uint8_t> decrypted_frame1_buffer(
FrameCrypto::GetPlaintextSize(encrypted_frame1));
- crypto.Decrypt(encrypted_frame1, absl::MakeSpan(decrypted_frame1_buffer));
+ crypto.Decrypt(encrypted_frame1, decrypted_frame1_buffer);
EncodedFrame decrypted_frame1;
encrypted_frame1.CopyMetadataTo(&decrypted_frame1);
- decrypted_frame1.data = ByteView(decrypted_frame1_buffer);
+ decrypted_frame1.data = decrypted_frame1_buffer;
EXPECT_EQ(frame1.frame_id, decrypted_frame1.frame_id);
ExpectByteViewsHaveSameBytes(frame1.data, decrypted_frame1.data);
}
diff --git a/cast/streaming/receiver.cc b/cast/streaming/receiver.cc
index bca4033..7dee2ed 100644
--- a/cast/streaming/receiver.cc
+++ b/cast/streaming/receiver.cc
@@ -144,7 +144,7 @@
return kNoFramesReady;
}
-EncodedFrame Receiver::ConsumeNextFrame(absl::Span<uint8_t> buffer) {
+EncodedFrame Receiver::ConsumeNextFrame(ByteBuffer buffer) {
TRACE_DEFAULT_SCOPED(TraceCategory::kReceiver);
// Assumption: The required call to AdvanceToNextFrame() ensures that
// |last_frame_consumed_| is set to one before the frame to be consumed here.
@@ -163,7 +163,7 @@
crypto_.Decrypt(encrypted_frame, buffer);
EncodedFrame frame;
encrypted_frame.CopyMetadataTo(&frame);
- frame.data = ByteView(buffer.data(), buffer.size());
+ frame.data = buffer;
frame.reference_time =
*entry.estimated_capture_time + ResolveTargetPlayoutDelay(frame_id);
diff --git a/cast/streaming/receiver.h b/cast/streaming/receiver.h
index d3269c6..7a6f9cd 100644
--- a/cast/streaming/receiver.h
+++ b/cast/streaming/receiver.h
@@ -29,6 +29,7 @@
#include "cast/streaming/session_config.h"
#include "cast/streaming/ssrc.h"
#include "platform/api/time.h"
+#include "platform/base/span.h"
#include "util/alarm.h"
namespace openscreen {
@@ -75,7 +76,7 @@
// std::vector<uint8_t> buffer;
// buffer.resize(next_frame_buffer_size);
// openscreen::cast::EncodedFrame encoded_frame =
-// receiver_->ConsumeNextFrame(absl::Span<uint8_t>(buffer));
+// receiver_->ConsumeNextFrame(buffer);
//
// display_.RenderFrame(decoder_.DecodeFrame(encoded_frame.data));
//
@@ -118,13 +119,6 @@
SessionConfig config);
~Receiver() override;
- // Templated implementation of ConsumeNextFrame() for use when absl cannot be
- // used explicitly.
- template<typename T>
- EncodedFrame ConsumeNextFrame(T buffer) {
- return ConsumeNextFrame(absl::Span<uint8_t>(buffer));
- }
-
// ReceiverBase overrides.
const SessionConfig& config() const override;
int rtp_timebase() const override;
@@ -133,7 +127,7 @@
void SetPlayerProcessingTime(Clock::duration needed_time) override;
void RequestKeyFrame() override;
int AdvanceToNextFrame() override;
- EncodedFrame ConsumeNextFrame(absl::Span<uint8_t> buffer) override;
+ EncodedFrame ConsumeNextFrame(ByteBuffer buffer) override;
// Allows setting picture loss indication for testing. In production, this
// should be done using the config.
diff --git a/cast/streaming/receiver_base.h b/cast/streaming/receiver_base.h
index 21d8d67..2eb739b 100644
--- a/cast/streaming/receiver_base.h
+++ b/cast/streaming/receiver_base.h
@@ -12,6 +12,7 @@
#include "cast/streaming/session_config.h"
#include "cast/streaming/ssrc.h"
#include "platform/api/time.h"
+#include "platform/base/span.h"
namespace openscreen {
namespace cast {
@@ -94,7 +95,7 @@
// |buffer| must point to a sufficiently-sized buffer that will be populated
// with the frame's payload data. Upon return |frame->data| will be set to the
// portion of the buffer that was populated.
- virtual EncodedFrame ConsumeNextFrame(absl::Span<uint8_t> buffer) = 0;
+ virtual EncodedFrame ConsumeNextFrame(ByteBuffer buffer) = 0;
// The default "player processing time" amount. See SetPlayerProcessingTime().
// This value is based on real world experimentation, however may vary
diff --git a/cast/streaming/receiver_packet_router.cc b/cast/streaming/receiver_packet_router.cc
index bb2776c..e94c5fa 100644
--- a/cast/streaming/receiver_packet_router.cc
+++ b/cast/streaming/receiver_packet_router.cc
@@ -8,7 +8,7 @@
#include "cast/streaming/packet_util.h"
#include "cast/streaming/receiver.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "util/osp_logging.h"
#include "util/stringprintf.h"
diff --git a/cast/streaming/receiver_unittest.cc b/cast/streaming/receiver_unittest.cc
index 9079021..74ce375 100644
--- a/cast/streaming/receiver_unittest.cc
+++ b/cast/streaming/receiver_unittest.cc
@@ -31,9 +31,9 @@
#include "gtest/gtest.h"
#include "platform/api/time.h"
#include "platform/api/udp_socket.h"
-#include "platform/base/byte_view.h"
#include "platform/base/error.h"
#include "platform/base/ip_address.h"
+#include "platform/base/span.h"
#include "platform/base/udp_packet.h"
#include "platform/test/byte_view_test_util.h"
#include "platform/test/fake_clock.h"
@@ -110,7 +110,7 @@
for (size_t i = 0; i < buffer_.size(); ++i) {
buffer_[i] = static_cast<uint8_t>(which + static_cast<int>(i));
}
- data = ByteView(buffer_);
+ data = buffer_;
}
static RtpTimeTicks GetRtpStartTime() {
@@ -327,8 +327,7 @@
const int payload_size = receiver()->AdvanceToNextFrame();
ASSERT_NE(Receiver::kNoFramesReady, payload_size);
std::vector<uint8_t> buffer(payload_size);
- EncodedFrame received_frame =
- receiver()->ConsumeNextFrame(absl::Span<uint8_t>(buffer));
+ EncodedFrame received_frame = receiver()->ConsumeNextFrame(buffer);
EXPECT_EQ(sent_frame.dependency, received_frame.dependency);
EXPECT_EQ(sent_frame.frame_id, received_frame.frame_id);
diff --git a/cast/streaming/sender_packet_router.cc b/cast/streaming/sender_packet_router.cc
index f198ca6..46c4348 100644
--- a/cast/streaming/sender_packet_router.cc
+++ b/cast/streaming/sender_packet_router.cc
@@ -9,7 +9,7 @@
#include "cast/streaming/constants.h"
#include "cast/streaming/packet_util.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "util/chrono_helpers.h"
#include "util/osp_logging.h"
#include "util/saturate_cast.h"
diff --git a/cast/streaming/sender_unittest.cc b/cast/streaming/sender_unittest.cc
index 53ea7a4..a3d6541 100644
--- a/cast/streaming/sender_unittest.cc
+++ b/cast/streaming/sender_unittest.cc
@@ -16,7 +16,6 @@
#include <vector>
#include "absl/types/optional.h"
-#include "absl/types/span.h"
#include "cast/streaming/compound_rtcp_builder.h"
#include "cast/streaming/constants.h"
#include "cast/streaming/encoded_frame.h"
@@ -35,7 +34,7 @@
#include "cast/streaming/testing/simple_socket_subscriber.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "platform/base/byte_view.h"
+#include "platform/base/span.h"
#include "platform/test/byte_view_test_util.h"
#include "platform/test/fake_clock.h"
#include "platform/test/fake_task_runner.h"
@@ -305,9 +304,9 @@
// testing should exist elsewhere to confirm frame play-out times with real
// Receivers.
decrypted->buffer.resize(FrameCrypto::GetPlaintextSize(encrypted));
- crypto_.Decrypt(encrypted, absl::MakeSpan(decrypted->buffer));
+ crypto_.Decrypt(encrypted, decrypted->buffer);
encrypted.CopyMetadataTo(decrypted);
- decrypted->data = ByteView(decrypted->buffer);
+ decrypted->data = decrypted->buffer;
incomplete_frames_.erase(frame_id);
OnFrameComplete(frame_id);
}
@@ -408,7 +407,7 @@
(frame_id - FrameId::first()));
frame->reference_time = reference_time;
PopulateFramePayloadBuffer(seed, num_payload_bytes, &frame->buffer);
- frame->data = ByteView(frame->buffer);
+ frame->data = frame->buffer;
}
// Confirms that all |sent_frames| exist in |received_frames|, with identical
diff --git a/platform/BUILD.gn b/platform/BUILD.gn
index aabc37a..c2d9f35 100644
--- a/platform/BUILD.gn
+++ b/platform/BUILD.gn
@@ -16,6 +16,7 @@
"base/interface_info.h",
"base/ip_address.h",
"base/location.h",
+ "base/span.h",
"base/tls_connect_options.h",
"base/tls_credentials.h",
"base/tls_listen_options.h",
@@ -238,10 +239,10 @@
sources = [
"api/serial_delete_ptr_unittest.cc",
"api/time_unittest.cc",
- "base/byte_view_unittest.cc",
"base/error_unittest.cc",
"base/ip_address_unittest.cc",
"base/location_unittest.cc",
+ "base/span_unittest.cc",
"base/udp_packet_unittest.cc",
]
diff --git a/platform/base/byte_view.h b/platform/base/byte_view.h
index 0efc131..bbfa8a3 100644
--- a/platform/base/byte_view.h
+++ b/platform/base/byte_view.h
@@ -5,97 +5,9 @@
#ifndef PLATFORM_BASE_BYTE_VIEW_H_
#define PLATFORM_BASE_BYTE_VIEW_H_
-#include <stddef.h>
+// Backwards-compatibility header for ByteView. This will be removed once
+// downstream includes are updated.
-#include <cassert>
-#include <cstddef>
-#include <cstdint>
-#include <vector>
-
-#include "platform/base/macros.h"
-
-namespace openscreen {
-
-// Contains a pointer and length to a span of continguous and unowned bytes.
-// The underlying data cannot be modified.
-//
-// The API is a slimmed-down version of a C++20 std::span<const uint8_t> and is
-// intended to be forwards-compatible. Support for iterators and front/back can
-// be added as needed; we don't intend to add support for static extents.
-//
-// NOTES:
-// - Although other span implementations allow passing zero to last(), we do
-// not, as the behavior is undefined. Callers should explicitly create an
-// empty ByteView instead.
-//
-// - operator== is not implemented to align with std::span. For more
-// discussion, this blog post has considerations when implementing operators
-// on types that don't own the data they depend upon:
-// https://abseil.io/blog/20180531-regular-types
-//
-// - Unit tests that want to compare the bytes behind two ByteViews can use
-// ExpectByteViewsHaveSameBytes().
-//
-class ByteView {
- public:
- constexpr ByteView() noexcept = default;
- constexpr ByteView(const uint8_t* data, size_t count)
- : data_(data), count_(count) {}
- explicit ByteView(const std::vector<uint8_t>& v)
- : data_(v.data()), count_(v.size()) {}
-
- constexpr ByteView(const ByteView&) noexcept = default;
- constexpr ByteView& operator=(const ByteView&) noexcept = default;
- ByteView(ByteView&&) noexcept = default;
- ByteView& operator=(ByteView&&) noexcept = default;
- ~ByteView() = default;
-
- constexpr const uint8_t* data() const noexcept { return data_; }
-
- constexpr const uint8_t& operator[](size_t idx) const {
- assert(idx < count_);
- return *(data_ + idx);
- }
-
- constexpr size_t size() const { return count_; }
-
- [[nodiscard]] constexpr bool empty() const { return count_ == 0; }
-
- constexpr ByteView first(size_t count) const {
- assert(count <= count_);
- return ByteView(data_, count);
- }
-
- constexpr ByteView last(size_t count) const {
- assert(count <= count_);
- assert(count != 0);
- return ByteView(data_ + (count_ - count), count);
- }
-
- constexpr const uint8_t* begin() const noexcept { return data_; }
- constexpr const uint8_t* end() const noexcept { return data_ + count_; }
-
- void remove_prefix(size_t count) noexcept {
- assert(count_ >= count);
- data_ += count;
- count_ -= count;
- }
-
- void remove_suffix(size_t count) noexcept {
- assert(count_ >= count);
- count_ -= count;
- }
-
- constexpr ByteView subspan(size_t offset, size_t count) const {
- assert(offset + count < count_);
- return ByteView(data_ + offset, count);
- }
-
- private:
- const uint8_t* data_{nullptr};
- size_t count_{0};
-};
-
-} // namespace openscreen
+#include "platform/base/span.h"
#endif // PLATFORM_BASE_BYTE_VIEW_H_
diff --git a/platform/base/byte_view_unittest.cc b/platform/base/byte_view_unittest.cc
deleted file mode 100644
index 4d9b521..0000000
--- a/platform/base/byte_view_unittest.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "platform/base/byte_view.h"
-
-#include "gtest/gtest.h"
-
-namespace {
-
-constexpr char kSampleBytes[] = "googleplex";
-const uint8_t* kSampleData =
- reinterpret_cast<const uint8_t* const>(&kSampleBytes[0]);
-constexpr size_t kSampleSize =
- sizeof(kSampleBytes) - 1; // Ignore null terminator.
-
-} // namespace
-
-namespace openscreen {
-
-TEST(ByteViewTest, TestBasics) {
- ByteView nullView;
- EXPECT_EQ(nullView.data(), nullptr);
- EXPECT_EQ(nullView.size(), size_t{0});
- EXPECT_TRUE(nullView.empty());
-
- ByteView googlePlex = ByteView(kSampleData, kSampleSize);
- EXPECT_EQ(googlePlex.data(), kSampleData);
- EXPECT_EQ(googlePlex.size(), kSampleSize);
- EXPECT_FALSE(googlePlex.empty());
-
- EXPECT_EQ(googlePlex[0], 'g');
- EXPECT_EQ(googlePlex[9], 'x');
-
- ByteView copyBytes = googlePlex;
- EXPECT_EQ(copyBytes.data(), googlePlex.data());
- EXPECT_EQ(copyBytes.size(), googlePlex.size());
-
- ByteView firstBytes(googlePlex.first(4));
- EXPECT_EQ(firstBytes.data(), googlePlex.data());
- EXPECT_EQ(firstBytes.size(), size_t{4});
- EXPECT_EQ(firstBytes[0], 'g');
- EXPECT_EQ(firstBytes[3], 'g');
-
- ByteView lastBytes(googlePlex.last(4));
- EXPECT_EQ(lastBytes.data(), googlePlex.data() + 6);
- EXPECT_EQ(lastBytes.size(), size_t{4});
- EXPECT_EQ(lastBytes[0], 'p');
- EXPECT_EQ(lastBytes[3], 'x');
-
- ByteView middleBytes(googlePlex.subspan(2, 4));
- EXPECT_EQ(middleBytes.data(), googlePlex.data() + 2);
- EXPECT_EQ(middleBytes.size(), size_t{4});
- EXPECT_EQ(middleBytes[0], 'o');
- EXPECT_EQ(middleBytes[3], 'e');
-}
-
-TEST(ByteViewTest, TestIterators) {
- ByteView googlePlex = ByteView(kSampleData, kSampleSize);
- size_t idx = 0;
-
- for (const uint8_t* it = googlePlex.begin(); it != googlePlex.end(); it++) {
- EXPECT_EQ(*it, kSampleBytes[idx]);
- idx++;
- }
-}
-
-TEST(ByteViewTest, TestRemove) {
- ByteView googlePlex = ByteView(kSampleData, kSampleSize);
-
- googlePlex.remove_prefix(2);
- EXPECT_EQ(googlePlex.size(), size_t{8});
- EXPECT_EQ(googlePlex[0], 'o');
-
- googlePlex.remove_suffix(2);
- EXPECT_EQ(googlePlex.size(), size_t{6});
- EXPECT_EQ(googlePlex[5], 'l');
-}
-
-} // namespace openscreen
diff --git a/platform/base/span.h b/platform/base/span.h
new file mode 100644
index 0000000..11edcf5
--- /dev/null
+++ b/platform/base/span.h
@@ -0,0 +1,128 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PLATFORM_BASE_SPAN_H_
+#define PLATFORM_BASE_SPAN_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cassert>
+#include <type_traits>
+#include <vector>
+
+namespace openscreen {
+
+template <typename T>
+class Span;
+
+// In Open Screen code, use these aliases for the most common types of Spans.
+// These can be converted to use std::span once the library supports C++20.
+using ByteView = Span<const uint8_t>;
+using ByteBuffer = Span<uint8_t>;
+
+namespace internal {
+
+template <typename From, typename To>
+using EnableIfConvertible = std::enable_if_t<
+ std::is_convertible<From (*)[], To (*)[]>::value>; // NOLINT
+
+} // namespace internal
+
+// Contains a pointer and length to a span of contiguous data.
+//
+// The API is a slimmed-down version of a C++20 std::span<T> and is intended to
+// be forwards-compatible with very slight modifications. We don't intend to
+// add support for static extents.
+//
+// NOTES:
+// - Although other span implementations allow passing zero to last(), we do
+// not, as the behavior is undefined. Callers should explicitly create an
+// empty Span instead.
+//
+// - operator== is not implemented to align with std::span. For more
+// discussion, this blog post has considerations when implementing operators
+// on types that don't own the data they depend upon:
+// https://abseil.io/blog/20180531-regular-types
+//
+// - Unit tests that want to compare the bytes behind two ByteViews can use
+// ExpectByteViewsHaveSameBytes().
+template <typename T>
+class Span {
+ public:
+ constexpr Span() noexcept = default;
+ constexpr Span(const Span&) noexcept = default;
+ Span(Span&& other) noexcept = default;
+ constexpr Span& operator=(const Span&) noexcept = default;
+ Span& operator=(Span&& other) noexcept = default;
+
+ constexpr Span(T* data, size_t count) : data_(data), count_(count) {}
+
+ template <typename U, typename = internal::EnableIfConvertible<U, T>>
+ Span(std::vector<U>& v) : data_(v.data()), count_(v.size()) {} // NOLINT
+
+ template <typename U, typename = internal::EnableIfConvertible<U, T>>
+ constexpr Span(const Span<U>& other) noexcept
+ : data_(other.data()), count_(other.size()) {} // NOLINT
+
+ template <typename U, typename = internal::EnableIfConvertible<U, T>>
+ constexpr Span& operator=(const Span<U>& other) noexcept {
+ data_ = other.data();
+ count_ = other.size();
+ return *this;
+ }
+
+ ~Span() = default;
+
+ constexpr T* data() const noexcept { return data_; }
+
+ constexpr T& operator[](size_t idx) const {
+ assert(idx < count_);
+ return *(data_ + idx);
+ }
+
+ constexpr size_t size() const { return count_; }
+
+ [[nodiscard]] constexpr bool empty() const { return count_ == 0; }
+
+ constexpr Span first(size_t count) const {
+ assert(count <= count_);
+ return Span(data_, count);
+ }
+
+ constexpr Span last(size_t count) const {
+ assert(count <= count_);
+ assert(count != 0);
+ return Span(data_ + (count_ - count), count);
+ }
+
+ constexpr T* begin() const noexcept { return data_; }
+ constexpr T* end() const noexcept { return data_ + count_; }
+ constexpr const T* cbegin() const noexcept { return data_; }
+ constexpr const T* cend() const noexcept { return data_ + count_; }
+
+ void remove_prefix(size_t count) noexcept {
+ assert(count_ >= count);
+ data_ += count;
+ count_ -= count;
+ }
+
+ void remove_suffix(size_t count) noexcept {
+ assert(count_ >= count);
+ count_ -= count;
+ }
+
+ constexpr Span subspan(size_t offset, size_t count) const {
+ assert(offset + count < count_);
+ return Span(data_ + offset, count);
+ }
+
+ private:
+ T* data_{nullptr};
+ size_t count_{0};
+};
+
+} // namespace openscreen
+
+#endif // PLATFORM_BASE_SPAN_H_
diff --git a/platform/base/span_unittest.cc b/platform/base/span_unittest.cc
new file mode 100644
index 0000000..f22a3ba
--- /dev/null
+++ b/platform/base/span_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/base/span.h"
+
+#include <stdint.h>
+
+#include <array>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+constexpr char kSampleBytes[] = "googleplex";
+const uint8_t* kSampleData =
+ reinterpret_cast<const uint8_t* const>(&kSampleBytes[0]);
+constexpr size_t kSampleSize =
+ sizeof(kSampleBytes) - 1; // Ignore null terminator.
+
+} // namespace
+
+namespace openscreen {
+
+TEST(SpanTest, TestBasics) {
+ ByteView nullView;
+ EXPECT_EQ(nullView.data(), nullptr);
+ EXPECT_EQ(nullView.size(), size_t{0});
+ EXPECT_TRUE(nullView.empty());
+
+ ByteView googlePlex = ByteView(kSampleData, kSampleSize);
+ EXPECT_EQ(googlePlex.data(), kSampleData);
+ EXPECT_EQ(googlePlex.size(), kSampleSize);
+ EXPECT_FALSE(googlePlex.empty());
+
+ EXPECT_EQ(googlePlex[0], 'g');
+ EXPECT_EQ(googlePlex[9], 'x');
+
+ ByteView copyBytes = googlePlex;
+ EXPECT_EQ(copyBytes.data(), googlePlex.data());
+ EXPECT_EQ(copyBytes.size(), googlePlex.size());
+
+ ByteView firstBytes(googlePlex.first(4));
+ EXPECT_EQ(firstBytes.data(), googlePlex.data());
+ EXPECT_EQ(firstBytes.size(), size_t{4});
+ EXPECT_EQ(firstBytes[0], 'g');
+ EXPECT_EQ(firstBytes[3], 'g');
+
+ ByteView lastBytes(googlePlex.last(4));
+ EXPECT_EQ(lastBytes.data(), googlePlex.data() + 6);
+ EXPECT_EQ(lastBytes.size(), size_t{4});
+ EXPECT_EQ(lastBytes[0], 'p');
+ EXPECT_EQ(lastBytes[3], 'x');
+
+ ByteView middleBytes(googlePlex.subspan(2, 4));
+ EXPECT_EQ(middleBytes.data(), googlePlex.data() + 2);
+ EXPECT_EQ(middleBytes.size(), size_t{4});
+ EXPECT_EQ(middleBytes[0], 'o');
+ EXPECT_EQ(middleBytes[3], 'e');
+}
+
+TEST(SpanTest, TestIterators) {
+ ByteView googlePlex = ByteView(kSampleData, kSampleSize);
+ size_t idx = 0;
+
+ for (const uint8_t* it = googlePlex.begin(); it != googlePlex.end(); it++) {
+ EXPECT_EQ(*it, kSampleBytes[idx]);
+ idx++;
+ }
+}
+
+TEST(SpanTest, TestRemove) {
+ ByteView googlePlex = ByteView(kSampleData, kSampleSize);
+
+ googlePlex.remove_prefix(2);
+ EXPECT_EQ(googlePlex.size(), size_t{8});
+ EXPECT_EQ(googlePlex[0], 'o');
+
+ googlePlex.remove_suffix(2);
+ EXPECT_EQ(googlePlex.size(), size_t{6});
+ EXPECT_EQ(googlePlex[5], 'l');
+}
+
+TEST(SpanTest, ConstConversions) {
+ std::array<uint8_t, kSampleSize> mutable_data;
+
+ // Pointer-and-size construction
+ Span<const uint8_t> const_span =
+ Span<const uint8_t>(kSampleData, kSampleSize);
+ Span<const uint8_t> const_span2 =
+ Span<const uint8_t>(mutable_data.data(), mutable_data.size());
+
+ Span<uint8_t> mutable_span =
+ Span<uint8_t>(mutable_data.data(), mutable_data.size());
+
+ // ILLEGAL!
+ // Span<uint8_t> mutable_span = Span<uint8_t>(kSampleData, kSampleSize);
+
+ // Copy constructors
+ Span<const uint8_t> const_span3(const_span);
+ Span<const uint8_t> const_span4(mutable_span);
+ Span<uint8_t> mutable_span2(mutable_span);
+ Span<uint8_t> mutable_span3(mutable_span);
+
+ // ILLEGAL!
+ // Span<uint8_t> mutable_span3(const_span);
+
+ // Move constructors
+ Span<const uint8_t> const_span5(std::move(const_span3));
+ Span<const uint8_t> const_span6(std::move(mutable_span2));
+ Span<uint8_t> mutable_span4(std::move(mutable_span3));
+
+ // ILLEGAL!
+ // Span<uint8_t> mutable_span5(std::move(const_span4));
+
+ // Copy assignment
+ Span<const uint8_t> const_span7;
+ const_span7 = const_span2;
+
+ Span<const uint8_t> const_span8;
+ const_span8 = mutable_span4;
+
+ // ILLEGAL!
+ // Span<uint8_t> mutable_span6;
+ // mutable_span6 = const_span5;
+
+ Span<uint8_t> mutable_span7;
+ mutable_span7 = mutable_span4;
+
+ // Move assignment
+ Span<const uint8_t> const_span9;
+ const_span9 = std::move(const_span5);
+
+ Span<const uint8_t> const_span10;
+ const_span10 = std::move(mutable_span4);
+
+ // ILLEGAL!
+ // Span<uint8_t> mutable_span8;
+ // mutable_span8 = std::move(const_span5);
+
+ Span<uint8_t> mutable_span9;
+ mutable_span9 = std::move(mutable_span4);
+
+ // Vector construction
+ std::vector<uint8_t> mutable_vector({1, 2, 3, 4});
+ Span<const uint8_t> const_span11(mutable_vector);
+ Span<uint8_t> mutable_span10(mutable_vector);
+}
+
+} // namespace openscreen
diff --git a/platform/test/byte_view_test_util.cc b/platform/test/byte_view_test_util.cc
index dddbda3..ece7227 100644
--- a/platform/test/byte_view_test_util.cc
+++ b/platform/test/byte_view_test_util.cc
@@ -7,7 +7,6 @@
#include <cstring>
#include "gtest/gtest.h"
-#include "platform/base/byte_view.h"
namespace openscreen {
namespace {
diff --git a/platform/test/byte_view_test_util.h b/platform/test/byte_view_test_util.h
index 5bd4af1..4e7b528 100644
--- a/platform/test/byte_view_test_util.h
+++ b/platform/test/byte_view_test_util.h
@@ -5,9 +5,9 @@
#ifndef PLATFORM_TEST_BYTE_VIEW_TEST_UTIL_H_
#define PLATFORM_TEST_BYTE_VIEW_TEST_UTIL_H_
-namespace openscreen {
+#include "platform/base/span.h"
-class ByteView;
+namespace openscreen {
// Asserts that `first` and `second` have the same non-zero length, and are
// views over the same bytes. (Not that they are the pointers to the same