mirror of git://git.sysmocom.de/ofono
208 lines
4.3 KiB
C
208 lines
4.3 KiB
C
/*
|
|
*
|
|
* AT chat library with GLib integration
|
|
*
|
|
* Copyright (C) 2008-2010 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);
|
|
}
|