Skip to content

File mem_block.hpp

File List > base > mem_block.hpp

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * mem_block.hpp - Memory Block Functionality
 *
 *  Memory Block is defined as a contiguous memory buffer.
 *
 *  Glossary:
 *
 *    block - the pair of memory pointer and size.
 *    stack block - a block on stack memory
 *    heap block - a block on heap memory; does not have its own structure (a simple block can be used).
 *    extensible block - a block that can be extended when required
 *    extensible stack block - a block that starts as a stack block and can be extended to a heap block.
 */

#ifndef _MEM_BLOCK_HPP_
#define _MEM_BLOCK_HPP_

#include "porting.h"

#include <memory.h>
#include <functional>

#include <cassert>
#include <cinttypes>

namespace cubmem
{
  const size_t DEFAULT_ALIGNMENT = 8;
  template <typename T>
  inline T *ptr_align (T *ptr);

  /*
   * Memory Block
   * - groups together memory address and its size
   * - doesn't own the memory, just points to it
   * - used to allocate, deallocate and share memory
   * - could be extended with helper info: allocator, src file&line where allocation was made, ...
   */
  struct block
  {
    public:
      size_t dim;
      char *ptr;

      inline block ();
      inline block (size_t dim, void *ptr);
      inline block (block &&b);             //move ctor

      inline block &operator= (block &&b);  //move assign

      inline void copy_to (block &dest) const;

      inline bool is_valid () const;

      inline void freemem ();

      inline char *move_ptr ();                                    //NOT RECOMMENDED! use move semantics: std::move()

    private:
      block (const block &) = delete;
      block &operator= (const block &) = delete;
  };

  // stack_block - 8-byte aligned stack block of size S
  //
  template <size_t S>
  class stack_block
  {
    public:
      static const size_t SIZE = S;

      stack_block (void) = default;
      inline char *get_ptr (void);
      inline const char *get_read_ptr () const;

    private:
      union
      {
    char m_buf[SIZE];
    std::int64_t dummy;
      };
  };

  // block_allocator - allocation, deallocation and reallocation of memory blocks. it preserves the contents of the
  //                   block on reallocation
  //
  struct EXPORT_IMPORT block_allocator
  {
    public:
      using alloc_func = std::function<void (block &b, size_t size)>;
      using dealloc_func = std::function<void (block &b)>;

      alloc_func m_alloc_f;   // allocator/reallocator
      dealloc_func m_dealloc_f;              // deallocator

      block_allocator () = delete;
      block_allocator (const alloc_func &alloc_f, const dealloc_func &dealloc_f);

      block_allocator &operator= (const block_allocator &other);
  };
  EXPORT_IMPORT extern const block_allocator STANDARD_BLOCK_ALLOCATOR;
  EXPORT_IMPORT extern const block_allocator EXPONENTIAL_STANDARD_BLOCK_ALLOCATOR;
  EXPORT_IMPORT extern const block_allocator CSTYLE_BLOCK_ALLOCATOR;

  // single_block_allocator - maintains and allocates a single memory block
  //
  // it is designed as a memory cache that can be reused for multiple purposes in multiple places. must be used with
  // care because it doesn't guarantee exclusive access to memory
  //
  // use get_block_allocator to pass the cached memory block to structures like extensible_buffer
  //
  class single_block_allocator
  {
    public:
      single_block_allocator (const block_allocator &base_alloc);
      ~single_block_allocator ();

      const block_allocator &get_block_allocator () const;   // a block allocator that always outputs m_block
      const block &get_block () const;

      char *get_ptr () const;
      size_t get_size () const;

      void reserve (size_t size);

    private:

      void allocate (block &b, size_t size);  // the output b will be always equal to m_block
      void deallocate (block &b);

      const block_allocator &m_base_allocator;    // allocator for m_block

      block m_block;                              // the single block
      block_allocator m_allocator;                // allocator that always outputs m_block
  };

  /* Memory Block - Extensible
   * - able to extend/reallocate to accommodate additional bytes
   * - owns the memory by default and it will free the memory in destructor unless it is moved:
   *    {
   *        cubmem::block_ext block{some_realloc, some_dealloc};//some_realloc/dealloc = functions, functors or lambdas
   *        //...
   *        //move it or it will be deallocated; simple copy => compiler error because it is not designed to be copied
   *        cubmem::block b = std::move(block);
   *    }
   */
  struct extensible_block
  {
    public:
      inline extensible_block ();                                          //default ctor
      inline extensible_block (extensible_block &&b);                              //move ctor
      inline extensible_block (const block_allocator &alloc);     //general ctor
      inline ~extensible_block ();                                         //dtor

      inline extensible_block &operator= (extensible_block &&b);                   //move assignment

      inline void copy_to (extensible_block &dest) const;
      inline void extend_by (size_t additional_bytes);
      inline void extend_to (size_t total_bytes);
      inline void freemem ();

      inline char *get_ptr ();
      inline const char *get_read_ptr () const;

      inline std::size_t get_size () const;

      inline char *release_ptr ();

    private:
      block m_block;
      const block_allocator *m_allocator;

      extensible_block (const extensible_block &) = delete;             //copy ctor
      extensible_block &operator= (const extensible_block &) = delete;  //copy assignment
  };

  // extensible_stack_block - extensible memory block that start with as a stack_block
  //
  template <size_t S>
  class extensible_stack_block
  {
    public:
      extensible_stack_block ();
      extensible_stack_block (const block_allocator &alloc);

      inline void extend_by (size_t additional_bytes);
      inline void extend_to (size_t total_bytes);

      inline char *get_ptr ();
      inline const char *get_read_ptr () const;

    private:
      stack_block<S> m_stack;
      extensible_block m_ext_block;
      bool m_use_stack;
  };
} // namespace cubmem

