• 环形队列


    在处理网络分包,包未收完整时,必定需要一个缓冲区来缓存数据,ring_buffer是最常用的选择。它是一个比较简单的数据结构,在学《数据结构》时想必大家都实现过,但我前几天就被它教育了一把,write时,一处分支漏了个等号,些许情况下会缓冲区溢出,导致堆栈一些数据被破坏,让我足足花了一整天才找到这个bug,我错误的地方如下:

     1 int
     2 rbuf_write(struct ring_buffer *self,const char *data,int n){
     3     assert(self);
     4     assert(data);
     5     if(n <= 0){
     6         return 0;
     7     }
     8     for(;;){
     9         unsigned int len = rbuf_length(self) + n;
    10         if(len >= INT_MAX){
    11             return -1;
    12         }
    13         if(self->cap - rbuf_length(self) -1 < n){
    14             rbuf__expand(self);
    15         }
    16         else{
    17                         //少了等号
    18             if(self->tail >self->head){
    19                 int copy1 = self->cap - self->tail;
    20                 if(copy1 > n ){
    21                     copy1 = n;
    22                 }
    23                 memcpy(self->buf + self->tail,data,copy1);
    24                 if(copy1 < n){
    25                     memcpy(self->buf,data + copy1,n - copy1);
    26                 }
    27             }
    28             else{
    29                 memcpy(self->buf + self->tail,data,n);
    30             }
    31             self->tail = (self->tail + n ) % self->cap;
    32             return n;
    33         }
    34     }
    35 }    

    第18行的判断应该是>=,否则当n>(self->cap - self->tail)就会溢出。


    这里再来回忆一番。我要的接口如下:

    1 struct ring_buffer;
    2 struct ring_buffer * rbuf_create(int n);
    3 void rbuf_destory(struct ring_buffer *);
    4 int rbuf_length(struct ring_buffer *);
    5 //return 0:length shorter than n, n:success
    6 int rbuf_read(struct ring_buffer *self,char *dest,int n);
    7 //return -1:length over INT_MAX  n:success
    8 int rbuf_write(struct ring_buffer *self,const char *data,int n);

    读写时要么读写指定的大小,要么失败。实现它,只需注意如下几点,就不会写错了:

    • 头进尾出,头指向下一个出队的位置,尾指向下一个入队的位置。
    • head == tail时为空队列
    • 队列长度为:(tail - head + cap) % cap
    • 因为头尾相等为空,所以队列可用空间要比容量少1,因次队列满为:队列长度 = cap - 1
    • 头尾的下一位置为:($@+n) % cap
    • 要根据头尾的位置做分段处理

    具体实现就放在github上了

  • 相关阅读:
    存储过程分页
    SQL內置Function游标函数
    SQL 2000中的触发器使用
    使用.NET自带的功能制作简单的注册码
    在ASP.NET里轻松实现缩略图
    推荐几个用得上且免费的 .NET控件
    SQL內置Function日期和时间函数
    常用的asp代碼和javascript代碼
    SQL內置Function元数据函数
    數據庫中代@@的參數說明
  • 原文地址:https://www.cnblogs.com/watercoldyi/p/5774802.html
Copyright © 2020-2023  润新知