<commit_before>/*
 * Copyright 2013 Matthew Harvey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef GUARD_entry_list_ctrl_hpp_03525603377970682
#define GUARD_entry_list_ctrl_hpp_03525603377970682

#include "entry_table_iterator.hpp"
#include "reconciliation_list_panel.hpp"
#include "summary_datum.hpp"
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/optional.hpp>
#include <sqloxx/handle_fwd.hpp>
#include <sqloxx/sql_statement_fwd.hpp>
#include <wx/event.h>
#include <wx/gdicmn.h>
#include <wx/listctrl.h>
#include <memory>
#include <unordered_set>
#include <vector>

namespace dcm
{

// Begin forward declarations

class Account;
class Entry;
class DateParser;
class OrdinaryJournal;
class DcmDatabaseConnection;

namespace gui
{

class ReconciliationEntryListCtrl;

// End forward declarations

/**
 * Displays a list of Entries. Derived classes must define various
 * pure virtual functions - and may override certain impure virtual functions
 * - to govern which Entries are displayed, and which columns of data are
 * displayed. However every EntryListCtrl will have the date of the Entry
 * showing in the first column, and should only ever show Entries from
 * OrdinaryJournals (not DraftJournals).
 *
 * @todo LOW PRIORITY Better document the interface with derived classes.
 */
class EntryListCtrl: public wxListCtrl
{
public:

	/**
	 * @returns a pointer to a heap-allocated EntryListCtrl, listing
	 * all and only the \e actual (non-budget) OrdinaryEntries stored in
	 * \e p_account.database_connection() which have \e p_account as their
	 * Account, and which lie between the date stored in p_maybe_min_date
	 * and the date stored in p_maybe_max_date, inclusive. If
	 * p_maybe_min_date is an uninitialized optional, then there is no
	 * minimum date; and if p_maybe_max_date is an uninitialized optional
	 * then there is no maximum date. Having said this, even if there is no
	 * minimum date, then the opening balance date may act as an effective
	 * minimum date...
	 *
	 * Caller has responsibility for managing the pointed-to memory.
	 */
	static EntryListCtrl* create_actual_ordinary_entry_list
	(	wxWindow* p_parent,
		wxSize const& p_size,
		sqloxx::Handle<Account> const& p_account,
		boost::optional<boost::gregorian::date> const& p_maybe_min_date =
			boost::optional<boost::gregorian::date>(),
		boost::optional<boost::gregorian::date> const& p_maybe_max_date =
			boost::optional<boost::gregorian::date>()
	);

	/**
	 * @returns a pointer to a heap-allocated EntryListCtrl, especially
	 * designed to facilitate balance sheet Account reconciliations.
	 *
	 * \e p_account should be handle to a balance sheet Account.
	 *
	 * Caller has responsibility for managing the pointed-to memory.
	 */
	static ReconciliationEntryListCtrl* create_reconciliation_entry_list
	(	ReconciliationListPanel* p_parent,
		wxSize const& p_size,
		sqloxx::Handle<Account> const& p_account,
		boost::gregorian::date const& p_min_date,
		boost::gregorian::date const& p_max_date
	);

	EntryListCtrl(EntryListCtrl const&) = delete;
	EntryListCtrl(EntryListCtrl&&) = delete;
	EntryListCtrl& operator=(EntryListCtrl const&) = delete;
	EntryListCtrl& operator=(EntryListCtrl&&) = delete;
	virtual ~EntryListCtrl();

	/**
	 * Update displayed entries to reflect that \e p_journal has been newly
	 * posted (having not previously existed in the database).
	 */
	void update_for_new(sqloxx::Handle<OrdinaryJournal> const& p_journal);

	/**
	 * Update displayed entries to reflect that \e p_account has been newly
	 * created and saved (having not previously existed in the database.
	 */
	void update_for_new(sqloxx::Handle<Account> const& p_account);

	/**
	 * Update displayed entries to reflect that an already-saved
	 * OrdinaryJournal \e p_journal has just been amended, and the amendments
	 * saved.
	 */
	void update_for_amended(sqloxx::Handle<OrdinaryJournal> const& p_journal);

	/**
	 * Update displayed entries to reflect that an already-saved Account \e
	 * p_account has just been amended, and the amendments saved.
	 */
	void update_for_amended(sqloxx::Handle<Account> const& p_account);

	/**
	 * Update displayed entries to reflect that the Entries with IDs
	 * (as in Entry::id()) in p_doomed_ids have been deleted from the
	 * database.
	 */
	void update_for_deleted(std::vector<sqloxx::Id> const& p_doomed_ids);

	/**
	 * Populates \e out with handles to the currently selected Entries
	 * (if any).
	 */
	void selected_entries(std::vector<sqloxx::Handle<Entry> >& out);

	/**
	 * Scroll to the bottom of the displayed list.
	 */
	void scroll_to_bottom();

	/**
	 * @returns a reference to a vector of SummaryDatum instances representing
	 * a summary of the information embodied in the EntryListCtrl.
	 */
	std::vector<SummaryDatum> const& summary_data() const;

protected:
	EntryListCtrl
	(	wxWindow* p_parent,
		wxSize const& p_size,
		DcmDatabaseConnection& p_database_connection
	);

	/**
	 * @returns the number of columns in the displayed list.
	 */
	int num_columns() const;

	/**
	 * @returns the (0-based) index of the column containing the date.
	 */
	int date_col_num() const;

	/**
	 * Sets column widths to a reasonable default size.
	 */
	void autosize_column_widths();

	/**
	 * Resizes the column in which Entry comments are displayed, so that, as far
	 * as possible, there is enough room for all the displayed contents as
	 * well as for a scrollbar. The comment column is shrunk, in preference to
	 * shrinking other columns, if required to fit all the columns in the
	 * visible area.
	 */
	void adjust_comment_column_to_fit();

	/**
	 * @returns the date displayed in the row indexed by \e p_row, using \e
	 * p_parser to parse the text of the date.
	 */
	boost::gregorian::date date_displayed
	(	long p_row,
		DateParser const& p_parser
	) const;

	/**
	 * \e Assuming the displayed Entries are already ordered in increasing order
	 * of date, returns the row index such that, if a new Entry dated \e p_date
	 * were to be inserted so as to preserve this order (while ordering
	 * equal-dated Entries from oldest to newest) then it would be inserted into
	 * a new row indexed with this index.
	 */
	long row_for_date(boost::gregorian::date const& p_date) const;

	DcmDatabaseConnection& database_connection();

	DcmDatabaseConnection const& database_connection() const;

private:

	/**
	 * Inheriting class should define to return \e true if and only if
	 * progress log should be displayed to user.
	 */
	virtual bool do_require_progress_log() const = 0;

	/**
	 * Inheriting class should implement to insert columns (using InsertColumn
	 * method of wxListCtrl), other than the date column.
	 */
	virtual void do_insert_non_date_columns() = 0;

	/**
	 * Inheriting class should implement to inspect an arbitrary
	 * sqloxx::Handle<Entry> \e p_entry and return \e true if and only if that
	 * Entry should be included in the displayed list.
	 */
	virtual bool do_approve_entry
	(	sqloxx::Handle<Entry> const& p_entry
	) const = 0;

	/**
	 * Inheriting class should implement to set the value for each of the
	 * non-date columns in arbitrary row indexed by \e p_row, to reflect the
	 * Entry represented by \e p_entry. (An implementation would generally use
	 * wxListCtrl::SetItem to do this.)
	 */
	virtual void do_set_non_date_columns (	long p_row, sqloxx::Handle<Entry>
	const& p_entry) = 0;

	/**
	 * Inheriting class should implement to set widths of all its columns.
	 */
	virtual void do_set_column_widths() = 0;

	/**
	 * Inheriting class should implement to return number of columns.
	 */
	virtual int do_get_num_columns() const = 0;

	/**
	 * Inheriting class should implement to return the index of the column
	 * that contains the comment of each Entry (can return -1 if there is no
	 * such column).
	 */
	virtual int do_get_comment_col_num() const = 0;

	/**
	 * Inheriting class should implement this so as to return a std::unique_ptr
	 * to a heap-allocated sqloxx::SQLStatement which is a "SELECT" SQL
	 * statement that selects just the primary key column from the database
	 * table in which Entry instances are stored.
	 */
	virtual std::unique_ptr<sqloxx::SQLStatement>
		do_create_entry_selector() = 0;

	/**
	 * This is called to update the displayed list to reflect that the Account
	 * handled by \e p_account has been edited and the edits saved. By default
	 * this does nothing. Inheriting class is free to override.
	 */
	virtual void do_update_for_amended
	(	sqloxx::Handle<Account> const& p_account
	);

	/**
	 * @returns a vector of SummaryDatum instances, representing a summary
	 * of the contents of the displayed list. By default this returns
	 * an empty vector. Inheriting classes are free to override to return
	 * meaningful data.
	 */
	virtual std::vector<SummaryDatum> const& do_get_summary_data() const;

	/**
	 * Called during initialization of the EntryListCtrl, to perform any
	 * actions required to initialize the vector of SummaryData that
	 * should be returned via summary_data(). This is called prior
	 * to populating the list with actual data for the Entries. By default,
	 * this does nothing. Inheriting classes are free to override this function.
	 */
	virtual void do_initialize_summary_data();

	/**
	 * Upon an Entry being added to the displayed list, this function is
	 * called to reflect the Entry, as appropriate, within the data that
	 * should be returned via summary_data(). By default this does nothing.
	 * Inheriting classes are free to override this function.
	 */
	virtual void do_process_candidate_entry_for_summary
	(	sqloxx::Handle<Entry> const& p_entry
	);

	/**
	 * Upon an Entry being removed from the displayed list, this function is
	 * called to reflect the removal of the Entry, as appropriate, from the
	 * data that should be returned via summary_data(). By default this
	 * does nothing. Inheriting classes are free to override this function.
	 */
	virtual void do_process_removal_for_summary(long p_row);

	void on_item_activated(wxListEvent& event);

	void set_column_widths();
	int scrollbar_width_allowance() const;

	 // To be called by factory functions prior to returning pointer to newly
	 // created EntryListCtrl.
	static void initialize(EntryListCtrl* p_entry_list_ctrl);

	void insert_columns();
	void insert_date_column();
	void populate();
	void process_push_candidate_entry(sqloxx::Handle<Entry> const& p_entry);

	// If p_row is set to -1 (as it is by default) and the candidate Entry
	// is "approved", then the row it will be inserted into will be determined
	// automatically; otherwise, the row will be given by p_row.
	void process_insertion_candidate_entry
	(	sqloxx::Handle<Entry> const& p_entry,
		long p_row = -1
	);

	// This inserts in correct date order (if p_row is -1) or at an explicitly
	// specified row (if p_row is non-negative). If p_row is less than -1,
	// then behaviour is undefined.
	void insert_entry(sqloxx::Handle<Entry> const& p_entry, long p_row = -1);

	void remove_if_present(sqloxx::Id p_entry_id);

	// This doesn't take care of sorting by date
	void push_back_entry(sqloxx::Handle<Entry> const& p_entry);

	// To remember which Entries have been added.
	typedef std::unordered_set<sqloxx::Id> IdSet;
	IdSet m_id_set;

	DcmDatabaseConnection& m_database_connection;

	DECLARE_EVENT_TABLE()

};  // class EntryListCtrl

}  // namespace gui
}  // namespace dcm

