#ifndef MEMPROF_DATA_INC
#define MEMPROF_DATA_INC
/*===-- MemProfData.inc - MemProf profiling runtime structures -*- 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
|*
\*===----------------------------------------------------------------------===*/
/*
 * This is the main file that defines all the data structure, signature,
 * constant literals that are shared across profiling runtime library,
 * and host tools (reader/writer).
 *
 * This file has two identical copies. The primary copy lives in LLVM and
 * the other one sits in compiler-rt/include/profile directory. To make changes
 * in this file, first modify the primary copy and copy it over to compiler-rt.
 * Testing of any change in this file can start only after the two copies are
 * synced up.
 *
\*===----------------------------------------------------------------------===*/
#include <string.h>

#ifdef _MSC_VER
#define PACKED(...) __pragma(pack(push,1)) __VA_ARGS__ __pragma(pack(pop))
#else
#define PACKED(...) __VA_ARGS__ __attribute__((__packed__))
#endif

// A 64-bit magic number to uniquely identify the raw binary memprof profile file.
#define MEMPROF_RAW_MAGIC_64                                                                        \
  ((uint64_t)255 << 56 | (uint64_t)'m' << 48 | (uint64_t)'p' << 40 | (uint64_t)'r' << 32 |          \
   (uint64_t)'o' << 24 | (uint64_t)'f' << 16 | (uint64_t)'r' << 8 | (uint64_t)129)

// The version number of the raw binary format.
#define MEMPROF_RAW_VERSION 5ULL

// Currently supported versions.
#define MEMPROF_RAW_SUPPORTED_VERSIONS {3ULL, 4ULL, 5ULL}

#define MEMPROF_V3_MIB_SIZE 132ULL;

#define MEMPROF_BUILDID_MAX_SIZE 32ULL

