#include "circlebuffer.h" #include #include #include #include /* memory copy */ #include "duck_mem.h" /* this is just a debugging trick so that we can "see" the free space */ void* circleread_memcpy(void* dst, void* src, int64_t count); void* circleread_memcpy(void* dst, void* src, int64_t count) { return duck_memcpy64(dst, src, count); } void CircleReport(const CircleBuffer_t* cb, const char* title) { printf("-----(%s)------\n", title); printf("max Size cb = %ld\n", cb->bufSize); printf("fills at = %ld\n", cb->bufSize * cb->percent / 100 ); printf("Current amount = %ld, level = %ld\n", cb->count, cb->count * 100 / cb->bufSize); } int ForwardBuffer(CircleBuffer_t* cb, int64_t len) { if (len >= (int64_t)cb->count) return -1; if ( (cb->head + len) < cb->bufSize ) cb->head += (int)len; else cb->head = (int)len - (cb->bufSize - cb->head); cb->count -= (int)len; return 0; } int RewindBuffer(CircleBuffer_t* cb, int64_t len) { if (len >= (int64_t)(cb->bufSize - cb->count) ) return -1; /* not enough history in buffer ! */ if (cb->head <= (size_t)len) { if (cb->wrapped == 0) return -1; cb->head = cb->bufSize - ((int)len - cb->head); cb->count += (int)len; return 0; } else { cb->head -= (int)len; cb->count += (int)len; } return 0; } void destroyCircleBuffer(CircleBuffer_t* cb) { assert(cb); if (cb->buffer) free(cb->buffer); if (cb->maxChunk) free(cb->maxChunk); } int resetCircleBuffer(CircleBuffer_t* cb) { cb->count = 0; cb->bytesConsumed = 0; cb->wrapped = 0; cb->starvedBytes = 0; cb->starvedRequests = 0; return 0; } int initCircleBuffer( CircleBuffer_t* cb, size_t countRecords, int percent, size_t maxChunk, FuncLock_t lock, FuncLock_t unlock ) { assert(cb); cb->buffer = (unsigned char * ) calloc(1, countRecords); cb->maxChunk = (unsigned char *) calloc(1, maxChunk); cb->maxChunkLen = maxChunk; if (cb->buffer) { cb->head = cb->count = 0; cb->balance = 0; cb->bufSize = countRecords; cb->bytesConsumed = 0; cb->muted = false; cb->percent = percent; cb->wrapped = 0; cb->lock = lock; cb->unlock = unlock; return 0; } else { return -1; /* error */ } } /* return zero if plenty of room and success */ /*-------------------------------------------*/ /* free space nested in the middle of the buffer is consider endSpace */ /* and when free space nested in middle, startSpace is considered to be zero */ /*---------------------------------------------------------------------------*/ int addToCircleBuffer(CircleBuffer_t* cb, void* data, size_t requestSpace) { int64_t freeSpace; /* count total free space in buffer */ int64_t head = cb->head; /* offset start of valid data */ int64_t tail = (cb->head + cb->count) % cb->bufSize; /* offest first free byte after valid data */ int64_t endSpace; freeSpace = cb->bufSize - cb->count; /* if not enough room to do the add */ /*----------------------------------*/ if (requestSpace > freeSpace) { assert(0); return CB_FULL; } endSpace = cb->bufSize - tail; if (tail >= head && requestSpace > endSpace) /* additional data write will wrap */ { duck_memcpy64(&cb->buffer[tail], data, endSpace); duck_memcpy64( cb->buffer, (unsigned char *)data+endSpace, requestSpace - endSpace); } else /* existing data wrapped around from end of buffer through beginning of buffer. */ { memcpy(&cb->buffer[tail], data, requestSpace); } cb->count += requestSpace; cb->balance += 1; return 0; /* -1 will mean error,m zero is OK */ } /* get info need so we can write direct as in memcpy into the circle buffer */ /*--------------------------------------------------------------------------*/ void FreeWrapless(const CircleBuffer_t* cb, void* handle, int64_t* sizeWrapless) { int64_t tail = (cb->head + cb->count) % cb->bufSize; if ((cb->head + cb->count) < cb->bufSize) { *((void **) handle) = &cb->buffer[tail]; *sizeWrapless = (cb->bufSize -(cb->head + cb->count)); } else { *((void **) handle) = &cb->buffer[tail]; *sizeWrapless = (cb->bufSize - cb->count); } } /* Please clone this sucker from readFromCircleBuffer */ int accessCircleBuffer(CircleBuffer_t* cb, void* handle1, size_t requestSize) { int64_t head = cb->head; int64_t tail = (cb->head + cb->count) % cb->bufSize; void** handle = (void **) handle1; void* dest = *handle; if (requestSize <= 0) { return requestSize; } if (cb->count < requestSize) { return -1; } if (tail > head) /* the data does not wrap ! */ { *handle = &cb->buffer[head]; } else /* the current data does wrap */ { /* but our read does not wrap */ if (head + requestSize < cb->bufSize) { *handle = &cb->buffer[head]; } else if (head + requestSize == cb->bufSize) { *handle = &cb->buffer[head]; } else /* our read will wrap ! */ { int64_t temp = cb->bufSize - head; dest = cb->maxChunk; assert(cb->maxChunkLen >= requestSize); circleread_memcpy( dest, &cb->buffer[head], temp); circleread_memcpy( ((unsigned char *) dest) + temp, cb->buffer, requestSize - temp); *handle = dest; } } cb->head = (cb->head + requestSize) % cb->bufSize; cb->count -= requestSize; cb->bytesConsumed += requestSize; cb->balance -= 1; return requestSize; /* records (16 bit or maybe other in future) */ } /* return count read , or -1 if not enough data */ /*----------------------------------------------*/ int readFromCircleBuffer(CircleBuffer_t* cb, void* dest, size_t requestSize) { int64_t head = cb->head; int64_t tail = (cb->head + cb->count) % cb->bufSize; if (cb->count < requestSize) { requestSize = cb->count; /* Give them what we have */ } if (requestSize <= 0) { return (int)requestSize; } if (tail > head) /* the data does not wrap ! */ { circleread_memcpy(dest, &cb->buffer[head], requestSize); } else /* the current data does wrap */ { /* but our read does not wrap */ if (head + requestSize < cb->bufSize) { circleread_memcpy(dest, &cb->buffer[head], requestSize); } else if (head + requestSize == cb->bufSize) { circleread_memcpy(dest, &cb->buffer[head], requestSize); memset(&cb->buffer[head], 0, (size_t)requestSize); /* optional, debug */ } else /* our read will wrap ! */ { int64_t temp = cb->bufSize - head; circleread_memcpy( dest, &cb->buffer[head], temp); circleread_memcpy( ((unsigned char *) dest) + temp, cb->buffer, requestSize - temp); } } cb->head = (cb->head + requestSize) % cb->bufSize; cb->count -= requestSize; cb->bytesConsumed += requestSize; cb->balance -= 1; return (int)requestSize; /* records (16 bit or maybe other in future) */ } void testCircleBuffer() { CircleBuffer_t temp; size_t bufSize = 256; const size_t maxInput = 256*3; size_t count = 0; size_t t; int i; const int maxRandom = 32; size_t chunkOut = 30; CircleRecord_t data[256*3]; initCircleBuffer(&temp, bufSize, 75, 256, 0, 0); /* who cares ... take the default seed value. */ while (count < maxInput || temp.count > chunkOut) { t = rand(); /* for whatever reason this seems to be a 16 bit random number */ t = t / ( 2 << (16 - 7) ) ; for(i = 0; i < (int)t; i++) { data[i] = (unsigned char ) ( count + i ); } if ( ((temp.bufSize - temp.count) >= t*sizeof(short)+1) && (count < (maxInput - maxRandom)) ) /* add 1 to keep buffer being completely filled */ { int64_t tail = (temp.head + temp.count) % temp.bufSize; addToCircleBuffer(&temp, data, t); printf("Add to buffer count = %ld. head = %ld, tail = %ld\n", t, temp.head, tail); count += t; } else /* not enough space in buffer, try to empty some out */ { int r; r = readFromCircleBuffer(&temp, data, chunkOut); if (r >= 0) { int64_t tail = (temp.head + temp.count) % temp.bufSize; for(i = 0; i < r; i++) printf("%ld ", data[i]); printf("\nRead from buffer. head = %ld, tail = %ld\n", temp.head, tail); } } } /* while we have not accumulated a large eough test ... */ } int CirclePercent(CircleBuffer_t* cb) { return (int)(cb->count * 100 / cb->bufSize); } int CircleAtLevel(CircleBuffer_t* cb) { return (int)(cb->count * 100 / cb->bufSize) >= cb->percent; } int CircleOverLevel(CircleBuffer_t* cb) { return (int)(cb->count * 100 / cb->bufSize) > cb->percent; }