| /* |
| * |
| * AT chat library with GLib integration |
| * |
| * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <string.h> |
| |
| #include <glib.h> |
| |
| #include "ringbuffer.h" |
| |
| #define MAX_SIZE 262144 |
| |
| struct ring_buffer { |
| unsigned char *buffer; |
| unsigned int size; |
| unsigned int mask; |
| unsigned int in; |
| unsigned int out; |
| }; |
| |
| struct ring_buffer *ring_buffer_new(unsigned int size) |
| { |
| unsigned int real_size = 1; |
| struct ring_buffer *buffer; |
| |
| /* Find the next power of two for size */ |
| while (real_size < size && real_size < MAX_SIZE) |
| real_size = real_size << 1; |
| |
| if (real_size > MAX_SIZE) |
| return NULL; |
| |
| buffer = g_slice_new(struct ring_buffer); |
| if (buffer == NULL) |
| return NULL; |
| |
| buffer->buffer = g_slice_alloc(real_size); |
| if (buffer->buffer == NULL) { |
| g_free(buffer); |
| return NULL; |
| } |
| |
| buffer->size = real_size; |
| buffer->mask = real_size - 1; |
| buffer->in = 0; |
| buffer->out = 0; |
| |
| return buffer; |
| } |
| |
| int ring_buffer_write(struct ring_buffer *buf, const void *data, |
| unsigned int len) |
| { |
| unsigned int end; |
| unsigned int offset; |
| const unsigned char *d = data; /* Needed to satisfy non-gcc compilers */ |
| |
| /* Determine how much we can actually write */ |
| len = MIN(len, buf->size - buf->in + buf->out); |
| |
| /* Determine how much to write before wrapping */ |
| offset = buf->in & buf->mask; |
| end = MIN(len, buf->size - offset); |
| memcpy(buf->buffer+offset, d, end); |
| |
| /* Now put the remainder on the beginning of the buffer */ |
| memcpy(buf->buffer, d + end, len - end); |
| |
| buf->in += len; |
| |
| return len; |
| } |
| |
| unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf, |
| unsigned int offset) |
| { |
| return buf->buffer + ((buf->in + offset) & buf->mask); |
| } |
| |
| int ring_buffer_avail_no_wrap(struct ring_buffer *buf) |
| { |
| unsigned int offset = buf->in & buf->mask; |
| unsigned int len = buf->size - buf->in + buf->out; |
| |
| return MIN(len, buf->size - offset); |
| } |
| |
| int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len) |
| { |
| len = MIN(len, buf->size - buf->in + buf->out); |
| buf->in += len; |
| |
| return len; |
| } |
| |
| int ring_buffer_read(struct ring_buffer *buf, void *data, unsigned int len) |
| { |
| unsigned int end; |
| unsigned int offset; |
| unsigned char *d = data; |
| |
| len = MIN(len, buf->in - buf->out); |
| |
| /* Grab data from buffer starting at offset until the end */ |
| offset = buf->out & buf->mask; |
| end = MIN(len, buf->size - offset); |
| memcpy(d, buf->buffer + offset, end); |
| |
| /* Now grab remainder from the beginning */ |
| memcpy(d + end, buf->buffer, len - end); |
| |
| buf->out += len; |
| |
| if (buf->out == buf->in) |
| buf->out = buf->in = 0; |
| |
| return len; |
| } |
| |
| int ring_buffer_drain(struct ring_buffer *buf, unsigned int len) |
| { |
| len = MIN(len, buf->in - buf->out); |
| |
| buf->out += len; |
| |
| if (buf->out == buf->in) |
| buf->out = buf->in = 0; |
| |
| return len; |
| } |
| |
| int ring_buffer_len_no_wrap(struct ring_buffer *buf) |
| { |
| unsigned int offset = buf->out & buf->mask; |
| unsigned int len = buf->in - buf->out; |
| |
| return MIN(len, buf->size - offset); |
| } |
| |
| unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf, |
| unsigned int offset) |
| { |
| return buf->buffer + ((buf->out + offset) & buf->mask); |
| } |
| |
| int ring_buffer_len(struct ring_buffer *buf) |
| { |
| if (buf == NULL) |
| return -1; |
| |
| return buf->in - buf->out; |
| } |
| |
| void ring_buffer_reset(struct ring_buffer *buf) |
| { |
| if (buf == NULL) |
| return; |
| |
| buf->in = 0; |
| buf->out = 0; |
| } |
| |
| int ring_buffer_avail(struct ring_buffer *buf) |
| { |
| if (buf == NULL) |
| return -1; |
| |
| return buf->size - buf->in + buf->out; |
| } |
| |
| int ring_buffer_capacity(struct ring_buffer *buf) |
| { |
| if (buf == NULL) |
| return -1; |
| |
| return buf->size; |
| } |
| |
| void ring_buffer_free(struct ring_buffer *buf) |
| { |
| if (buf == NULL) |
| return; |
| |
| g_slice_free1(buf->size, buf->buffer); |
| g_slice_free1(sizeof(struct ring_buffer), buf); |
| } |