|  | /* Copyright (c) 2015 Google Inc | 
|  | * Davide Libenzi <dlibenzi@google.com> | 
|  | * See LICENSE for details. | 
|  | */ | 
|  |  | 
|  | #include <kmalloc.h> | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <assert.h> | 
|  | #include <error.h> | 
|  | #include <circular_buffer.h> | 
|  |  | 
|  | bool circular_buffer_init(struct circular_buffer *cb, size_t size, char *mem) | 
|  | { | 
|  | cb->mem = mem; | 
|  | if (mem) | 
|  | cb->base = mem; | 
|  | else | 
|  | cb->base = kmalloc(size, MEM_WAIT); | 
|  | if (cb->base) { | 
|  | cb->rdptr = cb->wrptr = cb->base; | 
|  | cb->size = 0; | 
|  | cb->allocated = size; | 
|  | } | 
|  |  | 
|  | return cb->base != NULL; | 
|  | } | 
|  |  | 
|  | void circular_buffer_destroy(struct circular_buffer *cb) | 
|  | { | 
|  | if (cb->base) { | 
|  | if (cb->mem) | 
|  | kfree(cb->mem); | 
|  | cb->rdptr = cb->wrptr = cb->base = cb->mem = NULL; | 
|  | cb->size = cb->allocated = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void circular_buffer_clear(struct circular_buffer *cb) | 
|  | { | 
|  | cb->rdptr = cb->wrptr = cb->base; | 
|  | cb->size = 0; | 
|  | } | 
|  |  | 
|  | static bool circular_buffer_is_overlap(const struct circular_buffer *cb, | 
|  | const char *rptr, const char *wptr, | 
|  | size_t size) | 
|  | { | 
|  | /* Check if the current write operation [wptr, wptr+size) is overwriting | 
|  | * the block at which rptr in pointing to. | 
|  | */ | 
|  | return (cb->size > 0) && (rptr >= wptr) && (rptr < (wptr + size)); | 
|  | } | 
|  |  | 
|  | static void circular_buffer_write_skip(struct circular_buffer *cb, char *wrptr, | 
|  | size_t size) | 
|  | { | 
|  | /* Move the read pointer forward, so that the incoming write does not | 
|  | * overwrite the block the read pointer is looking at. | 
|  | */ | 
|  | while (circular_buffer_is_overlap(cb, cb->rdptr, wrptr, size)) { | 
|  | char *rdptr = cb->rdptr; | 
|  | size_t bsize = *(const cbuf_size_t *) rdptr; | 
|  |  | 
|  | if (likely(bsize)) { | 
|  | cb->rdptr = rdptr + bsize; | 
|  | cb->size -= bsize - sizeof(cbuf_size_t); | 
|  | if (unlikely(cb->size == 0)) | 
|  | cb->rdptr = cb->base; | 
|  | } else { | 
|  | cb->rdptr = cb->base; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t circular_buffer_write(struct circular_buffer *cb, | 
|  | const char *data, size_t size) | 
|  | { | 
|  | /* Data is written and evetually discarded in atomic blocks, in order to | 
|  | * maintain the consistency of the information stored in the buffer. | 
|  | */ | 
|  | char *wrptr = cb->wrptr; | 
|  | size_t wspace = (cb->base + cb->allocated) - wrptr; | 
|  | size_t esize = size + 2 * sizeof(cbuf_size_t); | 
|  |  | 
|  | if (unlikely(esize > cb->allocated)) | 
|  | return 0; | 
|  | /* If at the end of the buffer, the next block to be written does not fit, | 
|  | * we move the pointer to the beginning of the circular buffer. | 
|  | */ | 
|  | if (unlikely(esize > wspace)) { | 
|  | circular_buffer_write_skip(cb, wrptr, wspace); | 
|  | wrptr = cb->base; | 
|  | } | 
|  | circular_buffer_write_skip(cb, wrptr, esize); | 
|  |  | 
|  | /* Write the data and the end of sequence marker (0). | 
|  | */ | 
|  | *(cbuf_size_t *) wrptr = sizeof(cbuf_size_t) + size; | 
|  | memcpy(wrptr + sizeof(cbuf_size_t), data, size); | 
|  | cb->wrptr = wrptr + sizeof(cbuf_size_t) + size; | 
|  | cb->size += size; | 
|  |  | 
|  | *(cbuf_size_t *) cb->wrptr = 0; | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | size_t circular_buffer_read(struct circular_buffer *cb, char *data, size_t size, | 
|  | size_t off) | 
|  | { | 
|  | size_t asize = cb->size, rsize = 0; | 
|  | const char *rdptr = cb->rdptr; | 
|  |  | 
|  | while (asize > 0 && size > 0) { | 
|  | size_t bsize = *(const cbuf_size_t *) rdptr; | 
|  |  | 
|  | if (likely(bsize)) { | 
|  | size_t esize = bsize - sizeof(cbuf_size_t); | 
|  |  | 
|  | if (off >= esize) { | 
|  | off -= esize; | 
|  | } else { | 
|  | size_t csize = MIN(esize - off, size); | 
|  |  | 
|  | memcpy(data, rdptr + sizeof(cbuf_size_t) + off, csize); | 
|  | data += csize; | 
|  | size -= csize; | 
|  | rsize += csize; | 
|  | off = 0; | 
|  | } | 
|  | rdptr = rdptr + bsize; | 
|  | asize -= esize; | 
|  | } else { | 
|  | rdptr = cb->base; | 
|  | } | 
|  | } | 
|  |  | 
|  | return rsize; | 
|  | } |