#endif  // GUARD_entry_list_ctrl_hpp_03525603377970682
<commit_msg>Deleted an obsolete TODO.<commit_after>/*
 * Copyright 2013 Matthew Harvey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef GUARD_entry_list_ctrl_hpp_03525603377970682
#define GUARD_entry_list_ctrl_hpp_03525603377970682

#include "entry_table_iterator.hpp"
#include "reconciliation_list_panel.hpp"
#include "summary_datum.hpp"
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/optional.hpp>
#include <sqloxx/handle_fwd.hpp>
#include <sqloxx/sql_statement_fwd.hpp>
#include <wx/event.h>
#include <wx/gdicmn.h>
#include <wx/listctrl.h>
#include <memory>
#include <unordered_set>
#include <vector>

namespace dcm
{

// Begin forward declarations

class Account;
class Entry;
class DateParser;
class OrdinaryJournal;
class DcmDatabaseConnection;

namespace gui
{

class ReconciliationEntryListCtrl;

// End forward declarations

/**
 * Displays a list of Entries. Derived classes must define various
 * pure virtual functions - and may override certain impure virtual functions
 * - to govern which Entries are displayed, and which columns of data are
 * displayed. However every EntryListCtrl will have the date of the Entry
 * showing in the first column, and should only ever show Entries from
 * OrdinaryJournals (not DraftJournals).
 */
class EntryListCtrl: public wxListCtrl
{
public:

	/**
	 * @returns a pointer to a heap-allocated EntryListCtrl, listing
	 * all and only the \e actual (non-budget) OrdinaryEntries stored in
	 * \e p_account.database_connection() which have \e p_account as their
	 * Account, and which lie between the date stored in p_maybe_min_date
	 * and the date stored in p_maybe_max_date, inclusive. If
	 * p_maybe_min_date is an uninitialized optional, then there is no
	 * minimum date; and if p_maybe_max_date is an uninitialized optional
	 * then there is no maximum date. Having said this, even if there is no
	 * minimum date, then the opening balance date may act as an effective
	 * minimum date...
	 *
	 * Caller has responsibility for managing the pointed-to memory.
	 */
	static EntryListCtrl* create_actual_ordinary_entry_list
	(	wxWindow* p_parent,
		wxSize const& p_size,
		sqloxx::Handle<Account> const& p_account,
		boost::optional<boost::gregorian::date> const& p_maybe_min_date =
			boost::optional<boost::gregorian::date>(),
		boost::optional<boost::gregorian::date> const& p_maybe_max_date =
			boost::optional<boost::gregorian::date>()
	);

	/**
	 * @returns a pointer to a heap-allocated EntryListCtrl, especially
	 * designed to facilitate balance sheet Account reconciliations.
	 *
	 * \e p_account should be handle to a balance sheet Account.
	 *
	 * Caller has responsibility for managing the pointed-to memory.
	 */
	static ReconciliationEntryListCtrl* create_reconciliation_entry_list
	(	ReconciliationListPanel* p_parent,
		wxSize const& p_size,
		sqloxx::Handle<Account> const& p_account,
		boost::gregorian::date const& p_min_date,
		boost::gregorian::date const& p_max_date
	);

	EntryListCtrl(EntryListCtrl const&) = delete;
	EntryListCtrl(EntryListCtrl&&) = delete;
	EntryListCtrl& operator=(EntryListCtrl const&) = delete;
	EntryListCtrl& operator=(EntryListCtrl&&) = delete;
	virtual ~EntryListCtrl();

	/**
	 * Update displayed entries to reflect that \e p_journal has been newly
	 * posted (having not previously existed in the database).
	 */
	void update_for_new(sqloxx::Handle<OrdinaryJournal> const& p_journal);

	/**
	 * Update displayed entries to reflect that \e p_account has been newly
	 * created and saved (having not previously existed in the database.
	 */
	void update_for_new(sqloxx::Handle<Account> const& p_account);

	/**
	 * Update displayed entries to reflect that an already-saved
	 * OrdinaryJournal \e p_journal has just been amended, and the amendments
	 * saved.
	 */
	void update_for_amended(sqloxx::Handle<OrdinaryJournal> const& p_journal);

	/**
	 * Update displayed entries to reflect that an already-saved Account \e
	 * p_account has just been amended, and the amendments saved.
	 */
	void update_for_amended(sqloxx::Handle<Account> const& p_account);

	/**
	 * Update displayed entries to reflect that the Entries with IDs
	 * (as in Entry::id()) in p_doomed_ids have been deleted from the
	 * database.
	 */
	void update_for_deleted(std::vector<sqloxx::Id> const& p_doomed_ids);

	/**
	 * Populates \e out with handles to the currently selected Entries
	 * (if any).
	 */
	void selected_entries(std::vector<sqloxx::Handle<Entry> >& out);

	/**
	 * Scroll to the bottom of the displayed list.
	 */
	void scroll_to_bottom();

	/**
	 * @returns a reference to a vector of SummaryDatum instances representing
	 * a summary of the information embodied in the EntryListCtrl.
	 */
	std::vector<SummaryDatum> const& summary_data() const;

protected:
	EntryListCtrl
	(	wxWindow* p_parent,
		wxSize const& p_size,
		DcmDatabaseConnection& p_database_connection
	);

	/**
	 * @returns the number of columns in the displayed list.
	 */
	int num_columns() const;

	/**
	 * @returns the (0-based) index of the column containing the date.
	 */
	int date_col_num() const;

	/**
	 * Sets column widths to a reasonable default size.
	 */
	void autosize_column_widths();

	/**
	 * Resizes the column in which Entry comments are displayed, so that, as far
	 * as possible, there is enough room for all the displayed contents as
	 * well as for a scrollbar. The comment column is shrunk, in preference to
	 * shrinking other columns, if required to fit all the columns in the
	 * visible area.
	 */
	void adjust_comment_column_to_fit();

	/**
	 * @returns the date displayed in the row indexed by \e p_row, using \e
	 * p_parser to parse the text of the date.
	 */
	boost::gregorian::date date_displayed
	(	long p_row,
		DateParser const& p_parser
	) const;

	/**
	 * \e Assuming the displayed Entries are already ordered in increasing order
	 * of date, returns the row index such that, if a new Entry dated \e p_date
	 * were to be inserted so as to preserve this order (while ordering
	 * equal-dated Entries from oldest to newest) then it would be inserted into
	 * a new row indexed with this index.
	 */
	long row_for_date(boost::gregorian::date const& p_date) const;

	DcmDatabaseConnection& database_connection();

	DcmDatabaseConnection const& database_connection() const;

private:

	/**
	 * Inheriting class should define to return \e true if and only if
	 * progress log should be displayed to user.
	 */
	virtual bool do_require_progress_log() const = 0;

	/**
	 * Inheriting class should implement to insert columns (using InsertColumn
	 * method of wxListCtrl), other than the date column.
	 */
	virtual void do_insert_non_date_columns() = 0;

	/**
	 * Inheriting class should implement to inspect an arbitrary
	 * sqloxx::Handle<Entry> \e p_entry and return \e true if and only if that
	 * Entry should be included in the displayed list.
	 */
	virtual bool do_approve_entry
	(	sqloxx::Handle<Entry> const& p_entry
	) const = 0;

	/**
	 * Inheriting class should implement to set the value for each of the
	 * non-date columns in arbitrary row indexed by \e p_row, to reflect the
	 * Entry represented by \e p_entry. (An implementation would generally use
	 * wxListCtrl::SetItem to do this.)
	 */
	virtual void do_set_non_date_columns (	long p_row, sqloxx::Handle<Entry>
	const& p_entry) = 0;

	/**
	 * Inheriting class should implement to set widths of all its columns.
	 */
	virtual void do_set_column_widths() = 0;

	/**
	 * Inheriting class should implement to return number of columns.
	 */
	virtual int do_get_num_columns() const = 0;

	/**
	 * Inheriting class should implement to return the index of the column
	 * that contains the comment of each Entry (can return -1 if there is no
	 * such column).
	 */
	virtual int do_get_comment_col_num() const = 0;

	/**
	 * Inheriting class should implement this so as to return a std::unique_ptr
	 * to a heap-allocated sqloxx::SQLStatement which is a "SELECT" SQL
	 * statement that selects just the primary key column from the database
	 * table in which Entry instances are stored.
	 */
	virtual std::unique_ptr<sqloxx::SQLStatement>
		do_create_entry_selector() = 0;

	/**
	 * This is called to update the displayed list to reflect that the Account
	 * handled by \e p_account has been edited and the edits saved. By default
	 * this does nothing. Inheriting class is free to override.
	 */
	virtual void do_update_for_amended
	(	sqloxx::Handle<Account> const& p_account
	);

	/**
	 * @returns a vector of SummaryDatum instances, representing a summary
	 * of the contents of the displayed list. By default this returns
	 * an empty vector. Inheriting classes are free to override to return
	 * meaningful data.
	 */
	virtual std::vector<SummaryDatum> const& do_get_summary_data() const;

	/**
	 * Called during initialization of the EntryListCtrl, to perform any
	 * actions required to initialize the vector of SummaryData that
	 * should be returned via summary_data(). This is called prior
	 * to populating the list with actual data for the Entries. By default,
	 * this does nothing. Inheriting classes are free to override this function.
	 */
	virtual void do_initialize_summary_data();

	/**
	 * Upon an Entry being added to the displayed list, this function is
	 * called to reflect the Entry, as appropriate, within the data that
	 * should be returned via summary_data(). By default this does nothing.
	 * Inheriting classes are free to override this function.
	 */
	virtual void do_process_candidate_entry_for_summary
	(	sqloxx::Handle<Entry> const& p_entry
	);

	/**
	 * Upon an Entry being removed from the displayed list, this function is
	 * called to reflect the removal of the Entry, as appropriate, from the
	 * data that should be returned via summary_data(). By default this
	 * does nothing. Inheriting classes are free to override this function.
	 */
	virtual void do_process_removal_for_summary(long p_row);

	void on_item_activated(wxListEvent& event);

	void set_column_widths();
	int scrollbar_width_allowance() const;

	 // To be called by factory functions prior to returning pointer to newly
	 // created EntryListCtrl.
	static void initialize(EntryListCtrl* p_entry_list_ctrl);

	void insert_columns();
	void insert_date_column();
	void populate();
	void process_push_candidate_entry(sqloxx::Handle<Entry> const& p_entry);

	// If p_row is set to -1 (as it is by default) and the candidate Entry
	// is "approved", then the row it will be inserted into will be determined
	// automatically; otherwise, the row will be given by p_row.
	void process_insertion_candidate_entry
	(	sqloxx::Handle<Entry> const& p_entry,
		long p_row = -1
	);

	// This inserts in correct date order (if p_row is -1) or at an explicitly
	// specified row (if p_row is non-negative). If p_row is less than -1,
	// then behaviour is undefined.
	void insert_entry(sqloxx::Handle<Entry> const& p_entry, long p_row = -1);

	void remove_if_present(sqloxx::Id p_entry_id);

	// This doesn't take care of sorting by date
	void push_back_entry(sqloxx::Handle<Entry> const& p_entry);

	// To remember which Entries have been added.
	typedef std::unordered_set<sqloxx::Id> IdSet;
	IdSet m_id_set;

	DcmDatabaseConnection& m_database_connection;

	DECLARE_EVENT_TABLE()

};  // class EntryListCtrl

}  // namespace gui
}  // namespace dcm

#endif  // GUARD_entry_list_ctrl_hpp_03525603377970682
<|endoftext|>


<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_BITFIELD_HPP_INCLUDED
#define TORRENT_BITFIELD_HPP_INCLUDED

#include "libtorrent/assert.hpp"
#include "libtorrent/config.hpp"
#include <cstring> // for memset and memcpy
#include <cstdlib> // for malloc, free and realloc

