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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#ifndef PARTITION_ALLOC_PARTITION_SUPERPAGE_EXTENT_ENTRY_H_
#define PARTITION_ALLOC_PARTITION_SUPERPAGE_EXTENT_ENTRY_H_

#include <cstdint>

#include "partition_alloc/address_pool_manager.h"
#include "partition_alloc/address_pool_manager_types.h"
#include "partition_alloc/partition_alloc-inl.h"
#include "partition_alloc/partition_alloc_constants.h"
#include "partition_alloc/partition_alloc_forward.h"
#include "partition_alloc/partition_dcheck_helper.h"
#include "partition_alloc/reservation_offset_table.h"

// Should not include partition_root.h, partition_bucket.h, partition_page.h.

namespace partition_alloc::internal {

// An "extent" is a span of consecutive superpages. We link the partition's next
// extent (if there is one) to the very start of a superpage's metadata area.
struct PartitionSuperPageExtentEntry {
  PartitionRoot* root;
  PartitionSuperPageExtentEntry* next;
  uint16_t number_of_consecutive_super_pages;
  uint16_t number_of_nonempty_slot_spans;

  PA_ALWAYS_INLINE void IncrementNumberOfNonemptySlotSpans() {
    DCheckNumberOfPartitionPagesInSuperPagePayload(
        this, root, number_of_nonempty_slot_spans);
    ++number_of_nonempty_slot_spans;
  }

  PA_ALWAYS_INLINE void DecrementNumberOfNonemptySlotSpans() {
    PA_DCHECK(number_of_nonempty_slot_spans);
    --number_of_nonempty_slot_spans;
  }
};

static_assert(
    sizeof(PartitionSuperPageExtentEntry) <= kPageMetadataSize,
    "PartitionSuperPageExtentEntry must be able to fit in a metadata slot");
static_assert(kMaxSuperPagesInPool / kSuperPageSize <=
                  std::numeric_limits<
                      decltype(PartitionSuperPageExtentEntry ::
                                   number_of_consecutive_super_pages)>::max(),
              "number_of_consecutive_super_pages must be big enough");

// Returns the base of the first super page in the range of consecutive super
// pages.
//
// CAUTION! |extent| must point to the extent of the first super page in the
// range of consecutive super pages.
PA_ALWAYS_INLINE uintptr_t
SuperPagesBeginFromExtent(const PartitionSuperPageExtentEntry* extent) {
  PA_DCHECK(0 < extent->number_of_consecutive_super_pages);
  uintptr_t extent_as_uintptr = PartitionMetadataPageToSuperPage(
      reinterpret_cast<uintptr_t>(extent), GetMetadataOffset(extent->root));
  PA_DCHECK(ReservationOffsetTable::Get(extent_as_uintptr)
                .IsManagedByNormalBuckets(extent_as_uintptr));
  return base::bits::AlignDown(extent_as_uintptr, kSuperPageAlignment);
}

// Returns the end of the last super page in the range of consecutive super
// pages.
//
// CAUTION! |extent| must point to the extent of the first super page in the
// range of consecutive super pages.
PA_ALWAYS_INLINE uintptr_t
SuperPagesEndFromExtent(const PartitionSuperPageExtentEntry* extent) {
  return SuperPagesBeginFromExtent(extent) +
         (extent->number_of_consecutive_super_pages * kSuperPageSize);
}

}  // namespace partition_alloc::internal

#endif  // PARTITION_ALLOC_PARTITION_SUPERPAGE_EXTENT_ENTRY_H_
