MemBuf.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:9k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: MemBuf.c,v 1.22 1999/01/19 02:24:19 wessels Exp $
  3.  *
  4.  * DEBUG: section 59    auto-growing Memory Buffer with printf
  5.  * AUTHOR: Alex Rousskov
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  8.  * ----------------------------------------------------------
  9.  *
  10.  *  Squid is the result of efforts by numerous individuals from the
  11.  *  Internet community.  Development is led by Duane Wessels of the
  12.  *  National Laboratory for Applied Network Research and funded by the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. /*
  35.  * To-Do: use memory pools for .buf recycling @?@ @?@
  36.  */
  37. /*
  38.  * Rationale:
  39.  * ----------
  40.  * 
  41.  * Here is how one would comm_write an object without MemBuffer:
  42.  * 
  43.  * {
  44.  * -- allocate:
  45.  * buf = malloc(big_enough);
  46.  * 
  47.  * -- "pack":
  48.  * snprintf object(s) piece-by-piece constantly checking for overflows
  49.  * and maintaining (buf+offset);
  50.  * ...
  51.  * 
  52.  * -- write
  53.  * comm_write(buf, free, ...);
  54.  * }
  55.  * 
  56.  * The whole "packing" idea is quite messy: We are given a buffer of fixed
  57.  * size and we have to check all the time that we still fit. Sounds logical.
  58.  *
  59.  * However, what happens if we have more data? If we are lucky to stop before
  60.  * we overrun any buffers, we still may have garbage (e.g. half of ETag) in
  61.  * the buffer.
  62.  * 
  63.  * MemBuffer:
  64.  * ----------
  65.  * 
  66.  * MemBuffer is a memory-resident buffer with printf()-like interface. It
  67.  * hides all offest handling and overflow checking. Moreover, it has a
  68.  * build-in control that no partial data has been written.
  69.  * 
  70.  * MemBuffer is designed to handle relatively small data. It starts with a
  71.  * small buffer of configurable size to avoid allocating huge buffers all the
  72.  * time.  MemBuffer doubles the buffer when needed. It assert()s that it will
  73.  * not grow larger than a configurable limit. MemBuffer has virtually no
  74.  * overhead (and can even reduce memory consumption) compared to old
  75.  * "packing" approach.
  76.  * 
  77.  * MemBuffer eliminates both "packing" mess and truncated data:
  78.  * 
  79.  * {
  80.  * -- setup
  81.  * MemBuf buf;
  82.  * 
  83.  * -- required init with optional size tuning (see #defines for defaults)
  84.  * memBufInit(&buf, initial-size, absolute-maximum);
  85.  * 
  86.  * -- "pack" (no need to handle offsets or check for overflows)
  87.  * memBufPrintf(&buf, ...);
  88.  * ...
  89.  * 
  90.  * -- write
  91.  * comm_write_mbuf(fd, buf, handler, data);
  92.  *
  93.  * -- *iff* you did not give the buffer away, free it yourself
  94.  * -- memBufClean(&buf);
  95.  * }
  96.  */
  97. #include "squid.h"
  98. /* local constants */
  99. /* default values for buffer sizes, used by memBufDefInit */
  100. #define MEM_BUF_INIT_SIZE   (2*1024)
  101. #define MEM_BUF_MAX_SIZE    (2*1000*1024*1024)
  102. /* local routines */
  103. static void memBufGrow(MemBuf * mb, mb_size_t min_cap);
  104. /* init with defaults */
  105. void
  106. memBufDefInit(MemBuf * mb)
  107. {
  108.     memBufInit(mb, MEM_BUF_INIT_SIZE, MEM_BUF_MAX_SIZE);
  109. }
  110. /* init with specific sizes */
  111. void
  112. memBufInit(MemBuf * mb, mb_size_t szInit, mb_size_t szMax)
  113. {
  114.     assert(mb);
  115.     assert(szInit > 0 && szMax > 0);
  116.     mb->buf = NULL;
  117.     mb->size = 0;
  118.     mb->max_capacity = szMax;
  119.     mb->capacity = 0;
  120.     mb->freefunc = NULL;
  121.     memBufGrow(mb, szInit);
  122. }
  123. /*
  124.  * cleans the mb; last function to call if you do not give .buf away with
  125.  * memBufFreeFunc
  126.  */
  127. void
  128. memBufClean(MemBuf * mb)
  129. {
  130.     assert(mb);
  131.     assert(mb->buf);
  132.     assert(mb->freefunc); /* not frozen */
  133.     (*mb->freefunc) (mb->buf); /* free */
  134.     mb->freefunc = NULL; /* freeze */
  135.     mb->buf = NULL;
  136.     mb->size = mb->capacity = 0;
  137. }
  138. /* cleans the buffer without changing its capacity 
  139.  * if called with a Null buffer, calls memBufDefInit() */
  140. void
  141. memBufReset(MemBuf * mb)
  142. {
  143.     assert(mb);
  144.     if (memBufIsNull(mb)) {
  145. memBufDefInit(mb);
  146.     } else {
  147. assert(mb->freefunc); /* not frozen */
  148. /* reset */
  149. memset(mb->buf, 0, mb->capacity);
  150. mb->size = 0;
  151.     }
  152. }
  153. /* unfortunate hack to test if the buffer has been Init()ialized */
  154. int
  155. memBufIsNull(MemBuf * mb)
  156. {
  157.     assert(mb);
  158.     if (!mb->buf && !mb->max_capacity && !mb->capacity && !mb->size)
  159. return 1; /* is null (not initialized) */
  160.     assert(mb->buf && mb->max_capacity && mb->capacity); /* paranoid */
  161.     return 0;
  162. }
  163. /* calls memcpy, appends exactly size bytes, extends buffer if needed */
  164. void
  165. memBufAppend(MemBuf * mb, const char *buf, mb_size_t sz)
  166. {
  167.     assert(mb && buf && sz >= 0);
  168.     assert(mb->buf);
  169.     assert(mb->freefunc); /* not frozen */
  170.     if (sz > 0) {
  171. if (mb->size + sz > mb->capacity)
  172.     memBufGrow(mb, mb->size + sz);
  173. assert(mb->size + sz <= mb->capacity); /* paranoid */
  174. xmemcpy(mb->buf + mb->size, buf, sz);
  175. mb->size += sz;
  176.     }
  177. }
  178. /* calls memBufVPrintf */
  179. #if STDC_HEADERS
  180. void
  181. memBufPrintf(MemBuf * mb, const char *fmt,...)
  182. {
  183.     va_list args;
  184.     va_start(args, fmt);
  185. #else
  186. void
  187. memBufPrintf(va_alist)
  188.      va_dcl
  189. {
  190.     va_list args;
  191.     MemBuf *mb = NULL;
  192.     const char *fmt = NULL;
  193.     mb_size_t sz = 0;
  194.     va_start(args);
  195.     mb = va_arg(args, MemBuf *);
  196.     fmt = va_arg(args, char *);
  197. #endif
  198.     memBufVPrintf(mb, fmt, args);
  199.     va_end(args);
  200. }
  201. /* vprintf for other printf()'s to use; calls vsnprintf, extends buf if needed */
  202. void
  203. memBufVPrintf(MemBuf * mb, const char *fmt, va_list vargs)
  204. {
  205.     int sz = 0;
  206.     assert(mb && fmt);
  207.     assert(mb->buf);
  208.     assert(mb->freefunc); /* not frozen */
  209.     /* assert in Grow should quit first, but we do not want to have a scary infinite loop */
  210.     while (mb->capacity <= mb->max_capacity) {
  211. mb_size_t free_space = mb->capacity - mb->size;
  212. /* put as much as we can */
  213. sz = vsnprintf(mb->buf + mb->size, free_space, fmt, vargs);
  214. /* check for possible overflow */
  215. /* snprintf on Linuz returns -1 on overflows */
  216. /* snprintf on FreeBSD returns at least free_space on overflows */
  217. if (sz < 0 || sz >= free_space)
  218.     memBufGrow(mb, mb->capacity + 1);
  219. else
  220.     break;
  221.     }
  222.     mb->size += sz;
  223.     /* on Linux and FreeBSD, '' is not counted in return value */
  224.     /* on XXX it might be counted */
  225.     /* check that '' is appended and not counted */
  226.     if (!mb->size || mb->buf[mb->size - 1]) {
  227. assert(!mb->buf[mb->size]);
  228.     } else {
  229. mb->size--;
  230.     }
  231. }
  232. /*
  233.  * returns free() function to be used.
  234.  * Important:
  235.  *   calling this function "freezes" mb,
  236.  *   do not _update_ mb after that in any way
  237.  *   (you still can read-access .buf and .size)
  238.  */
  239. FREE *
  240. memBufFreeFunc(MemBuf * mb)
  241. {
  242.     FREE *ff;
  243.     assert(mb);
  244.     assert(mb->buf);
  245.     assert(mb->freefunc); /* not frozen */
  246.     ff = mb->freefunc;
  247.     mb->freefunc = NULL; /* freeze */
  248.     return ff;
  249. }
  250. /* grows (doubles) internal buffer to satisfy required minimal capacity */
  251. static void
  252. memBufGrow(MemBuf * mb, mb_size_t min_cap)
  253. {
  254.     mb_size_t new_cap;
  255.     MemBuf old_mb;
  256.     assert(mb);
  257.     assert(mb->capacity < min_cap);
  258.     /* determine next capacity */
  259.     new_cap = mb->capacity;
  260.     if (new_cap > 0)
  261. while (new_cap < min_cap)
  262.     new_cap *= 2; /* double */
  263.     else
  264. new_cap = min_cap;
  265.     /* last chance to fit before we assert(!overflow) */
  266.     if (new_cap > mb->max_capacity)
  267. new_cap = mb->max_capacity;
  268.     assert(new_cap <= mb->max_capacity); /* no overflow */
  269.     assert(new_cap > mb->capacity); /* progress */
  270.     old_mb = *mb;
  271.     /* allocate new memory */
  272.     switch (new_cap) {
  273.     case 2048:
  274. mb->buf = memAllocate(MEM_2K_BUF);
  275. mb->freefunc = &memFree2K;
  276. break;
  277.     case 4096:
  278. mb->buf = memAllocate(MEM_4K_BUF);
  279. mb->freefunc = &memFree4K;
  280. break;
  281.     case 8192:
  282. mb->buf = memAllocate(MEM_8K_BUF);
  283. mb->freefunc = &memFree8K;
  284. break;
  285.     default:
  286. /* recycle if old buffer was not "pool"ed */
  287. if (old_mb.freefunc == &xfree) {
  288.     mb->buf = xrealloc(old_mb.buf, new_cap);
  289.     old_mb.buf = NULL;
  290.     old_mb.freefunc = NULL;
  291.     /* init tail, just in case */
  292.     memset(mb->buf + mb->size, 0, new_cap - mb->size);
  293. } else {
  294.     mb->buf = xcalloc(1, new_cap);
  295.     mb->freefunc = &xfree;
  296. }
  297.     }
  298.     /* copy and free old buffer if needed */
  299.     if (old_mb.buf && old_mb.freefunc) {
  300. memcpy(mb->buf, old_mb.buf, old_mb.size);
  301. (*old_mb.freefunc) (old_mb.buf);
  302.     } else {
  303. assert(!old_mb.buf && !old_mb.freefunc);
  304.     }
  305.     /* done */
  306.     mb->capacity = new_cap;
  307. }
  308. /* Reports */
  309. /* puts report on MemBuf _module_ usage into mb */
  310. void
  311. memBufReport(MemBuf * mb)
  312. {
  313.     assert(mb);
  314.     memBufPrintf(mb, "memBufReport is not yet implemented @?@n");
  315. }