text: #ifndef INCLUDES_TARANTOOL_LSREGION_INTERNAL_H
  #define INCLUDES_TARANTOOL_LSREGION_INTERNAL_H
  /*
   * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
   *
   * Redistribution and use in source and binary forms, with or
   * without modification, are permitted provided that the following
   * conditions are met:
   *
   * 1. Redistributions of source code must retain the above
   *    copyright notice, this list of conditions and the
   *    following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above
   *    copyright notice, this list of conditions and the following
   *    disclaimer in the documentation and/or other materials
   *    provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
   * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   */
  
  #   if defined(__cplusplus)
  extern "C" {
  #   endif /* defined(__cplusplus) */
  
  #define LSLAB_NOT_USED_ID -1
  
  /**
   * Wrapper for a slab that tracks a size of used memory and
   * maximal identifier of memory that was allocated in the slab.
   */
  struct lslab {
  	/** Link in the lsregion.slabs list. */
  	struct rlist next_in_list;
  	/**
  	 * Slab allocated size.
  	 */
  	size_t slab_size;
  	/**
  	 * Size of used memory including aligned size of this
  	 * structure.
  	 */
  	size_t slab_used;
  	/**
  	 * Maximal id that was used to alloc data from the slab.
  	 */
  	int64_t max_id;
  };
  
  /**
   * Log structured allocator treats memory as a sequentially
   * written log. It allows to allocate memory chunks of any size,
   * but does not support free() of an individual chunk. Instead,
   * each chunk, when allocated, needs to be identified with an id.
   * It is assumed that ids are nondecreasing.
   * The chunks are stored in equally-sized slabs, obtained from
   * slab arena.
   * To free memory, the allocator requires an oldest id before
   * which all memory could be discarded. Upon free, it returns
   * all slabs containing chunks with smaller ids to the slab arena.
   *
   * id_i <= id_(i + 1)
   * *-------*   *-------*   *-------*                  *-------*
   * | slab  |-->| slab  |-->| slab  |-->            -->| slab  |
   * *-------*   *-------*   *-------*                  *-------*
   *  <= id1      <= id2 |    <= id3                     <= idN
   *                     |
   * truncate with id in [id2, id3) deletes from this position.
   */
  struct lsregion {
  	/**
  	 * List of memory slabs and the statistics. The older a
  	 * slab is, the closer it is placed to front of the list.
  	 */
  	struct slab_list slabs;
  	/** Slabs arena - source for memory slabs. */
  	struct slab_arena *arena;
  	struct lslab *cached;
  };
  
  /** Aligned size of the struct lslab. */
  static inline size_t
  lslab_sizeof()
  {
  	return small_align(sizeof(struct lslab), sizeof(intptr_t));
  }
  
  /** Initialize the lslab object. */
  static inline void
  lslab_create(struct lslab *slab, size_t size)
  {
  	rlist_create(&slab->next_in_list);
  	slab->slab_size = size;
  	slab->slab_used = lslab_sizeof();
  	slab->max_id = LSLAB_NOT_USED_ID;
  }
  
  /**
   * Size of the unused part of the slab.
   * @param slab     Slab container.
   * @retval Unsed memory size.
   */
  static inline size_t
  lslab_unused(const struct lslab *slab)
  {
  	assert(slab->slab_size >= slab->slab_used);
  	return slab->slab_size - slab->slab_used;
  }
  
  /**
   * Update slab statistics and meta according to new allocation.
   */
  static inline void
  lslab_use(struct lslab *slab, size_t size, int64_t id)
  {
  	assert(size <= lslab_unused(slab));
  	assert(slab->max_id <= id);
  	slab->slab_used += size;
  	slab->max_id = id;
  }
  
  /**
   * Pointer to the end of the used part of the slab.
   * @param slab Slab container.
   * @retval Pointer to the unused part of the slab.
   */
  static inline void *
  lslab_pos(struct lslab *slab)
  {
  	return (char *) slab + slab->slab_used;
  }
  
  /** Pointer to the end of the slab memory. */
  static inline void *
  lslab_end(struct lslab *slab)
  {
  	return (char *)slab + slab->slab_size;
  }
  
  /**
   * Initialize log structured allocator.
   * @param lsregion Allocator object.
   * @param arena    Slabs arena.
   */
  static inline void
  lsregion_create_orig(struct lsregion *lsregion, struct slab_arena *arena)
  {
  	assert(arena != NULL);
  	assert(arena->slab_size > lslab_sizeof());
  	slab_list_create(&lsregion->slabs);
  	lsregion->arena = arena;
  	lsregion->cached = NULL;
  }
  
  /** @sa lsregion_aligned_reserve_orig(). */
  void *
  lsregion_aligned_reserve_slow_orig(struct lsregion *lsregion, size_t size,
  			      size_t alignment, void **unaligned);
  
  /**
   * Make sure a next allocation of at least @a size bytes will not
   * fail, and will return the same result as this call, aligned by
   * @a alignment.
   * @param lsregion Allocator object.
   * @param size     Size to allocate.
   * @param alignment Byte alignment required for the result
   *        address.
   * @param[out] unaligned When the function succeeds, it returns
   *        aligned address, and stores base unaligned address to
   *        this variable. That helps to find how many bytes were
   *        wasted to make the alignment.
   *
   * @retval not NULL Success.
   * @retval NULL     Memory error.
   */
  static inline void *
  lsregion_aligned_reserve_orig(struct lsregion *lsregion, size_t size,
  			 size_t alignment, void **unaligned)
  {
  	/* If there is an existing slab then try to use it. */
  	if (! rlist_empty(&lsregion->slabs.slabs)) {
  		struct lslab *slab;
  		slab = rlist_last_entry(&lsregion->slabs.slabs, struct lslab,
  					next_in_list);
  		assert(slab != NULL);
  		*unaligned = lslab_pos(slab);
  		void *pos = (void *)small_align((size_t)*unaligned, alignment);
  		if ((char *)pos + size <= (char *)lslab_end(slab))
  			return pos;
  	}
  	return lsregion_aligned_reserve_slow_orig(lsregion, size, alignment,
  					     unaligned);
  }
  
  /**
   * Make sure a next allocation of at least @a size bytes will not
   * fail, and will return the same result as this call.
   * @param lsregion Allocator object.
   * @param size Size to allocate.
   *
   * @retval not-NULL Success.
   * @retval NULL Memory error.
   */
  static inline void *
  lsregion_reserve_orig(struct lsregion *lsregion, size_t size)
  {
  	void *unaligned = NULL;
  	void *res = lsregion_aligned_reserve_orig(lsregion, size, 1, &unaligned);
  	assert(res == NULL || res == unaligned);
  	return res;
  }
  
  /**
   * Allocate @a size bytes and associate the allocated block
   * with @a id.
   * @param lsregion Allocator object.
   * @param size Size to allocate.
   * @param id Memory chunk identifier.
   *
   * @retval not-NULL Success.
   * @retval NULL Memory error.
   */
  static inline void *
  lsregion_alloc_orig(struct lsregion *lsregion, size_t size, int64_t id)
  {
  	void *res = lsregion_reserve_orig(lsregion, size);
  	if (res == NULL)
  		return NULL;
  	struct lslab *slab = rlist_last_entry(&lsregion->slabs.slabs,
  					      struct lslab, next_in_list);
  	lslab_use(slab, size, id);
  	lsregion->slabs.stats.used += size;
  	return res;
  }
  
  /**
   * The same as normal alloc, but the resulting pointer is aligned
   * by @a alignment.
   */
  static inline void *
  lsregion_aligned_alloc_orig(struct lsregion *lsregion, size_t size, size_t alignment,
  		       int64_t id)
  {
  	void *unaligned;
  	void *res = lsregion_aligned_reserve_orig(lsregion, size, alignment,
  					     &unaligned);
  	if (res == NULL)
  		return NULL;
  	struct lslab *slab = rlist_last_entry(&lsregion->slabs.slabs,
  					      struct lslab, next_in_list);
  	size += (char *)res - (char *)unaligned;
  	lslab_use(slab, size, id);
  	lsregion->slabs.stats.used += size;
  	return res;
  }
  
  
  /**
   * Try to free all memory blocks in which the biggest identifier
   * is less or equal then the specified identifier.
   * @param lsregion Allocator object.
   * @param min_id   Free all memory blocks with
   *                 max_id <= this parameter.
   */
  static inline void
  lsregion_gc_orig(struct lsregion *lsregion, int64_t min_id)
  {
  	struct lslab *slab, *next;
  	size_t arena_slab_size = lsregion->arena->slab_size;
  	/*
  	 * First blocks are the oldest so free them until
  	 * max_id > min_id.
  	 */
  	rlist_foreach_entry_safe(slab, &lsregion->slabs.slabs, next_in_list,
  				 next) {
  		if (slab->max_id > min_id)
  			break;
  		rlist_del_entry(slab, next_in_list);
  		/*
  		 * lslab_sizeof() must not affect the used bytes
  		 * count.
  		 */
  		lsregion->slabs.stats.used -= slab->slab_used - lslab_sizeof();
  		if (slab->slab_size > arena_slab_size) {
  			/* Never put large slabs into cache */
  			quota_release(lsregion->arena->quota, slab->slab_size);
  			lsregion->slabs.stats.total -= slab->slab_size;
  			free(slab);
  		} else if (lsregion->cached != NULL) {
  			lsregion->slabs.stats.total -= slab->slab_size;
  			slab_unmap_orig(lsregion->arena, slab);
  		} else {
  			lslab_create(slab, slab->slab_size);
  			lsregion->cached = slab;
  		}
  	}
  }
  
  /**
   * Free all resources occupied by the allocator.
   * @param lsregion Allocator object.
   */
  static inline void
  lsregion_destroy_orig(struct lsregion *lsregion)
  {
  	if (! rlist_empty(&lsregion->slabs.slabs))
  		lsregion_gc_orig(lsregion, INT64_MAX);
  	if (lsregion->cached != NULL)
  		slab_unmap_orig(lsregion->arena, lsregion->cached);
  }
  
  /** Size of the allocated memory. */
  static inline size_t
  lsregion_used_orig(const struct lsregion *lsregion)
  {
  	return lsregion->slabs.stats.used;
  }
  
  /** Size of the allocated and reserved memory. */
  static inline size_t
  lsregion_total_orig(const struct lsregion *lsregion)
  {
  	return lsregion->slabs.stats.total;
  }
  
  #   if defined(TARMEMDBG) || defined(TARANTOOL_PICO_MEMORY_DEBUG_ON) || defined(TARARAM) // picodata memory debug
  void   lsregion_create(memory_epoch_queue **lsregion_value, struct slab_arena *arena);
  #   endif
  
  #   if defined(__cplusplus)
  } /* extern "C" */
  #   endif /* defined(__cplusplus) */
  
  #endif
  