Linux/Centos: 开源库uthash第六弹utringbuffer.h

文章目录

 

一、简介

1.1 介绍

utringbuffer.h提供的宏函数是基于utarray.h来实现的。因此,在学习这篇文章之前,请先学习utarray.h。

使用utringbuffer非常简单,只需要将utarray.h和utringbuffer.h拷贝到你的工程,并包含进你的源码即可:

#include "utringbuffer.h"

utringbuffer提供的方法与C++ STL的verctor方法类似。utringbuffer数据类型支持:构造(指定容量)、析构、迭代、push,但不支持pop。一旦ring-buffer满了,再压入一个元素将自动覆盖最老的一个元素。元素的类型可以是任何基本类型或者自定义结构

在环形缓冲区内部包含一个预先分配的内存区域,从位置0开始将元素复制到其中。当环形缓冲区满时,下一个push的元素将被放在位置0,覆盖最老的元素。环形缓冲区一旦满了,就永远不会变成不满的。

1.2 源码获取

utringbuffer.h的源码可以在GitHub上直接获取(src/utringbuffer.h):

https://github.com/troydhanson/uthash

二、使用方法

2.1 声明

不管环形缓冲区内部存储元素的类型是什么,环形缓冲区的数据类型是UT_ringbuffer。其声明方法如下:

UT_ringbuffer *history;

2.2 new and free

声明环形缓冲区后,接下来就可以使用utringbuffer_new创建环形缓冲区;最后,当你不再使用的时候,使用utringbuffer_free释放环形缓冲区和内部的所有元素。

2.3 使用

环形缓冲区核心特性包括将元素放入其中并对其进行迭代。utringbuffer提供了一些操作,可以一次处理单个元素或多个元素。

三、元素

使用整数或字符串类型作为元素,是动态array最简单的例子。

3.1 整型

下面的例子用整型使用ring-buffer,压入0 ~ 9数据,然后使用两种方式打印出来,最后释放。

/* Integer elements */
#include <stdio.h>
#include "utringbuffer.h"

int main() {
  UT_ringbuffer *history;
  int i, *p;

  utringbuffer_new(history, 7, &ut_int_icd);
  for(i=0; i < 10; i++) utringbuffer_push_back(history, &i);

  for (p = (int*)utringbuffer_front(history);
       p != NULL;
       p = (int*)utringbuffer_next(history, p)) {
    printf("%d\n", *p);  /* prints "3 4 5 6 7 8 9" */
  }

  for (i=0; i < utringbuffer_len(history); i++) {
    p = utringbuffer_eltptr(history, i);
    printf("%d\n", *p);  /* prints "3 4 5 6 7 8 9" */
  }

  utringbuffer_free(history);

  return 0;
}

utarray_push_back的第二个参数必须是指向元素类型的指针。

3.2 字符串

下面实例用字符串类型使用ring-buffer,压入两个字符串,打印然后释放。

/* string elements */
#include <stdio.h>
#include "utringbuffer.h"

int main() {
  UT_ringbuffer *strs;
  char *s, **p;

  utringbuffer_new(strs, 7, &ut_str_icd);

  s = "hello"; utringbuffer_push_back(strs, &s);
  s = "world"; utringbuffer_push_back(strs, &s);
  p = NULL;
  while ( (p=(char**)utringbuffer_next(strs,p))) {
    printf("%s\n",*p);
  }

  utringbuffer_free(strs);

  return 0;
}

在本例中,由于元素类型是char *,因此我们传入的参数为char **。

注意:push操作会导致源字符串到array的拷贝动作。

3.3 关于UT_icd

utringbuffer.h不仅仅支持整数和字符串,还支持其他任何类型的元素。除了整形和字符串类型外,你需要定义一个UT_icd帮助结构体,它包含utringbuffer用到的初始化、拷贝和释放等操作。

typedef struct {
    size_t sz;
    init_f *init;
    ctor_f *copy;
    dtor_f *dtor;
}

其中init、copy和dtor函数指针的原型如下:

typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
  • sz:是需要保存到array的元素的大小;
  • init:函数指针,这个函数是给utarray使用,而不是给utringbuffer。该函数将会在utarray需要初始化一个空元素时被调用。如果init为NULL,那么元素的所有值将会默认用memset设置为0;
  • copy:函数指针,该函数用于元素被push进atringbuffer时被调用,比如utringbuffer_push_back;如果copy为NULL,将会默认默认用memcpy按位进行拷贝;
  • dtor:函数指针,该函数用于元素从ring-buffer中移除时被调用,比如utringbuffer_push_back、utringbuffer_clear、utringbuffer_done和utringbuffer_free;如果元素不需要释放资源,则dtor可以设为NULL。

3.3.1 标准数据类型

下面的例子,使用UT_icd的默认方法,来使用C语言的其他标准类型,比如这里是long。

/* long elements */
#include <stdio.h>
#include "utringbuffer.h"

UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };

int main() {
  UT_ringbuffer *nums;
  long l, *p;
  utringbuffer_new(nums, 1, &long_icd);

  l=1; utringbuffer_push_back(nums, &l);
  l=2; utringbuffer_push_back(nums, &l);

  p=NULL;
  while((p = (long*)utringbuffer_next(nums,p))) printf("%ld\n", *p);

  utringbuffer_free(nums);
  return 0;
}

3.3.2 自定义结构体

用户自定义的结构体也可以作为utringbuffer的元素。如果自定义数据结构不需要特别的初始化、拷贝和释放处理,我们可以使用UT_icd的默认方法;但如果有自己的特殊操作,则需要自行定义对应的方法。

