在处理网络分包,包未收完整时,必定需要一个缓冲区来缓存数据,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
- 头尾的下一位置为:([email protected]+n) % cap
- 要根据头尾的位置做分段处理
具体实现就放在github上了
时间: 2024-08-08 13:45:47