// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BSSL_DER_INPUT_H_
#define BSSL_DER_INPUT_H_

#include <stddef.h>
#include <stdint.h>

#include <string>
#include <string_view>

#include <openssl/base.h>
#include <openssl/span.h>

namespace bssl::der {

// An opaque class that represents a fixed buffer of data of a fixed length,
// to be used as an input to other operations. An Input object does not own
// the data it references, so callers are responsible for making sure that
// the data outlives the Input object and any other associated objects.
//
// All data access for an Input should be done through the ByteReader class.
// This class and associated classes are designed with safety in mind to make it
// difficult to read memory outside of an Input. ByteReader provides a simple
// API for reading through the Input sequentially. For more complicated uses,
// multiple instances of a ByteReader for a particular Input can be created.
//
// TODO(crbug.com/boringssl/661): This class will gradually be replaced with
// bssl::Span<const uint8_t>. Avoid relying on APIs that are not part of
// bssl::Span.
class OPENSSL_EXPORT Input {
 public:
  // Creates an empty Input, one from which no data can be read.
  constexpr Input() = default;

  // Creates an Input from a span. The constructed Input is only valid as long
  // as |data| points to live memory. If constructed from, say, a
  // |std::vector<uint8_t>|, mutating the vector will invalidate the Input.
  constexpr Input(bssl::Span<const uint8_t> data) : data_(data) {}

  // Creates an Input from the given |data| and |len|.
  constexpr explicit Input(const uint8_t *data, size_t len)
      : data_(MakeConstSpan(data, len)) {}

  // Deprecated: Use StringAsBytes.
  //
  // Creates an Input from a std::string_view. The constructed Input is only
  // valid as long as |data| points to live memory. If constructed from, say, a
  // |std::string|, mutating the vector will invalidate the Input.
  explicit Input(std::string_view str)
      : data_(MakeConstSpan(reinterpret_cast<const uint8_t *>(str.data()),
                            str.size())) {}

  // The following APIs have the same semantics as in |bssl::Span|.
  constexpr Span<const uint8_t>::iterator begin() const {
    return data_.begin();
  }
  constexpr Span<const uint8_t>::iterator end() const { return data_.end(); }
  constexpr const uint8_t *data() const { return data_.data(); }
  constexpr size_t size() const { return data_.size(); }
  constexpr bool empty() const { return data_.empty(); }
  constexpr uint8_t operator[](size_t idx) const { return data_[idx]; }
  constexpr uint8_t front() const { return data_.front(); }
  constexpr uint8_t back() const { return data_.back(); }
  constexpr Input subspan(size_t pos = 0,
                          size_t len = Span<const uint8_t>::npos) const {
    return Input(data_.subspan(pos, len));
  }
  constexpr Input first(size_t len) const { return Input(data_.first(len)); }
  constexpr Input last(size_t len) const { return Input(data_.last(len)); }

  // Deprecated: use BytesAsStringView and convert to std::string.
  //
  // Returns a copy of the data represented by this object as a std::string.
  std::string AsString() const;

  // Deprecated: Use ByteAsString. 
  //
  // Returns a std::string_view pointing to the same data as the Input. The
  // resulting string_view must not outlive the data that was used to construct
  // this Input.
  std::string_view AsStringView() const { return BytesAsStringView(data_); }

  // Deprecated: This class implicitly converts to bssl::Span<const uint8_t>.
  //
  // Returns a span pointing to the same data as the Input. The resulting span
  // must not outlive the data that was used to construct this Input.
  Span<const uint8_t> AsSpan() const { return *this; }

  // Deprecated: Use size() instead.
  constexpr size_t Length() const { return size(); }

  // Deprecated: Use data() instead.
  constexpr const uint8_t *UnsafeData() const { return data(); }

 private:
  // TODO(crbug.com/770501): Replace this type with span altogether.
  Span<const uint8_t> data_;
};

// Return true if |lhs|'s data and |rhs|'s data are byte-wise equal.
OPENSSL_EXPORT bool operator==(Input lhs, Input rhs);

// Return true if |lhs|'s data and |rhs|'s data are not byte-wise equal.
OPENSSL_EXPORT bool operator!=(Input lhs, Input rhs);

// Returns true if |lhs|'s data is lexicographically less than |rhs|'s data.
OPENSSL_EXPORT constexpr bool operator<(Input lhs, Input rhs) {
  // This is `std::lexicographical_compare`, but that's not `constexpr` until
  // C++-20.
  auto *it1 = lhs.data();
  auto *it2 = rhs.data();
  const auto *end1 = lhs.data() + lhs.size();
  const auto *end2 = rhs.data() + rhs.size();
  for (; it1 != end1 && it2 != end2; ++it1, ++it2) {
    if (*it1 < *it2) {
      return true;
    } else if (*it2 < *it1) {
      return false;
    }
  }

  return it2 != end2;
}

// This class provides ways to read data from an Input in a bounds-checked way.
// The ByteReader is designed to read through the input sequentially. Once a
// byte has been read with a ByteReader, the caller can't go back and re-read
// that byte with the same reader. Of course, the caller can create multiple
// ByteReaders for the same input (or copy an existing ByteReader).
//
// For something simple like a single byte lookahead, the easiest way to do
// that is to copy the ByteReader and call ReadByte() on the copy - the original
// ByteReader will be unaffected and the peeked byte will be read through
// ReadByte(). For other read patterns, it can be useful to mark where one is
// in a ByteReader to be able to return to that spot.
//
// Some operations using Mark can also be done by creating a copy of the
// ByteReader. By using a Mark instead, you use less memory, but more
// importantly, you end up with an immutable object that matches the semantics
// of what is intended.
class OPENSSL_EXPORT ByteReader {
 public:
  // Creates a ByteReader to read the data represented by an Input.
  explicit ByteReader(Input in);

  // Reads a single byte from the input source, putting the byte read in
  // |*byte_p|. If a byte cannot be read from the input (because there is
  // no input left), then this method returns false.
  [[nodiscard]] bool ReadByte(uint8_t *out);

  // Reads |len| bytes from the input source, and initializes an Input to
  // point to that data. If there aren't enough bytes left in the input source,
  // then this method returns false.
  [[nodiscard]] bool ReadBytes(size_t len, Input *out);

  // Returns how many bytes are left to read.
  size_t BytesLeft() const { return data_.size(); }

  // Returns whether there is any more data to be read.
  bool HasMore();

 private:
  void Advance(size_t len);

  bssl::Span<const uint8_t> data_;
};

}  // namespace bssl::der

#endif  // BSSL_DER_INPUT_H_