// inline/template implementation

namespace cubmem
{
  //
  // alignment
  //
  template <typename T>
  T *
  ptr_align (T *ptr)
  {
    std::uintptr_t pt = (std::uintptr_t) ptr;
    pt = (pt + DEFAULT_ALIGNMENT - 1) & (DEFAULT_ALIGNMENT - 1);
    return (T *) pt;
  }

  //
  // block
  //
  block::block ()
    : dim { 0 }
    , ptr { NULL }
  {
  }

  block::block (block &&b)
    : dim {b.dim}
    , ptr {b.ptr}
  {
    b.dim = 0;
    b.ptr = NULL;
  }

  block::block (size_t dim, void *ptr)
    : dim {dim}
    , ptr { (char *) ptr}
  {
  }

  block &
  block::operator= (block &&b)   //move assign
  {
    if (this != &b)
      {
    dim = b.dim;
    ptr = b.ptr;
    b.dim = 0;
    b.ptr = NULL;
      }
    return *this;
  }

  void
  block::copy_to (block &dest) const
  {
    dest.dim = dim;
    dest.ptr = ptr;
  }

  bool
  block::is_valid () const
  {
    return (dim != 0 && ptr != NULL);
  }

  void
  block::freemem ()
  {
    if (is_valid ())
      {
    delete[] ptr;
    dim = 0;
    ptr = NULL;
      }
  }

  char *
  block::move_ptr ()
  {
    char *p = ptr;

    dim = 0;
    ptr = NULL;

    return p;
  }

  //
  // stack_block
  //
  template <size_t S>
  char *
  stack_block<S>::get_ptr (void)
  {
    return &m_buf[0];
  }

  template <size_t S>
  const char *
  stack_block<S>::get_read_ptr (void) const
  {
    return &m_buf[0];
  }

  //
  // extensible_block
  //
  extensible_block::extensible_block ()
    : extensible_block { STANDARD_BLOCK_ALLOCATOR }
  {
  }

  extensible_block::extensible_block (extensible_block &&b)
    : extensible_block { *b.m_allocator }
  {
    m_block = std::move (b.m_block);
  }

  extensible_block::extensible_block (const block_allocator &alloc)
    : m_block {}
    , m_allocator (&alloc)
  {
  }

  extensible_block &
  extensible_block::operator= (extensible_block &&b)
  {
    if (this != &b)
      {
    this->~extensible_block ();
    m_allocator = b.m_allocator;
    m_block.dim = b.m_block.dim;
    m_block.ptr = b.m_block.ptr;
    b.m_block.dim = 0;
    b.m_block.ptr = NULL;
      }
    return *this;
  }

  extensible_block::~extensible_block ()
  {
    m_allocator->m_dealloc_f (m_block);
  }

  void
  extensible_block::copy_to (extensible_block &dest) const
  {
    m_block.copy_to (dest.m_block);
    dest.m_allocator = m_allocator;
  }

  void
  extensible_block::extend_by (size_t additional_bytes)
  {
    m_allocator->m_alloc_f (m_block, m_block.dim + additional_bytes);
  }

  void
  extensible_block::extend_to (size_t total_bytes)
  {
    if (total_bytes <= m_block.dim)
      {
    return;
      }
    extend_by (total_bytes - m_block.dim);
  }

  void
  extensible_block::freemem ()
  {
    m_allocator->m_dealloc_f (m_block);
  }

  char *
  extensible_block::get_ptr ()
  {
    return m_block.ptr;
  }

  const char *
  extensible_block::get_read_ptr () const
  {
    return m_block.ptr;
  }

  std::size_t
  extensible_block::get_size () const
  {
    return m_block.dim;
  }

  char *
  extensible_block::release_ptr ()
  {
    char *ret_ptr = m_block.ptr;
    m_block.ptr = NULL;
    m_block.dim = 0;
    return ret_ptr;
  }

  //
  // extensible_stack_block
  //
  template <size_t S>
  extensible_stack_block<S>::extensible_stack_block ()
    : m_stack ()
    , m_ext_block ()
    , m_use_stack (true)
  {
  }

  template <size_t S>
  extensible_stack_block<S>::extensible_stack_block (const block_allocator &alloc)
    : m_stack ()
    , m_ext_block (alloc)
    , m_use_stack (true)
  {
  }

  template <size_t S>
  void
  extensible_stack_block<S>::extend_by (size_t additional_bytes)
  {
    if (m_use_stack)
      {
    m_ext_block.extend_to (m_stack.SIZE + additional_bytes);
    // copy data from m_stack to m_ext_block at first extension
    memcpy (m_ext_block.get_ptr (), m_stack.get_ptr (), m_stack.SIZE);
      }
    else
      {
    m_ext_block.extend_by (additional_bytes);
      }
    m_use_stack = false;
  }

  template <size_t S>
  void
  extensible_stack_block<S>::extend_to (size_t total_bytes)
  {
    if (total_bytes <= m_stack.SIZE)
      {
    return;
      }
    m_ext_block.extend_to (total_bytes);
    if (m_use_stack)
      {
    // copy data from m_stack to m_ext_block at first extension
    memcpy (m_ext_block.get_ptr (), m_stack.get_ptr (), m_stack.SIZE);
    m_use_stack = false;
      }
  }

  template <size_t S>
  char *
  extensible_stack_block<S>::get_ptr ()
  {
    return m_use_stack ? m_stack.get_ptr () : m_ext_block.get_ptr ();
  }

  template <size_t S>
  const char *
  extensible_stack_block<S>::get_read_ptr () const
  {
    return m_use_stack ? m_stack.get_read_ptr () : m_ext_block.get_read_ptr ();
  }
} // namespace cubmem

#endif // _MEM_BLOCK_HPP_