//===-- scudo_allocator.h ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// Header for scudo_allocator.cpp.
///
//===----------------------------------------------------------------------===//

#ifndef SCUDO_ALLOCATOR_H_
#define SCUDO_ALLOCATOR_H_

#include "scudo_platform.h"

namespace __scudo {

enum AllocType : u8 {
  FromMalloc    = 0,  // Memory block came from malloc, realloc, calloc, etc.
  FromNew       = 1,  // Memory block came from operator new.
  FromNewArray  = 2,  // Memory block came from operator new [].
  FromMemalign  = 3,  // Memory block came from memalign, posix_memalign, etc.
};

enum ChunkState : u8 {
  ChunkAvailable  = 0,
  ChunkAllocated  = 1,
  ChunkQuarantine = 2
};

// Our header requires 64 bits of storage. Having the offset saves us from
// using functions such as GetBlockBegin, that is fairly costly. Our first
// implementation used the MetaData as well, which offers the advantage of
// being stored away from the chunk itself, but accessing it was costly as
// well. The header will be atomically loaded and stored.
typedef u64 PackedHeader;
struct UnpackedHeader {
  u64 Checksum          : 16;
  u64 ClassId           : 8;
  u64 SizeOrUnusedBytes : 20;  // Size for Primary backed allocations, amount of
                               // unused bytes in the chunk for Secondary ones.
  u64 State             : 2;   // available, allocated, or quarantined
  u64 AllocType         : 2;   // malloc, new, new[], or memalign
  u64 Offset            : 16;  // Offset from the beginning of the backend
                               // allocation to the beginning of the chunk
                               // itself, in multiples of MinAlignment. See
                               // comment about its maximum value and in init().
};

typedef atomic_uint64_t AtomicPackedHeader;
COMPILER_CHECK(sizeof(UnpackedHeader) == sizeof(PackedHeader));

// Minimum alignment of 8 bytes for 32-bit, 16 for 64-bit
const uptr MinAlignmentLog = FIRST_32_SECOND_64(3, 4);
const uptr MaxAlignmentLog = 24;  // 16 MB
const uptr MinAlignment = 1 << MinAlignmentLog;
const uptr MaxAlignment = 1 << MaxAlignmentLog;

// constexpr version of __sanitizer::RoundUp without the extraneous CHECK.
// This way we can use it in constexpr variables and functions declarations.
constexpr uptr RoundUpTo(uptr Size, uptr Boundary) {
  return (Size + Boundary - 1) & ~(Boundary - 1);
}

namespace Chunk {
  constexpr uptr getHeaderSize() {
    return RoundUpTo(sizeof(PackedHeader), MinAlignment);
  }
}

#if SANITIZER_CAN_USE_ALLOCATOR64
const uptr AllocatorSpace = ~0ULL;
struct AP64 {
  static const uptr kSpaceBeg = AllocatorSpace;
  static const uptr kSpaceSize = AllocatorSize;
  static const uptr kMetadataSize = 0;
  typedef __scudo::SizeClassMap SizeClassMap;
  typedef NoOpMapUnmapCallback MapUnmapCallback;
  static const uptr kFlags =
      SizeClassAllocator64FlagMasks::kRandomShuffleChunks;
  using AddressSpaceView = LocalAddressSpaceView;
};
typedef SizeClassAllocator64<AP64> PrimaryT;
#else
struct AP32 {
  static const uptr kSpaceBeg = 0;
  static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
  static const uptr kMetadataSize = 0;
  typedef __scudo::SizeClassMap SizeClassMap;
  static const uptr kRegionSizeLog = RegionSizeLog;
  using AddressSpaceView = LocalAddressSpaceView;
  typedef NoOpMapUnmapCallback MapUnmapCallback;
  static const uptr kFlags =
      SizeClassAllocator32FlagMasks::kRandomShuffleChunks |
      SizeClassAllocator32FlagMasks::kUseSeparateSizeClassForBatch;
};
typedef SizeClassAllocator32<AP32> PrimaryT;
#endif  // SANITIZER_CAN_USE_ALLOCATOR64

#include "scudo_allocator_secondary.h"

typedef LargeMmapAllocator SecondaryT;

#include "scudo_allocator_combined.h"

typedef CombinedAllocator BackendT;
typedef CombinedAllocator::AllocatorCache AllocatorCacheT;

void initScudo();

void *scudoAllocate(uptr Size, uptr Alignment, AllocType Type);
void scudoDeallocate(void *Ptr, uptr Size, uptr Alignment, AllocType Type);
void *scudoRealloc(void *Ptr, uptr Size);
void *scudoCalloc(uptr NMemB, uptr Size);
void *scudoValloc(uptr Size);
void *scudoPvalloc(uptr Size);
int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size);
void *scudoAlignedAlloc(uptr Alignment, uptr Size);
uptr scudoMallocUsableSize(void *Ptr);

}  // namespace __scudo

#endif  // SCUDO_ALLOCATOR_H_
