diff --git a/repos/gems/include/gems/magic_ring_buffer.h b/repos/gems/include/gems/magic_ring_buffer.h
new file mode 100644
index 0000000000..5153e43a69
--- /dev/null
+++ b/repos/gems/include/gems/magic_ring_buffer.h
@@ -0,0 +1,155 @@
+/*
+ * \brief Region magic ring buffer
+ * \author Emery Hemingway
+ * \date 2018-02-01
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _INCLUDE__GEMS__RING_BUFFER_H_
+#define _INCLUDE__GEMS__RING_BUFFER_H_
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+namespace Genode {
+ template
+ struct Magic_ring_buffer;
+}
+
+/**
+ * A ring buffer that uses a single dataspace mapped twice in consecutive
+ * regions. This allows any operation that is less or equal to the size of
+ * the buffer to be read or written in a single pass.
+ */
+template
+class Genode::Magic_ring_buffer
+{
+ private:
+
+ Magic_ring_buffer(Magic_ring_buffer const &);
+ Magic_ring_buffer &operator = (Magic_ring_buffer const &);
+
+ Genode::Env &_env;
+
+ Ram_dataspace_capability _buffer_ds;
+
+ size_t const _ds_size = Dataspace_client(_buffer_ds).size();
+ size_t const _capacity = _ds_size / sizeof(TYPE);
+
+ Rm_connection _rm_connection { _env };
+
+ /* create region map (reserve address space) */
+ Region_map_client _rm { _rm_connection.create(_ds_size*2) };
+
+ /* attach map to global region map */
+ TYPE *_buffer = (TYPE *)_env.rm().attach(_rm.dataspace());
+
+ size_t _wpos = 0;
+ size_t _rpos = 0;
+
+
+ public:
+
+ /**
+ * Ring capacity of TYPE items
+ */
+ size_t capacity() { return _capacity; }
+
+ /**
+ * Constructor
+ *
+ * \param TYPE Ring item type, size of type must be a
+ * power of two and less than the page size
+ *
+ * \param env Env for dataspace allocation and mapping
+ * \param num_bytes Size of ring in bytes, may be rounded up
+ * to the next page boundry
+ *
+ * \throw Region_map::Region_conflict
+ * \throw Out_of_ram
+ * \throw Out_of_caps
+ *
+ */
+ Magic_ring_buffer(Genode::Env &env, size_t num_bytes)
+ : _env(env), _buffer_ds(_env.pd().alloc(num_bytes))
+ {
+ if (_ds_size % sizeof(TYPE)) {
+ error("Magic_ring_buffer cannot hold unaligned TYPE");
+ throw Exception();
+ }
+
+ /* attach buffer dataspace twice into reserved region */
+ _rm.attach_at(_buffer_ds, 0, _ds_size);
+ _rm.attach_at(_buffer_ds, _ds_size, _ds_size);
+ }
+
+ ~Magic_ring_buffer()
+ {
+ /* detach dataspace from reserved region */
+ _rm.detach((addr_t)_ds_size);
+ _rm.detach((addr_t)0);
+
+ /* detach reserved region */
+ _env.rm().detach((addr_t)_buffer);
+
+ /* free buffer */
+ _env.ram().free(_buffer_ds);
+ }
+
+ /**
+ * Number of items that may be written to ring
+ */
+ size_t write_avail() const
+ {
+ if (_wpos > _rpos)
+ return ((_rpos - _wpos + _capacity) % _capacity) - 2;
+ else if (_wpos < _rpos)
+ return _rpos - _wpos;
+ else
+ return _capacity - 2;
+ }
+
+ /**
+ * Number of items that may be read from ring
+ */
+ size_t read_avail() const
+ {
+ if (_wpos > _rpos)
+ return _wpos - _rpos;
+ else
+ return (_wpos - _rpos + _capacity) % _capacity;
+ }
+
+ /**
+ * Pointer to ring write address
+ */
+ TYPE *write_addr() const { return &_buffer[_wpos]; }
+
+ /**
+ * Pointer to ring read address
+ */
+ TYPE *read_addr() const { return &_buffer[_rpos]; }
+
+ /**
+ * Advance the ring write pointer
+ */
+ void fill(size_t items) {
+ _wpos = (_wpos+items) % _capacity; }
+
+ /**
+ * Advance the ring read pointer
+ */
+ void drain(size_t items) {
+ _rpos = (_rpos+items) % _capacity; }
+};
+
+#endif
diff --git a/repos/gems/recipes/api/gems/hash b/repos/gems/recipes/api/gems/hash
index efd8eda4aa..3cb2c3c24d 100644
--- a/repos/gems/recipes/api/gems/hash
+++ b/repos/gems/recipes/api/gems/hash
@@ -1 +1 @@
-2018-01-28 b383e364411d3ee377e40ad8ab19e77f9b4c9503
+2018-03-18 47dfc20e9d2493c3596be2c6c0d9fdcdd38ef7b2
diff --git a/repos/gems/run/magic_ring_buffer.run b/repos/gems/run/magic_ring_buffer.run
new file mode 100644
index 0000000000..4c9d59cab6
--- /dev/null
+++ b/repos/gems/run/magic_ring_buffer.run
@@ -0,0 +1,29 @@
+create_boot_directory
+
+install_config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+build "core init test/magic_ring_buffer"
+
+build_boot_image {
+ core ld.lib.so init test-magic_ring_buffer
+}
+
+append qemu_args " -nographic "
+
+run_genode_until "child .* exited with exit value 0.*\n" 10
diff --git a/repos/gems/src/test/magic_ring_buffer/main.cc b/repos/gems/src/test/magic_ring_buffer/main.cc
new file mode 100644
index 0000000000..deb94bdfff
--- /dev/null
+++ b/repos/gems/src/test/magic_ring_buffer/main.cc
@@ -0,0 +1,57 @@
+/*
+ * \brief Magic ring buffer test
+ * \author Emery Hemingway
+ * \date 2018-04-04
+ */
+
+/*
+ * Copyright (C) 2018 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* Genode includes */
+#include
+#include
+
+/* gems includes */
+#include
+
+void Component::construct(Genode::Env &env)
+{
+ using namespace Genode;
+
+ Magic_ring_buffer ring_buffer(env, 4097);
+
+ log("--- magic ring buffer test, ", ring_buffer.capacity(), " int ring ---");
+
+
+ int const count = ring_buffer.capacity()/3;
+
+ int total = 0;
+
+ for (int j = 0; j < 99; ++j) {
+ for (int i = 0; i < count; ++i) {
+ ring_buffer.write_addr()[i] = i;
+ }
+
+ ring_buffer.fill(count);
+
+ for (int i = 0; i < count; ++i) {
+ if (ring_buffer.read_addr()[i] != i) {
+ error("ring buffer corruption, ",
+ ring_buffer.read_addr()[i], " != ", i);
+ env.parent().exit(total+i);
+ return;
+ }
+ }
+ ring_buffer.drain(count);
+
+ total += count;
+ }
+
+ log("--- test complete, ", total, " ints passed through ring ---");
+ env.parent().exit(0);
+}
+
diff --git a/repos/gems/src/test/magic_ring_buffer/target.mk b/repos/gems/src/test/magic_ring_buffer/target.mk
new file mode 100644
index 0000000000..716d9fb7b3
--- /dev/null
+++ b/repos/gems/src/test/magic_ring_buffer/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-magic_ring_buffer
+SRC_CC = main.cc
+LIBS = base