/*
 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SHARED_BUFFER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SHARED_BUFFER_H_

#include <algorithm>
#include <utility>
#include <vector>

#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/checked_iterators.h"
#include "base/containers/span.h"
#include "base/numerics/safe_conversions.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "third_party/blink/renderer/platform/wtf/wtf_export.h"

namespace blink {

// This class is designed to store and manage large amounts of data that may be
// split into multiple segments.
class WTF_EXPORT SegmentedBuffer {
 public:
  class Segment {
   public:
    Segment(size_t start_position, Vector<char>&& data)
        : start_position_(start_position), data_(std::move(data)) {}
    ~Segment() = default;
    Segment(const Segment&) = delete;
    Segment& operator=(const Segment&) = delete;
    Segment(Segment&&) = default;
    Segment& operator=(Segment&&) = default;

    size_t start_position() const { return start_position_; }
    const Vector<char>& data() const { return data_; }

   private:
    size_t start_position_;
    Vector<char> data_;
  };

  // Iterator for SegmentedBuffer contents. An Iterator will get invalid once
  // the associated SegmentedBuffer is modified (e.g., Append() is called). An
  // Iterator doesn't retain the associated container.
  class WTF_EXPORT Iterator final {
   public:
    ~Iterator() = default;

    Iterator& operator++();
    Iterator operator++(int) {
      auto temp = *this;
      ++*this;
      return temp;
    }
    bool operator==(const Iterator& that) const {
      return std::ranges::equal(value_, that.value_) && buffer_ == that.buffer_;
    }
    const base::span<const char>& operator*() const {
      DCHECK(!IsEnd());
      return value_;
    }
    const base::span<const char>* operator->() const {
      DCHECK(!IsEnd());
      return &value_;
    }

   private:
    friend class SegmentedBuffer;
    // for end()
    explicit Iterator(const SegmentedBuffer* buffer);
    // for the rest
    Iterator(Vector<Segment>::const_iterator segment_it,
             size_t offset,
             const SegmentedBuffer* buffer);

    void Init(size_t offset);
    bool IsEnd() const { return segment_it_ == buffer_->segments_.end(); }

    Vector<Segment>::const_iterator segment_it_;
    base::span<const char> value_;
    const SegmentedBuffer* buffer_;
  };

  SegmentedBuffer() = default;
  ~SegmentedBuffer() = default;
  SegmentedBuffer(const SegmentedBuffer&) = delete;
  SegmentedBuffer& operator=(const SegmentedBuffer&) = delete;
  SegmentedBuffer(SegmentedBuffer&&) = default;
  SegmentedBuffer& operator=(SegmentedBuffer&&) = default;

  size_t size() const { return size_; }

  bool empty() const { return !size(); }

  void Append(base::span<const char> data);
  void Append(base::span<const unsigned char> data) {
    Append(base::as_chars(data));
  }
  void Append(Vector<char>&& vector);

  void Clear();

  Iterator begin() const;
  Iterator cbegin() const { return begin(); }
  Iterator end() const;
  Iterator cend() const { return end(); }

  // Copies the segmented data into a contiguous buffer.  Use GetSomeData() or
  // iterators if a copy is not required, as they are cheaper.
  // Supported Ts: blink::Vector<char>, std::vector<char>.
  template <typename T>
  T CopyAs() const;

  Vector<Vector<char>> TakeData() &&;

  // Returns an iterator for the given position of bytes. Returns |cend()| if
  // |position| is greater than or equal to |size()|.
  Iterator GetIteratorAt(base::StrictNumeric<size_t> position) const {
    return GetIteratorAtInternal(position);
  }

  // Copies `buffer.size()` bytes from the beginning of the content data into
  // `buffer` as a flat buffer. Returns true on success, otherwise the content
  // of `buffer` is not guaranteed.
  [[nodiscard]] bool GetBytes(base::span<uint8_t> buffer) const;

  void GetMemoryDumpNameAndSize(String& dump_name, size_t& dump_size) const;

  // Helper for providing a contiguous view of the data.  If the SegmentedBuffer
  // is segmented, this will copy/merge all segments into a temporary buffer.
  // In general, clients should use the efficient/segmented accessors.
  class WTF_EXPORT DeprecatedFlatData {
    STACK_ALLOCATED();

   public:
    using iterator = base::CheckedContiguousIterator<const char>;

    explicit DeprecatedFlatData(const SegmentedBuffer*);

    const char* data() const { return data_; }
    size_t size() const { return buffer_->size(); }

    // Iterators, so this type meets the requirements of
    // `std::ranges::contiguous_range`.
    iterator begin() const {
      // SAFETY: The merged data has the same number of elements as the
      // underlying segmented data, so this points to at most just past the end
      // of the valid region.
      return UNSAFE_BUFFERS(iterator(data(), data() + size()));
    }
    iterator end() const {
      // SAFETY: As in `begin()` above.
      return UNSAFE_BUFFERS(iterator(data(), data() + size(), data() + size()));
    }

   private:
    const SegmentedBuffer* buffer_;
    Vector<char> flat_buffer_;
    const char* data_;
  };

 private:
  Iterator GetIteratorAtInternal(size_t position) const;

  size_t size_ = 0;
  Vector<Segment> segments_;
};

// Current CopyAs specializations.
template <>
inline Vector<char> SegmentedBuffer::CopyAs() const {
  Vector<char> buffer;
  buffer.ReserveInitialCapacity(base::checked_cast<wtf_size_t>(size_));
  for (const auto& span : *this) {
    buffer.append_range(span);
  }
  DCHECK_EQ(buffer.size(), size_);
  return buffer;
}

template <>
inline Vector<uint8_t> SegmentedBuffer::CopyAs() const {
  Vector<uint8_t> buffer;
  buffer.ReserveInitialCapacity(base::checked_cast<wtf_size_t>(size_));
  for (const auto& span : *this) {
    buffer.append_range(span);
  }
  DCHECK_EQ(buffer.size(), size_);
  return buffer;
}

template <>
inline std::vector<char> SegmentedBuffer::CopyAs() const {
  std::vector<char> buffer;
  buffer.reserve(size_);

  for (const auto& span : *this) {
    buffer.insert(buffer.end(), span.begin(), span.end());
  }
  DCHECK_EQ(buffer.size(), size_);
  return buffer;
}

template <>
inline std::vector<uint8_t> SegmentedBuffer::CopyAs() const {
  std::vector<uint8_t> buffer;
  buffer.reserve(size_);

  for (const auto& span : *this) {
    auto byte_span = base::as_bytes(span);
    buffer.insert(buffer.end(), byte_span.begin(), byte_span.end());
  }
  DCHECK_EQ(buffer.size(), size_);
  return buffer;
}

// This is a RefCounted version of the SegmentedBuffer class.
class WTF_EXPORT SharedBuffer : public SegmentedBuffer,
                                public RefCounted<SharedBuffer> {
 public:
  static scoped_refptr<SharedBuffer> Create() {
    return base::AdoptRef(new SharedBuffer);
  }

  static scoped_refptr<SharedBuffer> Create(base::span<const char> data) {
    return base::AdoptRef(new SharedBuffer(data));
  }

  static scoped_refptr<SharedBuffer> Create(
      base::span<const unsigned char> data) {
    return base::AdoptRef(new SharedBuffer(data));
  }

  static scoped_refptr<SharedBuffer> Create(SegmentedBuffer&& data) {
    return base::AdoptRef(new SharedBuffer(std::move(data)));
  }

  static scoped_refptr<SharedBuffer> Create(Vector<char>&&);

 private:
  friend class RefCounted<SharedBuffer>;
  ~SharedBuffer();

  SharedBuffer();
  explicit SharedBuffer(wtf_size_t);
  explicit SharedBuffer(base::span<const char>);
  explicit SharedBuffer(base::span<const unsigned char>);
  explicit SharedBuffer(SegmentedBuffer&&);
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_SHARED_BUFFER_H_
