| // Copyright 2016 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 QUICHE_HTTP2_DECODER_DECODE_BUFFER_H_ |
| #define QUICHE_HTTP2_DECODER_DECODE_BUFFER_H_ |
| |
| // DecodeBuffer provides primitives for decoding various integer types found in |
| // HTTP/2 frames. It wraps a byte array from which we can read and decode |
| // serialized HTTP/2 frames, or parts thereof. DecodeBuffer is intended only for |
| // stack allocation, where the caller is typically going to use the DecodeBuffer |
| // instance as part of decoding the entire buffer before returning to its own |
| // caller. |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <cstdint> |
| |
| #include "absl/strings/string_view.h" |
| #include "http2/platform/api/http2_logging.h" |
| #include "common/platform/api/quiche_export.h" |
| |
| namespace http2 { |
| class DecodeBufferSubset; |
| |
| class QUICHE_EXPORT_PRIVATE DecodeBuffer { |
| public: |
| DecodeBuffer(const char* buffer, size_t len) |
| : buffer_(buffer), cursor_(buffer), beyond_(buffer + len) { |
| QUICHE_DCHECK(buffer != nullptr); |
| // We assume the decode buffers will typically be modest in size (i.e. often |
| // a few KB, perhaps as high as 100KB). Let's make sure during testing that |
| // we don't go very high, with 32MB selected rather arbitrarily. |
| const size_t kMaxDecodeBufferLength = 1 << 25; |
| QUICHE_DCHECK_LE(len, kMaxDecodeBufferLength); |
| } |
| explicit DecodeBuffer(absl::string_view s) |
| : DecodeBuffer(s.data(), s.size()) {} |
| // Constructor for character arrays, typically in tests. For example: |
| // const char input[] = { 0x11 }; |
| // DecodeBuffer b(input); |
| template <size_t N> |
| explicit DecodeBuffer(const char (&buf)[N]) : DecodeBuffer(buf, N) {} |
| |
| DecodeBuffer(const DecodeBuffer&) = delete; |
| DecodeBuffer operator=(const DecodeBuffer&) = delete; |
| |
| bool Empty() const { return cursor_ >= beyond_; } |
| bool HasData() const { return cursor_ < beyond_; } |
| size_t Remaining() const { |
| QUICHE_DCHECK_LE(cursor_, beyond_); |
| return beyond_ - cursor_; |
| } |
| size_t Offset() const { return cursor_ - buffer_; } |
| size_t FullSize() const { return beyond_ - buffer_; } |
| |
| // Returns the minimum of the number of bytes remaining in this DecodeBuffer |
| // and |length|, in support of determining how much of some structure/payload |
| // is in this DecodeBuffer. |
| size_t MinLengthRemaining(size_t length) const { |
| return std::min(length, Remaining()); |
| } |
| |
| // For string decoding, returns a pointer to the next byte/char to be decoded. |
| const char* cursor() const { return cursor_; } |
| // Advances the cursor (pointer to the next byte/char to be decoded). |
| void AdvanceCursor(size_t amount) { |
| QUICHE_DCHECK_LE(amount, |
| Remaining()); // Need at least that much remaining. |
| QUICHE_DCHECK_EQ(subset_, nullptr) |
| << "Access via subset only when present."; |
| cursor_ += amount; |
| } |
| |
| // Only call methods starting "Decode" when there is enough input remaining. |
| char DecodeChar() { |
| QUICHE_DCHECK_LE(1u, Remaining()); // Need at least one byte remaining. |
| QUICHE_DCHECK_EQ(subset_, nullptr) |
| << "Access via subset only when present."; |
| return *cursor_++; |
| } |
| |
| uint8_t DecodeUInt8(); |
| uint16_t DecodeUInt16(); |
| uint32_t DecodeUInt24(); |
| |
| // For 31-bit unsigned integers, where the 32nd bit is reserved for future |
| // use (i.e. the high-bit of the first byte of the encoding); examples: |
| // the Stream Id in a frame header or the Window Size Increment in a |
| // WINDOW_UPDATE frame. |
| uint32_t DecodeUInt31(); |
| |
| uint32_t DecodeUInt32(); |
| |
| protected: |
| #ifndef NDEBUG |
| // These are part of validating during tests that there is at most one |
| // DecodeBufferSubset instance at a time for any DecodeBuffer instance. |
| void set_subset_of_base(DecodeBuffer* base, const DecodeBufferSubset* subset); |
| void clear_subset_of_base(DecodeBuffer* base, |
| const DecodeBufferSubset* subset); |
| #endif |
| |
| private: |
| #ifndef NDEBUG |
| void set_subset(const DecodeBufferSubset* subset); |
| void clear_subset(const DecodeBufferSubset* subset); |
| #endif |
| |
| // Prevent heap allocation of DecodeBuffer. |
| static void* operator new(size_t s); |
| static void* operator new[](size_t s); |
| static void operator delete(void* p); |
| static void operator delete[](void* p); |
| |
| const char* const buffer_; |
| const char* cursor_; |
| const char* const beyond_; |
| const DecodeBufferSubset* subset_ = nullptr; // Used for QUICHE_DCHECKs. |
| }; |
| |
| // DecodeBufferSubset is used when decoding a known sized chunk of data, which |
| // starts at base->cursor(), and continues for subset_len, which may be |
| // entirely in |base|, or may extend beyond it (hence the MinLengthRemaining |
| // in the constructor). |
| // There are two benefits to using DecodeBufferSubset: it ensures that the |
| // cursor of |base| is advanced when the subset's destructor runs, and it |
| // ensures that the consumer of the subset can't go beyond the subset which |
| // it is intended to decode. |
| // There must be only a single DecodeBufferSubset at a time for a base |
| // DecodeBuffer, though they can be nested (i.e. a DecodeBufferSubset's |
| // base may itself be a DecodeBufferSubset). This avoids the AdvanceCursor |
| // being called erroneously. |
| class QUICHE_EXPORT_PRIVATE DecodeBufferSubset : public DecodeBuffer { |
| public: |
| DecodeBufferSubset(DecodeBuffer* base, size_t subset_len) |
| : DecodeBuffer(base->cursor(), base->MinLengthRemaining(subset_len)), |
| base_buffer_(base) { |
| #ifndef NDEBUG |
| DebugSetup(); |
| #endif |
| } |
| |
| DecodeBufferSubset(const DecodeBufferSubset&) = delete; |
| DecodeBufferSubset operator=(const DecodeBufferSubset&) = delete; |
| |
| ~DecodeBufferSubset() { |
| size_t offset = Offset(); |
| #ifndef NDEBUG |
| DebugTearDown(); |
| #endif |
| base_buffer_->AdvanceCursor(offset); |
| } |
| |
| private: |
| DecodeBuffer* const base_buffer_; |
| #ifndef NDEBUG |
| size_t start_base_offset_; // Used for QUICHE_DCHECKs. |
| size_t max_base_offset_; // Used for QUICHE_DCHECKs. |
| |
| void DebugSetup(); |
| void DebugTearDown(); |
| #endif |
| }; |
| |
| } // namespace http2 |
| |
| #endif // QUICHE_HTTP2_DECODER_DECODE_BUFFER_H_ |