// 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 "quiche/common/platform/api/quiche_export.h"
#include "quiche/common/platform/api/quiche_logging.h"

namespace http2 {
class DecodeBufferSubset;

class QUICHE_EXPORT DecodeBuffer {
 public:
  // 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. This is exposed
  // to support testing.
  static constexpr size_t kMaxDecodeBufferLength = 1 << 25;

  DecodeBuffer(const char* buffer, size_t len)
      : buffer_(buffer), cursor_(buffer), beyond_(buffer + len) {
    QUICHE_DCHECK(buffer != nullptr);
    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 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_