namespace llvm {
namespace memprof {
// A struct describing the header used for the raw binary memprof profile format.
PACKED(struct Header {
  uint64_t Magic;
  uint64_t Version;
  uint64_t TotalSize;
  uint64_t SegmentOffset;
  uint64_t MIBOffset;
  uint64_t StackOffset;
});

// A struct describing the information necessary to describe a /proc/maps
// segment entry for a particular binary/library identified by its build id.
PACKED(struct SegmentEntry {
  uint64_t Start;
  uint64_t End;
  uint64_t Offset;
  uint64_t BuildIdSize;
  uint8_t BuildId[MEMPROF_BUILDID_MAX_SIZE] = {0};

  // This constructor is only used in tests so don't set the BuildId.
  SegmentEntry(uint64_t S, uint64_t E, uint64_t O)
      : Start(S), End(E), Offset(O), BuildIdSize(0) {}

  SegmentEntry(const SegmentEntry& S) {
    Start = S.Start;
    End = S.End;
    Offset = S.Offset;
    BuildIdSize = S.BuildIdSize;
    memcpy(BuildId, S.BuildId, S.BuildIdSize);
  }

  SegmentEntry& operator=(const SegmentEntry& S) {
    Start = S.Start;
    End = S.End;
    Offset = S.Offset;
    BuildIdSize = S.BuildIdSize;
    memcpy(BuildId, S.BuildId, S.BuildIdSize);
    return *this;
  }

  bool operator==(const SegmentEntry& S) const {
    return Start == S.Start && End == S.End && Offset == S.Offset &&
           BuildIdSize == S.BuildIdSize &&
           memcmp(BuildId, S.BuildId, S.BuildIdSize) == 0;
  }
});

// Packed struct definition for MSVC. We can't use the PACKED macro defined in
// MemProfData.inc since it would mean we are embedding a directive (the
// #include for MIBEntryDef) into the macros which is undefined behaviour.
#ifdef _MSC_VER
__pragma(pack(push,1))
#endif

// A struct representing the heap allocation characteristics of a particular
// runtime context. This struct is shared between the compiler-rt runtime and
// the raw profile reader. The indexed format uses a separate, self-describing
// backwards compatible format.
struct MemInfoBlock{

#define MIBEntryDef(NameTag, Name, Type) Type Name;
#include "MIBEntryDef.inc"
#undef MIBEntryDef

bool operator==(const MemInfoBlock& Other) const {
  bool IsEqual = true;
#define MIBEntryDef(NameTag, Name, Type) \
  IsEqual = (IsEqual && Name == Other.Name);
#include "MIBEntryDef.inc"
#undef MIBEntryDef
  return IsEqual;
}

MemInfoBlock() {
#define MIBEntryDef(NameTag, Name, Type) Name = Type();
#include "MIBEntryDef.inc"
#undef MIBEntryDef
}

MemInfoBlock(uint32_t Size, uint64_t AccessCount, uint32_t AllocTs,
             uint32_t DeallocTs, uint32_t AllocCpu, uint32_t DeallocCpu,
             uintptr_t Histogram, uint32_t HistogramSize)
    : MemInfoBlock() {
  AllocCount = 1U;
  TotalAccessCount = AccessCount;
  MinAccessCount = AccessCount;
  MaxAccessCount = AccessCount;
  TotalSize = Size;
  MinSize = Size;
  MaxSize = Size;
  AllocTimestamp = AllocTs;
  DeallocTimestamp = DeallocTs;
  TotalLifetime = DeallocTimestamp - AllocTimestamp;
  MinLifetime = TotalLifetime;
  MaxLifetime = TotalLifetime;
  // Access density is accesses per byte. Multiply by 100 to include the
  // fractional part.
  TotalAccessDensity = AccessCount * 100 / Size;
  MinAccessDensity = TotalAccessDensity;
  MaxAccessDensity = TotalAccessDensity;
  // Lifetime access density is the access density per second of lifetime.
  // Multiply by 1000 to convert denominator lifetime to seconds (using a
  // minimum lifetime of 1ms to avoid divide by 0. Do the multiplication first
  // to reduce truncations to 0.
  TotalLifetimeAccessDensity =
      TotalAccessDensity * 1000 / (TotalLifetime ? TotalLifetime : 1);
  MinLifetimeAccessDensity = TotalLifetimeAccessDensity;
  MaxLifetimeAccessDensity = TotalLifetimeAccessDensity;
  AllocCpuId = AllocCpu;
  DeallocCpuId = DeallocCpu;
  NumMigratedCpu = AllocCpuId != DeallocCpuId;
  AccessHistogramSize = HistogramSize;
  AccessHistogram = Histogram;
}

void Merge(const MemInfoBlock &newMIB) {
  AllocCount += newMIB.AllocCount;

  TotalAccessCount += newMIB.TotalAccessCount;
  MinAccessCount = newMIB.MinAccessCount < MinAccessCount ? newMIB.MinAccessCount : MinAccessCount;
  MaxAccessCount = newMIB.MaxAccessCount > MaxAccessCount ? newMIB.MaxAccessCount : MaxAccessCount;

  TotalSize += newMIB.TotalSize;
  MinSize = newMIB.MinSize < MinSize ? newMIB.MinSize : MinSize;
  MaxSize = newMIB.MaxSize > MaxSize ? newMIB.MaxSize : MaxSize;

  TotalLifetime += newMIB.TotalLifetime;
  MinLifetime = newMIB.MinLifetime < MinLifetime ? newMIB.MinLifetime : MinLifetime;
  MaxLifetime = newMIB.MaxLifetime > MaxLifetime ? newMIB.MaxLifetime : MaxLifetime;

  TotalAccessDensity += newMIB.TotalAccessDensity;
  MinAccessDensity = newMIB.MinAccessDensity < MinAccessDensity
                         ? newMIB.MinAccessDensity
                         : MinAccessDensity;
  MaxAccessDensity = newMIB.MaxAccessDensity > MaxAccessDensity
                         ? newMIB.MaxAccessDensity
                         : MaxAccessDensity;

  TotalLifetimeAccessDensity += newMIB.TotalLifetimeAccessDensity;
  MinLifetimeAccessDensity =
      newMIB.MinLifetimeAccessDensity < MinLifetimeAccessDensity
          ? newMIB.MinLifetimeAccessDensity
          : MinLifetimeAccessDensity;
  MaxLifetimeAccessDensity =
      newMIB.MaxLifetimeAccessDensity > MaxLifetimeAccessDensity
          ? newMIB.MaxLifetimeAccessDensity
          : MaxLifetimeAccessDensity;

  // We know newMIB was deallocated later, so just need to check if it was
  // allocated before last one deallocated.
  NumLifetimeOverlaps += newMIB.AllocTimestamp < DeallocTimestamp;
  AllocTimestamp = newMIB.AllocTimestamp;
  DeallocTimestamp = newMIB.DeallocTimestamp;

  NumSameAllocCpu += AllocCpuId == newMIB.AllocCpuId;
  NumSameDeallocCpu += DeallocCpuId == newMIB.DeallocCpuId;
  AllocCpuId = newMIB.AllocCpuId;
  DeallocCpuId = newMIB.DeallocCpuId;

  // For merging histograms, we always keep the longer histogram, and add
  // values of shorter histogram to larger one.
  uintptr_t ShorterHistogram;
  uint32_t ShorterHistogramSize;
  if (newMIB.AccessHistogramSize > AccessHistogramSize) {
    ShorterHistogram = AccessHistogram;
    ShorterHistogramSize = AccessHistogramSize;
    // Swap histogram of current to larger histogram
    AccessHistogram = newMIB.AccessHistogram;
    AccessHistogramSize = newMIB.AccessHistogramSize;
  } else {
    ShorterHistogram = newMIB.AccessHistogram;
    ShorterHistogramSize = newMIB.AccessHistogramSize;
  }
  for (size_t i = 0; i < ShorterHistogramSize; ++i) {
    ((uint64_t *)AccessHistogram)[i] += ((uint64_t *)ShorterHistogram)[i];
  }
}

#ifdef _MSC_VER
} __pragma(pack(pop));
#else
} __attribute__((__packed__));
#endif

constexpr int MantissaBits = 12;
constexpr int ExponentBits = 4;
constexpr uint16_t MaxMantissa = (1U << MantissaBits) - 1;
constexpr uint16_t MaxExponent = (1U << ExponentBits) - 1;
constexpr uint64_t MaxRepresentableValue = static_cast<uint64_t>(MaxMantissa)
                                           << MaxExponent;

// Encodes a 64-bit unsigned integer into a 16-bit scaled integer format.
inline uint16_t encodeHistogramCount(uint64_t Count) {
  if (Count == 0)
    return 0;

  if (Count > MaxRepresentableValue)
    Count = MaxRepresentableValue;

  if (Count <= MaxMantissa)
    return Count;

  uint64_t M = Count;
  uint16_t E = 0;
  while (M > MaxMantissa) {
    M = (M + 1) >> 1;
    E++;
  }
  return (E << MantissaBits) | static_cast<uint16_t>(M);
}

// Decodes a 16-bit scaled integer and returns the
// decoded 64-bit unsigned integer.
inline uint64_t decodeHistogramCount(uint16_t EncodedValue) {
  const uint16_t E = EncodedValue >> MantissaBits;
  const uint16_t M = EncodedValue & MaxMantissa;
  return static_cast<uint64_t>(M) << E;
}

} // namespace memprof
} // namespace llvm

#endif
