Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

sbuf.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: sbuf.c,v 1.15.2.1 2004/12/07 03:05:40 pneumatus Exp $
 */

#include "sbuf.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "memory.h"
#include <stdio.h>
#include <stdlib.h>

SBuffer *small_pool = NULL;
SBuffer     *large_pool = NULL;
SBufBlock *sbuf_blocks = NULL;

SBufUser *user_pool = NULL;
SBufUserBlock *user_blocks = NULL;

int sbuf_small_used = 0, sbuf_small_total = 0;
int sbuf_large_used = 0, sbuf_large_total = 0;
int sbuf_user_used = 0, sbuf_user_total = 0;
int sbuf_blocks_used = 0, sbuf_userblocks_used = 0;

static void sbuf_allocblock(int size, int n, SBuffer **pool)
{
      SBufBlock *block;
      SBuffer *buf;
      int i;

      block = (SBufBlock *)MyMalloc(sizeof(SBufBlock));
      ASSERT(block != NULL);

      block->bufs = (SBuffer *)MyMalloc(size * n);
      ASSERT(block->bufs != NULL);

      block->num = n;
      block->next = sbuf_blocks;
      sbuf_blocks = block;
      sbuf_blocks_used++;

      buf = block->bufs;
      for (i = 0; i < n - 1; i++) {
            buf->bufsize = size - SBUF_BASE;
            buf->next = (SBuffer *)(((char *)buf) + size);
            buf = buf->next;
      }

      buf->bufsize = size - SBUF_BASE;
      buf->next = *pool;
      *pool = block->bufs;
}

static void sbuf_allocblock_small()
{
      int size = INITIAL_SBUF_SMALL;

      if ((size % SBUF_SMALL_TOTAL) != 0) {
            size += SBUF_SMALL_TOTAL;
      }

      sbuf_small_total += (size / SBUF_SMALL_TOTAL);
      sbuf_allocblock(SBUF_SMALL_TOTAL, (size / SBUF_SMALL_TOTAL), &small_pool);
}

static void sbuf_allocblock_large()
{
      int size = INITIAL_SBUF_LARGE;

      if ((size % SBUF_LARGE_TOTAL) != 0) {
            size += SBUF_LARGE_TOTAL;
      }

      sbuf_large_total += (size / SBUF_LARGE_TOTAL);
      sbuf_allocblock(SBUF_LARGE_TOTAL, (size / SBUF_LARGE_TOTAL), &large_pool);
}

static void sbuf_allocblock_users()
{
      SBufUserBlock *block;
      SBufUser *users;
      int i;

      block = (SBufUserBlock *)MyMalloc(sizeof(SBufUserBlock));
      ASSERT(block != NULL);

      block->users = (SBufUser *)MyMalloc(sizeof(SBufUser) * INITIAL_SBUF_USER_POOL);
      ASSERT(block->users != NULL);
      block->num = INITIAL_SBUF_USER_POOL;

      block->next = user_blocks;
      user_blocks = block;

      sbuf_userblocks_used++;
      sbuf_user_total += block->num;

      users = block->users;
      for (i = 0; i < block->num - 1; i++) {
            users->next = users + 1;
            users++;
      }

      users->next = user_pool;
      user_pool = block->users;
}

static void return_sbuf_to_pool(SBuffer *sbuf)
{
      ASSERT(sbuf != NULL);

      switch (sbuf->bufsize) {
            case SBUF_LARGE_BUFFER:
                  sbuf->next = large_pool;
                  large_pool = sbuf;
                  sbuf_large_used--;
                  break;
            case SBUF_SMALL_BUFFER:
                  sbuf->next = small_pool;
                  small_pool = sbuf;
                  sbuf_small_used--;
                  break;
            default:
                  ASSERT("unknown sbuf bufsize" == NULL);
      }
}

static void return_user_to_pool(SBufUser *user)
{
      ASSERT(user != NULL);
      user->next = user_pool;
      user_pool = user;
      sbuf_user_used--;
}

