text: #ifndef __bingo_ptr__
  #define __bingo_ptr__
  
  #include <new>
  #include <string>
  #include <thread>
  
  #include <safe_ptr.h>
  
  #include "base_cpp/exception.h"
  #include "base_cpp/obj_array.h"
  #include "base_cpp/os_sync_wrapper.h"
  #include "base_cpp/profiling.h"
  #include "base_cpp/tlscont.h"
  #include "bingo_mmf.h"
  
  using namespace indigo;
  
  namespace bingo
  {
      class BingoAllocator;
  
      struct BingoAddr
      {
          BingoAddr()
          {
              file_id = (size_t)-1;
              offset = (size_t)-1;
          }
  
          BingoAddr(size_t f_id, size_t off) : file_id(f_id), offset(off)
          {
          }
  
          size_t file_id;
          size_t offset;
  
          bool operator==(const BingoAddr& other)
          {
              return (file_id == other.file_id) && (offset == other.offset);
          }
  
          static const BingoAddr bingo_null;
      };
  
      template <typename T> class BingoPtr
      {
      public:
          BingoPtr()
          {
          }
  
          explicit BingoPtr(BingoAddr addr) : _addr(addr)
          {
          }
  
          explicit BingoPtr(size_t file_id, size_t offset) : _addr(file_id, offset)
          {
          }
  
          T* ptr();
  
          const T* ptr() const;
  
          T& ref()
          {
              return *ptr();
          }
  
          const T& ref() const
          {
              return *ptr();
          }
  
          T* operator->()
          {
              return ptr();
          }
  
          const T* operator->() const
          {
              return ptr();
          }
  
          BingoPtr<T> operator+(int off)
          {
              return BingoPtr<T>(_addr.file_id, _addr.offset + off * sizeof(T));
          }
  
          T& operator[](int idx)
          {
              return *(ptr() + idx);
          }
  
          /*
          qword serialize () const
          {
             return _offset;
          }
  
          void unserialize (qword data)
          {
             _offset = data;
          }
          */
  
          bool isNull()
          {
              return (_addr.offset == (size_t)-1) && (_addr.file_id == (size_t)-1);
          }
  
          void allocate(int count = 1);
  
          operator BingoAddr() const
          {
              return _addr;
          }
  
      private:
          BingoAddr _addr;
      };
  
      template <typename T> class BingoArray
      {
      public:
          BingoArray(int block_size = 10000) : _block_size(block_size)
          {
              _size = 0;
              _block_count = 0;
          }
  
          void resize(int new_size)
          {
              if (new_size > reserved())
              {
                  int blocks_count = (_size + _block_size - 1) / _block_size;
                  int new_blocks_count = (new_size + _block_size - 1) / _block_size;
  
                  if (new_blocks_count > _max_block_count)
                      throw Exception("BingoArray: block count limit is exceeded");
  
                  for (int i = blocks_count; i < new_blocks_count; i++)
                  {
                      _blocks[i].allocate(_block_size);
                      for (int j = 0; j < _block_size; j++)
                          new ((_blocks[i] + j).ptr()) T();
                  }
                  _block_count = new_blocks_count;
              }
  
              _size = new_size;
          }
  
          T& at(int index)
          {
              if (index < 0 || index >= _size)
                  throw Exception("BingoArray: incorrect idx %d (size=%d)", index, _size);
  
              return *(_blocks[index / _block_size].ptr() + index % _block_size);
          }
  
          const T& at(int index) const
          {
              if (index < 0 || index >= _size)
                  throw Exception("BingoArray: incorrect idx %d (size=%d)", index, _size);
  
              return *(_blocks[index / _block_size].ptr() + index % _block_size);
          }
  
          T& operator[](int index)
          {
              return at(index);
          }
  
          const T& operator[](int index) const
          {
              return at(index);
          }
  
          T& top()
          {
              int index = _size - 1;
  
              return *(_blocks[index / _block_size].ptr() + index % _block_size);
          }
  
          size_t find(const T& elem)
          {
              for (size_t i = 0; i < size(); i++)
              {
                  if (at(i) == elem)
                      return i;
              }
  
              return (size_t)-1;
          }
  
          T& push()
          {
              if (_size % _block_size == 0)
              {
                  int blocks_count = (_size + _block_size - 1) / _block_size;
  
                  _blocks[blocks_count].allocate(_block_size);
              }
  
              T* arr = _blocks[_size / _block_size].ptr();
              int idx_in_block = _size % _block_size;
  
              _size++;
  
              new (arr + idx_in_block) T();
  
              return arr[idx_in_block];
          }
  
          template <typename A> T& push(A& a)
          {
              if (_size % _block_size == 0)
              {
                  int blocks_count = (_size + _block_size - 1) / _block_size;
  
                  _blocks[blocks_count].allocate(_block_size);
              }
  
              T* arr = _blocks[_size / _block_size].ptr();
              int idx_in_block = _size % _block_size;
  
              _size++;
  
              new (arr + idx_in_block) T(a);
  
              return arr[idx_in_block];
          }
  
          void push(T elem)
          {
              T& new_elem = push();
  
              new_elem = elem;
          }
  
          int size() const
          {
              return _size;
          }
  
          int reserved() const
          {
              return _block_count * _block_size;
          }
  
      private:
          static const int _max_block_count = 40000;
  
          int _block_size;
          int _block_count;
          int _size;
          BingoPtr<T> _blocks[_max_block_count];
      };
  
      template <typename T> class BingoList
      {
      private:
          struct _Link
          {
              BingoPtr<T> data_ptr;
              BingoPtr<_Link> next_link;
              BingoPtr<_Link> prev_link;
  
              _Link()
              {
                  data_ptr = BingoPtr<T>(BingoAddr::bingo_null);
                  next_link = BingoPtr<_Link>(BingoAddr::bingo_null);
                  prev_link = BingoPtr<_Link>(BingoAddr::bingo_null);
              }
          };
  
      public:
          struct Iterator
          {
              BingoPtr<_Link> cur_link;
  
              Iterator() : cur_link(BingoPtr<T>(-1, -1))
              {
              }
              Iterator(BingoPtr<_Link> link) : cur_link(link)
              {
              }
              Iterator(const Iterator& it) : cur_link(it.cur_link)
              {
              }
  
              T& operator*()
              {
                  return cur_link->data_ptr.ref();
              }
  
              T* operator->()
              {
                  return cur_link->data_ptr.ptr();
              }
  
              Iterator& operator=(const Iterator& it)
              {
                  cur_link = it.cur_link;
  
                  return *this;
              }
  
              bool operator==(const Iterator& it) const
              {
                  if ((BingoAddr)cur_link == (BingoAddr)it.cur_link)
                      return true;
                  return false;
              }
  
              bool operator!=(const Iterator& it) const
              {
                  return !(*this == it);
              }
  
              Iterator& operator++(int)
              {
                  if ((BingoAddr)(cur_link->next_link) == BingoAddr::bingo_null)
                      throw Exception("BingoList::Iterator:operator++ There's no next link");
  
                  cur_link = cur_link->next_link;
  
                  return *this;
              }
  
              Iterator& operator--(int)
              {
                  if ((BingoAddr)(cur_link->prev_link) == BingoAddr::bingo_null)
                      throw Exception("BingoList::Iterator:operator-- There's no previous link");
  
                  cur_link = cur_link->prev_link;
  
                  return *this;
              }
          };
  
          BingoList()
          {
              _size = 0;
              _begin_link.allocate();
              new (_begin_link.ptr()) _Link();
              _end_link = _begin_link;
          }
  
          bool empty() const
          {
              if (_end_link == _begin_link)
                  return true;
              return false;
          }
  
          unsigned int size() const
          {
              return _size;
          }
  
          void insertBefore(Iterator pos, const BingoPtr<T> x)
          {
              BingoPtr<_Link> new_link;
              new_link.allocate();
              new (new_link.ptr()) _Link();
  
              new_link->data_ptr = x;
  
              new_link->next_link = pos.cur_link;
              new_link->prev_link = pos.cur_link->prev_link;
  
              if (!((BingoAddr)(pos.cur_link->prev_link) == BingoAddr::bingo_null))
                  pos.cur_link->prev_link->next_link = new_link;
              pos.cur_link->prev_link = new_link;
  
              if ((BingoAddr)(pos.cur_link) == (BingoAddr)(_begin_link))
                  _begin_link = new_link;
  
              _size++;
          }
  
          void insertBefore(Iterator pos, const T& x)
          {
              BingoPtr<T> data;
              data.allocate();
              data.ref() = x;
  
              insertBefore(pos, data);
          }
  
          void erase(Iterator& pos)
          {
              if (pos.cur_link->prev_link != -1)
                  pos.cur_link->prev_link->next_link = pos.cur_link->next_link;
              if (pos.cur_link->next_link != -1)
                  pos.cur_link->next_link->prev_link = pos.cur_link->prev_link;
  
              if (pos.cur_link == _begin_link)
                  _begin_link = pos.cur_link->next_link;
  
              if (pos.cur_link == _end_link)
                  throw Exception("BingoList:erase End link can't be removed");
  
              _size--;
          }
  
          void pushBack(const T& x)
          {
              insertBefore(end(), x);
          }
  
          void pushBack(const BingoPtr<T> x)
          {
              insertBefore(end(), x);
          }
  
          Iterator begin() const
          {
              return Iterator(_begin_link);
          }
  
          Iterator top() const
          {
              Iterator end_it(_end_link);
              end_it--;
              return end_it;
          }
  
          Iterator end() const
          {
              return Iterator(_end_link);
          }
  
      private:
          BingoPtr<_Link> _begin_link;
          BingoPtr<_Link> _end_link;
          int _size;
      };
  
      class MMFStorage;
  
      class BingoAllocator
      {
      public:
          template <typename T> friend class BingoPtr;
          friend class MMFStorage;
  
          static int getAllocatorDataSize();
  
      private:
          struct _BingoAllocatorData
          {
              _BingoAllocatorData() : _min_file_size(0), _max_file_size(0), _cur_file_id(0), _existing_files(0), _free_off(0)
              {
              }
  
              size_t _min_file_size;
              size_t _max_file_size;
              size_t _cur_file_id;
              dword _existing_files;
              size_t _free_off;
          };
  
          sf::safe_shared_hide_obj<ObjArray<MMFile>>* _mm_files;
  
          size_t _data_offset;
  
          static sf::safe_shared_hide_obj<PtrArray<BingoAllocator>>  _instances;
  
          std::string _filename;
          int _index_id;
  
          static void _create(const char* filename, size_t min_size, size_t max_size, size_t alloc_off, sf::safe_shared_hide_obj<ObjArray<MMFile>>& mm_files, int index_id);
  
          static void _load(const char* filename, size_t alloc_off, sf::safe_shared_hide_obj<ObjArray<MMFile>>& mm_files, int index_id, bool read_only);
  
          template <typename T> BingoAddr allocate(int count = 1)
          {
              size_t alloc_size, file_idx, file_off, file_size;
              _BingoAllocatorData* allocator_data;
              {
                  const auto mm_files = sf::slock_safe_ptr(*_mm_files);
                  byte* mmf_ptr = (byte*)mm_files->at(0).ptr();
  
                  allocator_data = (_BingoAllocatorData*)(mmf_ptr + _data_offset);
  
                  alloc_size = sizeof(T) * count;
  
                  file_idx = allocator_data->_cur_file_id;
                  file_off = allocator_data->_free_off;
                  file_size = mm_files->at((int)file_idx).size();
              }
  
              if (alloc_size > file_size - file_off)
                  _addFile(alloc_size);
  
  
              file_idx = allocator_data->_cur_file_id;
  
              file_size = sf::slock_safe_ptr(*_mm_files)->at((int)file_idx).size();
  
              size_t res_off = allocator_data->_free_off;
              size_t res_id = allocator_data->_cur_file_id;
              allocator_data->_free_off += alloc_size;
  
              if (allocator_data->_free_off == file_size)
                  _addFile(0);
  
              return BingoAddr(res_id, res_off);
          }
  
          static BingoAllocator* _getInstance();
  
          byte* _get(size_t file_id, size_t offset) const;
  
          BingoAllocator();
  
          void _addFile(size_t alloc_size);
  
          static size_t _getFileSize(size_t idx, size_t min_size, size_t max_size, dword sizes);
  
          static void _genFilename(int idx, const char* filename, std::string& out_name);
      };
  
      // Implementations for BingoPtr and BingoAllocator are dependent and thus implementation is here
      template <typename T> T* BingoPtr<T>::ptr()
      {
          const BingoAllocator* _allocator = BingoAllocator::_getInstance();
  
          return (T*)(_allocator->_get(_addr.file_id, _addr.offset));
      }
  
      template <typename T> const T* BingoPtr<T>::ptr() const
      {
          const BingoAllocator* _allocator = BingoAllocator::_getInstance();
  
          return (T*)(_allocator->_get(_addr.file_id, _addr.offset));
      }
  
      template <typename T> void BingoPtr<T>::allocate(int count)
      {
          BingoAllocator* _allocator = BingoAllocator::_getInstance();
  
          _addr = _allocator->allocate<T>(count);
      }
  }; // namespace bingo
  
  #endif //__bingo_ptr__
  