/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SRC_TRACE_PROCESSOR_CORE_UTIL_SLAB_H_
#define SRC_TRACE_PROCESSOR_CORE_UTIL_SLAB_H_

#include <cstddef>
#include <cstdint>
#include <type_traits>

#include "perfetto/ext/base/utils.h"
#include "perfetto/public/compiler.h"

namespace perfetto::trace_processor::core {

// A memory-aligned contiguous block of trivially constructible and destructible
// elements. Basically just a thin wrapper around a std::unique_ptr and a size
// but with enforced alignment and additional compile-time checks.
//
// This class enforces several important constraints:
// - Elements must be trivially constructible and destructible
//
// Usage example:
//   auto slab = Slab<float>::Alloc(1024);  // Allocates space for 1024 floats
//   for (size_t i = 0; i < slab.size(); ++i) {
//     slab[i] = static_cast<float>(i);
//   }
template <typename T>
class Slab {
 public:
  static_assert(std::is_trivially_constructible_v<T>,
                "Slab elements must be trivially constructible");
  static_assert(std::is_trivially_destructible_v<T>,
                "Slab elements must be trivially destructible");

  using value_type = T;
  using const_iterator = const T*;

  // Default constructor creates an empty slab.
  Slab() = default;

  // Move operations are supported.
  constexpr Slab(Slab&&) = default;
  constexpr Slab& operator=(Slab&&) = default;

  // Copy operations are deleted to avoid accidental copies.
  Slab(const Slab&) = delete;
  Slab& operator=(const Slab&) = delete;

  // Allocates a new slab with the specified number of elements.
  //
  // size: Number of elements to allocate space for.
  // Returns a new Slab object with the requested capacity.
  static Slab<T> Alloc(uint64_t size) {
    return Slab(
        static_cast<T*>(base::AlignedAlloc(alignof(T), size * sizeof(T))),
        size);
  }

  // Returns a pointer to the underlying data.
  PERFETTO_ALWAYS_INLINE const T* data() const { return data_.get(); }
  PERFETTO_ALWAYS_INLINE T* data() { return data_.get(); }

  // Returns the number of elements in the slab.
  PERFETTO_ALWAYS_INLINE uint64_t size() const { return size_; }

  // Returns iterators for range-based for loops.
  PERFETTO_ALWAYS_INLINE T* begin() const { return data_.get(); }
  PERFETTO_ALWAYS_INLINE T* end() const { return data_.get() + size_; }

  // Provides indexed access to elements.
  PERFETTO_ALWAYS_INLINE T& operator[](uint64_t i) const {
    return data_.get()[i];
  }

 private:
  // Constructor used by Alloc.
  Slab(T* data, uint64_t size) : data_(data), size_(size) {}

  // Aligned unique pointer that holds the allocated memory.
  base::AlignedUniquePtr<T> data_;

  // Number of elements in the slab.
  uint64_t size_ = 0;
};

}  // namespace perfetto::trace_processor::core

#endif  // SRC_TRACE_PROCESSOR_CORE_UTIL_SLAB_H_
