/*
 * Copyright (c) 1999-2001 Caucho Technology.  All rights reserved.
 *
 * Caucho Technology permits redistribution, modification and use
 * of this file in source and binary form ("the Software") under the
 * Caucho Developer Source License ("the License").  In particular, the following
 * conditions must be met:
 *
 * 1. Each copy or derived work of the Software must preserve the copyright
 *    notice and this notice unmodified.
 *
 * 2. Redistributions of the Software in source or binary form must include 
 *    an unmodified copy of the License, normally in a plain ASCII text
 *
 * 3. The names "Resin" or "Caucho" are trademarks of Caucho Technology and
 *    may not be used to endorse products derived from this software.
 *    "Resin" or "Caucho" may not appear in the names of products derived
 *    from this software.
 *
 * 4. Caucho Technology requests that attribution be given to Resin
 *    in any manner possible.  We suggest using the "Resin Powered"
 *    button or creating a "powered by Resin(tm)" link to
 *    http://www.caucho.com for each page served by Resin.
 *
 * This Software is provided "AS IS," without a warranty of any kind. 
 * ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.

 * CAUCHO TECHNOLOGY AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR
 * DISTRIBUTING SOFTWARE. IN NO EVENT WILL Caucho OR ITS LICENSORS BE LIABLE
 * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE SOFTWARE, EVEN IF HE HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.      
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <dirent.h>
/* probably system-dependent */
#include <dlfcn.h>
#include <jni.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <signal.h>

#include "cse.h"
#include "version.h"
#include "proxy.h"
#include "proxy_jni.h"

typedef struct chunk_t {
  int bucket;
  struct chunk_t *next;
} chunk_t;

static pthread_mutex_t mem_lock;
static int alloc = 0;
static chunk_t *buckets[1024];

static int
get_bucket(int size)
{
  size += 8;

  if (size < 4096)
    return (size + 255) >> 8;
  else
    return ((size + 4095) >> 12) + 32;
}

static int
get_chunk_size(int size)
{
  if (size + 8 < 4096)
    return 256 * ((size + 8 + 255) / 256);
  else
    return 4096 * ((size + 8 + 4095) / 4096);
}

static void
cse_init_bucket(int size, int alloc_size)
{
  char *data = malloc(alloc_size);
  int bucket = get_bucket(size);
  int chunk_size = get_chunk_size(size);
  int i;

  for (i = 0; i < alloc_size; i += chunk_size) {
    chunk_t *chunk = (chunk_t *) (data + i);
    chunk->bucket = bucket;
    chunk->next = buckets[bucket];
    buckets[bucket] = chunk;
  }
}
  
void
cse_mem_init()
{
  int i;
  
  for (i = 0; i < 4096; i += 256)
    cse_init_bucket(i, 64 * 1024);
  
  for (i = 4096; i < 4 * 4096; i += 4096)
    cse_init_bucket(i, 512 * 1024);
}

void *
cse_malloc(int size)
{
  int bucket = get_bucket(size);
  chunk_t *chunk = 0;

  pthread_mutex_lock(&mem_lock);
  chunk = buckets[bucket];
  if (chunk)
    buckets[bucket] = chunk->next;
  pthread_mutex_unlock(&mem_lock);

  if (! chunk) {
    chunk = (chunk_t *) malloc(get_chunk_size(size));
    /*
    printf("malloc size:%d bucket:%d chunk:%d\n",
           size, bucket, get_chunk_size(size));
    */
    chunk->bucket = bucket;
    chunk->next = 0;
  }

  return ((char *) chunk) + 8;
}

void
cse_free(void *v_data)
{
  chunk_t *chunk = (chunk_t *) (((char *) v_data) - 8);
  int bucket = chunk->bucket;

  if (bucket >= 0 && bucket < 1024) {
    pthread_mutex_lock(&mem_lock);
    chunk->next = buckets[bucket];
    buckets[bucket] = chunk;
    pthread_mutex_unlock(&mem_lock);
  }
  else
    fprintf(stderr, "no bucket\n");
}
