Add a common ringbuffer reading algorithm
Reading a ringbuffer backwards in order to process the most recent data first, is a very common operation. Nearly every dive computer backend has its own implementation. Thus with a common implementation, the amount of code duplication and complexity in the dive computer backends can be greatly reduced. The common algorithm is implemented as a simple ringbuffer stream, which takes care of all the technical details like the ringbuffer boundaries, alignment to the page size, using the optimal packet size and caching the remaining data.
This commit is contained in:
parent
f83d156fdb
commit
3f82a553bd
@ -362,6 +362,10 @@
|
||||
RelativePath="..\src\parser.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\rbstream.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\reefnet_sensus.c"
|
||||
>
|
||||
@ -680,6 +684,10 @@
|
||||
RelativePath="..\include\libdivecomputer\parser.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\rbstream.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\include\libdivecomputer\reefnet.h"
|
||||
>
|
||||
|
||||
@ -62,6 +62,7 @@ libdivecomputer_la_SOURCES = \
|
||||
citizen_aqualand.c citizen_aqualand_parser.c \
|
||||
divesystem_idive.c divesystem_idive_parser.c \
|
||||
ringbuffer.h ringbuffer.c \
|
||||
rbstream.h rbstream.c \
|
||||
checksum.h checksum.c \
|
||||
array.h array.c \
|
||||
buffer.c \
|
||||
|
||||
176
src/rbstream.c
Normal file
176
src/rbstream.c
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2016 Jef Driesen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rbstream.h"
|
||||
#include "context-private.h"
|
||||
#include "device-private.h"
|
||||
|
||||
struct dc_rbstream_t {
|
||||
dc_device_t *device;
|
||||
unsigned int pagesize;
|
||||
unsigned int packetsize;
|
||||
unsigned int begin;
|
||||
unsigned int end;
|
||||
unsigned int address;
|
||||
unsigned int available;
|
||||
unsigned int skip;
|
||||
unsigned char cache[];
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
ifloor (unsigned int x, unsigned int n)
|
||||
{
|
||||
// Round down to next lower multiple.
|
||||
return (x / n) * n;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
iceil (unsigned int x, unsigned int n)
|
||||
{
|
||||
// Round up to next higher multiple.
|
||||
return ((x + n - 1) / n) * n;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address)
|
||||
{
|
||||
dc_rbstream_t *rbstream = NULL;
|
||||
|
||||
if (out == NULL || device == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
// Page and packet size should be non-zero.
|
||||
if (pagesize == 0 || packetsize == 0) {
|
||||
ERROR (device->context, "Zero length page or packet size!");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Packet size should be a multiple of the page size.
|
||||
if (packetsize % pagesize != 0) {
|
||||
ERROR (device->context, "Packet size not a multiple of the page size!");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Ringbuffer boundaries should be aligned to the page size.
|
||||
if (begin % pagesize != 0 || end % pagesize != 0) {
|
||||
ERROR (device->context, "Ringbuffer not aligned to the page size!");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Address should be inside the ringbuffer.
|
||||
if (address < begin || address > end) {
|
||||
ERROR (device->context, "Address outside the ringbuffer!");
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
}
|
||||
|
||||
// Allocate memory.
|
||||
rbstream = (dc_rbstream_t *) malloc (sizeof(*rbstream) + packetsize);
|
||||
if (rbstream == NULL) {
|
||||
ERROR (device->context, "Failed to allocate memory.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
rbstream->device = device;
|
||||
rbstream->pagesize = pagesize;
|
||||
rbstream->packetsize = packetsize;
|
||||
rbstream->begin = begin;
|
||||
rbstream->end = end;
|
||||
rbstream->address = iceil(address, pagesize);
|
||||
rbstream->available = 0;
|
||||
rbstream->skip = rbstream->address - address;
|
||||
|
||||
*out = rbstream;
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size)
|
||||
{
|
||||
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||
|
||||
if (rbstream == NULL)
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
unsigned int address = rbstream->address;
|
||||
unsigned int available = rbstream->available;
|
||||
unsigned int skip = rbstream->skip;
|
||||
|
||||
unsigned int nbytes = 0;
|
||||
unsigned int offset = size;
|
||||
while (nbytes < size) {
|
||||
if (available == 0) {
|
||||
// Handle the ringbuffer wrap point.
|
||||
if (address == rbstream->begin)
|
||||
address = rbstream->end;
|
||||
|
||||
// Calculate the packet size.
|
||||
unsigned int len = rbstream->packetsize;
|
||||
if (rbstream->begin + len > address)
|
||||
len = address - rbstream->begin;
|
||||
|
||||
// Move to the begin of the current packet.
|
||||
address -= len;
|
||||
|
||||
// Read the packet into the cache.
|
||||
rc = dc_device_read (rbstream->device, address, rbstream->cache, rbstream->packetsize);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
available = len - skip;
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
unsigned int length = available;
|
||||
if (nbytes + length > size)
|
||||
length = size - nbytes;
|
||||
|
||||
offset -= length;
|
||||
available -= length;
|
||||
|
||||
memcpy (data + offset, rbstream->cache + available, length);
|
||||
|
||||
// Update and emit a progress event.
|
||||
if (progress) {
|
||||
progress->current += length;
|
||||
device_event_emit (rbstream->device, DC_EVENT_PROGRESS, progress);
|
||||
}
|
||||
|
||||
nbytes += length;
|
||||
}
|
||||
|
||||
rbstream->address = address;
|
||||
rbstream->available = available;
|
||||
rbstream->skip = skip;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
dc_status_t
|
||||
dc_rbstream_free (dc_rbstream_t *rbstream)
|
||||
{
|
||||
free (rbstream);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
78
src/rbstream.h
Normal file
78
src/rbstream.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* libdivecomputer
|
||||
*
|
||||
* Copyright (C) 2016 Jef Driesen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef DC_RBSTREAM_H
|
||||
#define DC_RBSTREAM_H
|
||||
|
||||
#include <libdivecomputer/device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* Opaque object representing a ringbuffer stream.
|
||||
*/
|
||||
typedef struct dc_rbstream_t dc_rbstream_t;
|
||||
|
||||
/**
|
||||
* Create a new ringbuffer stream.
|
||||
*
|
||||
* @param[out] rbstream A location to store the ringbuffer stream.
|
||||
* @param[in] device A valid device object.
|
||||
* @param[in] pagesize The page size in bytes.
|
||||
* @param[in] packetsize The packet size in bytes.
|
||||
* @param[in] begin The ringbuffer begin address.
|
||||
* @param[in] end The ringbuffer end address.
|
||||
* @param[in] address The stream start address.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_rbstream_new (dc_rbstream_t **rbstream, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address);
|
||||
|
||||
/**
|
||||
* Read data from the ringbuffer stream.
|
||||
*
|
||||
* @param[in] rbstream A valid ringbuffer stream.
|
||||
* @param[in] progress An (optional) progress event structure.
|
||||
* @param[out] data The memory buffer to read the data into.
|
||||
* @param[in] size The number of bytes to read.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size);
|
||||
|
||||
/**
|
||||
* Destroy the ringbuffer stream.
|
||||
*
|
||||
* @param[in] rbstream A valid ringbuffer stream.
|
||||
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
|
||||
* on failure.
|
||||
*/
|
||||
dc_status_t
|
||||
dc_rbstream_free (dc_rbstream_t *rbstream);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* DC_RBSTREAM_H */
|
||||
Loading…
x
Reference in New Issue
Block a user