text: 
  /*
   * Copyright (C) Igor Sysoev
   * Copyright (C) Nginx, Inc.
   */
  
  
  #include <ngx_config.h>
  #include <ngx_core.h>
  
  
  ngx_buf_t *
  ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
  {
      ngx_buf_t *b;
  
      b = ngx_calloc_buf(pool);
      if (b == NULL) {
          return NULL;
      }
  
      b->start = ngx_palloc(pool, size);
      if (b->start == NULL) {
          return NULL;
      }
  
      /*
       * set by ngx_calloc_buf():
       *
       *     b->file_pos = 0;
       *     b->file_last = 0;
       *     b->file = NULL;
       *     b->shadow = NULL;
       *     b->tag = 0;
       *     and flags
       */
  
      b->pos = b->start;
      b->last = b->start;
      b->end = b->last + size;
      b->temporary = 1;
  
      return b;
  }
  
  
  /* *
   * 创建一个缓冲区的链表结构
   * 用缓冲区的链表结构,来管理整个缓冲区;
   * 缓冲区链表与内存池直接关联;
   * */
  ngx_chain_t *
  ngx_alloc_chain_link(ngx_pool_t *pool)
  {
      ngx_chain_t  *cl;
  
      cl = pool->chain;
  
      //如果内存池上有, 则取第一个, 并将内存池的chain指针后移
      if (cl) {
          pool->chain = cl->next;
          return cl;
      }
  
      cl = ngx_palloc(pool, sizeof(ngx_chain_t));
      if (cl == NULL) {
          return NULL;
      }
  
      return cl;
  }
  
  
  ngx_chain_t *
  ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs)
  {
      u_char       *p;
      ngx_int_t     i;
      ngx_buf_t    *b;
      ngx_chain_t  *chain, *cl, **ll;
  
      p = ngx_palloc(pool, bufs->num * bufs->size);
      if (p == NULL) {
          return NULL;
      }
  
      ll = &chain;
  
      for (i = 0; i < bufs->num; i++) {
  
          b = ngx_calloc_buf(pool);
          if (b == NULL) {
              return NULL;
          }
  
          /*
           * set by ngx_calloc_buf():
           *
           *     b->file_pos = 0;
           *     b->file_last = 0;
           *     b->file = NULL;
           *     b->shadow = NULL;
           *     b->tag = 0;
           *     and flags
           *
           */
  
          b->pos = p;
          b->last = p;
          b->temporary = 1;
  
          b->start = p;
          p += bufs->size;
          b->end = p;
  
          cl = ngx_alloc_chain_link(pool);
          if (cl == NULL) {
              return NULL;
          }
  
          cl->buf = b;
          *ll = cl;
          ll = &cl->next;
      }
  
      *ll = NULL;
  
      return chain;
  }
  
  
  ngx_int_t
  ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)
  {
      ngx_chain_t  *cl, **ll;
  
      ll = chain;
  
      for (cl = *chain; cl; cl = cl->next) {
          ll = &cl->next;
      }
  
      while (in) {
          cl = ngx_alloc_chain_link(pool);
          if (cl == NULL) {
              return NGX_ERROR;
          }
  
          cl->buf = in->buf;
          *ll = cl;
          ll = &cl->next;
          in = in->next;
      }
  
      *ll = NULL;
  
      return NGX_OK;
  }
  
  /* *
   * 获取一条未使用的buf链表,这个链表中只有一个buf
   * */
  ngx_chain_t *
  ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)
  {
      ngx_chain_t  *cl;
  
      //空闲链表不为空, 则取free链中的第一个chain返回
      if (*free) {
          cl = *free;
          *free = cl->next;
          cl->next = NULL;
          return cl;
      }
  
      //分配一个链表
      cl = ngx_alloc_chain_link(p);
      if (cl == NULL) {
          return NULL;
      }
  
      //在该链表上分配一个buf
      cl->buf = ngx_calloc_buf(p);
      if (cl->buf == NULL) {
          return NULL;
      }
  
      cl->next = NULL;
  
      return cl;
  }
  
  
  void
  ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy,
      ngx_chain_t **out, ngx_buf_tag_t tag)
  {
      ngx_chain_t  *cl;
  
      if (*out) {
          if (*busy == NULL) {
              *busy = *out;
  
          } else {
              for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
  
              cl->next = *out;
          }
  
          *out = NULL;
      }
  
      while (*busy) {
          cl = *busy;
  
          if (ngx_buf_size(cl->buf) != 0) {
              break;
          }
  
          if (cl->buf->tag != tag) {
              *busy = cl->next;
              ngx_free_chain(p, cl);
              continue;
          }
  
          cl->buf->pos = cl->buf->start;
          cl->buf->last = cl->buf->start;
  
          *busy = cl->next;
          cl->next = *free;
          *free = cl;
      }
  }
  
  
  off_t
  ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit)
  {
      off_t         total, size, aligned, fprev;
      ngx_fd_t      fd;
      ngx_chain_t  *cl;
  
      total = 0;
  
      cl = *in;
      fd = cl->buf->file->fd;
  
      do {
          size = cl->buf->file_last - cl->buf->file_pos;
  
          if (size > limit - total) {
              size = limit - total;
  
              aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
                         & ~((off_t) ngx_pagesize - 1);
  
              if (aligned <= cl->buf->file_last) {
                  size = aligned - cl->buf->file_pos;
              }
  
              total += size;
              break;
          }
  
          total += size;
          fprev = cl->buf->file_pos + size;
          cl = cl->next;
  
      } while (cl
               && cl->buf->in_file
               && total < limit
               && fd == cl->buf->file->fd
               && fprev == cl->buf->file_pos);
  
      *in = cl;
  
      return total;
  }
  
  
  ngx_chain_t *
  ngx_chain_update_sent(ngx_chain_t *in, off_t sent)
  {
      off_t  size;
  
      for ( /* void */ ; in; in = in->next) {
  
          if (ngx_buf_special(in->buf)) {
              continue;
          }
  
          if (sent == 0) {
              break;
          }
  
          size = ngx_buf_size(in->buf);
  
          if (sent >= size) {
              sent -= size;
  
              if (ngx_buf_in_memory(in->buf)) {
                  in->buf->pos = in->buf->last;
              }
  
              if (in->buf->in_file) {
                  in->buf->file_pos = in->buf->file_last;
              }
  
              continue;
          }
  
          if (ngx_buf_in_memory(in->buf)) {
              in->buf->pos += (size_t) sent;
          }
  
          if (in->buf->in_file) {
              in->buf->file_pos += sent;
          }
  
          break;
      }
  
      return in;
  }
  