/*
 * \brief  Wrapper for Trace::Buffer that adds some convenient functionality
 * \author Martin Stein
 * \date   2018-01-12
 */

/*
 * 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 _TRACE__TRACE_BUFFER_H_
#define _TRACE__TRACE_BUFFER_H_

/* Genode includes */
#include <base/trace/buffer.h>


/**
 * Wrapper for Trace::Buffer that adds some convenient functionality
 */
class Trace_buffer
{
	private:
		using Entry = Genode::Trace::Buffer::Entry;

		Genode::Trace::Buffer        &_buffer;
		Entry                         _curr { Entry::invalid() };
		unsigned long long            _lost_count { 0 };

	public:

		Trace_buffer(Genode::Trace::Buffer &buffer) : _buffer(buffer) { }

		/**
		 * Call functor for each entry that wasn't yet processed
		 */
		void for_each_new_entry(auto const &fn, bool update = true)
		{
			using namespace Genode;

			if (!_buffer.initialized())
				return;

			bool lost = _buffer.lost_entries() != _lost_count;
			if (lost) {
				warning("lost ", _buffer.lost_entries() - _lost_count,
				        ", entries; you might want to raise buffer size");
				_lost_count = (unsigned)_buffer.lost_entries();
			}

			Entry entry { _curr };

			/**
			 * Iterate over all entries that were not processed yet.
			 *
			 * A note on terminology: The head of the buffer marks the write
			 * position. The first entry is the one that starts at the lowest
			 * memory address. The next entry returns an invalid entry
			 * if the 'last' entry of the buffer (highest address) was reached.
			 */
			for (; !entry.head(); entry = _buffer.next(entry)) {
				/* continue at first entry if we hit the end of the buffer */
				if (entry.last())
					entry = _buffer.first();

				/* skip empty entries */
				if (entry.empty())
					continue;

				/* functor may return false to continue processing later on */
				if (!fn(entry))
					break;
			}

			/* remember the next to be processed entry in _curr */
			if (update) _curr = entry;
		}

		void * address() const { return &_buffer; }

		bool empty() const { return !_buffer.initialized() || _curr.head(); }
};

#endif /* _TRACE__TRACE_BUFFER_H_ */
