216 lines
4.4 KiB
C
216 lines
4.4 KiB
C
/* Copyright (c) 2021-2023 Barcelona Supercomputing Center (BSC)
|
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
|
|
//#define ENABLE_DEBUG
|
|
|
|
#include "sort.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "bay.h"
|
|
#include "chan.h"
|
|
#include "value.h"
|
|
|
|
static int
|
|
cmp_int64(const void *a, const void *b)
|
|
{
|
|
int64_t aa = *(const int64_t *) a;
|
|
int64_t bb = *(const int64_t *) b;
|
|
|
|
if (aa < bb)
|
|
return -1;
|
|
else if (aa > bb)
|
|
return +1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/** Replaces the value old in the array arr by new, while keeping the
|
|
* array arr sorted.
|
|
*
|
|
* Preconditions:
|
|
* - arr is sorted
|
|
* - old is in arr
|
|
* - old != new
|
|
*/
|
|
void
|
|
sort_replace(int64_t *arr, int64_t n, int64_t old, int64_t new)
|
|
{
|
|
if (unlikely(old == new))
|
|
die("old == new");
|
|
|
|
/* Remove the old first then insert the new */
|
|
if (old < new) {
|
|
int64_t i = 0;
|
|
|
|
/* Quick jump to middle if less than old */
|
|
int64_t m = n / 2;
|
|
if (arr[m] < old)
|
|
i = m;
|
|
|
|
/* Skip content until old, no need to copy */
|
|
for (; arr[i] < old; i++)
|
|
;
|
|
|
|
/* Copy middle section replacing old */
|
|
for (; arr[i + 1] <= new && i < n - 1; i++)
|
|
arr[i] = arr[i + 1];
|
|
|
|
/* Place new */
|
|
arr[i] = new;
|
|
} else { /* new < old */
|
|
int64_t i = 0;
|
|
|
|
/* Find old, must be found */
|
|
for (; arr[i] < old; i++)
|
|
;
|
|
|
|
/* Shift right to replace old */
|
|
for (; arr[i - 1] > new && i > 0; i--)
|
|
arr[i] = arr[i - 1];
|
|
|
|
/* Invariant: Either i == 0 or arr[i] <= new
|
|
* and the element arr[i] must be gone */
|
|
|
|
/* Place new */
|
|
arr[i] = new;
|
|
}
|
|
}
|
|
|
|
/** Called when an input channel changes its value */
|
|
static int
|
|
sort_cb_input(struct chan *in_chan, void *ptr)
|
|
{
|
|
struct sort_input *input = ptr;
|
|
struct sort *sort = input->sort;
|
|
struct value cur;
|
|
|
|
if (chan_read(in_chan, &cur) != 0) {
|
|
err("chan_read() failed\n");
|
|
return -1;
|
|
}
|
|
|
|
int64_t new = 0;
|
|
if (cur.type == VALUE_INT64)
|
|
new = cur.i;
|
|
|
|
int64_t index = input->index;
|
|
int64_t old = sort->values[index];
|
|
|
|
/* Nothing to do if no change */
|
|
if (old == new)
|
|
return 0;
|
|
|
|
dbg("sort input %s changed", in_chan->name);
|
|
|
|
/* Otherwise recompute the outputs */
|
|
sort->values[index] = new;
|
|
|
|
if (likely(sort->copied)) {
|
|
sort_replace(sort->sorted, sort->n, old, new);
|
|
} else {
|
|
memcpy(sort->sorted, sort->values, sort->n * sizeof(int64_t));
|
|
qsort(sort->sorted, sort->n, sizeof(int64_t), cmp_int64);
|
|
sort->copied = 1;
|
|
}
|
|
|
|
for (int64_t i = 0; i < sort->n; i++) {
|
|
struct value val = value_int64(sort->sorted[i]);
|
|
struct value last;
|
|
if (chan_read(&sort->outputs[i], &last) != 0) {
|
|
err("chan_read failed");
|
|
return -1;
|
|
}
|
|
|
|
if (value_is_equal(&last, &val))
|
|
continue;
|
|
|
|
dbg("writting value %s into channel %s",
|
|
value_str(val),
|
|
sort->outputs[i].name);
|
|
|
|
if (chan_set(&sort->outputs[i], val) != 0) {
|
|
err("chan_set failed");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sort_init(struct sort *sort, struct bay *bay, int64_t n, const char *name)
|
|
{
|
|
memset(sort, 0, sizeof(struct sort));
|
|
sort->bay = bay;
|
|
sort->n = n;
|
|
sort->inputs = calloc(n, sizeof(struct sort_input));
|
|
if (sort->inputs == NULL) {
|
|
err("calloc failed:");
|
|
return -1;
|
|
}
|
|
sort->outputs = calloc(n, sizeof(struct chan));
|
|
if (sort->outputs == NULL) {
|
|
err("calloc failed:");
|
|
return -1;
|
|
}
|
|
sort->values = calloc(n, sizeof(int64_t));
|
|
if (sort->values == NULL) {
|
|
err("calloc failed:");
|
|
return -1;
|
|
}
|
|
sort->sorted = calloc(n, sizeof(int64_t));
|
|
if (sort->sorted == NULL) {
|
|
err("calloc failed:");
|
|
return -1;
|
|
}
|
|
|
|
/* Init and register outputs */
|
|
for (int64_t i = 0; i < n; i++) {
|
|
struct chan *out = &sort->outputs[i];
|
|
chan_init(out, CHAN_SINGLE, "%s.out%ld", name, i);
|
|
|
|
/* The sort module may write multiple times to the same
|
|
* channel if we update more than one input. */
|
|
chan_prop_set(out, CHAN_DIRTY_WRITE, 1);
|
|
|
|
/* No duplicate checks are done by sort module, so we
|
|
* simply allow them */
|
|
chan_prop_set(out, CHAN_ALLOW_DUP, 1);
|
|
|
|
if (bay_register(bay, out) != 0) {
|
|
err("bay_register out%ld failed", i);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sort_set_input(struct sort *sort, int64_t index, struct chan *chan)
|
|
{
|
|
struct sort_input *input = &sort->inputs[index];
|
|
|
|
if (input->chan != NULL) {
|
|
err("input %d already has a channel", index);
|
|
return -1;
|
|
}
|
|
|
|
input->chan = chan;
|
|
input->index = index;
|
|
input->sort = sort;
|
|
|
|
if (bay_add_cb(sort->bay, BAY_CB_DIRTY, chan, sort_cb_input, input, 1) == NULL) {
|
|
err("bay_add_cb failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct chan *
|
|
sort_get_output(struct sort *sort, int64_t index)
|
|
{
|
|
return &sort->outputs[index];
|
|
}
|