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 */