/* Structure type */
#include <stdio.h>
#include "utringbuffer.h"

typedef struct {
    int a;
    int b;
} intpair_t;

UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};

int main() {

  UT_ringbuffer *pairs;
  intpair_t ip, *p;
  utringbuffer_new(pairs, 7, &intpair_icd);

  ip.a=1;  ip.b=2;  utringbuffer_push_back(pairs, &ip);
  ip.a=10; ip.b=20; utringbuffer_push_back(pairs, &ip);

  for(p=(intpair_t*)utringbuffer_front(pairs);
      p!=NULL;
      p=(intpair_t*)utringbuffer_next(pairs,p)) {
    printf("%d %d\n", p->a, p->b);
  }

  utringbuffer_free(pairs);
  return 0;
}

在实际的使用中,我们的结构体是需要有特殊的初始化、拷贝和释放函数的。比如,当我们的结构体包含一个指针指向另外一块区域的时候,我们就需要自定义UT_icd中对应的init、copy和dtor方法了。

这里用到了两个概念:

  • 浅拷贝:只是拷贝结构体中的内容;
  • 深拷贝:将结构体与结构体指针成员指向的所有内存全部拷贝。

下面是一个实现深拷贝的实例:定义了一个整形和字符串数据,初始化时分配字符串内容,拷贝的时候拷贝字符串内容,释放的时候也要释放字符串的内容。

#include <stdio.h>
#include <stdlib.h>
#include "utringbuffer.h"

typedef struct {
    int a;
    char *s;
} intchar_t;

void intchar_copy(void *_dst, const void *_src) {
  intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src;
  dst->a = src->a;
  dst->s = src->s ? strdup(src->s) : NULL;
}

void intchar_dtor(void *_elt) {
  intchar_t *elt = (intchar_t*)_elt;
  free(elt->s);
}

UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};

int main() {
  UT_ringbuffer *intchars;
  intchar_t ic, *p;
  utringbuffer_new(intchars, 2, &intchar_icd);

  ic.a=1; ic.s="hello"; utringbuffer_push_back(intchars, &ic);
  ic.a=2; ic.s="world"; utringbuffer_push_back(intchars, &ic);
  ic.a=3; ic.s="peace"; utringbuffer_push_back(intchars, &ic);

  p=NULL;
  while( (p=(intchar_t*)utringbuffer_next(intchars,p))) {
    printf("%d %s\n", p->a, (p->s ? p->s : "null"));
    /* prints "2 world 3 peace" */
  }

  utringbuffer_free(intchars);
  return 0;
}

四、引用

下表列出了utringbuffer常用的操作宏,所有方法类似于C++的vector类。

Operationsdescription
utringbuffer_new(UT_ringbuffer *a, int n, UT_icd *icd)allocate a new ringbuffer
utringbuffer_free(UT_ringbuffer *a)free an allocated ringbuffer
utringbuffer_init(UT_ringbuffer *a, int n, UT_icd *icd)init a ringbuffer (non-alloc)
utringbuffer_done(UT_ringbuffer *a)dispose of a ringbuffer (non-alloc)
utringbuffer_clear(UT_ringbuffer *a)clear all elements from a, making it empty
utringbuffer_push_back(UT_ringbuffer *a, element *p)push element p onto a
utringbuffer_len(UT_ringbuffer *a)get length of a
utringbuffer_empty(UT_ringbuffer *a)get whether a is empty
utringbuffer_full(UT_ringbuffer *a)get whether a is full
utringbuffer_eltptr(UT_ringbuffer *a, int j)get pointer of element from index
utringbuffer_eltidx(UT_ringbuffer *a, element *e)get index of element from pointer
utringbuffer_front(UT_ringbuffer *a)get oldest element of a
utringbuffer_next(UT_ringbuffer *a, element *e)get element of a following e (front if e is NULL)
utringbuffer_prev(UT_ringbuffer *a, element *e)get element of a before e (back if e is NULL)
utringbuffer_back(UT_ringbuffer *a)get newest element of a

五、注意

  1. utringbuffer_new and utringbuffer_free are used to allocate a new ring-buffer and to free it, while utringbuffer_init and utringbuffer_done can be used if the UT_ringbuffer is already allocated and just needs to be initialized or have its internal resources freed.
  2. Both utringbuffer_new and utringbuffer_init take a second parameter n indicating the capacity of the ring-buffer, that is, the size at which the ring-buffer is considered “full” and begins to overwrite old elements with newly pushed ones.
  3. Once a ring-buffer has become full, it will never again become un-full except by means of utringbuffer_clear. There is no way to “pop” a single old item from the front of the ring-buffer. You can simulate this ability by maintaining a separate integer count of the number of “logically popped elements”, and starting your iteration with utringbuffer_eltptr(a, popped_count) instead of with utringbuffer_front(a).
  4. Pointers to elements (obtained using utringbuffer_eltptr, utringbuffer_front, utringbuffer_next, etc.) are not generally invalidated by utringbuffer_push_back, because utringbuffer does not perform reallocation; however, a pointer to the oldest element may suddenly turn into a pointer to the newest element if utringbuffer_push_back is called while the buffer is full.
  5. The elements of a ring-buffer are stored in contiguous memory, but once the ring-buffer has become full, it is no longer true that the elements are contiguously in order from oldest to newest; i.e., (element *)utringbuffer_front(a) + utringbuffer_len(a)-1 is not generally equal to (element *)utringbuffer_back(a).
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值