blob: 3bf8364dcb5bf8aa0ef0d8d1728b3b8d7a956b84 [file] [log] [blame] [edit]
/*
* Functions to maintain a list of numeric node ranges. Depending upon the
* parameter "sorted" used when adding elements, this list may be a strictly-
* ordered, singly-linked list to represent disjoint node ranges of the type
* 'a' (single node) or 'a-b' (range, with a < b).
*
* For example, '1,7-8,20,33-29'
*
* Copyright (c) 2009-2011 Centro Svizzero di Calcolo Scientifico (CSCS)
* Licensed under the GPLv2.
*/
#include "basil_alps.h"
#define CRAY_MAX_DIGITS 5 /* nid%05d format */
/* internal constructor */
static struct nodespec *_ns_new(uint32_t start, uint32_t end)
{
struct nodespec *new = xmalloc(sizeof(*new));
new->start = start;
new->end = end;
return new;
}
/**
* _ns_add_range - Insert/merge new range into existing nodespec list.
* @head: head of the ordered list
* @new_start: start value of node range to insert
* @new_end: end value of node range to insert
* @sorted: if set, then maintain @head as duplicate-free list, ordered
* in ascending order of node-specifier intervals, with a gap of
* at least 2 between adjacent entries. Otherwise maintain @head
* as a list of elements in the order added
*
* Maintains @head as list
* Returns 0 if ok, -1 on failure.
*/
static int _ns_add_range(struct nodespec **head,
uint32_t new_start, uint32_t new_end, bool sorted)
{
struct nodespec *cur = *head, *next;
assert(new_start <= new_end);
if (!sorted) {
if (cur) {
while (cur->next) /* find end of list */
cur = cur->next;
if (new_start == (cur->end + 1))
cur->end = new_end;
else
cur->next = _ns_new(new_start, new_end);
} else {
*head = _ns_new(new_start, new_end);
}
return 0;
}
if (cur == NULL || new_end + 1 < cur->start) {
*head = _ns_new(new_start, new_end);
(*head)->next = cur;
return 0;
}
for (next = cur->next;
new_start > cur->end + 1;
cur = next, next = cur->next)
if (next == NULL || new_end + 1 < next->start) {
next = _ns_new(new_start, new_end);
next->next = cur->next;
cur->next = next;
return 0;
}
/* new_start <= cur->end + 1 */
if (new_start < cur->start)
cur->start = new_start;
if (new_end <= cur->end)
return 0;
cur->end = new_end;
while ((next = cur->next) && next->start <= new_end + 1) {
if (next->end > new_end)
cur->end = next->end;
cur->next = next->next;
xfree(next);
}
/* next == NULL || next->start > new_end + 1 */
return 0;
}
/** Add a single node (1-element range) */
extern int ns_add_node(struct nodespec **head, uint32_t node_id, bool sorted)
{
return _ns_add_range(head, node_id, node_id, sorted);
}
/* count the number of nodes starting at @head */
static int ns_count_nodes(const struct nodespec *head)
{
const struct nodespec *cur;
uint32_t node_count = 0;
for (cur = head; cur; cur = cur->next)
node_count += cur->end - cur->start + 1;
return node_count;
}
/**
* ns_ranged_string - Write compressed node specification to buffer.
* @head: start of nodespec list
* @buf: buffer to write to
* @buflen: size of @buf
* Returns number of characters written if successful, -1 on overflow.
*/
static ssize_t ns_ranged_string(const struct nodespec *head,
char *buf, size_t buflen)
{
const struct nodespec *cur;
ssize_t n, len = 0;
for (cur = head; cur; cur = cur->next) {
if (cur != head) {
n = snprintf(buf + len, buflen - len, ",");
if (n < 0 || (len += n) >= buflen)
return -1;
}
n = snprintf(buf + len, buflen - len, "%u", cur->start);
if (n < 0 || (len += n) >= buflen)
return -1;
if (cur->start != cur->end) {
n = snprintf(buf + len, buflen - len, "-%u", cur->end);
if (n < 0 || (len += n) >= buflen)
return -1;
}
}
return len;
}
/* Compress @head into nodestring. Result must be xfree()d. */
char *ns_to_string(const struct nodespec *head)
{
char *buf = NULL;
size_t size = ns_count_nodes(head);
if (size) {
/* Over-estimation: using all digits, plus either '-' or '\0' */
size *= CRAY_MAX_DIGITS + 1;
buf = xmalloc(size);
if (buf == NULL)
fatal("can not allocate %d", (int)size);
if (ns_ranged_string(head, buf, size) < 0)
fatal("can not expand nodelist expression");
}
return buf;
}