blob: b87648886ebd40fd77b025e16ca2342b94787392 [file] [log] [blame]
/*
* 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_