diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 2f3dcdf..1e03b33 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -362,6 +362,10 @@ RelativePath="..\src\parser.c" > + + @@ -680,6 +684,10 @@ RelativePath="..\include\libdivecomputer\parser.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index c100380..db5f742 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/rbstream.c b/src/rbstream.c new file mode 100644 index 0000000..07188e0 --- /dev/null +++ b/src/rbstream.c @@ -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 +#include + +#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; +} diff --git a/src/rbstream.h b/src/rbstream.h new file mode 100644 index 0000000..be1d8f6 --- /dev/null +++ b/src/rbstream.h @@ -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 + +#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 */