namespace libtorrent
{
	struct TORRENT_EXPORT bitfield
	{
		bitfield(): m_bytes(0), m_size(0), m_own(false) {}
		bitfield(int bits): m_bytes(0), m_size(0), m_own(false)
		{ resize(bits); }
		bitfield(int bits, bool val): m_bytes(0), m_size(0), m_own(false)
		{ resize(bits, val); }
		bitfield(char const* b, int bits): m_bytes(0), m_size(0), m_own(false)
		{ assign(b, bits); }
		bitfield(bitfield const& rhs): m_bytes(0), m_size(0), m_own(false)
		{ assign(rhs.bytes(), rhs.size()); }

		void borrow_bytes(char* b, int bits)
		{
			dealloc();
			m_bytes = (unsigned char*)b;
			m_size = bits;
			m_own = false;
		}
		~bitfield() { dealloc(); }

		void assign(char const* b, int bits)
		{ resize(bits); std::memcpy(m_bytes, b, (bits + 7) / 8); clear_trailing_bits(); }

		bool operator[](int index) const
		{ return get_bit(index); }

		bool get_bit(int index) const
		{
			TORRENT_ASSERT(index >= 0);
			TORRENT_ASSERT(index < m_size);
			return (m_bytes[index / 8] & (0x80 >> (index & 7))) != 0;
		}

		void clear_bit(int index)
		{
			TORRENT_ASSERT(index >= 0);
			TORRENT_ASSERT(index < m_size);
			m_bytes[index / 8] &= ~(0x80 >> (index & 7));
		}

		void set_bit(int index)
		{
			TORRENT_ASSERT(index >= 0);
			TORRENT_ASSERT(index < m_size);
			m_bytes[index / 8] |= (0x80 >> (index & 7));
		}

		std::size_t size() const { return m_size; }
		bool empty() const { return m_size == 0; }

		char const* bytes() const { return (char*)m_bytes; }

		bitfield& operator=(bitfield const& rhs)
		{
			assign(rhs.bytes(), rhs.size());
			return *this;
		}

		int count() const
		{
			// 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111,
			// 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111
			const static char num_bits[] =
			{
				0, 1, 1, 2, 1, 2, 2, 3,
				1, 2, 2, 3, 2, 3, 3, 4
			};

			int ret = 0;
			const int num_bytes = m_size / 8;
			for (int i = 0; i < num_bytes; ++i)
			{
				ret += num_bits[m_bytes[i] & 0xf] + num_bits[m_bytes[i] >> 4];
			}

			int rest = m_size - num_bytes * 8;
			for (int i = 0; i < rest; ++i)
			{
				ret += (m_bytes[num_bytes] >> (7-i)) & 1;
			}
			TORRENT_ASSERT(ret <= m_size);
			TORRENT_ASSERT(ret >= 0);
			return ret;
		}

		struct const_iterator
		{
		friend struct bitfield;

			typedef bool value_type;
			typedef ptrdiff_t difference_type;
			typedef bool const* pointer;
			typedef bool& reference;
			typedef std::forward_iterator_tag iterator_category;

			bool operator*() { return (*byte & bit) != 0; }
			const_iterator& operator++() { inc(); return *this; }
			const_iterator operator++(int)
			{ const_iterator ret(*this); inc(); return ret; }
			const_iterator& operator--() { dec(); return *this; }
			const_iterator operator--(int)
			{ const_iterator ret(*this); dec(); return ret; }

			const_iterator(): byte(0), bit(0x80) {}
			bool operator==(const_iterator const& rhs) const
			{ return byte == rhs.byte && bit == rhs.bit; }

			bool operator!=(const_iterator const& rhs) const
			{ return byte != rhs.byte || bit != rhs.bit; }

		private:
			void inc()
			{
				TORRENT_ASSERT(byte);
				if (bit == 0x01)
				{
					bit = 0x80;
					++byte;
				}
				else
				{
					bit >>= 1;
				}
			}
			void dec()
			{
				TORRENT_ASSERT(byte);
				if (bit == 0x80)
				{
					bit = 0x01;
					--byte;
				}
				else
				{
					bit <<= 1;
				}
			}
			const_iterator(unsigned char const* ptr, int offset)
				: byte(ptr), bit(0x80 >> offset) {}
			unsigned char const* byte;
			int bit;
		};

		const_iterator begin() const { return const_iterator(m_bytes, 0); }
		const_iterator end() const { return const_iterator(m_bytes + m_size / 8, m_size & 7); }

		void resize(int bits, bool val)
		{
			int s = m_size;
			int b = m_size & 7;
			resize(bits);
			if (s >= m_size) return;
			int old_size_bytes = (s + 7) / 8;
			int new_size_bytes = (m_size + 7) / 8;
			if (val)
			{
				if (old_size_bytes && b) m_bytes[old_size_bytes - 1] |= (0xff >> b);
				if (old_size_bytes < new_size_bytes)
					std::memset(m_bytes + old_size_bytes, 0xff, new_size_bytes - old_size_bytes);
				clear_trailing_bits();
			}
			else
			{
				if (old_size_bytes < new_size_bytes)
					std::memset(m_bytes + old_size_bytes, 0x00, new_size_bytes - old_size_bytes);
			}
		}

		void set_all()
		{
			std::memset(m_bytes, 0xff, (m_size + 7) / 8);
			clear_trailing_bits();
		}

		void clear_all()
		{
			std::memset(m_bytes, 0x00, (m_size + 7) / 8);
		}

		void resize(int bits)
		{
			const int b = (bits + 7) / 8;
			if (m_bytes)
			{
				if (m_own)
				{
					m_bytes = (unsigned char*)std::realloc(m_bytes, b);
					m_own = true;
				}
				else if (bits > m_size)
				{
					unsigned char* tmp = (unsigned char*)std::malloc(b);
					std::memcpy(tmp, m_bytes, (std::min)(int(m_size + 7)/ 8, b));
					m_bytes = tmp;
					m_own = true;
				}
			}
			else
			{
				m_bytes = (unsigned char*)std::malloc(b);
				m_own = true;
			}
			m_size = bits;
			clear_trailing_bits();
		}

		void free() { dealloc(); m_size = 0; }

	private:

		void clear_trailing_bits()
		{
			// clear the tail bits in the last byte
			if (m_size & 7) m_bytes[(m_size + 7) / 8 - 1] &= 0xff << (8 - (m_size & 7));
		}

		void dealloc() { if (m_own) std::free(m_bytes); m_bytes = 0; }
		unsigned char* m_bytes;
		int m_size:31; // in bits
		bool m_own:1;
	};

}

#endif // TORRENT_BITFIELD_HPP_INCLUDED

<commit_msg>make electric fence happy by not allocating 0 bytes<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_BITFIELD_HPP_INCLUDED
#define TORRENT_BITFIELD_HPP_INCLUDED

#include "libtorrent/assert.hpp"
#include "libtorrent/config.hpp"
#include <cstring> // for memset and memcpy
#include <cstdlib> // for malloc, free and realloc

namespace libtorrent
{
	struct TORRENT_EXPORT bitfield
	{
		bitfield(): m_bytes(0), m_size(0), m_own(false) {}
		bitfield(int bits): m_bytes(0), m_size(0), m_own(false)
		{ resize(bits); }
		bitfield(int bits, bool val): m_bytes(0), m_size(0), m_own(false)
		{ resize(bits, val); }
		bitfield(char const* b, int bits): m_bytes(0), m_size(0), m_own(false)
		{ assign(b, bits); }
		bitfield(bitfield const& rhs): m_bytes(0), m_size(0), m_own(false)
		{ assign(rhs.bytes(), rhs.size()); }

		void borrow_bytes(char* b, int bits)
		{
			dealloc();
			m_bytes = (unsigned char*)b;
			m_size = bits;
			m_own = false;
		}
		~bitfield() { dealloc(); }

		void assign(char const* b, int bits)
		{ resize(bits); std::memcpy(m_bytes, b, (bits + 7) / 8); clear_trailing_bits(); }

		bool operator[](int index) const
		{ return get_bit(index); }

		bool get_bit(int index) const
		{
			TORRENT_ASSERT(index >= 0);
			TORRENT_ASSERT(index < m_size);
			return (m_bytes[index / 8] & (0x80 >> (index & 7))) != 0;
		}

		void clear_bit(int index)
		{
			TORRENT_ASSERT(index >= 0);
			TORRENT_ASSERT(index < m_size);
			m_bytes[index / 8] &= ~(0x80 >> (index & 7));
		}

		void set_bit(int index)
		{
			TORRENT_ASSERT(index >= 0);
			TORRENT_ASSERT(index < m_size);
			m_bytes[index / 8] |= (0x80 >> (index & 7));
		}

		std::size_t size() const { return m_size; }
		bool empty() const { return m_size == 0; }

		char const* bytes() const { return (char*)m_bytes; }

		bitfield& operator=(bitfield const& rhs)
		{
			assign(rhs.bytes(), rhs.size());
			return *this;
		}

		int count() const
		{
			// 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111,
			// 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111
			const static char num_bits[] =
			{
				0, 1, 1, 2, 1, 2, 2, 3,
				1, 2, 2, 3, 2, 3, 3, 4
			};

			int ret = 0;
			const int num_bytes = m_size / 8;
			for (int i = 0; i < num_bytes; ++i)
			{
				ret += num_bits[m_bytes[i] & 0xf] + num_bits[m_bytes[i] >> 4];
			}

			int rest = m_size - num_bytes * 8;
			for (int i = 0; i < rest; ++i)
			{
				ret += (m_bytes[num_bytes] >> (7-i)) & 1;
			}
			TORRENT_ASSERT(ret <= m_size);
			TORRENT_ASSERT(ret >= 0);
			return ret;
		}

		struct const_iterator
		{
		friend struct bitfield;

			typedef bool value_type;
			typedef ptrdiff_t difference_type;
			typedef bool const* pointer;
			typedef bool& reference;
			typedef std::forward_iterator_tag iterator_category;

			bool operator*() { return (*byte & bit) != 0; }
			const_iterator& operator++() { inc(); return *this; }
			const_iterator operator++(int)
			{ const_iterator ret(*this); inc(); return ret; }
			const_iterator& operator--() { dec(); return *this; }
			const_iterator operator--(int)
			{ const_iterator ret(*this); dec(); return ret; }

			const_iterator(): byte(0), bit(0x80) {}
			bool operator==(const_iterator const& rhs) const
			{ return byte == rhs.byte && bit == rhs.bit; }

			bool operator!=(const_iterator const& rhs) const
			{ return byte != rhs.byte || bit != rhs.bit; }

		private:
			void inc()
			{
				TORRENT_ASSERT(byte);
				if (bit == 0x01)
				{
					bit = 0x80;
					++byte;
				}
				else
				{
					bit >>= 1;
				}
			}
			void dec()
			{
				TORRENT_ASSERT(byte);
				if (bit == 0x80)
				{
					bit = 0x01;
					--byte;
				}
				else
				{
					bit <<= 1;
				}
			}
			const_iterator(unsigned char const* ptr, int offset)
				: byte(ptr), bit(0x80 >> offset) {}
			unsigned char const* byte;
			int bit;
		};

		const_iterator begin() const { return const_iterator(m_bytes, 0); }
		const_iterator end() const { return const_iterator(m_bytes + m_size / 8, m_size & 7); }

		void resize(int bits, bool val)
		{
			int s = m_size;
			int b = m_size & 7;
			resize(bits);
			if (s >= m_size) return;
			int old_size_bytes = (s + 7) / 8;
			int new_size_bytes = (m_size + 7) / 8;
			if (val)
			{
				if (old_size_bytes && b) m_bytes[old_size_bytes - 1] |= (0xff >> b);
				if (old_size_bytes < new_size_bytes)
					std::memset(m_bytes + old_size_bytes, 0xff, new_size_bytes - old_size_bytes);
				clear_trailing_bits();
			}
			else
			{
				if (old_size_bytes < new_size_bytes)
					std::memset(m_bytes + old_size_bytes, 0x00, new_size_bytes - old_size_bytes);
			}
		}

		void set_all()
		{
			std::memset(m_bytes, 0xff, (m_size + 7) / 8);
			clear_trailing_bits();
		}

		void clear_all()
		{
			std::memset(m_bytes, 0x00, (m_size + 7) / 8);
		}

		void resize(int bits)
		{
			TORRENT_ASSERT(bits >= 0);
			const int b = (bits + 7) / 8;
			if (m_bytes)
			{
				if (m_own)
				{
					m_bytes = (unsigned char*)std::realloc(m_bytes, b);
					m_own = true;
				}
				else if (bits > m_size)
				{
					unsigned char* tmp = (unsigned char*)std::malloc(b);
					std::memcpy(tmp, m_bytes, (std::min)(int(m_size + 7)/ 8, b));
					m_bytes = tmp;
					m_own = true;
				}
			}
			else if (bits > 0)
			{
				m_bytes = (unsigned char*)std::malloc(b);
				m_own = true;
			}
			m_size = bits;
			clear_trailing_bits();
		}

		void free() { dealloc(); m_size = 0; }

	private:

		void clear_trailing_bits()
		{
			// clear the tail bits in the last byte
			if (m_size & 7) m_bytes[(m_size + 7) / 8 - 1] &= 0xff << (8 - (m_size & 7));
		}

		void dealloc() { if (m_own) std::free(m_bytes); m_bytes = 0; }
		unsigned char* m_bytes;
		int m_size:31; // in bits
		bool m_own:1;
	};

}

#endif // TORRENT_BITFIELD_HPP_INCLUDED

<|endoftext|>


<commit_before>/*********************************************************************************
 *
 *  MIT License
 *
 *  Copyright (c) 2017 Nenad Zikic
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *
 *********************************************************************************/

#pragma once

#include <list>
#include <unordered_map>
#include <vector>
#include <optional>
#include <mutex>
#include <functional>
#include <ostream>

namespace lru_cache
{
    namespace detail
    {
        /* Dummy replacement for mutex which defaults to cache with no thread safety */
        class nolocking
        {
        public:
            void lock() {}
            void unlock() {}
            bool try_lock() { return true; }
        };
    }

    /* Associative Least Recently Used (LRU) cache */
    template <typename Key_t, typename Value_t, typename Lock_t = detail::nolocking>
    class LRU_cache
    {
        static_assert(
            std::is_copy_constructible_v<Value_t>,
            "Cache requires copy-constructible objects, otherwise data access with get member function would pop the data out of the cache, which renders cache pointless.");

    protected:
        using List_t = std::list<std::pair<Key_t, Value_t>>;
        using Map_t = std::unordered_map<Key_t, typename List_t::iterator>;
        using Lock = std::scoped_lock<Lock_t>;

        List_t  m_list;
        Map_t   m_map;
        const size_t  m_capacity;

        mutable Lock_t m_lock;

        std::function<void(Value_t)> m_onEvict;

        void evict()
        {
            auto lru = --m_list.end();

            if (m_onEvict)
            {
                m_onEvict(lru->second);
            }

            m_map.erase(lru->first);
            m_list.pop_back();
        }

    public:

        explicit LRU_cache(size_t capacity = 8)
            : m_list()
            , m_map(capacity)
            , m_capacity(capacity)
        {
        }

        virtual ~LRU_cache() = default;

        LRU_cache(const LRU_cache&) = delete;
        LRU_cache& operator=(const LRU_cache&) = delete;

        LRU_cache(LRU_cache&&) = default;
        LRU_cache& operator=(LRU_cache&&) = default;

        template <typename Func>
        void setOnEvictNotifier(Func callback)
        {
            static_assert(std::is_invocable_v<Func, Value_t>, "Invalid type, expected function or callable object, or object callable to be called with argument of type Value_t");
            m_onEvict = callback;
        }

        /*
         * Inserts an element (value) with the given key (key).
         * If a value with the fiven key exists, function does nothing.
         */
        template <
            typename Key_tt = Key_t,
            typename Value_tt = Value_t,
            class Key_tt_MustBe_Key_t = std::enable_if_t<std::is_same_v<std::remove_reference_t<Key_tt>, Key_t>>,
            class Value_tt_MustBe_Value_t = std::enable_if_t<std::is_same_v<std::remove_reference_t<Value_tt>, Value_t>>>
        void insert(Key_tt&& key, Value_tt&& value)
        {
            Lock lock(m_lock);

            // lookup value in cache
            auto it = m_map.find(key);
            if (it == m_map.end())
            {
                // insert item into cache (and check if cache is full)
                if (m_list.size() >= m_capacity)
                {
                    evict();
                }

                // insert element
                m_list.emplace_front(std::make_pair(key, std::forward<Value_tt>(value)));
                m_map[std::forward<Key_tt>(key)] = m_list.begin();
            }
        }

        /*
         * Retrieves the value by the given key (key).
         * If the given key isn't in cache, the function returns empty std::optional object.
         */
        template <typename Key_tt = Key_t,
            class TypeMustBe_Key_t = std::enable_if_t<std::is_same_v<std::remove_reference_t<Key_tt>, Key_t>>>
        std::optional<Value_t> get(Key_tt&& key)
        {
            Lock lock(m_lock);

            // look in the map if element is present
            auto it = m_map.find(key);
            // if not return the empty optional
            if (it == m_map.end())
            {
                return std::nullopt;
            }

            // element is present...
            auto list_iterator = it->second;

            if (list_iterator == m_list.begin())
            {
                return { list_iterator->second };
            }
            else
            {
                // move the accessed element to front of the list
                m_list.splice(m_list.begin(), m_list, list_iterator);
                // and update hash map
                m_map[key] = m_list.begin();

                return { m_list.begin()->second };
            }
        }

        /*
         * Purges the cache
         */
        virtual void purge()
        {
            Lock lock(m_lock);

            m_list.clear();
            m_map.clear();
        }

        /*
         * Releases all key/value pairs into a vector and purges the cache.
         */
        virtual std::vector<std::pair<Key_t, Value_t>> release()
        {
            Lock lock(m_lock);

            std::vector<std::pair<Key_t, Value_t>> v;

            for (auto& pair : m_list)
            {
                v.emplace_back(std::move(pair));
            }

            purge();
            return v;
        }

        /*
         * Friend funtion for outputing key/value pairs.
         */
        friend std::ostream& operator<<(std::ostream& os, const LRU_cache& cache)
        {
            for (auto& pair : cache.m_list)
            {
                os << "Key: " << pair.first << "\tValue: " << pair.second << std::endl;
            }

            return os;
        }
    };
}
<commit_msg>Adding indentation for section modifiers (public, private, protected).<commit_after>/*********************************************************************************
 *
 *  MIT License
 *
 *  Copyright (c) 2017 Nenad Zikic
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in all
 *  copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 *
 *********************************************************************************/

#pragma once

#include <list>
#include <unordered_map>
#include <vector>
#include <optional>
#include <mutex>
#include <functional>
#include <ostream>

namespace lru_cache
{
    namespace detail
    {
        /* Dummy replacement for mutex which defaults to cache with no thread safety */
        class nolocking
        {
            public:
                void lock() {}
                void unlock() {}
                bool try_lock() { return true; }
        };
    }

    /* Associative Least Recently Used (LRU) cache */
    template <typename Key_t, typename Value_t, typename Lock_t = detail::nolocking>
    class LRU_cache
    {
            static_assert(
                    std::is_copy_constructible_v<Value_t>,
                    "Cache requires copy-constructible objects, otherwise data access with get member function would pop the data out of the cache, which renders cache pointless.");

        protected:
            using List_t = std::list<std::pair<Key_t, Value_t>>;
            using Map_t = std::unordered_map<Key_t, typename List_t::iterator>;
            using Lock = std::scoped_lock<Lock_t>;

            List_t  m_list;
            Map_t   m_map;
            const size_t  m_capacity;

            mutable Lock_t m_lock;

            std::function<void(Value_t)> m_onEvict;

            void evict()
            {
                auto lru = --m_list.end();

                if (m_onEvict)
                {
                    m_onEvict(lru->second);
                }

                m_map.erase(lru->first);
                m_list.pop_back();
            }

        public:

            explicit LRU_cache(size_t capacity = 8)
                : m_list()
                , m_map(capacity)
                , m_capacity(capacity)
            {
            }

            virtual ~LRU_cache() = default;

            LRU_cache(const LRU_cache&) = delete;
            LRU_cache& operator=(const LRU_cache&) = delete;

            LRU_cache(LRU_cache&&) = default;
            LRU_cache& operator=(LRU_cache&&) = default;

            template <typename Func>
            void setOnEvictNotifier(Func callback)
            {
                static_assert(std::is_invocable_v<Func, Value_t>, "Invalid type, expected function or callable object, or object callable to be called with argument of type Value_t");
                m_onEvict = callback;
            }

            /*
             * Inserts an element (value) with the given key (key).
             * If a value with the fiven key exists, function does nothing.
             */
            template <
                    typename Key_tt = Key_t,
                    typename Value_tt = Value_t,
                    class Key_tt_MustBe_Key_t = std::enable_if_t<std::is_same_v<std::remove_reference_t<Key_tt>, Key_t>>,
                    class Value_tt_MustBe_Value_t = std::enable_if_t<std::is_same_v<std::remove_reference_t<Value_tt>, Value_t>>>
            void insert(Key_tt&& key, Value_tt&& value)
            {
                Lock lock(m_lock);

                // lookup value in cache
                auto it = m_map.find(key);
                if (it == m_map.end())
                {
                    // insert item into cache (and check if cache is full)
                    if (m_list.size() >= m_capacity)
                    {
                        evict();
                    }

                    // insert element
                    m_list.emplace_front(std::make_pair(key, std::forward<Value_tt>(value)));
                    m_map[std::forward<Key_tt>(key)] = m_list.begin();
                }
            }

            /*
             * Retrieves the value by the given key (key).
             * If the given key isn't in cache, the function returns empty std::optional object.
             */
            template <typename Key_tt = Key_t,
                      class TypeMustBe_Key_t = std::enable_if_t<std::is_same_v<std::remove_reference_t<Key_tt>, Key_t>>>
            std::optional<Value_t> get(Key_tt&& key)
            {
                Lock lock(m_lock);

                // look in the map if element is present
                auto it = m_map.find(key);
                // if not return the empty optional
                if (it == m_map.end())
                {
                    return std::nullopt;
                }

                // element is present...
                auto list_iterator = it->second;

                if (list_iterator == m_list.begin())
                {
                    return { list_iterator->second };
                }
                else
                {
                    // move the accessed element to front of the list
                    m_list.splice(m_list.begin(), m_list, list_iterator);
                    // and update hash map
                    m_map[key] = m_list.begin();

                    return { m_list.begin()->second };
                }
            }

            /*
             * Purges the cache
             */
            virtual void purge()
            {
                Lock lock(m_lock);

                m_list.clear();
                m_map.clear();
            }

            /*
             * Releases all key/value pairs into a vector and purges the cache.
             */
            virtual std::vector<std::pair<Key_t, Value_t>> release()
            {
                Lock lock(m_lock);

                std::vector<std::pair<Key_t, Value_t>> v;

                for (auto& pair : m_list)
                {
                    v.emplace_back(std::move(pair));
                }

                purge();
                return v;
            }

