📄 circular_buffer.inl
字号:
/* * circularbuffer.inl * * Copyright (c) 2004 Network for Educational Technology ETH * * Written by Hannes Friederich, Andreas Fenkart. * Based on work of Shawn Pai-Hsiang Hsiao * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * maccoreaudio.h * */#ifndef CIRCULAR_BUFFER_H#define CIRCULAR_BUFFER_H//#define PTRACE_CIRC(level, str) PTRACE(level, str) #define PTRACE_CIRC(level, str) /** * Simple circular buffer for one producer and consumer with a head and a * tail that chase each other. The capacity is 1 byte bigger than necessary * due to a sentinel, to tell apart full from empty by the following equations: * * full := head_next == tail * empty := head == tail * * Inspired by CircularBuffer from beaudio. * We need a lock when updating the tail index, due to the overwrite mechanism * in case of buffer overflow. The head index needs no locking because * overwrite does not make sense in case of buffer underrun. * * Keep in mind that this buffer does no know about frames or packets, it's up * to you to make sure you get the right number of bytes. In doubt use size() * to compute how many frames/packets it is save to drain/fill. * */class CircularBuffer{ public: explicit CircularBuffer(PINDEX len) : capacity(len + 1), /* plus sentinel */ head(0), tail(0) { buffer = (char *)malloc(capacity*sizeof(char)); if(!buffer) { PTRACE(1, "Could not allocate circular buffer"); init = false; } else { init = true; } /* * Mutex to block write thread when buffer is full */ int rval; rval = pthread_mutex_init(&mutex, NULL); if (rval) { PTRACE(1, __func__ << " can not init mutex"); init = false; //return FALSE; } rval = pthread_cond_init(&cond, NULL); if (rval) { PTRACE(1, __func__ << " can not init cond"); init = false; //return FALSE; } } ~CircularBuffer() { if(buffer){ std::free(buffer); buffer = NULL; } pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); } BOOL Full() { /* head + 1 == tail */ return head_next() == tail; } BOOL Empty() { return head == tail; } PINDEX size(){ /* sentinel is outside of occupied area */ return (head < tail ) ? head + capacity - tail : head - tail; } PINDEX free(){ return (capacity - size() -1 /*sentinel */); } /** * Fill inserts data into the circular buffer. * Remind that lock and overwrite are mutually exclusive. If you set lock, * overwrite will be ignored */ PINDEX Fill(const char* inbuf, PINDEX len, Boolean lock = true, Boolean overwrite = false); /** See also Fill */ PINDEX Drain(char* outbuf, PINDEX len, Boolean lock = true); private: PINDEX head_next() { //return (head + 1 == capacity) ? 0 : (head + 1); return (head + 1 % capacity); } void increment_index(PINDEX &index, PINDEX inc) { index += inc; index %= capacity; } void increment_head(PINDEX inc) { increment_index(head, inc); } void increment_tail(PINDEX inc) { increment_index(tail, inc); } private: char* buffer; const PINDEX capacity; PINDEX head, tail; Boolean init; pthread_mutex_t mutex; pthread_cond_t cond;};int CircularBuffer::Fill(const char *inbuf, PINDEX len, Boolean lock, Boolean overwrite){ int done = 0, todo = 0; PTRACE_CIRC(1, "Fill " << len << " bytes, contains " << size()); if(inbuf== NULL){ PTRACE(1, __func__ << "Input buffer is empty"); return 0; } while(done != len && !Full()) { if(head >= tail) { // head unwrapped, fill from head till end of buffer if(tail == 0) /* buffer[capacity] == sentinel */ todo = MIN(capacity -1 /*sentinel*/ - head, len - done); else todo = MIN(capacity - head, len - done); } else { // fill from head till tail todo = MIN(tail -1 /*sentinel*/ - head, len - done); } memcpy(buffer + head, inbuf + done, todo); done += todo; increment_head(todo); PTRACE_CIRC(1, "copied " << done << " from input buffer" << " available " << size()); } // What to do if buffer is full and more bytes // need to be copied ? if(Full() && done != len && (overwrite || lock)) { PTRACE_CIRC(1, __func__ << "Circular buffer is full, while Fill " << len); if(lock) { pthread_mutex_lock(&mutex); PTRACE_CIRC(1, "Fill going to sleep"); pthread_cond_wait(&cond, &mutex); // pthread_cond_timedwait PTRACE_CIRC(1, "Fill woke up"); pthread_mutex_unlock(&mutex); } else if(overwrite){ pthread_mutex_lock(&mutex); if(Full()){ tail += len - done; // also shifts sentinel tail %= capacity; // wrap around } pthread_mutex_unlock(&mutex); } // try again done += Fill(inbuf + done, len - done, lock, overwrite); } // wake up read thread if necessary if(!Empty()) pthread_cond_signal(&cond); PTRACE_CIRC(1, __func__ << " " << len << " bytes, stored " << done << " available " << size()); return done;}PINDEX CircularBuffer::Drain(char *outbuf, PINDEX len, Boolean lock) { PINDEX done = 0, todo = 0; PTRACE_CIRC(6, __func__ << " " << len << " bytes, available " << size() ); if(outbuf == NULL){ PTRACE(1, __func__ << " Out buffer is NULL"); return PINDEX(0); } /* protect agains buffer corruption when write thread * is overwriting */ pthread_mutex_lock(&mutex); PTRACE(6, "aquired lock"); while(done != len && !Empty()) { if(head >= tail) { // head unwrapped todo = MIN(head - tail, len - done); } else { // head wrapped todo = MIN(capacity - tail, len - done); } memcpy(outbuf + done, buffer + tail, todo); done += todo; increment_tail(todo); PTRACE_CIRC(1, __func__ << " for " << len " bytes, copied " << done << " to output buffer, available in buffer " << size()); } if(done != len && (lock)) /* && Empty() */ { PTRACE(1, "Buffer underrun for Drain " << len << " bytes"); } // what to do if not as many bytes are available then // requested ? if(done != len && (lock)) /* && Empty() */ { if (lock){ pthread_cond_wait(&cond, &mutex); PTRACE_CIRC(2, "Read thread woke up, calling Drain"); pthread_mutex_unlock(&mutex); // race with write thread... done += Drain(outbuf + done, len - done, lock); PTRACE_CIRC(2, "Returned from recursive"); } } pthread_mutex_unlock(&mutex); if(!Full()) pthread_cond_signal(&cond); PTRACE_CIRC(2, "End Drain " << len << " bytes, fetched " << done << " buffer fill " << size() ); return done;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -