/* -*- c++ -*- ----------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
   https://www.lammps.org/, Sandia National Laboratories
   LAMMPS development team: developers@lammps.org

   Copyright (2003) Sandia Corporation.  Under the terms of Contract
   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
   certain rights in this software.  This software is distributed under
   the GNU General Public License.

   See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */

/* ----------------------------------------------------------------------
   templated class for storing chunks of datums in pages
------------------------------------------------------------------------- */

#ifndef LAMMPS_MY_PAGE_H
#define LAMMPS_MY_PAGE_H

#include "lmptype.h"

namespace LAMMPS_NS {

struct HyperOneCoeff {
  double biascoeff;
  tagint tag;
};

template <class T> class MyPage {
 public:
  int ndatum;    // total # of stored datums
  int nchunk;    // total # of stored chunks
  MyPage();
  virtual ~MyPage();

  int init(int user_maxchunk = 1, int user_pagesize = 1024, int user_pagedelta = 1);

  T *get(int n = 1);

  /** Get pointer to location that can store *maxchunk* items.
   *
   * This will return the same pointer as the previous call to
   * this function unless vgot() is called afterwards to record
   * how many items of the chunk were actually used.
   *
   * \return pointer to chunk of memory or null pointer if run out of memory */

  T *vget()
  {
    if (index + maxchunk <= pagesize) return &page[index];
    ipage++;
    if (ipage == npage) {
      allocate();
      if (errorflag) return nullptr;
    }
    page = pages[ipage];
    index = 0;
    return &page[index];
  }

  /** Mark *N* items as used of the chunk reserved with a preceding call to vget().
   *
   * This will advance the internal pointer inside the current memory page.
   * It is not necessary to call this function for *N* = 0, that is the reserved
   * storage was not used.  A following call to vget() will then reserve the
   * same location again.  It is an error if *N* > *maxchunk*.
   *
   * \param  n  Number of items used in previously reserved chunk */

  void vgot(int n)
  {
    if (n > maxchunk) errorflag = 1;
    ndatum += n;
    nchunk++;
    index += n;
  }

  void reset();

  /** Return total size of allocated pages
   *
   * \return total storage used in bytes */

  double size() const { return (double) npage * pagesize * sizeof(T); }

  /** Return error status
   *
   * \return 0 if no error, 1 requested chunk size > maxchunk, 2 if malloc failed */

  int status() const { return errorflag; }

 private:
  T **pages;    // list of allocated pages
  T *page;      // ptr to current page
  int npage;    // # of allocated pages
  int ipage;    // index of current page
  int index;    // current index on current page

  int maxchunk;     // max # of datums in one requested chunk
  int pagesize;     // # of datums in one page, default = 1024
  int pagedelta;    // # of pages to allocate at once, default = 1

  int errorflag;    // flag > 0 if error has occurred
                    // 1 = chunk size exceeded maxchunk
                    // 2 = memory allocation error
#if defined(_OPENMP)
  char pad[64];     // to avoid false sharing with multi-threading
#endif
  void allocate();
  void deallocate();
};

}    // namespace LAMMPS_NS

#endif