            /*
             * Friend funtion for outputing key/value pairs.
             */
            friend std::ostream& operator<<(std::ostream& os, const LRU_cache& cache)
            {
                for (auto& pair : cache.m_list)
                {
                    os << "Key: " << pair.first << "\tValue: " << pair.second << std::endl;
                }

                return os;
            }
    };
}
<|endoftext|>


<commit_before>#ifndef FLOATING_POINT_HPP
#define FLOATING_POINT_HPP

#include <cmath>

#include <limits>
#include <type_traits>

namespace osrm
{
namespace util
{

template <typename FloatT> bool epsilon_compare(const FloatT number1, const FloatT number2)
{
    static_assert(std::is_floating_point<FloatT>::value, "type must be floating point");
    return (std::abs(number1 - number2) < std::numeric_limits<FloatT>::epsilon());
}
}
}

#endif // FLOATING_POINT_HPP
<commit_msg>Removes floating point epsilon comparator<commit_after><|endoftext|>


<commit_before>#pragma once

#include <type_traits>
namespace gcl::mp::type_traits
{
    template <class T>
    struct is_template : std::false_type {};
    template <class... T_args, template <class...> class T>
    struct is_template<T<T_args...>> : std::true_type {};
    template <auto... values, template <auto...> class T>
    struct is_template<T<values...>> : std::true_type {};
    template <class T>
    static inline constexpr auto is_template_v = is_template<T>::value;

    template <class T_concrete, template <class...> class T>
    struct is_instance_of : std::false_type {};
    template <template <class...> class T, class... T_args>
    struct is_instance_of<T<T_args...>, T> : std::true_type {};
    template <class T_concrete, template <class...> class T>
    static inline constexpr auto is_instance_of_v = is_instance_of<T_concrete, T>::value;

    template <class T, typename... Args>
    class is_brace_constructible {
        template <typename /*= void*/, typename U, typename... U_args>
        struct impl : std::false_type {};
        template <typename U, typename... U_args>
        struct impl<std::void_t<decltype(U{std::declval<U_args>()...})>, U, U_args...> : std::true_type {};

      public:
        constexpr inline static auto value = impl<std::void_t<>, T, Args...>::value;
    };
    template <class T, typename... Args>
    constexpr static inline auto is_brace_constructible_v = is_brace_constructible<T, Args...>::value;

    template <bool evaluation>
    using if_t = std::conditional_t<evaluation, std::true_type, std::false_type>;
    template <bool evaluation>
    constexpr static inline auto if_v = std::conditional_t<evaluation, std::true_type, std::false_type>::value;
}
#include <bitset>
#include <tuple>
#include <array>
namespace gcl::mp::type_traits
{
    template <template <typename> class trait, typename... Ts>
    struct trait_result {
        constexpr static auto as_bitset_v = []() consteval
        {
            using bitset_type = std::bitset<sizeof...(Ts)>;
            using bitset_initializer_t = unsigned long long;
            bitset_initializer_t value{0};
            return bitset_type{((value = (value << 1 | trait<Ts>::value)), ...)};
        }
        ();

        template <template <typename...> class T>
        using as_t = T<typename trait<Ts>::type...>;
        template <template <typename...> class T>
        constexpr static auto as_v = T{trait<Ts>::value...};

        using as_tuple_t = as_t<std::tuple>;
        constexpr static auto as_tuple_v = std::tuple{trait<Ts>::value...};

        template <class Int = int>
        using as_integer_sequence = std::integer_sequence<Int, trait<Ts>::value...>;

        // constexpr static auto as_array_v = std::array{trait<Ts>::value...}; // OK with GCC 10.2
        using as_array_t = std::array<std::tuple_element_t<0, decltype(std::tuple{trait<Ts>::value...})>, sizeof...(Ts)>;
        constexpr static auto as_array_v = as_array_t{trait<Ts>::value...};
    };
}
namespace gcl::mp::type_trait
{   // detection not covered by `is_instance_of`
    template <typename T>
    struct is_std_array : std::false_type {};
    template <typename T, std::size_t N>
    struct is_std_array<std::array<T, N>> : std::true_type {};
    template <typename T>
    using is_std_array_t = is_std_array<T>::type;
    template <typename T>
    constexpr static auto is_std_array_v = is_std_array_t<T>::value;
}

namespace gcl::mp::type_traits::tests::is_brace_constructible_v
{
    struct toto {
        int i;
    };
    struct titi {
        explicit titi(int) {}
    };

    static_assert(type_traits::is_brace_constructible_v<toto>);
    static_assert(type_traits::is_brace_constructible_v<toto, int>);
    static_assert(type_traits::is_brace_constructible_v<toto, char>);
    static_assert(not type_traits::is_brace_constructible_v<toto, char*>);
    static_assert(not type_traits::is_brace_constructible_v<titi>);
    static_assert(type_traits::is_brace_constructible_v<titi, int>);
    static_assert(type_traits::is_brace_constructible_v<titi, char>);
    static_assert(not type_traits::is_brace_constructible_v<titi, char*>);
}
namespace gcl::mp::type_traits::tests::if_t
{
    static_assert(std::is_same_v<type_traits::if_t<true>, std::true_type>);
    static_assert(std::is_same_v<type_traits::if_t<false>, std::false_type>);
    static_assert(type_traits::if_v<true> == true);
    static_assert(type_traits::if_v<false> == false);
}
#if __cpp_concepts
#include <concepts>
namespace gcl::mp::type_traits::tests::if_t
{
    // clang-format off
    template <typename T>
    concept is_red_colored = requires(T)
    {
        { T::color == decltype(T::color)::red } -> std::convertible_to<bool>;
        { type_traits::if_t<T::color == decltype(T::color)::red>{}} -> std::same_as<std::true_type>;
    };
    enum colors { red, blue, green };
    struct smthg_blue { constexpr static auto color = colors::blue; };
    struct smthg_red { constexpr static auto color = colors::red; };
    // clang-format on

    static_assert(not is_red_colored<smthg_blue>);
    static_assert(is_red_colored<smthg_red>);
}
#endif
namespace gcl::mp::type_traits::tests::trait_results
{
    template <typename T>
    using is_int = std::is_same<int, T>;
    using results = trait_result<is_int, char, int, bool>;

    // static_assert(decltype(results::as_bitset_v){2UL} == results::as_bitset_v); // std::bitset::operator== is not cx
    constexpr auto expected_result_as_bitset = decltype(results::as_bitset_v){2UL};
    static_assert(expected_result_as_bitset[0] == results::as_bitset_v[0]);
    static_assert(expected_result_as_bitset[1] == results::as_bitset_v[1]);
    static_assert(expected_result_as_bitset[2] == results::as_bitset_v[2]);

    using results_as_tuple = results::as_t<std::tuple>;
    using results_as_tuple_value_type = std::decay_t<decltype(results::as_v<std::tuple>)>;
    static_assert(std::tuple_size_v<results_as_tuple> == std::tuple_size_v<results_as_tuple_value_type>);

    using expected_result_as_tuple = std::tuple<std::false_type, std::true_type, std::false_type>;
    static_assert(std::is_same_v<results::as_t<std::tuple>, expected_result_as_tuple>);
    static_assert(std::is_same_v<results::as_integer_sequence<int>, std::integer_sequence<int, 0, 1, 0>>);
    static_assert(results::as_array_v == std::array{false, true, false});

    template <typename... Ts>
    struct type_pack {};
    using results_as_type_pack = results::as_t<type_pack>;
    using expected_result_as_type_pack = type_pack<std::false_type, std::true_type, std::false_type>;
    static_assert(std::is_same_v<results_as_type_pack, expected_result_as_type_pack>);
}<commit_msg>[gcl::type_trait] : Fix error, add tests<commit_after>#pragma once

#include <type_traits>
namespace gcl::mp::type_traits
{
    template <class T>
    struct is_template : std::false_type {};
    template <class... T_args, template <class...> class T>
    struct is_template<T<T_args...>> : std::true_type {};
    template <auto... values, template <auto...> class T>
    struct is_template<T<values...>> : std::true_type {};
    template <class T>
    static inline constexpr auto is_template_v = is_template<T>::value;

    template <class T_concrete, template <class...> class T>
    struct is_instance_of : std::false_type {};
    template <template <class...> class T, class... T_args>
    struct is_instance_of<T<T_args...>, T> : std::true_type {};
    template <class T_concrete, template <class...> class T>
    static inline constexpr auto is_instance_of_v = is_instance_of<T_concrete, T>::value;

    template <class T, typename... Args>
    class is_brace_constructible {
        template <typename /*= void*/, typename U, typename... U_args>
        struct impl : std::false_type {};
        template <typename U, typename... U_args>
        struct impl<std::void_t<decltype(U{std::declval<U_args>()...})>, U, U_args...> : std::true_type {};

      public:
        constexpr inline static auto value = impl<std::void_t<>, T, Args...>::value;
    };
    template <class T, typename... Args>
    constexpr static inline auto is_brace_constructible_v = is_brace_constructible<T, Args...>::value;

    template <bool evaluation>
    using if_t = std::conditional_t<evaluation, std::true_type, std::false_type>;
    template <bool evaluation>
    constexpr static inline auto if_v = std::conditional_t<evaluation, std::true_type, std::false_type>::value;
}
#include <bitset>
#include <tuple>
#include <array>
namespace gcl::mp::type_traits
{
    template <template <typename> class trait, typename... Ts>
    struct trait_result {
        constexpr static auto as_bitset_v = []() consteval
        {
            using bitset_type = std::bitset<sizeof...(Ts)>;
            using bitset_initializer_t = unsigned long long;
            bitset_initializer_t value{0};
            return bitset_type{((value = (value << 1 | trait<Ts>::value)), ...)};
        }
        ();

        template <template <typename...> class T>
        using as_t = T<typename trait<Ts>::type...>;
        template <template <typename...> class T>
        constexpr static auto as_v = T{trait<Ts>::value...};

        using as_tuple_t = as_t<std::tuple>;
        constexpr static auto as_tuple_v = std::tuple{trait<Ts>::value...};

        template <class Int = int>
        using as_integer_sequence = std::integer_sequence<Int, trait<Ts>::value...>;

        // constexpr static auto as_array_v = std::array{trait<Ts>::value...}; // OK with GCC 10.2
        using as_array_t = std::array<std::tuple_element_t<0, decltype(std::tuple{trait<Ts>::value...})>, sizeof...(Ts)>;
        constexpr static auto as_array_v = as_array_t{trait<Ts>::value...};
    };
}
namespace gcl::mp::type_traits
{   // detection not covered by `is_instance_of`
    template <typename T>
    struct is_std_array : std::false_type {};
    template <typename T, std::size_t N>
    struct is_std_array<std::array<T, N>> : std::true_type {};
    template <typename T>
    using is_std_array_t = typename is_std_array<T>::type;
    template <typename T>
    constexpr static auto is_std_array_v = is_std_array_t<T>::value;
}

namespace gcl::mp::type_traits::tests::is_brace_constructible_v
{
    struct toto {
        int i;
    };
    struct titi {
        explicit titi(int) {}
    };

