ocarina/core/queue.c

367 lines
8.0 KiB
C

/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <core/queue.h>
#include <core/string.h>
#include <stdlib.h>
static int track_less_than(const void *a, const void *b, void *data)
{
struct track *lhs = (struct track *)a;
struct track *rhs = (struct track *)b;
GSList *cur = (GSList *)data;
int res, field;
while (cur) {
field = GPOINTER_TO_INT(cur->data);
if (field > 0)
res = track_compare(lhs, rhs, field);
else
res = track_compare(rhs, lhs, abs(field));
if (res == 0) {
cur = g_slist_next(cur);
continue;
}
break;
};
return res;
}
static inline void *__queue_init(struct queue *queue, void *data)
{
if (queue->q_ops)
return queue->q_ops->qop_init(queue, data);
return NULL;
}
static inline void __queue_deinit(struct queue *queue)
{
if (queue->q_ops)
queue->q_ops->qop_deinit(queue);
}
static inline unsigned int __queue_added(struct queue *queue,
struct track *track,
unsigned int pos)
{
queue->q_length += track->tr_length;
if (queue->q_ops)
queue->q_ops->qop_added(queue, pos);
return pos;
}
static inline unsigned int __queue_add_head(struct queue *queue,
struct track *track)
{
g_queue_push_head(&queue->q_tracks, track);
return __queue_added(queue, track, 0);
}
static inline unsigned int __queue_add_tail(struct queue *queue,
struct track *track)
{
g_queue_push_tail(&queue->q_tracks, track);
return __queue_added(queue, track, queue_size(queue) - 1);
}
static inline unsigned int __queue_add_sorted(struct queue *queue,
struct track *track)
{
struct queue_iter it;
queue_for_each(queue, &it) {
if (track_less_than(queue_iter_val(&it), track, queue->q_sort) > 0) {
g_queue_insert_before(&queue->q_tracks, it.it_iter, track);
return __queue_added(queue, track, it.it_pos);
}
}
return __queue_add_tail(queue, track);
}
static inline bool __queue_erase(struct queue *queue, struct queue_iter *it)
{
struct track *track = queue_iter_val(it);
if (queue->q_ops)
return queue->q_ops->qop_erase(queue, track);
return true;
}
static inline void __queue_remove(struct queue *queue, struct queue_iter *it)
{
struct track *track = queue_iter_val(it);
unsigned int pos = it->it_pos;
GList *link = it->it_iter;
queue_iter_prev(it);
g_queue_delete_link(&queue->q_tracks, link);
queue->q_length -= track->tr_length;
if (queue->q_ops)
queue->q_ops->qop_removed(queue, pos);
}
static inline void __queue_clear(struct queue *queue, unsigned int n)
{
queue->q_length = 0;
if (queue->q_ops)
queue->q_ops->qop_cleared(queue, n);
}
static inline void __queue_updated(struct queue *queue, unsigned int pos)
{
if (queue->q_ops)
queue->q_ops->qop_updated(queue, pos);
}
static inline struct track *__queue_selected(struct queue *queue, unsigned int pos)
{
struct track *track = queue_iter_val(&queue->q_cur);
if (queue_has_flag(queue, Q_REPEAT) == false)
__queue_remove(queue, &queue->q_cur);
else
__queue_updated(queue, pos);
return track;
}
static inline void __queue_save(struct queue *queue, enum queue_flags flag)
{
if (queue->q_ops && queue_has_flag(queue, flag))
queue->q_ops->qop_save(queue, flag);
}
void queue_init(struct queue *queue, unsigned int flags,
const struct queue_ops *ops, void *data)
{
queue->q_flags = flags;
queue->q_length = 0;
queue->q_sort = NULL;
queue->q_ops = ops;
g_queue_init(&queue->q_tracks);
queue_iter_init(queue, &queue->q_cur);
queue->q_private = __queue_init(queue, data);
}
void queue_deinit(struct queue *queue)
{
queue_clear(queue);
__queue_deinit(queue);
g_slist_free(queue->q_sort);
queue->q_sort = NULL;
}
void queue_save_flags(struct queue *queue, struct file *file)
{
GSList *cur = queue->q_sort;
int field;
file_writef(file, "%u %u", queue->q_flags, g_slist_length(cur));
while (cur) {
field = GPOINTER_TO_INT(cur->data);
file_writef(file, " %u %d", abs(field) - 1, field > 0);
cur = g_slist_next(cur);
}
file_writef(file, "\n");
}
void queue_save_tracks(struct queue *queue, struct file *file)
{
struct queue_iter it;
file_writef(file, "%u", queue_size(queue));
queue_for_each(queue, &it)
file_writef(file, " %u", track_index(queue_iter_val(&it)));
}
void queue_load_flags(struct queue *queue, struct file *file)
{
unsigned int flags, n, i;
int field, ascending;
file_readf(file, "%u %u", &flags, &n);
for (i = 0; i < n; i++) {
file_readf(file, "%u %d", &field, &ascending);
queue_sort(queue, field + 1, (i == 0) ? true : false);
if (ascending == false)
queue_sort(queue, field + 1, false);
}
queue->q_flags |= flags;
}
void queue_load_tracks(struct queue *queue, struct file *file)
{
unsigned int i, n, t;
file_readf(file, "%u ", &n);
for (i = 0; i < n; i++) {
file_readf(file, "%u", &t);
queue_add(queue, track_get(t));
}
}
void queue_set_flag(struct queue *queue, enum queue_flags flag)
{
queue->q_flags |= flag;
__queue_save(queue, Q_SAVE_FLAGS);
}
void queue_unset_flag(struct queue *queue, enum queue_flags flag)
{
if (!queue_has_flag(queue, flag))
return;
queue->q_flags &= ~flag;
if (flag == Q_ADD_FRONT)
queue_resort(queue);
__queue_save(queue, Q_SAVE_FLAGS);
}
unsigned int queue_add(struct queue *queue, struct track *track)
{
if (queue_has_flag(queue, Q_ADD_FRONT))
return __queue_add_head(queue, track);
if (queue->q_sort)
return __queue_add_sorted(queue, track);
return __queue_add_tail(queue, track);
}
void queue_erase(struct queue *queue, unsigned int index)
{
struct queue_iter it;
queue_iter_set(queue, &it, index);
if (__queue_erase(queue, &it))
__queue_remove(queue, &it);
}
void queue_remove(struct queue *queue, unsigned int index)
{
struct queue_iter it;
queue_iter_set(queue, &it, index);
__queue_remove(queue, &it);
}
unsigned int queue_remove_all(struct queue *queue, struct track *track)
{
unsigned int count = 0;
struct queue_iter it;
while (queue_at(queue, 0) == track) {
queue_remove(queue, 0);
count++;
}
queue_for_each(queue, &it) {
if (queue_iter_val(&it) == track) {
__queue_remove(queue, &it);
count++;
}
}
return count;
}
void queue_clear(struct queue *queue)
{
unsigned int n = queue_size(queue);
g_queue_clear(&queue->q_tracks);
__queue_clear(queue, n);
}
bool queue_has(struct queue *queue, struct track *track)
{
struct queue_iter it;
queue_for_each(queue, &it) {
if (queue_iter_val(&it) == track)
return true;
}
return false;
}
void queue_updated(struct queue *queue, struct track *track)
{
struct queue_iter it;
queue_for_each(queue, &it) {
if (queue_iter_val(&it) == track)
__queue_updated(queue, it.it_pos);
}
}
struct track *queue_selected(struct queue *queue, unsigned int index)
{
if (index > queue_size(queue))
return NULL;
if (queue->q_cur.it_pos != index)
queue_iter_set(queue, &queue->q_cur, index);
return __queue_selected(queue, index);
}
struct track *queue_next(struct queue *queue)
{
unsigned int pos, size = queue_size(queue);
if (!queue_has_flag(queue, Q_ENABLED))
return NULL;
else if (size == 0)
return NULL;
if (size == 1)
queue_iter_set(queue, &queue->q_cur, 0);
else if (queue_has_flag(queue, Q_RANDOM)) {
pos = g_random_int_range(1, (size < 15) ? size : size / 3);
pos += queue->q_cur.it_pos;
queue_iter_set(queue, &queue->q_cur, pos % size);
} else {
queue_iter_next(&queue->q_cur);
if (!queue->q_cur.it_iter)
queue_iter_set(queue, &queue->q_cur, 0);
}
return __queue_selected(queue, queue->q_cur.it_pos);
}
void queue_resort(struct queue *queue)
{
g_queue_sort(&queue->q_tracks, track_less_than, queue->q_sort);
for (unsigned int i = 0; i < queue_size(queue); i++)
__queue_updated(queue, i);
}
void queue_sort(struct queue *queue, enum compare_t sort, bool reset)
{
GSList *cur = NULL;
int field;
if (queue_has_flag(queue, Q_NO_SORT))
return;
if (reset) {
g_slist_free(queue->q_sort);
queue->q_sort = NULL;
}
cur = queue->q_sort;
while (cur) {
field = GPOINTER_TO_INT(cur->data);
if (abs(field) == sort) {
cur->data = GINT_TO_POINTER(-field);
goto out_sort;
}
cur = g_slist_next(cur);
}
queue->q_sort = g_slist_append(queue->q_sort, GINT_TO_POINTER(sort));
out_sort:
queue_resort(queue);
__queue_save(queue, Q_SAVE_SORT);
}