Your IP : 216.73.216.48
/*
* index.h
* Copyright 2014 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions, and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions, and the following disclaimer in the documentation
* provided with the distribution.
*
* This software is provided "as is" and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising from
* the use of this software.
*/
#ifndef LIBAUDCORE_INDEX_H
#define LIBAUDCORE_INDEX_H
#include <libaudcore/templates.h>
/*
* Index is a lightweight list class similar to std::vector, but with the
* following differences:
* - The base implementation is type-agnostic, so it only needs to be compiled
* once. Type-safety is provided by a thin template subclass.
* - Objects are moved in memory without calling any assignment operator.
* Be careful to use only objects that can handle this.
*/
class IndexBase
{
public:
typedef int (* CompareFunc) (const void * a, const void * b, void * userdata);
constexpr IndexBase () :
m_data (nullptr),
m_len (0),
m_size (0) {}
void clear (aud::EraseFunc erase_func); // use as destructor
IndexBase (IndexBase && b) :
m_data (b.m_data),
m_len (b.m_len),
m_size (b.m_size)
{
b.m_data = nullptr;
b.m_len = 0;
b.m_size = 0;
}
void steal (IndexBase && b, aud::EraseFunc erase_func)
{
if (this != & b)
{
clear (erase_func);
new (this) IndexBase (std::move (b));
}
}
void * begin ()
{ return m_data; }
const void * begin () const
{ return m_data; }
void * end ()
{ return (char *) m_data + m_len; }
const void * end () const
{ return (char *) m_data + m_len; }
int len () const
{ return m_len; }
void * insert (int pos, int len); // no fill
void insert (int pos, int len, aud::FillFunc fill_func);
void insert (const void * from, int pos, int len, aud::CopyFunc copy_func);
void remove (int pos, int len, aud::EraseFunc erase_func);
void erase (int pos, int len, aud::FillFunc fill_func, aud::EraseFunc erase_func);
void shift (int from, int to, int len, aud::FillFunc fill_func, aud::EraseFunc erase_func);
void move_from (IndexBase & b, int from, int to, int len, bool expand,
bool collapse, aud::FillFunc fill_func, aud::EraseFunc erase_func);
void sort (CompareFunc compare, int elemsize, void * userdata);
private:
void * m_data;
int m_len, m_size;
};
template<class T>
class Index : private IndexBase
{
public:
typedef int (* CompareFunc) (const T & a, const T & b, void * userdata);
constexpr Index () :
IndexBase () {}
// use with care!
IndexBase & base ()
{ return * this; }
void clear ()
{ IndexBase::clear (aud::erase_func<T> ()); }
~Index ()
{ clear (); }
Index (Index && b) :
IndexBase (std::move (b)) {}
void operator= (Index && b)
{ steal (std::move (b), aud::erase_func<T> ()); }
T * begin ()
{ return (T *) IndexBase::begin (); }
const T * begin () const
{ return (const T *) IndexBase::begin (); }
T * end ()
{ return (T *) IndexBase::end (); }
const T * end () const
{ return (const T *) IndexBase::end (); }
int len () const
{ return cooked (IndexBase::len ()); }
T & operator[] (int i)
{ return begin ()[i]; }
const T & operator[] (int i) const
{ return begin ()[i]; }
void insert (int pos, int len)
{ IndexBase::insert (raw (pos), raw (len), aud::fill_func<T> ()); }
void insert (const T * from, int pos, int len)
{ IndexBase::insert (from, raw (pos), raw (len), aud::copy_func<T> ()); }
void remove (int pos, int len)
{ IndexBase::remove (raw (pos), raw (len), aud::erase_func<T> ()); }
void erase (int pos, int len)
{ IndexBase::erase (raw (pos), raw (len), aud::fill_func<T> (), aud::erase_func<T> ()); }
void shift (int from, int to, int len)
{ IndexBase::shift (raw (from), raw (to), raw (len), aud::fill_func<T> (), aud::erase_func<T> ()); }
void move_from (Index<T> & b, int from, int to, int len, bool expand, bool collapse)
{ IndexBase::move_from (b, raw (from), raw (to), raw (len), expand,
collapse, aud::fill_func<T> (), aud::erase_func<T> ()); }
template<class ... Args>
T & append (Args && ... args)
{
return * aud::construct<T>::make (IndexBase::insert (-1, sizeof (T)),
std::forward<Args> (args) ...);
}
int find (const T & val) const
{
for (const T * iter = begin (); iter != end (); iter ++)
{
if (* iter == val)
return iter - begin ();
}
return -1;
}
template<class F>
void remove_if (F func, bool clear_if_empty = false)
{
T * iter = begin ();
while (iter != end ())
{
if (func (* iter))
remove (iter - begin (), 1);
else
iter ++;
}
if (clear_if_empty && ! len ())
clear ();
}
void sort (CompareFunc compare, void * userdata)
{
struct state_t {
CompareFunc compare;
void * userdata;
};
auto wrapper = [] (const void * a, const void * b, void * userdata) -> int
{
auto state = (const state_t *) userdata;
return state->compare (* (const T *) a, * (const T *) b, state->userdata);
};
const state_t state = {compare, userdata};
IndexBase::sort (wrapper, sizeof (T), (void *) & state);
}
// for use of Index as a raw data buffer
// unlike insert(), does not zero-fill any added space
void resize (int size)
{
static_assert (std::is_trivial<T>::value, "for basic types only");
int diff = size - len ();
if (diff > 0)
IndexBase::insert (-1, raw (diff));
else if (diff < 0)
IndexBase::remove (raw (size), -1, nullptr);
}
private:
static constexpr int raw (int len)
{ return len * sizeof (T); }
static constexpr int cooked (int len)
{ return len / sizeof (T); }
};
#endif // LIBAUDCORE_INDEX_H