void init_sbuf()
{
      sbuf_allocblock_small();
      sbuf_allocblock_large();
      sbuf_allocblock_users();
}

SBuffer *sbuf_alloc(int size)
{
      SBuffer *sbuf;

      if (size >= SBUF_SMALL_BUFFER) {
            if ((sbuf = large_pool) == NULL) {
                  sbuf_allocblock_large();
                  if ((sbuf = large_pool) == NULL) {
                        return NULL;
                  }
            }
            large_pool = large_pool->next;
            sbuf_large_used++;
            sbuf->bufsize = SBUF_LARGE_BUFFER;
      }
      else {
            if ((sbuf = small_pool) == NULL) {
                  sbuf_allocblock_small();
                  if ((sbuf = small_pool) == NULL) {
                        return sbuf_alloc(SBUF_SMALL_BUFFER + 1);
                  }
            }
            small_pool = small_pool->next;
            sbuf_small_used++;
            sbuf->bufsize = SBUF_SMALL_BUFFER;
      }

      sbuf->refcount = 0;
      sbuf->end = ((char *)sbuf) + SBUF_BASE;
      sbuf->next = NULL;
      sbuf->shared = 0;

      return sbuf;
}

SBufUser *user_alloc()
{
      SBufUser *user;

      if ((user = user_pool) == NULL) {
            sbuf_allocblock_users();
            if ((user = user_pool) == NULL) {
                  return NULL;
            }
      }
      user_pool = user_pool->next;
      sbuf_user_used++;

      user->next = NULL;
      user->start = NULL;
      user->buf = NULL;

      return user;
}

int sbuf_alloc_error()
{
      outofmemory();
      return -1;
}

int sbuf_begin_share(const char *data, int len, void **sbuf_ptr)
{
      SBuffer *sbuf;

      if (len > (SBUF_LARGE_BUFFER - 2)) {
            len = (SBUF_LARGE_BUFFER - 2);
      }
      if ((sbuf = sbuf_alloc(len + 2)) == NULL) {
            return sbuf_alloc_error();
      }

      memcpy(sbuf->end, data, len);

      sbuf->end += len;
      *sbuf->end++ = '\r';
      *sbuf->end++ = '\n';

      sbuf->refcount = 0;
      sbuf->shared = 1;

      *sbuf_ptr = (void *)sbuf;

      return 1;
}

void sbuf_end_share(void **sbuf_ptr, int sbuf_cnt)
{
      SBuffer **shares = (SBuffer **)sbuf_ptr;
      int i;

      for (i = 0; i < sbuf_cnt; i++) {
            if (shares[i] == NULL) {
                  continue;
            }

            shares[i]->shared = 0;
            if (!shares[i]->refcount) {
                  return_sbuf_to_pool(shares[i]);
            }
      }
}

int sbuf_put_share(SBuf *buf, void *sbuf_ptr)
{
      SBufUser *user;
      SBuffer *sbuf = (SBuffer *)sbuf_ptr;

      if (sbuf == NULL) {
            return -1;
      }

      sbuf->refcount++;

      user = user_alloc();
      user->buf = sbuf;
      user->start = (char *)(user->buf) + SBUF_BASE;

      if (!buf->length) {
            buf->head = buf->tail = user;
      }
      else {
            buf->tail->next = user;
            buf->tail = user;
      }
      buf->length += (user->buf->end - user->start);
      return 1;
}


