Blame src/microhttpd/memorypool.c

Packit 875988
/*
Packit 875988
     This file is part of libmicrohttpd
Packit 875988
     Copyright (C) 2007, 2009, 2010 Daniel Pittman and Christian Grothoff
Packit 875988
Packit 875988
     This library is free software; you can redistribute it and/or
Packit 875988
     modify it under the terms of the GNU Lesser General Public
Packit 875988
     License as published by the Free Software Foundation; either
Packit 875988
     version 2.1 of the License, or (at your option) any later version.
Packit 875988
Packit 875988
     This library is distributed in the hope that it will be useful,
Packit 875988
     but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 875988
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 875988
     Lesser General Public License for more details.
Packit 875988
Packit 875988
     You should have received a copy of the GNU Lesser General Public
Packit 875988
     License along with this library; if not, write to the Free Software
Packit 875988
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 875988
*/
Packit 875988
Packit 875988
/**
Packit 875988
 * @file memorypool.c
Packit 875988
 * @brief memory pool
Packit 875988
 * @author Christian Grothoff
Packit 875988
 */
Packit 875988
#include "memorypool.h"
Packit 875988
Packit 875988
/* define MAP_ANONYMOUS for Mac OS X */
Packit 875988
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
Packit 875988
#define MAP_ANONYMOUS MAP_ANON
Packit 875988
#endif
Packit 875988
#ifndef MAP_FAILED
Packit 875988
#define MAP_FAILED ((void*)-1)
Packit 875988
#endif
Packit 875988
Packit 875988
/**
Packit 875988
 * Align to 2x word size (as GNU libc does).
Packit 875988
 */
Packit 875988
#define ALIGN_SIZE (2 * sizeof(void*))
Packit 875988
Packit 875988
/**
Packit 875988
 * Round up 'n' to a multiple of ALIGN_SIZE.
Packit 875988
 */
Packit 875988
#define ROUND_TO_ALIGN(n) ((n+(ALIGN_SIZE-1)) & (~(ALIGN_SIZE-1)))
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Handle for a memory pool.  Pools are not reentrant and must not be
Packit 875988
 * used by multiple threads.
Packit 875988
 */
Packit 875988
struct MemoryPool
Packit 875988
{
Packit 875988
Packit 875988
  /**
Packit 875988
   * Pointer to the pool's memory
Packit 875988
   */
Packit 875988
  char *memory;
Packit 875988
Packit 875988
  /**
Packit 875988
   * Size of the pool.
Packit 875988
   */
Packit 875988
  size_t size;
Packit 875988
Packit 875988
  /**
Packit 875988
   * Offset of the first unallocated byte.
Packit 875988
   */
Packit 875988
  size_t pos;
Packit 875988
Packit 875988
  /**
Packit 875988
   * Offset of the last unallocated byte.
Packit 875988
   */
Packit 875988
  size_t end;
Packit 875988
Packit 875988
  /**
Packit 875988
   * #MHD_NO if pool was malloc'ed, #MHD_YES if mmapped (VirtualAlloc'ed for W32).
Packit 875988
   */
Packit 875988
  int is_mmap;
Packit 875988
};
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Free the memory given by @a ptr. Calls "free(ptr)".  This function
Packit 875988
 * should be used to free the username returned by
Packit 875988
 * #MHD_digest_auth_get_username().
Packit 875988
 * @note Since v0.9.56
Packit 875988
 *
Packit 875988
 * @param ptr pointer to free.
Packit 875988
 */
