/*
 * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2020 SAP SE. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef GTEST_METASPACE_METASPACEGTESTCOMMON_HPP
#define GTEST_METASPACE_METASPACEGTESTCOMMON_HPP

#include "memory/allocation.hpp"
#include "runtime/os.hpp"
#include "utilities/globalDefinitions.hpp"
#include "unittest.hpp"

/////////////////////////////////////////////////////////////////////
// A little mockup to mimick and test the CommitMask in various tests

class TestMap {
  const size_t _len;
  char* _arr;
public:
  TestMap(size_t len) : _len(len), _arr(nullptr) {
    _arr = NEW_C_HEAP_ARRAY(char, len, mtInternal);
    memset(_arr, 0, _len);
  }
  ~TestMap() { FREE_C_HEAP_ARRAY(char, _arr); }

  int get_num_set(size_t from, size_t to) const {
    int result = 0;
    for(size_t i = from; i < to; i++) {
      if (_arr[i] > 0) {
        result++;
      }
    }
    return result;
  }

  size_t get_num_set() const { return get_num_set(0, _len); }

  void set_range(size_t from, size_t to) {
    memset(_arr + from, 1, to - from);
  }

  void clear_range(size_t from, size_t to) {
    memset(_arr + from, 0, to - from);
  }

  bool at(size_t pos) const {
    return _arr[pos] == 1;
  }

};

///////////////////////////////////////////////////////////
// Helper class for generating random allocation sizes
class RandSizeGenerator {
  const size_t _min; // [
  const size_t _max; // )
  const float _outlier_chance; // 0.0 -- 1.0
  const size_t _outlier_min; // [
  const size_t _outlier_max; // )
public:
  RandSizeGenerator(size_t min, size_t max) :
    _min(min),
    _max(max),
    _outlier_chance(0.0),
    _outlier_min(min),
    _outlier_max(max)
  {}

  RandSizeGenerator(size_t min, size_t max, float outlier_chance, size_t outlier_min, size_t outlier_max) :
    _min(min),
    _max(max),
    _outlier_chance(outlier_chance),
    _outlier_min(outlier_min),
    _outlier_max(outlier_max)
  {}

  size_t min() const { return _min; }
  size_t max() const { return _max; }

  size_t get() const {
    size_t l1 = _min;
    size_t l2 = _max;
    int r = os::random() % 1000;
    if ((float)r < _outlier_chance * 1000.0) {
      l1 = _outlier_min;
      l2 = _outlier_max;
    }
    const size_t d = l2 - l1;
    return l1 + (os::random() % d);
  }

}; // end RandSizeGenerator

size_t get_random_size(size_t min, size_t max);

///////////////////////////////////////////////////////////
// Function to test-access a memory range

void zap_range(MetaWord* p, size_t word_size);

// "fill_range_with_pattern" fills a range of heap words with pointers to itself.
//
// The idea is to fill a memory range with a pattern which is both marked clearly to the caller
// and cannot be moved without becoming invalid.
//
// The filled range can be checked with check_range_for_pattern. One also can only check
// a sub range of the original range.
void fill_range_with_pattern(MetaWord* p, uintx pattern, size_t word_size);
void check_range_for_pattern(const MetaWord* p, uintx pattern, size_t word_size);

// Writes a uniqe pattern to p
void mark_address(MetaWord* p, uintx pattern);
// checks pattern at address
void check_marked_address(const MetaWord* p, uintx pattern);

// Similar to fill_range_with_pattern, but only marks start and end. This is optimized for cases
// where fill_range_with_pattern just is too slow.
// Use check_marked_range to check the range. In contrast to check_range_for_pattern, only the original
// range can be checked.
void mark_range(MetaWord* p, uintx pattern, size_t word_size);
void check_marked_range(const MetaWord* p, uintx pattern, size_t word_size);

void mark_range(MetaWord* p, size_t word_size);
void check_marked_range(const MetaWord* p, size_t word_size);

//////////////////////////////////////////////////////////
// Some helpers to avoid typing out those annoying casts for nullptr

#define ASSERT_NOT_NULL(ptr)      ASSERT_NE(nullptr, (void*)ptr)
#define ASSERT_NULL(ptr)          ASSERT_EQ(nullptr, (void*)ptr)
#define EXPECT_NOT_NULL(ptr)      EXPECT_NE(nullptr, (void*)ptr)
#define EXPECT_NULL(ptr)          EXPECT_EQ(nullptr, (void*)ptr)

#define ASSERT_0(v)               ASSERT_EQ((intptr_t)0, (intptr_t)v)
#define ASSERT_NOT_0(v)           ASSERT_NE((intptr_t)0, (intptr_t)v)
#define EXPECT_0(v)               EXPECT_EQ((intptr_t)0, (intptr_t)v)
#define EXPECT_NOT_0(v)           EXPECT_NE((intptr_t)0, (intptr_t)v)
#define ASSERT_GT0(v)             ASSERT_GT((intptr_t)v, (intptr_t)0)
#define EXPECT_GT0(v)             EXPECT_GT((intptr_t)v, (intptr_t)0)

//////////////////////////////////////////////////////////
// logging

// Define "LOG_PLEASE" to switch on logging for a particular test before inclusion of this header.
#ifdef LOG_PLEASE
  #define LOG(...) { printf(__VA_ARGS__); printf("\n"); fflush(stdout); }
#else
  #define LOG(...)
#endif

//////////////////////////////////////////////////////////
// Helper

size_t get_workingset_size();

// A simple preallocated buffer used to "feed" someone.
// Mimicks chunk retirement leftover blocks.
class FeederBuffer {

  MetaWord* _buf;

  // Buffer capacity in size of words.
  const size_t _cap;

  // Used words.
  size_t _used;

public:

  FeederBuffer(size_t size) : _buf(nullptr), _cap(size), _used(0) {
    _buf = NEW_C_HEAP_ARRAY(MetaWord, _cap, mtInternal);
  }

  ~FeederBuffer() {
    FREE_C_HEAP_ARRAY(MetaWord, _buf);
  }

  MetaWord* get(size_t word_size) {
    if (_used + word_size > _cap) {
      return nullptr;
    }
    MetaWord* p = _buf + _used;
    _used += word_size;
    return p;
  }

  bool is_valid_pointer(MetaWord* p) const {
    return p >= _buf && p < _buf + _used;
  }

  bool is_valid_range(MetaWord* p, size_t word_size) const {
    return is_valid_pointer(p) &&
           word_size > 0 ? is_valid_pointer(p + word_size - 1) : true;
  }

};

#endif // GTEST_METASPACE_METASPACEGTESTCOMMON_HPP