int sbuf_put(SBuf *buf, const char *data, int len)
{
      SBufUser **users, *user;
      int chunk;

      ASSERT(buf != NULL);
      ASSERT(data != NULL);

      users = (!buf->length) ? &buf->head : &buf->tail;

      if ((user = *users) != NULL && (user->buf->refcount > 1)) {
            user->next = user_alloc();
            if ((user = user->next) == NULL) {
                  return sbuf_alloc_error();
            }

            *users = user;

            user->buf = sbuf_alloc(len);
            user->buf->refcount = 1;
            user->start = user->buf->end;
      }

      buf->length += len;

      while (len) {
            if ((user = *users) == NULL) {
                  if ((user = user_alloc()) == NULL) {
                        return sbuf_alloc_error();
                  }
                  *users = user;
                  buf->tail = user;

                  user->buf = sbuf_alloc(len);
                  user->buf->refcount = 1;
                  user->start = user->buf->end;
            }

            chunk = (((char *)user->buf) + SBUF_BASE + user->buf->bufsize) - user->buf->end;
            if (chunk) {
                  if (chunk > len) {
                        chunk = len;
                  }

                  memcpy(user->buf->end, data, chunk);

                  user->buf->end += chunk;
                  data += chunk;

                  len -= chunk;
            }
            users = &(user->next);
      }

      return 1;
}

void sbuf_delete(SBuf *buf, int len)
{
      int chunk;

      ASSERT(buf != NULL);

      if (len > buf->length) {
            len = buf->length;
      }

      buf->length -= len;
      while (len) {
            chunk = buf->head->buf->end - buf->head->start;
            if (chunk > len) {
                  chunk = len;
            }

            buf->head->start += chunk;
            len -= chunk;

            if (buf->head->start == buf->head->buf->end) {
                  SBufUser *user = buf->head;
                  buf->head = buf->head->next;

                  user->buf->refcount--;
                  if (!user->buf->refcount && !user->buf->shared) {
                        return_sbuf_to_pool(user->buf);
                  }
                  return_user_to_pool(user);
            }
      }
      if (buf->head == NULL) {
            buf->tail = NULL;
            ASSERT(buf->length == 0);
      }
}

char *sbuf_map(SBuf *buf, int *len)
{
      if (buf->length) {
            *len = buf->head->buf->end - buf->head->start;
            return buf->head->start;
      }

      *len = 0;
      return NULL;
}

int sbuf_flush(SBuf *buf)
{
      SBufUser *user;
      char *data;

      ASSERT(buf != NULL);

      if (!buf->length) {
            return 0;
      }

      while (buf->head != NULL) {
            data = buf->head->start;

            while ((data < buf->head->buf->end) && IsEOL(*data)) {
                  data++;
            }

            buf->length -= data - buf->head->start;
            buf->head->start = data;

            if (data < buf->head->buf->end) {
                  break;
            }

            user = buf->head;
            buf->head = buf->head->next;

            user->buf->refcount--;
            if (!user->buf->refcount && !user->buf->shared) {
                  return_sbuf_to_pool(user->buf);
            }
            return_user_to_pool(user);
      }

      if (buf->head == NULL) {
            buf->tail = NULL;
      }

      return buf->length;
}

int sbuf_getmsg(SBuf *buf, char *data, int len)
{
      SBufUser *user;
      int copied = 0;
      char *p, *max;

      if (!sbuf_flush(buf)) {
            return 0;
      }
      
      Debug((DEBUG_DEBUG, "sbuf_getmsg() len %d", len));
      for (user = buf->head; (user != NULL) && (len > 0); user = user->next) {
            if ((max = user->start + len) > user->buf->end) {
                  max = user->buf->end;
            }

            p = user->start;
            while ((p < max) && !IsEOL(*p)) {
                  *data++ = *p++;
            }

            copied += p - user->start;
            len -= p - user->start;

            if (p < max) {
                  *data = '\0';
                  sbuf_delete(buf, copied);
                  sbuf_flush(buf);
                  return copied;
            }
      }

      return 0;
}

int sbuf_get(SBuf *buf, char *data, int len)
{
      char *p;
      int chunk, copied = 0;

      if (!buf->length) {
            return 0;
      }

      while (len && (p = sbuf_map(buf, &chunk)) != NULL) {
            if (chunk > len) {
                  chunk = len;
            }

            memcpy(data, p, chunk);

            copied += chunk;
            data += chunk;
            len -= chunk;

            sbuf_delete(buf, chunk);
      }

      return copied;
}

Generated by  Doxygen 1.6.0   Back to index