Packit 875988
_MHD_EXTERN void
Packit 875988
MHD_free (void *ptr)
Packit 875988
{
Packit 875988
  free (ptr);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Create a memory pool.
Packit 875988
 *
Packit 875988
 * @param max maximum size of the pool
Packit 875988
 * @return NULL on error
Packit 875988
 */
Packit 875988
struct MemoryPool *
Packit 875988
MHD_pool_create (size_t max)
Packit 875988
{
Packit 875988
  struct MemoryPool *pool;
Packit 875988
Packit 875988
  pool = malloc (sizeof (struct MemoryPool));
Packit 875988
  if (NULL == pool)
Packit 875988
    return NULL;
Packit 875988
#if defined(MAP_ANONYMOUS) || defined(_WIN32)
Packit 875988
  if (max <= 32 * 1024)
Packit 875988
    pool->memory = MAP_FAILED;
Packit 875988
  else
Packit 875988
#if defined(MAP_ANONYMOUS) && !defined(_WIN32)
Packit 875988
    pool->memory = mmap (NULL,
Packit 875988
                         max,
Packit 875988
                         PROT_READ | PROT_WRITE,
Packit 875988
			 MAP_PRIVATE | MAP_ANONYMOUS,
Packit 875988
                         -1,
Packit 875988
                         0);
Packit 875988
#elif defined(_WIN32)
Packit 875988
    pool->memory = VirtualAlloc (NULL,
Packit 875988
                                 max,
Packit 875988
                                 MEM_COMMIT | MEM_RESERVE,
Packit 875988
                                 PAGE_READWRITE);
Packit 875988
#endif
Packit 875988
#else
Packit 875988
  pool->memory = MAP_FAILED;
Packit 875988
#endif
Packit 875988
  if ( (MAP_FAILED == pool->memory) ||
Packit 875988
       (NULL == pool->memory))
Packit 875988
    {
Packit 875988
      pool->memory = malloc (max);
Packit 875988
      if (NULL == pool->memory)
Packit 875988
        {
Packit 875988
          free (pool);
Packit 875988
          return NULL;
Packit 875988
        }
Packit 875988
      pool->is_mmap = MHD_NO;
Packit 875988
    }
Packit 875988
  else
Packit 875988
    {
Packit 875988
      pool->is_mmap = MHD_YES;
Packit 875988
    }
Packit 875988
  pool->pos = 0;
Packit 875988
  pool->end = max;
Packit 875988
  pool->size = max;
Packit 875988
  return pool;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Destroy a memory pool.
Packit 875988
 *
Packit 875988
 * @param pool memory pool to destroy
Packit 875988
 */
Packit 875988
void
Packit 875988
MHD_pool_destroy (struct MemoryPool *pool)
Packit 875988
{
Packit 875988
  if (NULL == pool)
Packit 875988
    return;
Packit 875988
  if (MHD_NO == pool->is_mmap)
Packit 875988
    free (pool->memory);
Packit 875988
  else
Packit 875988
#if defined(MAP_ANONYMOUS) && !defined(_WIN32)
Packit 875988
    munmap (pool->memory,
Packit 875988
            pool->size);
Packit 875988
#elif defined(_WIN32)
Packit 875988
    VirtualFree (pool->memory,
Packit 875988
                 0,
Packit 875988
                 MEM_RELEASE);
Packit 875988
#else
Packit 875988
    abort ();
Packit 875988
#endif
Packit 875988
  free (pool);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Check how much memory is left in the @a pool
Packit 875988
 *
Packit 875988
 * @param pool pool to check
Packit 875988
 * @return number of bytes still available in @a pool
Packit 875988
 */
Packit 875988
size_t
Packit 875988
MHD_pool_get_free (struct MemoryPool *pool)
Packit 875988
{
Packit 875988
  return (pool->end - pool->pos);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Allocate size bytes from the pool.
Packit 875988
 *
Packit 875988
 * @param pool memory pool to use for the operation
Packit 875988
 * @param size number of bytes to allocate
Packit 875988
 * @param from_end allocate from end of pool (set to #MHD_YES);
Packit 875988
 *        use this for small, persistent allocations that
Packit 875988
 *        will never be reallocated
Packit 875988
 * @return NULL if the pool cannot support size more
Packit 875988
 *         bytes
Packit 875988
 */
Packit 875988
void *
Packit 875988
MHD_pool_allocate (struct MemoryPool *pool,
Packit 875988
		   size_t size,
Packit 875988
                   int from_end)
Packit 875988
{
Packit 875988
  void *ret;
Packit 875988
  size_t asize;
Packit 875988
Packit 875988
  asize = ROUND_TO_ALIGN (size);
Packit 875988
  if ( (0 == asize) && (0 != size) )
Packit 875988
    return NULL; /* size too close to SIZE_MAX */
Packit 875988
  if ( (pool->pos + asize > pool->end) ||
Packit 875988
       (pool->pos + asize < pool->pos))
Packit 875988
    return NULL;
Packit 875988
  if (from_end == MHD_YES)
Packit 875988
    {
Packit 875988
      ret = &pool->memory[pool->end - asize];
Packit 875988
      pool->end -= asize;
Packit 875988
    }
Packit 875988
  else
Packit 875988
    {
Packit 875988
      ret = &pool->memory[pool->pos];
Packit 875988
      pool->pos += asize;
Packit 875988
    }
Packit 875988
  return ret;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Reallocate a block of memory obtained from the pool.
Packit 875988
 * This is particularly efficient when growing or
Packit 875988
 * shrinking the block that was last (re)allocated.
Packit 875988
 * If the given block is not the most recently
Packit 875988
 * (re)allocated block, the memory of the previous
Packit 875988
 * allocation may be leaked until the pool is
Packit 875988
 * destroyed (and copying the data maybe required).
Packit 875988
 *
Packit 875988
 * @param pool memory pool to use for the operation
Packit 875988
 * @param old the existing block
Packit 875988
 * @param old_size the size of the existing block
Packit 875988
 * @param new_size the new size of the block
Packit 875988
 * @return new address of the block, or
Packit 875988
 *         NULL if the pool cannot support @a new_size
Packit 875988
 *         bytes (old continues to be valid for @a old_size)
Packit 875988
 */
Packit 875988
void *
Packit 875988
MHD_pool_reallocate (struct MemoryPool *pool,
Packit 875988
                     void *old,
Packit 875988
		     size_t old_size,
Packit 875988
		     size_t new_size)
Packit 875988
{
Packit 875988
  void *ret;
Packit 875988
  size_t asize;
Packit 875988
Packit 875988
  asize = ROUND_TO_ALIGN (new_size);
Packit 875988
  if ( (0 == asize) &&
Packit 875988
       (0 != new_size) )
Packit 875988
    return NULL; /* new_size too close to SIZE_MAX */
Packit 875988
  if ( (pool->end < old_size) ||
Packit 875988
       (pool->end < asize) )
Packit 875988
    return NULL;                /* unsatisfiable or bogus request */
Packit 875988
Packit 875988
  if ( (pool->pos >= old_size) &&
Packit 875988
       (&pool->memory[pool->pos - old_size] == old) )
Packit 875988
    {
Packit 875988
      /* was the previous allocation - optimize! */
Packit 875988
      if (pool->pos + asize - old_size <= pool->end)
Packit 875988
        {
Packit 875988
          /* fits */
Packit 875988
          pool->pos += asize - old_size;
Packit 875988
          if (asize < old_size)      /* shrinking - zero again! */
Packit 875988
            memset (&pool->memory[pool->pos],
Packit 875988
                    0,
Packit 875988
                    old_size - asize);
Packit 875988
          return old;
Packit 875988
        }
Packit 875988
      /* does not fit */
Packit 875988
      return NULL;
Packit 875988
    }
Packit 875988
  if (asize <= old_size)
Packit 875988
    return old;                 /* cannot shrink, no need to move */
Packit 875988
  if ((pool->pos + asize >= pool->pos) &&
Packit 875988
      (pool->pos + asize <= pool->end))
Packit 875988
    {
Packit 875988
      /* fits */
Packit 875988
      ret = &pool->memory[pool->pos];
Packit 875988
      if (0 != old_size)
Packit 875988
        memmove (ret,
Packit 875988
                 old,
Packit 875988
                 old_size);
Packit 875988
      pool->pos += asize;
Packit 875988
      return ret;
Packit 875988
    }
Packit 875988
  /* does not fit */
Packit 875988
  return NULL;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Clear all entries from the memory pool except
Packit 875988
 * for @a keep of the given @a size. The pointer
Packit 875988
 * returned should be a buffer of @a new_size where
Packit 875988
 * the first @a copy_bytes are from @a keep.
Packit 875988
 *
Packit 875988
 * @param pool memory pool to use for the operation
Packit 875988
 * @param keep pointer to the entry to keep (maybe NULL)
Packit 875988
 * @param copy_bytes how many bytes need to be kept at this address
Packit 875988
 * @param new_size how many bytes should the allocation we return have?
Packit 875988
 *                 (should be larger or equal to @a copy_bytes)
Packit 875988
 * @return addr new address of @a keep (if it had to change)
Packit 875988
 */
Packit 875988
void *
Packit 875988
MHD_pool_reset (struct MemoryPool *pool,
Packit 875988
		void *keep,
Packit 875988
		size_t copy_bytes,
Packit 875988
                size_t new_size)
Packit 875988
{
Packit 875988
  if ( (NULL != keep) &&
Packit 875988
       (keep != pool->memory) )
Packit 875988
    {
Packit 875988
      if (0 != copy_bytes)
Packit 875988
        memmove (pool->memory,
Packit 875988
                 keep,
Packit 875988
                 copy_bytes);
Packit 875988
      keep = pool->memory;
Packit 875988
    }
Packit 875988
  pool->end = pool->size;
Packit 875988
  /* technically not needed, but safer to zero out */
Packit 875988
  if (pool->size > copy_bytes)
Packit 875988
    memset (&pool->memory[copy_bytes],
Packit 875988
            0,
Packit 875988
            pool->size - copy_bytes);
Packit 875988
  if (NULL != keep)
Packit 875988
    pool->pos = ROUND_TO_ALIGN (new_size);
Packit 875988
  return keep;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/* end of memorypool.c */