    static_assert(type_traits::is_brace_constructible_v<toto>);
    static_assert(type_traits::is_brace_constructible_v<toto, int>);
    static_assert(type_traits::is_brace_constructible_v<toto, char>);
    static_assert(not type_traits::is_brace_constructible_v<toto, char*>);
    static_assert(not type_traits::is_brace_constructible_v<titi>);
    static_assert(type_traits::is_brace_constructible_v<titi, int>);
    static_assert(type_traits::is_brace_constructible_v<titi, char>);
    static_assert(not type_traits::is_brace_constructible_v<titi, char*>);
}
namespace gcl::mp::type_traits::tests::if_t
{
    static_assert(std::is_same_v<type_traits::if_t<true>, std::true_type>);
    static_assert(std::is_same_v<type_traits::if_t<false>, std::false_type>);
    static_assert(type_traits::if_v<true> == true);
    static_assert(type_traits::if_v<false> == false);
}
#if __cpp_concepts
#include <concepts>
namespace gcl::mp::type_traits::tests::if_t
{
    // clang-format off
    template <typename T>
    concept is_red_colored = requires(T)
    {
        { T::color == decltype(T::color)::red } -> std::convertible_to<bool>;
        { type_traits::if_t<T::color == decltype(T::color)::red>{}} -> std::same_as<std::true_type>;
    };
    enum colors { red, blue, green };
    struct smthg_blue { constexpr static auto color = colors::blue; };
    struct smthg_red { constexpr static auto color = colors::red; };
    // clang-format on

    static_assert(not is_red_colored<smthg_blue>);
    static_assert(is_red_colored<smthg_red>);
}
#endif
namespace gcl::mp::type_traits::tests::trait_results
{
    template <typename T>
    using is_int = std::is_same<int, T>;
    using results = trait_result<is_int, char, int, bool>;

    // static_assert(decltype(results::as_bitset_v){2UL} == results::as_bitset_v); // std::bitset::operator== is not cx
    constexpr auto expected_result_as_bitset = decltype(results::as_bitset_v){2UL};
    static_assert(expected_result_as_bitset[0] == results::as_bitset_v[0]);
    static_assert(expected_result_as_bitset[1] == results::as_bitset_v[1]);
    static_assert(expected_result_as_bitset[2] == results::as_bitset_v[2]);

    using results_as_tuple = results::as_t<std::tuple>;
    using results_as_tuple_value_type = std::decay_t<decltype(results::as_v<std::tuple>)>;
    static_assert(std::tuple_size_v<results_as_tuple> == std::tuple_size_v<results_as_tuple_value_type>);

    using expected_result_as_tuple = std::tuple<std::false_type, std::true_type, std::false_type>;
    static_assert(std::is_same_v<results::as_t<std::tuple>, expected_result_as_tuple>);
    static_assert(std::is_same_v<results::as_integer_sequence<int>, std::integer_sequence<int, 0, 1, 0>>);
    static_assert(results::as_array_v == std::array{false, true, false});

    template <typename... Ts>
    struct type_pack {};
    using results_as_type_pack = results::as_t<type_pack>;
    using expected_result_as_type_pack = type_pack<std::false_type, std::true_type, std::false_type>;
    static_assert(std::is_same_v<results_as_type_pack, expected_result_as_type_pack>);
}
namespace gcl::mp::type_traits::tests::is_std_array
{
    static_assert(gcl::mp::type_traits::is_std_array_v<std::array<char, 3>>);
    static_assert(not gcl::mp::type_traits::is_std_array_v<char[3]>);
}<|endoftext|>


<commit_before>#ifndef GUI_HPP
#define GUI_HPP
#include <wx/toplevel.h>
#include "MainFrame.hpp"
#include "Notifier.hpp"
#include <string>
#include <vector>
#include <stack>
#include <mutex>

#include "Preset.hpp"

namespace Slic3r { namespace GUI {

class App: public wxApp
{
public:
    virtual bool OnInit();
    App() : wxApp() {}

    /// Save position, size, and maximize state for a TopLevelWindow (includes Frames) by name in Settings.
    void save_window_pos(const wxTopLevelWindow* window, const wxString& name );

    /// Move/resize a named TopLevelWindow (includes Frames) from Settings
    void restore_window_pos(wxTopLevelWindow* window, const wxString& name );

    /// Function to add callback functions to the idle loop stack.
    void CallAfter(std::function<void()> cb_function);

    void OnUnhandledException() override;

private:
    std::unique_ptr<Notifier> notifier {nullptr};
    std::vector<Presets> presets { preset_types, Presets() };

    void load_presets();

    wxString datadir {""};
    const std::string LogChannel {"APP"}; //< Which log these messages should go to.

    /// Lock to guard the callback stack
    std::mutex callback_register;

    /// callbacks registered to run during idle event.
    std::stack<std::function<void()> > cb {};
};

}} // namespace Slic3r::GUI
#endif // GUI_HPP
<commit_msg>Quick reference macro to GUI with the cast applied so access is available<commit_after>#ifndef GUI_HPP
#define GUI_HPP
#include <wx/toplevel.h>
#include "MainFrame.hpp"
#include "Notifier.hpp"
#include <string>
#include <vector>
#include <stack>
#include <mutex>

#include "Preset.hpp"

namespace Slic3r { namespace GUI {

class App: public wxApp
{
public:
    virtual bool OnInit();
    App() : wxApp() {}

    /// Save position, size, and maximize state for a TopLevelWindow (includes Frames) by name in Settings.
    void save_window_pos(const wxTopLevelWindow* window, const wxString& name );

    /// Move/resize a named TopLevelWindow (includes Frames) from Settings
    void restore_window_pos(wxTopLevelWindow* window, const wxString& name );

    /// Function to add callback functions to the idle loop stack.
    void CallAfter(std::function<void()> cb_function);

    void OnUnhandledException() override;

private:
    std::unique_ptr<Notifier> notifier {nullptr};
    std::vector<Presets> presets { preset_types, Presets() };

    void load_presets();

    wxString datadir {""};
    const std::string LogChannel {"APP"}; //< Which log these messages should go to.

    /// Lock to guard the callback stack
    std::mutex callback_register;

    /// callbacks registered to run during idle event.
    std::stack<std::function<void()> > cb {};
};

/// Quick reference to this app with its cast applied.
#define SLIC3RAPP dynamic_cast<App*>(wxTheApp)

}} // namespace Slic3r::GUI
#endif // GUI_HPP
<|endoftext|>


<commit_before>#include "map/Octree.h"

Octree::Octree() {

	// initialize the first stack block

	for (int i = 0; i < 0x8000; i++) {
		descriptor_buffer[i] = 0;
	}
}

void Octree::Generate(char* data, sf::Vector3i dimensions) {

	// Launch the recursive generator at (0,0,0) as the first point
	// and the octree dimension as the initial block size
	std::tuple<uint64_t, uint64_t> root_node = GenerationRecursion(data, dimensions, sf::Vector3i(0, 0, 0), OCT_DIM/2);

	// ========= DEBUG ==============
	PrettyPrintUINT64(std::get<0>(root_node), &output_stream);
	output_stream << "    " << OCT_DIM << "    " << counter++ << std::endl;
	// ==============================

	// ============= TEMP!!! ===================
	if (stack_pos - 1 > stack_pos) {
		global_pos -= stack_pos;
		stack_pos = 0x8000;
	}
	else {
		stack_pos -= 1;
	}

	memcpy(&descriptor_buffer[stack_pos + global_pos], &std::get<0>(root_node), 1 * sizeof(uint64_t));
	// ========================================

	DumpLog(&output_stream, "raw_output.txt");

}

// Copy to stack enables the hybrid depth-breadth first tree by taking
// a list of valid non-leaf child descriptors contained under a common parent.

// It takes the list of children, and the current level in the voxel hierarchy.
// It returns the index to the first element of the

// This is all fine and dandy, but we have the problem where we need to assign
// relative pointers to objects so we need to keep track of where their children are
// being assigned.

uint64_t Octree::copy_to_stack(std::vector<uint64_t> children, unsigned int voxel_scale) {

	// Check for the 15 bit boundry
	if (stack_pos - children.size() > stack_pos) {
		global_pos = stack_pos;
		stack_pos = 0x8000;
	}
	else {
		stack_pos -= children.size();
	}

	// Copy to stack needs to keep track of an "anchor_stack" which will hopefully facilitate
	// relative pointer generation for items being copied to the stack

	// We need to return the relative pointer to the child node list
	// 16 bits, one far bit, one sign bit? 14 bits == +- 16384

	// Worth halving the ptr reach to enable backwards ptrs?
	// could increase packability allowing far ptrs and attachments to come before or after

	//stack_pos -= children.size();
	memcpy(&descriptor_buffer[stack_pos + global_pos], children.data(), children.size() * sizeof(uint64_t));

	// Return the bitmask encoding the index of that value
	// If we tripped the far bit, allocate a far index to the stack and place
	// it at the bottom of the child_descriptor node level array
	// And then shift the far bit to 1

	// If not, shift the index to its correct place
	return stack_pos;
}

bool Octree::get_voxel(sf::Vector3i position) {

	// Struct that holds the state necessary to continue the traversal from the found voxel
	oct_state state;

	// push the root node to the parent stack
	uint64_t head = descriptor_buffer[root_index];
	state.parent_stack[state.parent_stack_position] = head;

	// Set our initial dimension and the position at the corner of the oct to keep track of our position
	int dimension = OCT_DIM;
	sf::Vector3i quad_position(0, 0, 0);

	// While we are not at the required resolution
	//		Traverse down by setting the valid/leaf mask to the subvoxel
	//		Check to see if it is valid
	//			Yes?
	//				Check to see if it is a leaf
	//				No? Break
	//				Yes? Scale down to the next hierarchy, push the parent to the stack
	//
	//			No?
	//				Break
	while (dimension > 1) {

		// So we can be a little bit tricky here and increment our
		// array index that holds our masks as we build the idx.
		// Adding 1 for X, 2 for Y, and 4 for Z
		int mask_index = 0;

		// Do the logic steps to find which sub oct we step down into
		if (position.x >= (dimension / 2) + quad_position.x) {

			// Set our voxel position to the (0,0) of the correct oct
			quad_position.x += (dimension / 2);

			// increment the mask index and mentioned above
			mask_index += 1;

			// Set the idx to represent the move
			state.idx_stack[state.scale] |= idx_set_x_mask;

		}
		if (position.y >= (dimension / 2) + quad_position.y) {

			quad_position.y |= (dimension / 2);

			mask_index += 2;

			state.idx_stack[state.scale] ^= idx_set_y_mask;

		}
		if (position.z >= (dimension / 2) + quad_position.z) {

			quad_position.z += (dimension / 2);

			mask_index += 4;

			state.idx_stack[state.scale] |= idx_set_z_mask;

		}

		// Check to see if we are on a valid oct
		if ((head >> 16) & mask_8[mask_index]) {

			// Check to see if it is a leaf
			if ((head >> 24) & mask_8[mask_index]) {

				// If it is, then we cannot traverse further as CP's won't have been generated
				return true;
			}

			// If all went well and we found a valid non-leaf oct then we will traverse further down the hierarchy
			state.scale++;
			dimension /= 2;

			// Count the number of valid octs that come before and add it to the index to get the position
			// Negate it by one as it counts itself
			int count = count_bits((uint8_t)(head >> 16) & count_mask_8[mask_index]) - 1;

			// access the element at which head points to and then add the specified number of indices
			// to get to the correct child descriptor
			head = descriptor_buffer[(head & child_pointer_mask) + count];

			// Increment the parent stack position and put the new oct node as the parent
			state.parent_stack_position++;
			state.parent_stack[state.parent_stack_position] = head;

		}
		else {
			// If the oct was not valid, then no CP's exists any further
			// This implicitly says that if it's non-valid then it must be a leaf!!

			// It appears that the traversal is now working but I need
			// to focus on how to now take care of the end condition.
			// Currently it adds the last parent on the second to lowest
			// oct CP. Not sure if thats correct
			return false;
		}
	}

	return true;
}

void Octree::print_block(int block_pos) {

	std::stringstream sss;
	for (int i = block_pos; i < (int)pow(2, 15); i++) {
		PrettyPrintUINT64(descriptor_buffer[i], &sss);
		sss << "\n";
	}
	DumpLog(&sss, "raw_data.txt");

}

std::tuple<uint64_t, uint64_t> Octree::GenerationRecursion(char* data, sf::Vector3i dimensions, sf::Vector3i pos, unsigned int voxel_scale) {

	// The 8 subvoxel coords starting from the 1th direction, the direction of the origin of the 3d grid
	// XY, Z++, XY
	std::vector<sf::Vector3i> v = {
		sf::Vector3i(pos.x              , pos.y              , pos.z),
		sf::Vector3i(pos.x + voxel_scale, pos.y              , pos.z),
		sf::Vector3i(pos.x              , pos.y + voxel_scale, pos.z),
		sf::Vector3i(pos.x + voxel_scale, pos.y + voxel_scale, pos.z),
		sf::Vector3i(pos.x              , pos.y              , pos.z + voxel_scale),
		sf::Vector3i(pos.x + voxel_scale, pos.y              , pos.z + voxel_scale),
		sf::Vector3i(pos.x              , pos.y + voxel_scale, pos.z + voxel_scale),
		sf::Vector3i(pos.x + voxel_scale, pos.y + voxel_scale, pos.z + voxel_scale)
	};

	// A tuple holding the child descriptor that we're going to fill out and the
	// absolute position of it within the descriptor buffer
	std::tuple<uint64_t, uint64_t> descriptor_and_position(0, 0);

	// If we hit the 1th voxel scale then we need to query the 3D grid
	// and get the voxel at that position. I assume in the future when I
	// want to do chunking / loading of raw data I can edit the voxel access
	if (voxel_scale == 1) {

		// Setting the individual valid mask bits
		// These don't bound check, should they?
		for (int i = 0; i < v.size(); i++) {
			if (get1DIndexedVoxel(data, dimensions, v.at(i)))
				SetBit(i + 16, &std::get<0>(descriptor_and_position));
		}

		// We are querying leafs, so we need to fill the leaf mask
		std::get<0>(descriptor_and_position) |= 0xFF000000;

		// The CP will be left blank, contour mask and ptr will need to
		// be added here later
		return descriptor_and_position;

	}

	std::vector<std::tuple<uint64_t, uint64_t>> descriptor_position_array;

	// Generate down the recursion, returning the descriptor of the current node
	for (int i = 0; i < v.size(); i++) {

		std::tuple<uint64_t, uint64_t> child(0, 0);

		// Get the child descriptor from the i'th to 8th subvoxel
		child = GenerationRecursion(data, dimensions, v.at(i), voxel_scale / 2);

		// =========== Debug ===========
		PrettyPrintUINT64(std::get<0>(child), &output_stream);
		output_stream << "    " << voxel_scale << "    " << counter++ << std::endl;
		// =============================

		// If the child is a leaf (contiguous) of non-valid values
		if (IsLeaf(std::get<0>(child)) && !CheckLeafSign(std::get<0>(child))) {
			// Leave the valid mask 0, set leaf mask to 1
			SetBit(i + 16 + 8, &std::get<0>(descriptor_and_position));
		}

		// If the child is valid and not a leaf
		else {

			// Set the valid mask, and add it to the descriptor array
			SetBit(i + 16, &std::get<0>(descriptor_and_position));
			descriptor_position_array.push_back(child);
		}
	}

	// We are working bottom up so we need to subtract from the stack position
	// the amount of elements we want to use

	int worst_case_insertion_size = descriptor_position_array.size() * 2;

	// check to see if we exceeded this page header, if so set the header and move the global position
	if (page_header_counter - worst_case_insertion_size <= 0) {

		// Jump to the page headers position and reset the counter
		descriptor_buffer_position -= 0x8000 - page_header_counter;
		page_header_counter = 0x8000;

		// Fill the space with blank
		memcpy(&descriptor_buffer[descriptor_buffer_position], &current_info_section_position, sizeof(uint64_t));

		descriptor_buffer_position--;

	}

	// We gotta go backwards as memcpy of a vector can be emulated by starting from the rear
	for (int i = descriptor_position_array.size() - 1; i >= 0; i--) {

		uint64_t relative_distance = std::get<1>(descriptor_position_array.at(i)) - descriptor_buffer_position;

		// check to see if the
		if (relative_distance > 0x8000) {
			memcpy(&descriptor_buffer[descriptor_buffer_position], &std::get<1>(descriptor_position_array.at(i)), sizeof(uint64_t));
			descriptor_buffer_position--;
		}

		memcpy(&descriptor_buffer[descriptor_buffer_position], descriptor_position_array.at(i), descriptor_array.size() * sizeof(uint64_t));

	}

	if (stack_pos - descriptor_position_array.size() > stack_pos) {
		global_pos -= stack_pos;
		stack_pos = 0x8000;
	}
	else {
		stack_pos -= descriptor_position_array.size();
	}

	memcpy(&descriptor_buffer[stack_pos + global_pos], descriptor_array.data(), descriptor_array.size() * sizeof(uint64_t));

	// Return the node up the stack
	return descriptor_and_position;
}

char Octree::get1DIndexedVoxel(char* data, sf::Vector3i dimensions, sf::Vector3i position) {
	return data[position.x + OCT_DIM * (position.y + OCT_DIM * position.z)];
}

<commit_msg>First draft of the revised octree generation code<commit_after>#include "map/Octree.h"

Octree::Octree() {

	// initialize the first stack block

	for (int i = 0; i < 0x8000; i++) {
		descriptor_buffer[i] = 0;
	}
}

void Octree::Generate(char* data, sf::Vector3i dimensions) {

	// Launch the recursive generator at (0,0,0) as the first point
	// and the octree dimension as the initial block size
	std::tuple<uint64_t, uint64_t> root_node = GenerationRecursion(data, dimensions, sf::Vector3i(0, 0, 0), OCT_DIM/2);

	// ========= DEBUG ==============
	PrettyPrintUINT64(std::get<0>(root_node), &output_stream);
	output_stream << "    " << OCT_DIM << "    " << counter++ << std::endl;
	// ==============================

	// ============= TEMP!!! ===================
	if (stack_pos - 1 > stack_pos) {
		global_pos -= stack_pos;
		stack_pos = 0x8000;
	}
	else {
		stack_pos -= 1;
	}

	memcpy(&descriptor_buffer[stack_pos + global_pos], &std::get<0>(root_node), 1 * sizeof(uint64_t));
	// ========================================

	DumpLog(&output_stream, "raw_output.txt");

}

// Copy to stack enables the hybrid depth-breadth first tree by taking
// a list of valid non-leaf child descriptors contained under a common parent.

// It takes the list of children, and the current level in the voxel hierarchy.
// It returns the index to the first element of the

// This is all fine and dandy, but we have the problem where we need to assign
// relative pointers to objects so we need to keep track of where their children are
// being assigned.

uint64_t Octree::copy_to_stack(std::vector<uint64_t> children, unsigned int voxel_scale) {

	// Check for the 15 bit boundry
	if (stack_pos - children.size() > stack_pos) {
		global_pos = stack_pos;
		stack_pos = 0x8000;
	}
	else {
		stack_pos -= children.size();
	}

	// Copy to stack needs to keep track of an "anchor_stack" which will hopefully facilitate
	// relative pointer generation for items being copied to the stack

	// We need to return the relative pointer to the child node list
	// 16 bits, one far bit, one sign bit? 14 bits == +- 16384

	// Worth halving the ptr reach to enable backwards ptrs?
	// could increase packability allowing far ptrs and attachments to come before or after

	//stack_pos -= children.size();
	memcpy(&descriptor_buffer[stack_pos + global_pos], children.data(), children.size() * sizeof(uint64_t));

	// Return the bitmask encoding the index of that value
	// If we tripped the far bit, allocate a far index to the stack and place
	// it at the bottom of the child_descriptor node level array
	// And then shift the far bit to 1

	// If not, shift the index to its correct place
	return stack_pos;
}

bool Octree::get_voxel(sf::Vector3i position) {

	// Struct that holds the state necessary to continue the traversal from the found voxel
	oct_state state;

	// push the root node to the parent stack
	uint64_t head = descriptor_buffer[root_index];
	state.parent_stack[state.parent_stack_position] = head;

	// Set our initial dimension and the position at the corner of the oct to keep track of our position
	int dimension = OCT_DIM;
	sf::Vector3i quad_position(0, 0, 0);

	// While we are not at the required resolution
	//		Traverse down by setting the valid/leaf mask to the subvoxel
	//		Check to see if it is valid
	//			Yes?
	//				Check to see if it is a leaf
	//				No? Break
	//				Yes? Scale down to the next hierarchy, push the parent to the stack
	//
	//			No?
	//				Break
	while (dimension > 1) {

		// So we can be a little bit tricky here and increment our
		// array index that holds our masks as we build the idx.
		// Adding 1 for X, 2 for Y, and 4 for Z
		int mask_index = 0;

		// Do the logic steps to find which sub oct we step down into
		if (position.x >= (dimension / 2) + quad_position.x) {

			// Set our voxel position to the (0,0) of the correct oct
			quad_position.x += (dimension / 2);

			// increment the mask index and mentioned above
			mask_index += 1;

			// Set the idx to represent the move
			state.idx_stack[state.scale] |= idx_set_x_mask;

		}
		if (position.y >= (dimension / 2) + quad_position.y) {

			quad_position.y |= (dimension / 2);

			mask_index += 2;

			state.idx_stack[state.scale] ^= idx_set_y_mask;

		}
		if (position.z >= (dimension / 2) + quad_position.z) {

			quad_position.z += (dimension / 2);

			mask_index += 4;

			state.idx_stack[state.scale] |= idx_set_z_mask;

		}

		// Check to see if we are on a valid oct
		if ((head >> 16) & mask_8[mask_index]) {

			// Check to see if it is a leaf
			if ((head >> 24) & mask_8[mask_index]) {

				// If it is, then we cannot traverse further as CP's won't have been generated
				return true;
			}

			// If all went well and we found a valid non-leaf oct then we will traverse further down the hierarchy
			state.scale++;
			dimension /= 2;

			// Count the number of valid octs that come before and add it to the index to get the position
			// Negate it by one as it counts itself
			int count = count_bits((uint8_t)(head >> 16) & count_mask_8[mask_index]) - 1;

			// access the element at which head points to and then add the specified number of indices
			// to get to the correct child descriptor
			head = descriptor_buffer[(head & child_pointer_mask) + count];

			// Increment the parent stack position and put the new oct node as the parent
			state.parent_stack_position++;
			state.parent_stack[state.parent_stack_position] = head;

		}
		else {
			// If the oct was not valid, then no CP's exists any further
			// This implicitly says that if it's non-valid then it must be a leaf!!

			// It appears that the traversal is now working but I need
			// to focus on how to now take care of the end condition.
			// Currently it adds the last parent on the second to lowest
			// oct CP. Not sure if thats correct
			return false;
		}
	}

	return true;
}

void Octree::print_block(int block_pos) {

	std::stringstream sss;
	for (int i = block_pos; i < (int)pow(2, 15); i++) {
		PrettyPrintUINT64(descriptor_buffer[i], &sss);
		sss << "\n";
	}
	DumpLog(&sss, "raw_data.txt");

}

std::tuple<uint64_t, uint64_t> Octree::GenerationRecursion(char* data, sf::Vector3i dimensions, sf::Vector3i pos, unsigned int voxel_scale) {

	// The 8 subvoxel coords starting from the 1th direction, the direction of the origin of the 3d grid
	// XY, Z++, XY
	std::vector<sf::Vector3i> v = {
		sf::Vector3i(pos.x              , pos.y              , pos.z),
		sf::Vector3i(pos.x + voxel_scale, pos.y              , pos.z),
		sf::Vector3i(pos.x              , pos.y + voxel_scale, pos.z),
		sf::Vector3i(pos.x + voxel_scale, pos.y + voxel_scale, pos.z),
		sf::Vector3i(pos.x              , pos.y              , pos.z + voxel_scale),
		sf::Vector3i(pos.x + voxel_scale, pos.y              , pos.z + voxel_scale),
		sf::Vector3i(pos.x              , pos.y + voxel_scale, pos.z + voxel_scale),
		sf::Vector3i(pos.x + voxel_scale, pos.y + voxel_scale, pos.z + voxel_scale)
	};

	// A tuple holding the child descriptor that we're going to fill out and the
	// absolute position of it within the descriptor buffer
	std::tuple<uint64_t, uint64_t> descriptor_and_position(0, 0);

	// If we hit the 1th voxel scale then we need to query the 3D grid
	// and get the voxel at that position. I assume in the future when I
	// want to do chunking / loading of raw data I can edit the voxel access
	if (voxel_scale == 1) {

		// Setting the individual valid mask bits
		// These don't bound check, should they?
		for (int i = 0; i < v.size(); i++) {
			if (get1DIndexedVoxel(data, dimensions, v.at(i)))
				SetBit(i + 16, &std::get<0>(descriptor_and_position));
		}

		// We are querying leafs, so we need to fill the leaf mask
		std::get<0>(descriptor_and_position) |= 0xFF000000;

		// The CP will be left blank, contour mask and ptr will need to
		// be added here later
		return descriptor_and_position;

	}

	std::vector<std::tuple<uint64_t, uint64_t>> descriptor_position_array;

	// Generate down the recursion, returning the descriptor of the current node
	for (int i = 0; i < v.size(); i++) {

		std::tuple<uint64_t, uint64_t> child(0, 0);

		// Get the child descriptor from the i'th to 8th subvoxel
		child = GenerationRecursion(data, dimensions, v.at(i), voxel_scale / 2);

		// =========== Debug ===========
		PrettyPrintUINT64(std::get<0>(child), &output_stream);
		output_stream << "    " << voxel_scale << "    " << counter++ << std::endl;
		// =============================

		// If the child is a leaf (contiguous) of non-valid values
		if (IsLeaf(std::get<0>(child)) && !CheckLeafSign(std::get<0>(child))) {
			// Leave the valid mask 0, set leaf mask to 1
			SetBit(i + 16 + 8, &std::get<0>(descriptor_and_position));
		}

		// If the child is valid and not a leaf
		else {

			// Set the valid mask, and add it to the descriptor array
			SetBit(i + 16, &std::get<0>(descriptor_and_position));
			descriptor_position_array.push_back(child);
		}
	}

	// We are working bottom up so we need to subtract from the stack position
	// the amount of elements we want to use

	int worst_case_insertion_size = descriptor_position_array.size() * 2;

	// check to see if we exceeded this page header, if so set the header and move the global position
	if (page_header_counter - worst_case_insertion_size <= 0) {

		// Jump to the page headers position and reset the counter
		descriptor_buffer_position -= 0x8000 - page_header_counter;
		page_header_counter = 0x8000;

		// Fill the space with blank
		memcpy(&descriptor_buffer[descriptor_buffer_position], &current_info_section_position, sizeof(uint64_t));

		descriptor_buffer_position--;

	}

	unsigned int far_pointer_count = 0;
	uint64_t far_pointer_block_position = descriptor_buffer_position;

	// Count the far pointers we need to allocate
	for (int i = descriptor_position_array.size() - 1; i >= 0; i--) {

		// this is not the actual relative distance write, so we pessimistically guess that we will have
		// the worst relative distance via the insertion size
		uint64_t relative_distance = std::get<1>(descriptor_position_array.at(i)) - (descriptor_buffer_position - worst_case_insertion_size);

		// check to see if we tripped the far pointer
		if (relative_distance > 0x8000) {

			// This is writing the ABSOLUTE POSITION for far pointers, is this what I want?
			memcpy(&descriptor_buffer[descriptor_buffer_position], &std::get<1>(descriptor_position_array.at(i)), sizeof(uint64_t));
			descriptor_buffer_position--;

			far_pointer_count++;
		}
	}

	// We gotta go backwards as memcpy of a vector can be emulated by starting from the rear
	for (int i = descriptor_position_array.size() - 1; i >= 0; i--) {

		// just gonna redo the far pointer check loosing a couple of cycles but oh well
		uint64_t relative_distance = std::get<1>(descriptor_position_array.at(i)) - descriptor_buffer_position;

		uint64_t descriptor = std::get<0>(descriptor_position_array.at(i));

		// check to see if the
		if (relative_distance > 0x8000) {

			descriptor |= far_bit_mask;
			descriptor |= far_pointer_block_position;

			far_pointer_block_position--;

		} else {

			descriptor |= relative_distance;

		}

		// We have finished building the CD so we push it onto the buffer
		memcpy(&descriptor_buffer[descriptor_buffer_position], &descriptor, sizeof(uint64_t));
		descriptor_buffer_position--;

	}

	std::get<1>(descriptor_and_position) = descriptor_buffer_position + 1;

	// Return the node up the stack
	return descriptor_and_position;
}

char Octree::get1DIndexedVoxel(char* data, sf::Vector3i dimensions, sf::Vector3i position) {
	return data[position.x + OCT_DIM * (position.y + OCT_DIM * position.z)];
}

<|endoftext|>


<commit_before>
#include "Grabber.h"

#include "tis_logging.h"

using namespace tis_imaging;

Grabber::Grabber ()
    : device(nullptr), pipeline(std::make_shared<PipelineManager>())
{}

Grabber::~Grabber ()
{
    if (isDeviceOpen())
        closeDevice();
}

bool Grabber::openDevice (const CaptureDevice& _device)
{
    if (pipeline->getPipelineStatus() == PIPELINE_PLAYING ||
        pipeline->getPipelineStatus() == PIPELINE_PAUSED)
    {
        return false;
    }

    if (isDeviceOpen())
    {
        bool ret = closeDevice();
        if (ret == false)
        {
            tis_log(TIS_LOG_ERROR, "Unable to close previous device.");
            return false;
        }
    }

    open_device = _device;

    device = openDeviceInterface(open_device);

    if (device == nullptr)
    {
        return false;
    }

    device_properties = device->getProperties();

    tis_log(TIS_LOG_DEBUG, "Retrieved %d properties",device_properties.size());
    bool ret = pipeline->setSource(device);

    if (ret == true)
    {
        pipeline_properties = pipeline->getFilterProperties();
    }

    return true;
}

bool Grabber::isDeviceOpen () const
{
    if (device != nullptr)
    {
        return true;
    }

    return false;
}

CaptureDevice Grabber::getDevice () const
{
    return this->open_device;
}

bool Grabber::closeDevice ()
{
    std::string name = open_device.getName();

    pipeline->destroyPipeline();

    open_device = CaptureDevice ();
    device.reset();
    device_properties.clear();

    tis_log(TIS_LOG_INFO, "Closed device %s.", name.c_str());

    return true;
}

std::vector<Property> Grabber::getAvailableProperties ()
{
    if (!isDeviceOpen())
    {
        return std::vector<Property>();
    }

    std::vector<Property> props;

    for (const auto& p : device_properties)
    {
        props.push_back(*p);
    }

    for ( const auto& p : pipeline_properties)
    {
        props.push_back(*p);
    }

    return props;
}

std::vector<VideoFormatDescription> Grabber::getAvailableVideoFormats () const
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return std::vector<VideoFormatDescription>();
    }

    return pipeline->getAvailableVideoFormats();
}

bool Grabber::setVideoFormat (const VideoFormat& _format)
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return false;
    }

    return this->device->setVideoFormat(_format);
}

VideoFormat Grabber::getActiveVideoFormat () const
{
    if(!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return VideoFormat();
    }

    return device->getActiveVideoFormat();
}

bool Grabber::startStream (std::shared_ptr<ImageSink> sink)
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return false;
    }
    pipeline->setSink(sink);

    return pipeline->setPipelineStatus(PIPELINE_PLAYING);
}

bool Grabber::stopStream ()
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return false;
    }

    return pipeline->setPipelineStatus(PIPELINE_STOPPED);
}

<commit_msg>Propagated VideoFormat to pipeline<commit_after>
#include "Grabber.h"

#include "tis_logging.h"

using namespace tis_imaging;

Grabber::Grabber ()
    : device(nullptr), pipeline(std::make_shared<PipelineManager>())
{}

Grabber::~Grabber ()
{
    if (isDeviceOpen())
        closeDevice();
}

bool Grabber::openDevice (const CaptureDevice& _device)
{
    if (pipeline->getPipelineStatus() == PIPELINE_PLAYING ||
        pipeline->getPipelineStatus() == PIPELINE_PAUSED)
    {
        return false;
    }

    if (isDeviceOpen())
    {
        bool ret = closeDevice();
        if (ret == false)
        {
            tis_log(TIS_LOG_ERROR, "Unable to close previous device.");
            return false;
        }
    }

    open_device = _device;

    device = openDeviceInterface(open_device);

    if (device == nullptr)
    {
        return false;
    }

    device_properties = device->getProperties();

    tis_log(TIS_LOG_DEBUG, "Retrieved %d properties",device_properties.size());
    bool ret = pipeline->setSource(device);

    if (ret == true)
    {
        pipeline_properties = pipeline->getFilterProperties();
    }

    return true;
}

bool Grabber::isDeviceOpen () const
{
    if (device != nullptr)
    {
        return true;
    }

    return false;
}

CaptureDevice Grabber::getDevice () const
{
    return this->open_device;
}

bool Grabber::closeDevice ()
{
    std::string name = open_device.getName();

    pipeline->destroyPipeline();

    open_device = CaptureDevice ();
    device.reset();
    device_properties.clear();

    tis_log(TIS_LOG_INFO, "Closed device %s.", name.c_str());

    return true;
}

std::vector<Property> Grabber::getAvailableProperties ()
{
    if (!isDeviceOpen())
    {
        return std::vector<Property>();
    }

    std::vector<Property> props;

    for (const auto& p : device_properties)
    {
        props.push_back(*p);
    }

    for ( const auto& p : pipeline_properties)
    {
        props.push_back(*p);
    }

    return props;
}

std::vector<VideoFormatDescription> Grabber::getAvailableVideoFormats () const
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return std::vector<VideoFormatDescription>();
    }

    return pipeline->getAvailableVideoFormats();
}

bool Grabber::setVideoFormat (const VideoFormat& _format)
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return false;
    }

    pipeline->setVideoFormat(_format);

    return this->device->setVideoFormat(_format);
}

VideoFormat Grabber::getActiveVideoFormat () const
{
    if(!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return VideoFormat();
    }

    return device->getActiveVideoFormat();
}

bool Grabber::startStream (std::shared_ptr<ImageSink> sink)
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return false;
    }
    pipeline->setSink(sink);

    return pipeline->setPipelineStatus(PIPELINE_PLAYING);
}

bool Grabber::stopStream ()
{
    if (!isDeviceOpen())
    {
        tis_log(TIS_LOG_ERROR, "No open device");
        return false;
    }

    return pipeline->setPipelineStatus(PIPELINE_STOPPED);
}

<|endoftext|>