From cf3aa45b7a9b20f6e51862eabc1b7fcf54154b3b Mon Sep 17 00:00:00 2001 From: Chris Su Date: Fri, 2 Jan 2026 03:29:32 -0500 Subject: [PATCH 01/35] Boiler code for the C backend rewrite --- .../engine/src-crewrite/constants.h | 32 +++++ .../engine/src-crewrite/cost_function.c | 0 .../engine/src-crewrite/cost_function.h | 0 .../engine/src-crewrite/interval.c | 0 .../engine/src-crewrite/interval.h | 41 ++++++ src/elastisched/engine/src-crewrite/job.c | 0 src/elastisched/engine/src-crewrite/job.h | 0 src/elastisched/engine/src-crewrite/policy.c | 9 ++ src/elastisched/engine/src-crewrite/policy.h | 27 ++++ .../engine/src-crewrite/schedule.c | 0 .../engine/src-crewrite/schedule.h | 0 .../engine/src-crewrite/simulated_annealer.h | 0 src/elastisched/engine/src-crewrite/tag.c | 117 ++++++++++++++++++ src/elastisched/engine/src-crewrite/tag.h | 95 ++++++++++++++ .../engine/src-crewrite/topo_sort.h | 0 15 files changed, 321 insertions(+) create mode 100644 src/elastisched/engine/src-crewrite/constants.h create mode 100644 src/elastisched/engine/src-crewrite/cost_function.c create mode 100644 src/elastisched/engine/src-crewrite/cost_function.h create mode 100644 src/elastisched/engine/src-crewrite/interval.c create mode 100644 src/elastisched/engine/src-crewrite/interval.h create mode 100644 src/elastisched/engine/src-crewrite/job.c create mode 100644 src/elastisched/engine/src-crewrite/job.h create mode 100644 src/elastisched/engine/src-crewrite/policy.c create mode 100644 src/elastisched/engine/src-crewrite/policy.h create mode 100644 src/elastisched/engine/src-crewrite/schedule.c create mode 100644 src/elastisched/engine/src-crewrite/schedule.h create mode 100644 src/elastisched/engine/src-crewrite/simulated_annealer.h create mode 100644 src/elastisched/engine/src-crewrite/tag.c create mode 100644 src/elastisched/engine/src-crewrite/tag.h create mode 100644 src/elastisched/engine/src-crewrite/topo_sort.h diff --git a/src/elastisched/engine/src-crewrite/constants.h b/src/elastisched/engine/src-crewrite/constants.h new file mode 100644 index 0000000..492269a --- /dev/null +++ b/src/elastisched/engine/src-crewrite/constants.h @@ -0,0 +1,32 @@ +#ifndef CONSTANTS +#define CONSTANTS + +#include + +#include "tag.h" + +typedef uint32_t time_t; + +const time_t MINUTE = (time_t)60; +const time_t HOUR_TO_MINUTES = (time_t)60; +const time_t DAY_TO_HOURS = (time_t)24; +const time_t WEEK_TO_DAYS = (time_t)7; + +const time_t HOUR = ((time_t)60 * MINUTE); +const time_t DAY = ((time_t)24 * HOUR); +const time_t WEEK = ((time_t)7 * DAY); + +const time_t AFTERNOON_START = 17; + +const double FRIDAY_HOURLY_COST_FACTOR = 2.0f; +const double SATURDAY_HOURLY_COST_FACTOR = 3.0f; + +const double EXP_DOWNFACTOR = 0.1f; +const double HOURLY_COST_FACTOR = 1.0f; + +const struct Tag WORK_TAG = {.name="ELASTISCHED_WORK_TYPE\0"}; +const double ILLEGAL_SCHEDULE_COST = 1e12f; + +const double EPSILON = 1e-5f; +const unsigned int DEFAULT_RNG_SEED = 1337; +#endif diff --git a/src/elastisched/engine/src-crewrite/cost_function.c b/src/elastisched/engine/src-crewrite/cost_function.c new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/cost_function.h b/src/elastisched/engine/src-crewrite/cost_function.h new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/interval.c b/src/elastisched/engine/src-crewrite/interval.c new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/interval.h b/src/elastisched/engine/src-crewrite/interval.h new file mode 100644 index 0000000..a9e44be --- /dev/null +++ b/src/elastisched/engine/src-crewrite/interval.h @@ -0,0 +1,41 @@ +#ifndef INTERVAL_H +#define INTERVAL_H + +#include +#include "constants.h" + +typedef struct Interval { + time_t low; + time_t high; +} Interval; + +enum Color { RED, BLACK }; + +typedef struct Node { + Node* left; + Node* right; + Node* parent; + Interval* interval; + void* value; + time_t max; + Color color; +} Node; + +typedef struct IntervalMap { + Node* root; +} IntervalMap; + +time_t interval_get_low(Interval* interval); +time_t interval_get_high(Interval* interval); +bool interval_eq(Interval* U, Interval* V); +bool interval_overlaps(Interval* U, Interval* V); +bool interval_contains(Interval* U, Interval* V); +time_t interval_length(Interval* interval); +bool is_valid_interval(Interval* interval); + +IntervalMap* mk_interval_map(); +void interval_map_insert(IntervalMap*, Interval* key, void* value); + +void free_interval_map(IntervalMap* map); +#endif + diff --git a/src/elastisched/engine/src-crewrite/job.c b/src/elastisched/engine/src-crewrite/job.c new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/job.h b/src/elastisched/engine/src-crewrite/job.h new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/policy.c b/src/elastisched/engine/src-crewrite/policy.c new file mode 100644 index 0000000..0cc970d --- /dev/null +++ b/src/elastisched/engine/src-crewrite/policy.c @@ -0,0 +1,9 @@ +#include "policy.h" + +uint8_t get_max_splits(Policy* policy) { return policy->max_splits; } +time_t get_min_split_duration(Policy* policy) { return policy->min_split_duration; } +uint8_t get_scheduling_policies(Policy* policy) { return policy->scheduling_policies; } + +bool is_splittable(Policy* policy) { return (policy->scheduling_policies & 1u) != 0; } +bool is_overlappable(Policy* policy) { return ((policy->scheduling_policies & 2u) >> 1) != 0; } +bool is_invisible(Policy* policy) { return ((policy->scheduling_policies & 4u) >> 2) != 0; } \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/policy.h b/src/elastisched/engine/src-crewrite/policy.h new file mode 100644 index 0000000..a29a4e8 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/policy.h @@ -0,0 +1,27 @@ +#ifndef POLICY_H +#define POLICY_H + +#include +#include +#include "constants.h" + +/** + * @brief Scheduling policy struct, which + * defines the configurations for how a job + * may be scheduled. Each job may have its own policy. + */ +typedef struct Policy { + uint8_t max_splits; /// how many times a splittable job may be split + time_t min_split_duration; /// minimum duration that a splittable job can be split into + uint8_t scheduling_policies; /// overloaded bitfield: bit 0 = is_splittable, bit 1 = is_overlappable, bit 2 = is_invisible +} Policy; + +uint8_t get_max_splits(Policy* policy); +time_t get_min_split_duration(Policy* policy); +uint8_t get_scheduling_policies(Policy* policy); + +bool is_splittable(Policy* policy); +bool is_overlappable(Policy* policy); +bool is_invisible(Policy* policy); + +#endif diff --git a/src/elastisched/engine/src-crewrite/schedule.c b/src/elastisched/engine/src-crewrite/schedule.c new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/schedule.h b/src/elastisched/engine/src-crewrite/schedule.h new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/simulated_annealer.h b/src/elastisched/engine/src-crewrite/simulated_annealer.h new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/tag.c b/src/elastisched/engine/src-crewrite/tag.c new file mode 100644 index 0000000..d610347 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/tag.c @@ -0,0 +1,117 @@ +#include "tag.h" + +/** + * @brief returns True if the names of ```U``` and ```V``` are the same + */ +bool tag_eq(Tag* U, Tag* V) { + return strcmp(U->name, V->name) == 0; +} + +int tag_cmp(Tag* U, Tag* V) { + return strcmp(U->name, V->name); +} + +/** + * @brief sets the description of ```U``` to ```description``` + */ +void tag_set_description(Tag* U, char* description) { + U->description = description; + return; +} + +/** + * @brief constructs a TagSet with capacity ```capacity``` + * + * @param capacity number of tags that the TagSet can hold + * @return TagSet* + */ +TagSet* mk_tag_set(size_t capacity) { + TagSet* tag_set = malloc(sizeof(TagSet)); + if (!tag_set) return NULL; + + Tag* data = malloc(capacity * sizeof(Tag)); + if (!data) { + free(tag_set); + return NULL; + } + + tag_set->size = 0; + tag_set->capacity = capacity; + tag_set->data = data; + + return tag_set; +} + +/** + * @brief resizes a set by doubling capacity and copying memory + * + * @param set set to resize + * @return true if resize successful, false otherwise + */ +bool resize_set(TagSet* set) { + set->capacity *= 2; + Tag* data = malloc(set->capacity * sizeof(Tag)); + if (!data) return false; + + memcpy((void*)data, (void*)set->data, set->size * sizeof(Tag)); + free(set->data); + set->data = data; + return true; +} + +/** + * @brief Find the insert index + * + * @param set the set to look in. + * Internally, ```set```->data should be sorted. + * @param tag the tag to look for + * @return size_t + */ +size_t set_insert_index(TagSet* set, Tag* tag) { + size_t l = 0; + size_t r = set->size; + + while (l < r) { + size_t mid = l + (r - l) / 2; + int _cmp = tag_cmp(tag, &(set->data)[mid]); + if (_cmp == 0) return mid; + if (_cmp < 0) r = mid; + else l = mid + 1; + } + return l; +} + + +/** + * @brief Helper function for inserting tag into a set + * + * @param set the set to insert to + * @param tag the tag to insert + * @param insert_ind the index to insert at + */ +void set_insert(TagSet* set, Tag* tag, size_t insert_ind) { + if (insert_ind < set->size) { + memmove(&set->data[insert_ind + 1], + &set->data[insert_ind], + (set->size - insert_ind) * sizeof(Tag)); + } + set->data[insert_ind] = *tag; + set->size++; +} + +/** + * @brief Helper function for addiing tag into a set + * + * @param set set to add to + * @param tag the tag to add + * @return true if no memory failures, false otherwise + */ +bool set_add(TagSet* set, Tag* tag) { + size_t index = set_insert_index(set, tag); + if (index < set->size && tag_eq(tag, &set->data[index])) return true; + + if (set->size == set->capacity && !resize_set(set)) return false; + + set_insert(set, tag, index); + return true; +} diff --git a/src/elastisched/engine/src-crewrite/tag.h b/src/elastisched/engine/src-crewrite/tag.h new file mode 100644 index 0000000..be72fc7 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/tag.h @@ -0,0 +1,95 @@ +#ifndef TAG_H +#define TAG_H + +#include +#include +#include +#include + +/** + * Tag.h + * + * Definitions for working with the Tag and TagSet structs. + * TagSet in practice does not get very large nor updated + * often so we opt for a simple vector-based sorted set. + * + * Two tags are equivalent if they share the same name, + * not necessarily the same description. + * + * This allows for O(logn) search time and membership + * checking, O(n) set difference and O(n) time to add + * to the set. + */ + + +typedef struct Tag { + char* name; + char* description; +} Tag; + +typedef struct TagSet { + Tag* data; + size_t size; + size_t capacity; +} TagSet; + +/** + * @brief returns True if the names of ```U``` and ```V``` are the same + */ +bool tag_eq(Tag* U, Tag* V); + +/** + * @brief comparator for Tag type + */ +int tag_cmp(Tag* U, Tag* V); + +/** + * @brief sets the description of ```U``` to ```description``` + */ +void tag_set_description(Tag* U, char* description); + +/** + * @brief constructs a TagSet with capacity ```capacity``` + * + * @param capacity number of tags that the TagSet can hold + * @return TagSet* + */ +TagSet* mk_tag_set(size_t capacity); + +/** + * @brief resizes a set by doubling capacity and copying memory + * + * @param set set to resize + * @return true if resize successful, false otherwise + */ +bool resize_set(TagSet* set); + +/** + * @brief Find the insert index + * + * @param set the set to look in. + * Internally, ```set```->data should be sorted. + * @param tag the tag to look for + * @return size_t + */ +size_t set_insert_index(TagSet* set, Tag* tag); + +/** + * @brief Helper function for inserting tag into a set + * + * @param set the set to insert to + * @param tag the tag to insert + * @param insert_ind the index to insert at + */ +void set_insert(TagSet* set, Tag* tag, size_t insert_ind); + +/** + * @brief Helper function for addiing tag into a set + * + * @param set set to add to + * @param tag the tag to add + * @return true if no memory failures, false otherwise + */ +bool set_add(TagSet* set, Tag* tag); + +#endif diff --git a/src/elastisched/engine/src-crewrite/topo_sort.h b/src/elastisched/engine/src-crewrite/topo_sort.h new file mode 100644 index 0000000..e69de29 From 3807b2fe28e37b20a2f434b53bbcba144c217045 Mon Sep 17 00:00:00 2001 From: Chris Su Date: Sat, 3 Jan 2026 16:30:31 -0500 Subject: [PATCH 02/35] Tag C API --- .../engine/src-crewrite/constants.h | 2 + .../engine/src-crewrite/interval.c | 113 ++++++++++++ .../engine/src-crewrite/interval.h | 24 +-- src/elastisched/engine/src-crewrite/policy.c | 10 +- src/elastisched/engine/src-crewrite/policy.h | 10 +- src/elastisched/engine/src-crewrite/tag.c | 166 +++++++++++------- src/elastisched/engine/src-crewrite/tag.h | 53 ++++-- 7 files changed, 269 insertions(+), 109 deletions(-) diff --git a/src/elastisched/engine/src-crewrite/constants.h b/src/elastisched/engine/src-crewrite/constants.h index 492269a..455afc4 100644 --- a/src/elastisched/engine/src-crewrite/constants.h +++ b/src/elastisched/engine/src-crewrite/constants.h @@ -29,4 +29,6 @@ const double ILLEGAL_SCHEDULE_COST = 1e12f; const double EPSILON = 1e-5f; const unsigned int DEFAULT_RNG_SEED = 1337; + +const size_t INITIAL_TAGSET_CAPACITY = 8; #endif diff --git a/src/elastisched/engine/src-crewrite/interval.c b/src/elastisched/engine/src-crewrite/interval.c index e69de29..e16b7ab 100644 --- a/src/elastisched/engine/src-crewrite/interval.c +++ b/src/elastisched/engine/src-crewrite/interval.c @@ -0,0 +1,113 @@ +#include "interval.h" + +bool interval_eq(Interval* U, Interval* V) { + return (U->low == V->low) && (U->high == V->high); +} + +bool interval_overlaps(Interval* U, Interval* V) { + return !(U->high < V->low || V->high < U->low); +} + +bool interval_contains(Interval* U, Interval* V) { + return (U->low <= V->low && V->high <= U->high); +} + +time_t interval_length(Interval* interval) { + return interval->high - interval->low; +} + +time_t interval_is_valid(Interval* interval) { + return interval->high >= interval->low; +} + +Node* mk_leaf_node(Node* parent, Interval* interval, + void* value, time_t max, Color color +) { + Node* node = malloc(sizeof(Node)); + if (!node) return NULL; + + node->parent = parent; + node->interval = interval; + node->value = value; + node->max = max; + node->color = color; + + node->left = NULL; + node->right = NULL; + + return node; +} + +Node* mk_node(Node* left, Node* right, Node* parent, + Interval* interval, void* value, time_t max, Color color +) { + Node* node = malloc(sizeof(Node)); + if (!node) return NULL; + + node->left = left; + node->right = right; + node->parent = parent; + node->interval = interval; + node->value = value; + node->max = max; + node->color = color; + + return node; +} + +IntervalMap* mk_intmap(Node* root) { + IntervalMap* map = malloc(sizeof(IntervalMap)); + if (!map) return NULL; + + map->root = root; + return map; +} + +void intmap_insert(IntervalMap* map, Interval* key, void* value) { + return; +} + +void intmap_delete(IntervalMap* map) { + return; +} + +void intmap_free(IntervalMap* map) { + return; +} + +void intmap_left_rotate(IntervalMap* map, Node* x) { + Node* y = x->right; + x->right = y->left; + if (y->left) y->left->parent = x; + + y->parent = x->parent; + if (!x->parent) { + map->root = y; + } else if (x == x->parent->left) { + x->parent->left = y; + } else { + x->parent->right = y; + } + y->left = x; + x->parent = y; +} + +void intmap_right_rotate(IntervalMap* map, Node* y) { + Node* x = y->left; + + y->left = x->right; + if (x->right) x->right->parent = y; + + x->parent = y->parent; + if (!y->parent) { + map->root = x; + } else if (y == y->parent->left) { + y->parent->left = x; + } else { + y->parent->right = x; + } + + x->right = y; + y->parent = x; +} + diff --git a/src/elastisched/engine/src-crewrite/interval.h b/src/elastisched/engine/src-crewrite/interval.h index a9e44be..ea93ef3 100644 --- a/src/elastisched/engine/src-crewrite/interval.h +++ b/src/elastisched/engine/src-crewrite/interval.h @@ -9,7 +9,10 @@ typedef struct Interval { time_t high; } Interval; -enum Color { RED, BLACK }; +typedef enum { + RED, + BLACK } +Color; typedef struct Node { Node* left; @@ -22,20 +25,21 @@ typedef struct Node { } Node; typedef struct IntervalMap { - Node* root; + void* root; } IntervalMap; -time_t interval_get_low(Interval* interval); -time_t interval_get_high(Interval* interval); bool interval_eq(Interval* U, Interval* V); bool interval_overlaps(Interval* U, Interval* V); bool interval_contains(Interval* U, Interval* V); time_t interval_length(Interval* interval); -bool is_valid_interval(Interval* interval); +bool interval_is_valid(Interval* interval); -IntervalMap* mk_interval_map(); -void interval_map_insert(IntervalMap*, Interval* key, void* value); - -void free_interval_map(IntervalMap* map); -#endif +Node* mk_leaf_node(Node* parent, Interval* interval, + void* value, time_t max, Color color); +Node* mk_node(Node* left, Node* right, Node* parent, + Interval* interval, void* value, time_t max, Color color); +IntervalMap* mk_intmap(Node* root); +void intmap_insert(IntervalMap* map, Interval* key, void* value); +void intmap_free(IntervalMap* map); +#endif \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/policy.c b/src/elastisched/engine/src-crewrite/policy.c index 0cc970d..9d72f81 100644 --- a/src/elastisched/engine/src-crewrite/policy.c +++ b/src/elastisched/engine/src-crewrite/policy.c @@ -1,9 +1,5 @@ #include "policy.h" -uint8_t get_max_splits(Policy* policy) { return policy->max_splits; } -time_t get_min_split_duration(Policy* policy) { return policy->min_split_duration; } -uint8_t get_scheduling_policies(Policy* policy) { return policy->scheduling_policies; } - -bool is_splittable(Policy* policy) { return (policy->scheduling_policies & 1u) != 0; } -bool is_overlappable(Policy* policy) { return ((policy->scheduling_policies & 2u) >> 1) != 0; } -bool is_invisible(Policy* policy) { return ((policy->scheduling_policies & 4u) >> 2) != 0; } \ No newline at end of file +bool policy_is_splittable(Policy* policy) { return (policy->scheduling_policies & 1u) != 0; } +bool policy_is_overlappable(Policy* policy) { return ((policy->scheduling_policies & 2u) >> 1) != 0; } +bool policy_is_invisible(Policy* policy) { return ((policy->scheduling_policies & 4u) >> 2) != 0; } \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/policy.h b/src/elastisched/engine/src-crewrite/policy.h index a29a4e8..ace4d64 100644 --- a/src/elastisched/engine/src-crewrite/policy.h +++ b/src/elastisched/engine/src-crewrite/policy.h @@ -16,12 +16,8 @@ typedef struct Policy { uint8_t scheduling_policies; /// overloaded bitfield: bit 0 = is_splittable, bit 1 = is_overlappable, bit 2 = is_invisible } Policy; -uint8_t get_max_splits(Policy* policy); -time_t get_min_split_duration(Policy* policy); -uint8_t get_scheduling_policies(Policy* policy); - -bool is_splittable(Policy* policy); -bool is_overlappable(Policy* policy); -bool is_invisible(Policy* policy); +bool policy_is_splittable(Policy* policy); +bool policy_is_overlappable(Policy* policy); +bool policy_is_invisible(Policy* policy); #endif diff --git a/src/elastisched/engine/src-crewrite/tag.c b/src/elastisched/engine/src-crewrite/tag.c index d610347..109fc4b 100644 --- a/src/elastisched/engine/src-crewrite/tag.c +++ b/src/elastisched/engine/src-crewrite/tag.c @@ -1,32 +1,17 @@ #include "tag.h" -/** - * @brief returns True if the names of ```U``` and ```V``` are the same - */ -bool tag_eq(Tag* U, Tag* V) { +bool tag_eq(const Tag* U, const Tag* V) { return strcmp(U->name, V->name) == 0; } -int tag_cmp(Tag* U, Tag* V) { - return strcmp(U->name, V->name); -} +int tag_cmp(const Tag* U, const Tag* V) { + int c = strcmp(U->name, V->name); -/** - * @brief sets the description of ```U``` to ```description``` - */ -void tag_set_description(Tag* U, char* description) { - U->description = description; - return; + return (c > 0) - (c < 0); } -/** - * @brief constructs a TagSet with capacity ```capacity``` - * - * @param capacity number of tags that the TagSet can hold - * @return TagSet* - */ -TagSet* mk_tag_set(size_t capacity) { - TagSet* tag_set = malloc(sizeof(TagSet)); +TagContainer* mk_tag_container(size_t capacity) { + TagContainer* tag_set = malloc(sizeof(TagContainer)); if (!tag_set) return NULL; Tag* data = malloc(capacity * sizeof(Tag)); @@ -42,38 +27,29 @@ TagSet* mk_tag_set(size_t capacity) { return tag_set; } -/** - * @brief resizes a set by doubling capacity and copying memory - * - * @param set set to resize - * @return true if resize successful, false otherwise - */ -bool resize_set(TagSet* set) { - set->capacity *= 2; - Tag* data = malloc(set->capacity * sizeof(Tag)); - if (!data) return false; - - memcpy((void*)data, (void*)set->data, set->size * sizeof(Tag)); - free(set->data); - set->data = data; +void tag_container_free(TagContainer* set) { + free((void*)set->data); + free(set); + return; +} + +bool ts_resize(TagContainer* set) { + size_t new_capacity = (set->capacity == 0) ? + INITIAL_TAGSET_CAPACITY : set->capacity * 2; + Tag* new_data = realloc(set->data, new_capacity * sizeof(Tag)); + if (!new_data) return false; + set->data = new_data; + set->capacity = new_capacity; return true; } -/** - * @brief Find the insert index - * - * @param set the set to look in. - * Internally, ```set```->data should be sorted. - * @param tag the tag to look for - * @return size_t - */ -size_t set_insert_index(TagSet* set, Tag* tag) { +size_t ts_insert_index(TagContainer* set, Tag tag) { size_t l = 0; size_t r = set->size; while (l < r) { size_t mid = l + (r - l) / 2; - int _cmp = tag_cmp(tag, &(set->data)[mid]); + int _cmp = tag_cmp(&tag, &(set->data)[mid]); if (_cmp == 0) return mid; if (_cmp < 0) r = mid; else l = mid + 1; @@ -81,37 +57,93 @@ size_t set_insert_index(TagSet* set, Tag* tag) { return l; } - -/** - * @brief Helper function for inserting tag into a set - * - * @param set the set to insert to - * @param tag the tag to insert - * @param insert_ind the index to insert at - */ -void set_insert(TagSet* set, Tag* tag, size_t insert_ind) { +void ts_insert(TagContainer* set, Tag tag, size_t insert_ind) { if (insert_ind < set->size) { memmove(&set->data[insert_ind + 1], &set->data[insert_ind], (set->size - insert_ind) * sizeof(Tag)); } - set->data[insert_ind] = *tag; + set->data[insert_ind] = tag; set->size++; } -/** - * @brief Helper function for addiing tag into a set - * - * @param set set to add to - * @param tag the tag to add - * @return true if no memory failures, false otherwise - */ -bool set_add(TagSet* set, Tag* tag) { - size_t index = set_insert_index(set, tag); - if (index < set->size && tag_eq(tag, &set->data[index])) return true; +bool ts_add(TagContainer* set, Tag tag) { + size_t insert_index = ts_insert_index(set, tag); + if (insert_index < set->size && + tag_eq(&tag, &set->data[insert_index])) return true; - if (set->size == set->capacity && !resize_set(set)) return false; + if (set->size == set->capacity && !ts_resize(set)) return false; - set_insert(set, tag, index); + ts_insert(set, tag, insert_index); return true; } + +bool ts_in(TagContainer* set, Tag tag) { + size_t insert_index = ts_insert_index(set, tag); + return (insert_index < set->size && + tag_eq(&tag, &set->data[insert_index])); +} + +TagContainer* ts_union(TagContainer* U, TagContainer* V) { + if (U->size == 0) { + TagContainer* set_union = mk_tag_set(V->size); + if (!set_union) return NULL; + memcpy(set_union->data, V->data, V->size * sizeof(Tag)); + set_union->size = V->size; + return set_union; + } + if (V->size == 0) { + TagContainer* set_union= mk_tag_set(U->size); + if (!set_union) return NULL; + memcpy(set_union->data, U->data, U->size * sizeof(Tag)); + set_union->size = U->size; + return set_union; + } + + size_t u_ptr = 0; + size_t v_ptr = 0; + size_t insert_ind = 0; + TagContainer* set_union = mk_tag_container(U->size + V->size); + if (!set_union) return NULL; + + while (u_ptr < U->size && v_ptr < V->size) { + Tag* curr_u_tag = &U->data[u_ptr]; + Tag* curr_v_tag = &V->data[v_ptr]; + switch (tag_cmp(curr_u_tag, curr_v_tag)) { + case -1: + set_union->data[insert_ind++] = *curr_u_tag; + u_ptr++; + break; + case 0: + set_union->data[insert_ind++] = *curr_u_tag; + u_ptr++; v_ptr++; + break; + case 1: + set_union->data[insert_ind++] = *curr_v_tag; + v_ptr++; + break; + } + set_union->size++; + } + + while (u_ptr < U->size) set_union->data[insert_ind++] = U->data[u_ptr++]; + while (v_ptr < V->size) set_union->data[insert_ind++] = V->data[v_ptr++]; + + set_union->size = insert_ind; + + return set_union; +}; + +TagContainer* ts_intersection(TagContainer* U, TagContainer* V) { + TagContainer* set_intersection = mk_tag_container(min(U->capacity, V->capacity)); + + size_t u_ptr = 0; + size_t v_ptr = 0; + size_t insert_ind = 0; + + while(u_ptr < U->size) { + Tag* curr_u_tag = &U->data[u_ptr]; + Tag* curr_v_tag = &V->data[v_ptr]; + if (tag_eq(curr_u_tag, curr_v_tag)) set_intersection->data[insert_ind++] = U->data[u_ptr++]; + } +} diff --git a/src/elastisched/engine/src-crewrite/tag.h b/src/elastisched/engine/src-crewrite/tag.h index be72fc7..e95a6c0 100644 --- a/src/elastisched/engine/src-crewrite/tag.h +++ b/src/elastisched/engine/src-crewrite/tag.h @@ -6,10 +6,16 @@ #include #include +#include "constants.h" + /** * Tag.h * - * Definitions for working with the Tag and TagSet structs. + * Definitions for working with the Tag and TagContainer structs. + * + * TagContainer is a simple container struct which allows for + * functions to operate on it and implement Sets and Vectors. + * * TagSet in practice does not get very large nor updated * often so we opt for a simple vector-based sorted set. * @@ -27,11 +33,11 @@ typedef struct Tag { char* description; } Tag; -typedef struct TagSet { +typedef struct TagContainer { Tag* data; size_t size; size_t capacity; -} TagSet; +} TagContainer; /** * @brief returns True if the names of ```U``` and ```V``` are the same @@ -44,17 +50,12 @@ bool tag_eq(Tag* U, Tag* V); int tag_cmp(Tag* U, Tag* V); /** - * @brief sets the description of ```U``` to ```description``` - */ -void tag_set_description(Tag* U, char* description); - -/** - * @brief constructs a TagSet with capacity ```capacity``` + * @brief constructs a TagContainer with capacity ```capacity``` * - * @param capacity number of tags that the TagSet can hold - * @return TagSet* + * @param capacity number of tags that the TagContainer can hold + * @return TagContainer* */ -TagSet* mk_tag_set(size_t capacity); +TagContainer* mk_tag_container(size_t capacity); /** * @brief resizes a set by doubling capacity and copying memory @@ -62,7 +63,7 @@ TagSet* mk_tag_set(size_t capacity); * @param set set to resize * @return true if resize successful, false otherwise */ -bool resize_set(TagSet* set); +bool ts_resize(TagContainer* set); /** * @brief Find the insert index @@ -70,9 +71,9 @@ bool resize_set(TagSet* set); * @param set the set to look in. * Internally, ```set```->data should be sorted. * @param tag the tag to look for - * @return size_t + * @return first index, i, such that tag <= set[j], i<=j Date: Sun, 4 Jan 2026 01:11:37 -0500 Subject: [PATCH 03/35] dll, map, and vec definitions --- .../engine/src-crewrite/constants.h | 27 ++-- src/elastisched/engine/src-crewrite/dll.c | 128 ++++++++++++++++++ src/elastisched/engine/src-crewrite/dll.h | 14 ++ .../engine/src-crewrite/interval.c | 14 +- .../engine/src-crewrite/interval.h | 31 +++-- src/elastisched/engine/src-crewrite/job.c | 5 + src/elastisched/engine/src-crewrite/job.h | 29 ++++ src/elastisched/engine/src-crewrite/map.c | 42 ++++++ src/elastisched/engine/src-crewrite/map.h | 23 ++++ src/elastisched/engine/src-crewrite/queue.c | 0 .../engine/src-crewrite/schedule.c | 10 ++ .../engine/src-crewrite/schedule.h | 15 ++ src/elastisched/engine/src-crewrite/tag.c | 62 ++++++--- src/elastisched/engine/src-crewrite/tag.h | 81 ++++++++--- .../engine/src-crewrite/topo_sort.h | 21 +++ src/elastisched/engine/src-crewrite/utils.c | 22 +++ src/elastisched/engine/src-crewrite/utils.h | 8 ++ src/elastisched/engine/src-crewrite/vec.h | 20 +++ 18 files changed, 480 insertions(+), 72 deletions(-) create mode 100644 src/elastisched/engine/src-crewrite/dll.c create mode 100644 src/elastisched/engine/src-crewrite/dll.h create mode 100644 src/elastisched/engine/src-crewrite/map.c create mode 100644 src/elastisched/engine/src-crewrite/map.h create mode 100644 src/elastisched/engine/src-crewrite/queue.c create mode 100644 src/elastisched/engine/src-crewrite/utils.c create mode 100644 src/elastisched/engine/src-crewrite/utils.h create mode 100644 src/elastisched/engine/src-crewrite/vec.h diff --git a/src/elastisched/engine/src-crewrite/constants.h b/src/elastisched/engine/src-crewrite/constants.h index 455afc4..2907a52 100644 --- a/src/elastisched/engine/src-crewrite/constants.h +++ b/src/elastisched/engine/src-crewrite/constants.h @@ -2,21 +2,20 @@ #define CONSTANTS #include +#include -#include "tag.h" +typedef uint32_t sec_t; -typedef uint32_t time_t; +const sec_t MINUTE = (sec_t)60; +const sec_t HOUR_TO_MINUTES = (sec_t)60; +const sec_t DAY_TO_HOURS = (sec_t)24; +const sec_t WEEK_TO_DAYS = (sec_t)7; -const time_t MINUTE = (time_t)60; -const time_t HOUR_TO_MINUTES = (time_t)60; -const time_t DAY_TO_HOURS = (time_t)24; -const time_t WEEK_TO_DAYS = (time_t)7; +const sec_t HOUR = ((sec_t)60 * MINUTE); +const sec_t DAY = ((sec_t)24 * HOUR); +const sec_t WEEK = ((sec_t)7 * DAY); -const time_t HOUR = ((time_t)60 * MINUTE); -const time_t DAY = ((time_t)24 * HOUR); -const time_t WEEK = ((time_t)7 * DAY); - -const time_t AFTERNOON_START = 17; +const sec_t AFTERNOON_START = 17; const double FRIDAY_HOURLY_COST_FACTOR = 2.0f; const double SATURDAY_HOURLY_COST_FACTOR = 3.0f; @@ -24,11 +23,13 @@ const double SATURDAY_HOURLY_COST_FACTOR = 3.0f; const double EXP_DOWNFACTOR = 0.1f; const double HOURLY_COST_FACTOR = 1.0f; -const struct Tag WORK_TAG = {.name="ELASTISCHED_WORK_TYPE\0"}; const double ILLEGAL_SCHEDULE_COST = 1e12f; const double EPSILON = 1e-5f; const unsigned int DEFAULT_RNG_SEED = 1337; -const size_t INITIAL_TAGSET_CAPACITY = 8; +const size_t INITIAL_TAGCONTAINER_CAPACITY = 8; +const size_t INITIAL_CONTAINER_CAPACITY = 256; +const size_t INITIAL_MAP_CAPACITY = 32; + #endif diff --git a/src/elastisched/engine/src-crewrite/dll.c b/src/elastisched/engine/src-crewrite/dll.c new file mode 100644 index 0000000..114204d --- /dev/null +++ b/src/elastisched/engine/src-crewrite/dll.c @@ -0,0 +1,128 @@ +#include "dll.h" + +typedef struct dll_node dll_node; + +struct dll_node { + dll_node* prev; + dll_node* next; + void* value; +}; + +struct dll { + dll_node* head; + dll_node* tail; + size_t size; +}; + +dll* mk_dll() { + dll* deque = malloc(sizeof(dll)); + if (!deque) return NULL; + + deque->head = NULL; + deque->tail = NULL; + deque->size = 0; + + return deque; +} + +void dll_free(dll* deque, void (*free_fn)(void *)) { + if (!deque) return; + dll_node* curr = deque->head; + while (curr != NULL) { + dll_node* temp = curr->next; + if (free_fn) free_fn(curr->value); + free(curr); + curr = temp; + } + + free(deque); +} + +void* dll_head(dll* deque) { + if (!deque->head) return NULL; + return deque->head->value; +} + +void* dll_tail(dll* deque) { + if (!deque->tail) return NULL; + return deque->tail->value; +} + +void dll_append(dll* deque, void* e) { + dll_node* new_tail = malloc(sizeof(dll_node)); + if (!new_tail) return; + new_tail->value = e; + if (!deque->head) { + deque->head = new_tail; + deque->tail = new_tail; + new_tail->prev = NULL; + new_tail->next = NULL; + } else { + new_tail->prev = deque->tail; + new_tail->next = NULL; + deque->tail = new_tail; + } + deque->size++; + return; +} + +void dll_prepend(dll* deque, void* e) { + dll_node* new_head = malloc(sizeof(dll_node)); + if (!new_head) return; + new_head->value = e; + if (!deque->head) { + deque->head = new_head; + deque->tail = new_head; + new_head->prev = NULL; + new_head->next = NULL; + } else { + new_head->prev = NULL; + new_head->next = deque->head; + deque->head = new_head; + } + deque->size++; + return; +} + +void* dll_popleft(dll* deque) { + if (deque->size == 0) return NULL; + + dll_node* temp = deque->head; + void* value = temp->value; + + if (deque->size == 1) { + deque->head = NULL; + deque->tail = NULL; + } else { + deque->head = deque->head->next; + deque->head->next->prev = NULL; + } + + deque->size--; + free(temp); + return value; +} + +void* dll_popright(dll* deque) { + if (deque->size == 0) return NULL; + + dll_node* temp = deque->tail; + void* value = temp->value; + + if (deque->size == 1) { + deque->head = NULL; + deque->tail = NULL; + } + else { + deque->tail = deque->tail->prev; + deque->tail->prev->next = NULL; + } + + deque->size--; + free(temp); + return value; +} + +size_t dll_size(dll* deque) { + return deque->size; +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/dll.h b/src/elastisched/engine/src-crewrite/dll.h new file mode 100644 index 0000000..4257b75 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/dll.h @@ -0,0 +1,14 @@ +#include + +typedef struct dll dll; + +dll* mk_dll(); +void dll_free(dll* deque, void (*free_fn)(void* e)); + +void* dll_head(dll* deque); +void* dll_tail(dll* deque); +void dll_append(dll* deque, void* e); +void dll_prepend(dll* deque, void* e); +void* dll_popleft(dll* deque); +void* dll_popright(dll* deque); +size_t dll_size(dll* deque); diff --git a/src/elastisched/engine/src-crewrite/interval.c b/src/elastisched/engine/src-crewrite/interval.c index e16b7ab..37fc703 100644 --- a/src/elastisched/engine/src-crewrite/interval.c +++ b/src/elastisched/engine/src-crewrite/interval.c @@ -1,27 +1,27 @@ #include "interval.h" -bool interval_eq(Interval* U, Interval* V) { +bool interval_eq(const Interval* U, const Interval* V) { return (U->low == V->low) && (U->high == V->high); } -bool interval_overlaps(Interval* U, Interval* V) { +bool interval_overlaps(const Interval* U, const Interval* V) { return !(U->high < V->low || V->high < U->low); } -bool interval_contains(Interval* U, Interval* V) { +bool interval_contains(const Interval* U, const Interval* V) { return (U->low <= V->low && V->high <= U->high); } -time_t interval_length(Interval* interval) { +sec_t interval_length(const Interval* interval) { return interval->high - interval->low; } -time_t interval_is_valid(Interval* interval) { +bool interval_is_valid(const Interval* interval) { return interval->high >= interval->low; } Node* mk_leaf_node(Node* parent, Interval* interval, - void* value, time_t max, Color color + void* value, sec_t max, Color color ) { Node* node = malloc(sizeof(Node)); if (!node) return NULL; @@ -39,7 +39,7 @@ Node* mk_leaf_node(Node* parent, Interval* interval, } Node* mk_node(Node* left, Node* right, Node* parent, - Interval* interval, void* value, time_t max, Color color + Interval* interval, void* value, sec_t max, Color color ) { Node* node = malloc(sizeof(Node)); if (!node) return NULL; diff --git a/src/elastisched/engine/src-crewrite/interval.h b/src/elastisched/engine/src-crewrite/interval.h index ea93ef3..cfe60b7 100644 --- a/src/elastisched/engine/src-crewrite/interval.h +++ b/src/elastisched/engine/src-crewrite/interval.h @@ -2,11 +2,12 @@ #define INTERVAL_H #include +#include #include "constants.h" typedef struct Interval { - time_t low; - time_t high; + sec_t low; + sec_t high; } Interval; typedef enum { @@ -14,32 +15,34 @@ typedef enum { BLACK } Color; -typedef struct Node { +typedef struct Node Node; + +struct Node { Node* left; Node* right; Node* parent; Interval* interval; void* value; - time_t max; + sec_t max; Color color; -} Node; +}; typedef struct IntervalMap { - void* root; + Node* root; } IntervalMap; -bool interval_eq(Interval* U, Interval* V); -bool interval_overlaps(Interval* U, Interval* V); -bool interval_contains(Interval* U, Interval* V); -time_t interval_length(Interval* interval); -bool interval_is_valid(Interval* interval); +bool interval_eq(const Interval* U, const Interval* V); +bool interval_overlaps(const Interval* U, const Interval* V); +bool interval_contains(const Interval* U, const Interval* V); +sec_t interval_length(const Interval* interval); +bool interval_is_valid(const Interval* interval); Node* mk_leaf_node(Node* parent, Interval* interval, - void* value, time_t max, Color color); + void* value, sec_t max, Color color); Node* mk_node(Node* left, Node* right, Node* parent, - Interval* interval, void* value, time_t max, Color color); + Interval* interval, void* value, sec_t max, Color color); IntervalMap* mk_intmap(Node* root); void intmap_insert(IntervalMap* map, Interval* key, void* value); void intmap_free(IntervalMap* map); -#endif \ No newline at end of file +#endif diff --git a/src/elastisched/engine/src-crewrite/job.c b/src/elastisched/engine/src-crewrite/job.c index e69de29..1dabd44 100644 --- a/src/elastisched/engine/src-crewrite/job.c +++ b/src/elastisched/engine/src-crewrite/job.c @@ -0,0 +1,5 @@ +#include "job.h" + +bool job_is_rigid(Job *job) { + return job->duration == interval_length(&job->schedulable_time_range); +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/job.h b/src/elastisched/engine/src-crewrite/job.h index e69de29..3d00593 100644 --- a/src/elastisched/engine/src-crewrite/job.h +++ b/src/elastisched/engine/src-crewrite/job.h @@ -0,0 +1,29 @@ +#ifndef JOB_H +#define JOB_H + +#include "constants.h" +#include "utils.h" +#include "interval.h" +#include "policy.h" +#include "tag.h" + +typedef struct Job { + sec_t duration; + Interval schedulable_time_range; + Interval scheduled_time_range; + ID id; + Policy policy; + DependencyContainer* dependency_set; + TagContainer* tag_set; +} Job; + +typedef struct JobVec { + Job* data; + size_t size; + size_t capacity; +} JobVec; + +bool job_is_rigid(Job* job); +char* job_to_string(Job* job); + +#endif \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/map.c b/src/elastisched/engine/src-crewrite/map.c new file mode 100644 index 0000000..8787443 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/map.c @@ -0,0 +1,42 @@ +#include "map.h" + +#include "constants.h" +#include "dll.h" + +typedef struct bucket_vec { + size_t size; + size_t capacity; + dll* bucket; +} bucket_vec; + +struct map { + size_t num_keys; + size_t num_values; + size_t capacity; + uint64_t (*hash_fn)(void* e); + int (*cmp_fn)(void* u, void* v); + void (*free_fn)(void* e); + bucket_vec* buckets; +}; + +map* mk_map(uint64_t (*hash_fn)(void* e), + int (*cmp_fn)(void* u, void* v), + void (*free_fn)(void* e)) +{ + map* dict = malloc(sizeof(map)); + if (!dict) return NULL; + + dict->buckets = malloc(INITIAL_MAP_CAPACITY * sizeof(bucket_vec)); + if (!dict->buckets) { + free(dict); + return NULL; + } + + dict->num_keys = 0; + dict->num_values = 0; + dict->capacity = INITIAL_MAP_CAPACITY; + dict->hash_fn = hash_fn; + dict->cmp_fn = cmp_fn; + dict->free_fn = free_fn; + +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/map.h b/src/elastisched/engine/src-crewrite/map.h new file mode 100644 index 0000000..7fd018c --- /dev/null +++ b/src/elastisched/engine/src-crewrite/map.h @@ -0,0 +1,23 @@ +#ifndef MAP_H +#define MAP_H + +#include +#include +#include + +#include "constants.h" + +typedef struct map map; + +map* mk_map(uint64_t (*hash_fn)(void* e), + int (*cmp_fn)(void* u, void* v), + void (*free_fn)(void* e)); + +void map_free(map* dict); + +void map_insert(map* dict, void* key, void* value); +bool map_in(map* dict, void* key); +bool map_get(map* dict, void* key); +bool map_size(map* dict); + +#endif \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/queue.c b/src/elastisched/engine/src-crewrite/queue.c new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/schedule.c b/src/elastisched/engine/src-crewrite/schedule.c index e69de29..1c2a491 100644 --- a/src/elastisched/engine/src-crewrite/schedule.c +++ b/src/elastisched/engine/src-crewrite/schedule.c @@ -0,0 +1,10 @@ +#include "schedule.h" + +void schedule_add_job(Schedule* schedule, const Job* job) { + vec_pushback(schedule->scheduledJobs, *job); + return; +} + +void schedule_clear(Schedule* schedule) { + vec_clear(schedule->scheduledJobs); +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/schedule.h b/src/elastisched/engine/src-crewrite/schedule.h index e69de29..8abfbbc 100644 --- a/src/elastisched/engine/src-crewrite/schedule.h +++ b/src/elastisched/engine/src-crewrite/schedule.h @@ -0,0 +1,15 @@ +#ifndef SCHEDULE_H +#define SCHEDULE_H + +#include "job.h" +#include "utils.h" +#include "vec.h" + +typedef struct Schedule { + JobVec* scheduledJobs; +} Schedule; + +void schedule_add_job(Schedule* schedule, const Job* job); +void schedule_clear(Schedule* schedule); + +#endif \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/tag.c b/src/elastisched/engine/src-crewrite/tag.c index 109fc4b..46175d0 100644 --- a/src/elastisched/engine/src-crewrite/tag.c +++ b/src/elastisched/engine/src-crewrite/tag.c @@ -27,19 +27,19 @@ TagContainer* mk_tag_container(size_t capacity) { return tag_set; } -void tag_container_free(TagContainer* set) { - free((void*)set->data); - free(set); +void tag_container_free(TagContainer* container) { + free((void*)container->data); + free(container); return; } -bool ts_resize(TagContainer* set) { - size_t new_capacity = (set->capacity == 0) ? - INITIAL_TAGSET_CAPACITY : set->capacity * 2; - Tag* new_data = realloc(set->data, new_capacity * sizeof(Tag)); +bool tag_container_resize(TagContainer* container) { + size_t new_capacity = (container->capacity == 0) ? + INITIAL_TAGCONTAINER_CAPACITY : container->capacity * 2; + Tag* new_data = realloc(container->data, new_capacity * sizeof(Tag)); if (!new_data) return false; - set->data = new_data; - set->capacity = new_capacity; + container->data = new_data; + container->capacity = new_capacity; return true; } @@ -72,7 +72,9 @@ bool ts_add(TagContainer* set, Tag tag) { if (insert_index < set->size && tag_eq(&tag, &set->data[insert_index])) return true; - if (set->size == set->capacity && !ts_resize(set)) return false; + if (set->size == set->capacity && + !tag_container_resize(set)) + return false; ts_insert(set, tag, insert_index); return true; @@ -86,14 +88,14 @@ bool ts_in(TagContainer* set, Tag tag) { TagContainer* ts_union(TagContainer* U, TagContainer* V) { if (U->size == 0) { - TagContainer* set_union = mk_tag_set(V->size); + TagContainer* set_union = mk_tag_container(V->size); if (!set_union) return NULL; memcpy(set_union->data, V->data, V->size * sizeof(Tag)); set_union->size = V->size; return set_union; } if (V->size == 0) { - TagContainer* set_union= mk_tag_set(U->size); + TagContainer* set_union= mk_tag_container(U->size); if (!set_union) return NULL; memcpy(set_union->data, U->data, U->size * sizeof(Tag)); set_union->size = U->size; @@ -135,15 +137,35 @@ TagContainer* ts_union(TagContainer* U, TagContainer* V) { }; TagContainer* ts_intersection(TagContainer* U, TagContainer* V) { - TagContainer* set_intersection = mk_tag_container(min(U->capacity, V->capacity)); + TagContainer* set_intersection = mk_tag_container(min(U->size, V->size)); + if (!set_intersection) return NULL; - size_t u_ptr = 0; - size_t v_ptr = 0; + TagContainer* anchor = U->size > V->size ? V : U; + TagContainer* ot = U->size > V->size ? U : V; size_t insert_ind = 0; - - while(u_ptr < U->size) { - Tag* curr_u_tag = &U->data[u_ptr]; - Tag* curr_v_tag = &V->data[v_ptr]; - if (tag_eq(curr_u_tag, curr_v_tag)) set_intersection->data[insert_ind++] = U->data[u_ptr++]; + for (int i = 0; i < anchor->size; i++) { + if (ts_in(ot, anchor->data[i])) { + set_intersection->data[insert_ind++] = anchor->data[i]; + } } + + set_intersection->size = insert_ind; + return set_intersection; } + +int _tag_cmp(const void* u, const void* v) { + return tag_cmp((Tag*)u, (Tag*)v); +} + +bool ts_is_valid(TagContainer* set) { + return set->size <= set->capacity && + is_sorted((void*)set->data, set->size, sizeof(Tag), &_tag_cmp); +} + +bool tv_pushback(TagContainer* vec, Tag tag) { + if (vec->size == vec->capacity && + !tag_container_resize(vec)) return false; + + vec->data[vec->size++] = tag; + return true; +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/tag.h b/src/elastisched/engine/src-crewrite/tag.h index e95a6c0..efdf5d8 100644 --- a/src/elastisched/engine/src-crewrite/tag.h +++ b/src/elastisched/engine/src-crewrite/tag.h @@ -7,6 +7,8 @@ #include #include "constants.h" +#include "utils.h" + /** * Tag.h @@ -39,15 +41,18 @@ typedef struct TagContainer { size_t capacity; } TagContainer; +typedef Tag ID; /// an dependency ID is a Tag with an empty description +typedef TagContainer DependencyContainer; + /** * @brief returns True if the names of ```U``` and ```V``` are the same */ -bool tag_eq(Tag* U, Tag* V); +bool tag_eq(const Tag* U, const Tag* V); /** * @brief comparator for Tag type */ -int tag_cmp(Tag* U, Tag* V); +int tag_cmp(const Tag* U, const Tag* V); /** * @brief constructs a TagContainer with capacity ```capacity``` @@ -58,12 +63,19 @@ int tag_cmp(Tag* U, Tag* V); TagContainer* mk_tag_container(size_t capacity); /** - * @brief resizes a set by doubling capacity and copying memory + * @brief frees a TagContainer and its data + * + * @param container + */ +void tag_container_free(TagContainer* container); + +/** + * @brief resizes a container by doubling capacity and copying memory * - * @param set set to resize + * @param container * @return true if resize successful, false otherwise */ -bool ts_resize(TagContainer* set); +bool tag_container_resize(TagContainer* container); /** * @brief Find the insert index @@ -73,40 +85,73 @@ bool ts_resize(TagContainer* set); * @param tag the tag to look for * @return first index, i, such that tag <= set[j], i<=j v ? v : u; +} + +size_t max(size_t u, size_t v) { + return u > v ? u: v; +} + +bool is_sorted(const void* base, size_t count, size_t elem_size, + int (*cmp)(const void*, const void*)) { + if (count < 2) return true; + + const char* bytes = (const char*)base; + for (size_t i = 1; i < count; i++) { + const void* prev = bytes + (i - 1) * elem_size; + const void* curr = bytes + i * elem_size; + if (cmp(prev, curr) > 0) return false; + } + return true; +} diff --git a/src/elastisched/engine/src-crewrite/utils.h b/src/elastisched/engine/src-crewrite/utils.h new file mode 100644 index 0000000..cdbb6d7 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/utils.h @@ -0,0 +1,8 @@ +#include +#include +#include "constants.h" + +size_t min(size_t u, size_t v); +size_t max(size_t u, size_t v); +bool is_sorted(const void* base, size_t count, size_t elem_size, + int (*cmp)(const void*, const void*)); \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/vec.h b/src/elastisched/engine/src-crewrite/vec.h new file mode 100644 index 0000000..28cb33b --- /dev/null +++ b/src/elastisched/engine/src-crewrite/vec.h @@ -0,0 +1,20 @@ +/** + * vec.h + * + * Macros for working with arbitrary C vectors + */ + +#define vec_pushback(vec, e) \ + do { \ + if (vec->size >= vec->capacity) { \ + if (vec->size == 0) vec->capacity = INITIAL_CONTAINER_CAPACITY; \ + else vec->capacity *= 2; \ + vec->data = realloc(vec->data, vec->capacity * sizeof(*vec->data)); \ + } \ + vec->data[vec->size++] = e; \ + } while (0) + +#define vec_clear(vec) \ + do { \ + vec->size = 0; \ + } while (0) \ No newline at end of file From c412f49feaa008cf79fd4f9c60f5359aed500341 Mon Sep 17 00:00:00 2001 From: Chris Su Date: Mon, 5 Jan 2026 13:21:02 -0500 Subject: [PATCH 04/35] Topological sorting --- .../engine/src-crewrite/constants.h | 2 + src/elastisched/engine/src-crewrite/dll.c | 45 ++++- src/elastisched/engine/src-crewrite/dll.h | 15 +- src/elastisched/engine/src-crewrite/hash.h | 3 + src/elastisched/engine/src-crewrite/map.c | 180 ++++++++++++++++-- src/elastisched/engine/src-crewrite/map.h | 20 +- src/elastisched/engine/src-crewrite/queue.c | 0 .../engine/src-crewrite/schedule.c | 4 +- .../engine/src-crewrite/schedule.h | 2 +- src/elastisched/engine/src-crewrite/tag.h | 2 + .../engine/src-crewrite/topo_sort.c | 66 +++++++ .../engine/src-crewrite/topo_sort.h | 4 + src/elastisched/engine/src-crewrite/vec.h | 5 + 13 files changed, 317 insertions(+), 31 deletions(-) create mode 100644 src/elastisched/engine/src-crewrite/hash.h delete mode 100644 src/elastisched/engine/src-crewrite/queue.c create mode 100644 src/elastisched/engine/src-crewrite/topo_sort.c diff --git a/src/elastisched/engine/src-crewrite/constants.h b/src/elastisched/engine/src-crewrite/constants.h index 2907a52..696129e 100644 --- a/src/elastisched/engine/src-crewrite/constants.h +++ b/src/elastisched/engine/src-crewrite/constants.h @@ -32,4 +32,6 @@ const size_t INITIAL_TAGCONTAINER_CAPACITY = 8; const size_t INITIAL_CONTAINER_CAPACITY = 256; const size_t INITIAL_MAP_CAPACITY = 32; +const size_t ELASTISCHED_INTERNAL_DLL_SZ = 3 * sizeof(size_t); + #endif diff --git a/src/elastisched/engine/src-crewrite/dll.c b/src/elastisched/engine/src-crewrite/dll.c index 114204d..60206c0 100644 --- a/src/elastisched/engine/src-crewrite/dll.c +++ b/src/elastisched/engine/src-crewrite/dll.c @@ -38,14 +38,14 @@ void dll_free(dll* deque, void (*free_fn)(void *)) { free(deque); } -void* dll_head(dll* deque) { +dll_node* dll_head(dll* deque) { if (!deque->head) return NULL; - return deque->head->value; + return deque->head; } -void* dll_tail(dll* deque) { +dll_node* dll_tail(dll* deque) { if (!deque->tail) return NULL; - return deque->tail->value; + return deque->tail; } void dll_append(dll* deque, void* e) { @@ -84,11 +84,10 @@ void dll_prepend(dll* deque, void* e) { return; } -void* dll_popleft(dll* deque) { +dll_node* dll_popleft(dll* deque) { if (deque->size == 0) return NULL; dll_node* temp = deque->head; - void* value = temp->value; if (deque->size == 1) { deque->head = NULL; @@ -100,14 +99,13 @@ void* dll_popleft(dll* deque) { deque->size--; free(temp); - return value; + return temp; } -void* dll_popright(dll* deque) { +dll_node* dll_popright(dll* deque) { if (deque->size == 0) return NULL; dll_node* temp = deque->tail; - void* value = temp->value; if (deque->size == 1) { deque->head = NULL; @@ -120,9 +118,36 @@ void* dll_popright(dll* deque) { deque->size--; free(temp); - return value; + return temp; } size_t dll_size(dll* deque) { return deque->size; +} + +void dll_remove(dll* deque, dll_node* node) { + if (node == deque->head) deque->head = deque->head->next; + if (node == deque->tail) deque->tail = deque->tail->prev; + + if (node->prev) { + node->prev->next = node->next; + } + + if (node->next) { + node->next->prev = node->prev; + } + + return; +} + +dll_node* dll_next(dll_node* node) { + return node->next; +} + +dll_node* dll_prev(dll_node* node) { + return node->prev; +} + +void* dll_node_get_value(dll_node* node) { + return node->value; } \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/dll.h b/src/elastisched/engine/src-crewrite/dll.h index 4257b75..2b6d2de 100644 --- a/src/elastisched/engine/src-crewrite/dll.h +++ b/src/elastisched/engine/src-crewrite/dll.h @@ -1,14 +1,21 @@ #include +typedef struct dll_node dll_node; typedef struct dll dll; dll* mk_dll(); void dll_free(dll* deque, void (*free_fn)(void* e)); -void* dll_head(dll* deque); -void* dll_tail(dll* deque); +dll_node* dll_head(dll* deque); +dll_node* dll_tail(dll* deque); void dll_append(dll* deque, void* e); void dll_prepend(dll* deque, void* e); -void* dll_popleft(dll* deque); -void* dll_popright(dll* deque); +dll_node* dll_popleft(dll* deque); +dll_node* dll_popright(dll* deque); + +void dll_remove(dll* deque, dll_node* node); +dll_node* dll_next(dll_node* node); +dll_node* dll_prev(dll_node* node); +void* dll_node_get_value(dll_node* node); + size_t dll_size(dll* deque); diff --git a/src/elastisched/engine/src-crewrite/hash.h b/src/elastisched/engine/src-crewrite/hash.h new file mode 100644 index 0000000..3f84eee --- /dev/null +++ b/src/elastisched/engine/src-crewrite/hash.h @@ -0,0 +1,3 @@ +#include + +uint64_t string_hash(char* stri) \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/map.c b/src/elastisched/engine/src-crewrite/map.c index 8787443..1bb7d18 100644 --- a/src/elastisched/engine/src-crewrite/map.c +++ b/src/elastisched/engine/src-crewrite/map.c @@ -1,18 +1,20 @@ #include "map.h" -#include "constants.h" -#include "dll.h" - +/** + * @brief Struct which manages map buckets + * in chaining hash-map. + * + * Note: that size may not always equal + * num_items since there may be collisions. + */ typedef struct bucket_vec { - size_t size; - size_t capacity; - dll* bucket; + size_t size; /// num of occupied data + size_t capacity; /// length of data + size_t num_items; + dll** data; } bucket_vec; struct map { - size_t num_keys; - size_t num_values; - size_t capacity; uint64_t (*hash_fn)(void* e); int (*cmp_fn)(void* u, void* v); void (*free_fn)(void* e); @@ -32,11 +34,165 @@ map* mk_map(uint64_t (*hash_fn)(void* e), return NULL; } - dict->num_keys = 0; - dict->num_values = 0; - dict->capacity = INITIAL_MAP_CAPACITY; + dict->buckets->size = 0; + dict->buckets->num_items = 0; + dict->buckets->capacity = INITIAL_MAP_CAPACITY; + dict->buckets->data = NULL; + dict->hash_fn = hash_fn; dict->cmp_fn = cmp_fn; dict->free_fn = free_fn; + + return dict; +} + +void map_free(map* dict) { + return; +} + +void _map_insert(map* dict, void* key, void* value) { + size_t h = (size_t)dict->hash_fn(key); + size_t insert_index = mix64_hash(h) & (dict->buckets->capacity - 1); + dll* deque = dict->buckets->data[insert_index]; + + if (deque) { + item* kv = malloc(sizeof(item)); + if (!kv) return; + + kv->key = key; kv->value = value; + dll_append(deque, (void*)kv); + } else { + item* kv = malloc(sizeof(item)); + if (!kv) return; + + kv->key = key; kv->value = value; + dll* bucket = mk_dll(); + dll_append(bucket, (void*)kv); + *(&deque) = bucket; + dict->buckets->size++; + } + + dict->buckets->num_items++; + return; +} + +void rebuild_map(map* dict) { + map* temp_dict = malloc(sizeof(map)); + if (!temp_dict) { + fprintf(stderr, "error: malloc failed during map rebuild\n"); + return; + } + + temp_dict->hash_fn = dict->hash_fn; + temp_dict->cmp_fn = dict->cmp_fn; + temp_dict->free_fn = dict->free_fn; + for (size_t i = 0; i < dict->buckets->capacity; i++) { + dll* deque = dict->buckets->data[i]; + if (deque) { + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + _map_insert(temp_dict, (void*)curr_item->key, + (void*)curr_item->value); + curr = dll_next(curr); + } + + dll_free(deque, NULL); + } + } + + dict = temp_dict; + return; +} + +void map_insert(map* dict, void* key, void* value) { + if (map_in(dict, key)) return; + + if (alpha_meets_threshold(dict->buckets->num_items, + dict->buckets->size)) { + dict->buckets->capacity *= 2; + rebuild_map(dict); + } + _map_insert(dict, key, value); + return; +} + +bool map_in(map* dict, void* key) { + size_t h = (size_t)dict->hash_fn(key); + size_t index = mix64_hash(h) & (dict->buckets->capacity - 1); + dll* deque = dict->buckets->data[index]; + + if (deque) { + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + if (dict->cmp_fn(key, curr_item->key) == 0) { + return true; + } + curr = dll_next(curr); + } + } + return false; +} + +void* map_get(map* dict, void* key) { + size_t h = (size_t)dict->hash_fn(key); + size_t index = mix64_hash(h) & (dict->buckets->capacity - 1); + dll* deque = dict->buckets->data[index]; + + if (deque) { + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + if (dict->cmp_fn(key, curr_item->key) == 0) { + return curr_item->value; + } + curr = dll_next(curr); + } + } + return NULL; +} + +void map_delete(map* dict, void* key) { + size_t h = (size_t)dict->hash_fn(key); + size_t index = mix64_hash(h) & (dict->buckets->capacity - 1); + dll* deque = dict->buckets->data[index]; + + if (deque) { + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + if (dict->cmp_fn(key, curr_item->key) == 0) { + dll_remove(deque, curr); + return; + // TODO: need to free something here + } + curr = dll_next(curr); + } + } + return; +} + +size_t map_size(map* dict) { + return dict->buckets->num_items; +} + +vec_items* map_items(map* dict) { + vec_items* items = malloc(sizeof(vec_items)); + if (!items) return NULL; + + for (size_t i = 0; i < dict->buckets->capacity; i++) { + dll* deque = dict->buckets->data[i]; + if (deque) { + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + vec_pushback(items, curr_item); + curr = dll_next(curr); + } + } + } + + return items; } \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/map.h b/src/elastisched/engine/src-crewrite/map.h index 7fd018c..57b3689 100644 --- a/src/elastisched/engine/src-crewrite/map.h +++ b/src/elastisched/engine/src-crewrite/map.h @@ -6,8 +6,22 @@ #include #include "constants.h" +#include "dll.h" +#include "vec.h" typedef struct map map; +typedef struct map set; + +typedef struct item { + void* key; + void* value; +} item; + +typedef struct vec_items { + size_t size; + size_t capacity; + item** data; +} vec_items; map* mk_map(uint64_t (*hash_fn)(void* e), int (*cmp_fn)(void* u, void* v), @@ -17,7 +31,9 @@ void map_free(map* dict); void map_insert(map* dict, void* key, void* value); bool map_in(map* dict, void* key); -bool map_get(map* dict, void* key); -bool map_size(map* dict); +void* map_get(map* dict, void* key); +void map_delete(map* dict, void* key); +size_t map_size(map* dict); +vec_items* map_items(map* dict); #endif \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/queue.c b/src/elastisched/engine/src-crewrite/queue.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/elastisched/engine/src-crewrite/schedule.c b/src/elastisched/engine/src-crewrite/schedule.c index 1c2a491..d1f6dfd 100644 --- a/src/elastisched/engine/src-crewrite/schedule.c +++ b/src/elastisched/engine/src-crewrite/schedule.c @@ -1,10 +1,10 @@ #include "schedule.h" void schedule_add_job(Schedule* schedule, const Job* job) { - vec_pushback(schedule->scheduledJobs, *job); + vec_pushback(schedule->scheduled_jobs, *job); return; } void schedule_clear(Schedule* schedule) { - vec_clear(schedule->scheduledJobs); + vec_clear(schedule->scheduled_jobs); } \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/schedule.h b/src/elastisched/engine/src-crewrite/schedule.h index 8abfbbc..37114b2 100644 --- a/src/elastisched/engine/src-crewrite/schedule.h +++ b/src/elastisched/engine/src-crewrite/schedule.h @@ -6,7 +6,7 @@ #include "vec.h" typedef struct Schedule { - JobVec* scheduledJobs; + JobVec* scheduled_jobs; } Schedule; void schedule_add_job(Schedule* schedule, const Job* job); diff --git a/src/elastisched/engine/src-crewrite/tag.h b/src/elastisched/engine/src-crewrite/tag.h index efdf5d8..6eb5f65 100644 --- a/src/elastisched/engine/src-crewrite/tag.h +++ b/src/elastisched/engine/src-crewrite/tag.h @@ -54,6 +54,8 @@ bool tag_eq(const Tag* U, const Tag* V); */ int tag_cmp(const Tag* U, const Tag* V); +uint64_t tag_hash(const Tag* U); + /** * @brief constructs a TagContainer with capacity ```capacity``` * diff --git a/src/elastisched/engine/src-crewrite/topo_sort.c b/src/elastisched/engine/src-crewrite/topo_sort.c new file mode 100644 index 0000000..1731d23 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/topo_sort.c @@ -0,0 +1,66 @@ +#include "topo_sort.h" + +#define id_hash tag_hash +#define id_cmp tag_cmp + +DependencyCheckResult* check_dependency_violations(const Schedule* schedule) { + DependencyCheckResult* result = malloc(sizeof(DependencyCheckResult)); + if (!result) return NULL; + result->has_violations = false; + result->violations = NULL; + result->has_cyclic_dependencies = false; + + JobVec* scheduled_jobs = schedule->scheduled_jobs; + + if (scheduled_jobs->size == 0) return result; + + map* id_to_job = mk_map(&id_hash, &id_cmp, NULL); + for (size_t i = 0; i < scheduled_jobs->size; i++) { + map_insert(id_to_job, + (void*)&scheduled_jobs->data[i].id, + (void*)&scheduled_jobs->data[i]); + } + + map* adj_list = mk_map(&id_hash, &id_cmp, NULL); + map* in_degree = mk_map(&id_hash, &id_cmp, NULL); + + for (size_t i = 0; i < scheduled_jobs->size; i++) { + ID* job_id = &scheduled_jobs->data[i].id; + JobVec* job_vec = malloc(sizeof(JobVec)); + size_t* in_degree = malloc(sizeof(size_t)); + map_insert(adj_list, (void*)job_id, (void*)job_vec); + map_insert(in_degree, (void*)job_id, (void*)in_degree); + } + + for (size_t i = 0; i < scheduled_jobs->size; i++) { + ID* job_this = &scheduled_jobs->data[i].id; + DependencyContainer* job_dependencies = scheduled_jobs->data[i].dependency_set; + for (size_t j = 0; j < job_dependencies->size; j++) { + ID* job_ot = &job_dependencies->data[j]; + if (map_in(id_to_job, (void*)job_ot)) { + map_insert(adj_list, (void*)job_ot, (void*)job_this); + map_insert(in_degree, (void*)job_this, + *(size_t*)map_get(in_degree, (void*)job_this) + 1); + } + } + } + + dll* queue = mk_dll(); + DependencyContainer* topo_order = mk_tag_container(8); + vec_items* items = map_items(in_degree); + + for (size_t i = 0; i < items->size; i++) { + if (*(size_t*)items->data[i]->value == 0) { + dll_append(queue, items->data[i]->key); + } + } + + while (dll_size(queue)) { + ID* curr_id = (ID*)dll_node_get_value(dll_popleft(queue)); + vec_pushback(topo_order, *curr_id); + + for (size_t i = 0; i < ((JobVec*)map_get(adj_list, (void*)curr_id))->size; i++) { + + } + } +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/topo_sort.h b/src/elastisched/engine/src-crewrite/topo_sort.h index eb19a71..1e97c8e 100644 --- a/src/elastisched/engine/src-crewrite/topo_sort.h +++ b/src/elastisched/engine/src-crewrite/topo_sort.h @@ -1,8 +1,10 @@ #include "constants.h" #include "dll.h" +#include "map.h" #include "tag.h" #include "utils.h" +#include "schedule.h" typedef struct DependencyViolation { ID job_id; @@ -19,3 +21,5 @@ typedef struct DependencyCheckResult { bool has_cyclic_dependencies; } DependencyCheckResult; +DependencyCheckResult* check_dependency_violations(const Schedule* schedule); + diff --git a/src/elastisched/engine/src-crewrite/vec.h b/src/elastisched/engine/src-crewrite/vec.h index 28cb33b..9d5d378 100644 --- a/src/elastisched/engine/src-crewrite/vec.h +++ b/src/elastisched/engine/src-crewrite/vec.h @@ -4,6 +4,11 @@ * Macros for working with arbitrary C vectors */ +#define vec_realloc(vec) \ + do { \ + vec->data = realloc(vec->data, vec->capacity * sizeof(*vec->data)); \ + } while (0) + #define vec_pushback(vec, e) \ do { \ if (vec->size >= vec->capacity) { \ From 960c3066e7abeac69199f7c3a079f64f164f8a77 Mon Sep 17 00:00:00 2001 From: Chris Su Date: Tue, 6 Jan 2026 22:24:04 -0500 Subject: [PATCH 05/35] Debugging --- .../engine/src-crewrite/CMakeLists.txt | 0 .../engine/src-crewrite/constants.h | 44 +-- .../engine/src-crewrite/cost_function.h | 4 + src/elastisched/engine/src-crewrite/dll.c | 50 +-- src/elastisched/engine/src-crewrite/dll.h | 11 +- src/elastisched/engine/src-crewrite/hash.h | 33 +- .../engine/src-crewrite/interval.c | 90 +++++- src/elastisched/engine/src-crewrite/job.c | 34 ++- src/elastisched/engine/src-crewrite/job.h | 2 +- src/elastisched/engine/src-crewrite/map.c | 289 ++++++++++-------- src/elastisched/engine/src-crewrite/map.h | 10 +- src/elastisched/engine/src-crewrite/policy.c | 14 +- src/elastisched/engine/src-crewrite/policy.h | 1 + .../engine/src-crewrite/schedule.c | 4 +- .../engine/src-crewrite/schedule.h | 6 +- .../engine/src-crewrite/simulated_annealer.h | 4 + src/elastisched/engine/src-crewrite/tag.c | 32 +- src/elastisched/engine/src-crewrite/tag.h | 2 + .../engine/src-crewrite/topo_sort.c | 213 +++++++++++-- .../engine/src-crewrite/topo_sort.h | 7 + src/elastisched/engine/src-crewrite/utils.h | 7 +- src/elastisched/engine/src-crewrite/vec.h | 12 +- 22 files changed, 634 insertions(+), 235 deletions(-) create mode 100644 src/elastisched/engine/src-crewrite/CMakeLists.txt diff --git a/src/elastisched/engine/src-crewrite/CMakeLists.txt b/src/elastisched/engine/src-crewrite/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/constants.h b/src/elastisched/engine/src-crewrite/constants.h index 696129e..8edff34 100644 --- a/src/elastisched/engine/src-crewrite/constants.h +++ b/src/elastisched/engine/src-crewrite/constants.h @@ -1,37 +1,37 @@ -#ifndef CONSTANTS -#define CONSTANTS +#ifndef ELASTISCHED_CONSTANTS_H +#define ELASTISCHED_CONSTANTS_H #include -#include +#include typedef uint32_t sec_t; -const sec_t MINUTE = (sec_t)60; -const sec_t HOUR_TO_MINUTES = (sec_t)60; -const sec_t DAY_TO_HOURS = (sec_t)24; -const sec_t WEEK_TO_DAYS = (sec_t)7; +static const sec_t MINUTE = (sec_t)60; +static const sec_t HOUR_TO_MINUTES = (sec_t)60; +static const sec_t DAY_TO_HOURS = (sec_t)24; +static const sec_t WEEK_TO_DAYS = (sec_t)7; -const sec_t HOUR = ((sec_t)60 * MINUTE); -const sec_t DAY = ((sec_t)24 * HOUR); -const sec_t WEEK = ((sec_t)7 * DAY); +static const sec_t HOUR = ((sec_t)60 * MINUTE); +static const sec_t DAY = ((sec_t)24 * HOUR); +static const sec_t WEEK = ((sec_t)7 * DAY); -const sec_t AFTERNOON_START = 17; +static const sec_t AFTERNOON_START = 17; -const double FRIDAY_HOURLY_COST_FACTOR = 2.0f; -const double SATURDAY_HOURLY_COST_FACTOR = 3.0f; +static const double FRIDAY_HOURLY_COST_FACTOR = 2.0; +static const double SATURDAY_HOURLY_COST_FACTOR = 3.0; -const double EXP_DOWNFACTOR = 0.1f; -const double HOURLY_COST_FACTOR = 1.0f; +static const double EXP_DOWNFACTOR = 0.1; +static const double HOURLY_COST_FACTOR = 1.0; -const double ILLEGAL_SCHEDULE_COST = 1e12f; +static const double ILLEGAL_SCHEDULE_COST = 1e12; -const double EPSILON = 1e-5f; -const unsigned int DEFAULT_RNG_SEED = 1337; +static const double EPSILON = 1e-5; +static const unsigned int DEFAULT_RNG_SEED = 1337; -const size_t INITIAL_TAGCONTAINER_CAPACITY = 8; -const size_t INITIAL_CONTAINER_CAPACITY = 256; -const size_t INITIAL_MAP_CAPACITY = 32; +static const size_t INITIAL_TAGCONTAINER_CAPACITY = 8; +static const size_t INITIAL_CONTAINER_CAPACITY = 256; +static const size_t INITIAL_MAP_CAPACITY = 32; -const size_t ELASTISCHED_INTERNAL_DLL_SZ = 3 * sizeof(size_t); +static const size_t ELASTISCHED_INTERNAL_DLL_SZ = 3 * sizeof(size_t); #endif diff --git a/src/elastisched/engine/src-crewrite/cost_function.h b/src/elastisched/engine/src-crewrite/cost_function.h index e69de29..10422a2 100644 --- a/src/elastisched/engine/src-crewrite/cost_function.h +++ b/src/elastisched/engine/src-crewrite/cost_function.h @@ -0,0 +1,4 @@ +#ifndef ELASTISCHED_COST_FUNCTION_H +#define ELASTISCHED_COST_FUNCTION_H + +#endif diff --git a/src/elastisched/engine/src-crewrite/dll.c b/src/elastisched/engine/src-crewrite/dll.c index 60206c0..e04cfda 100644 --- a/src/elastisched/engine/src-crewrite/dll.c +++ b/src/elastisched/engine/src-crewrite/dll.c @@ -1,4 +1,5 @@ #include "dll.h" +#include typedef struct dll_node dll_node; @@ -39,16 +40,17 @@ void dll_free(dll* deque, void (*free_fn)(void *)) { } dll_node* dll_head(dll* deque) { - if (!deque->head) return NULL; + if (!deque) return NULL; return deque->head; } dll_node* dll_tail(dll* deque) { - if (!deque->tail) return NULL; + if (!deque) return NULL; return deque->tail; } void dll_append(dll* deque, void* e) { + if (!deque) return; dll_node* new_tail = malloc(sizeof(dll_node)); if (!new_tail) return; new_tail->value = e; @@ -60,6 +62,7 @@ void dll_append(dll* deque, void* e) { } else { new_tail->prev = deque->tail; new_tail->next = NULL; + deque->tail->next = new_tail; deque->tail = new_tail; } deque->size++; @@ -67,6 +70,7 @@ void dll_append(dll* deque, void* e) { } void dll_prepend(dll* deque, void* e) { + if (!deque) return; dll_node* new_head = malloc(sizeof(dll_node)); if (!new_head) return; new_head->value = e; @@ -78,66 +82,66 @@ void dll_prepend(dll* deque, void* e) { } else { new_head->prev = NULL; new_head->next = deque->head; + deque->head->prev = new_head; deque->head = new_head; } deque->size++; return; } -dll_node* dll_popleft(dll* deque) { - if (deque->size == 0) return NULL; +void* dll_popleft(dll* deque) { + if (!deque || deque->size == 0) return NULL; dll_node* temp = deque->head; + void* value = temp->value; if (deque->size == 1) { deque->head = NULL; deque->tail = NULL; } else { deque->head = deque->head->next; - deque->head->next->prev = NULL; + deque->head->prev = NULL; } deque->size--; free(temp); - return temp; + return value; } -dll_node* dll_popright(dll* deque) { - if (deque->size == 0) return NULL; +void* dll_popright(dll* deque) { + if (!deque || deque->size == 0) return NULL; dll_node* temp = deque->tail; + void* value = temp->value; if (deque->size == 1) { deque->head = NULL; deque->tail = NULL; - } - else { + } else { deque->tail = deque->tail->prev; - deque->tail->prev->next = NULL; + deque->tail->next = NULL; } deque->size--; free(temp); - return temp; + return value; } size_t dll_size(dll* deque) { - return deque->size; + return deque ? deque->size : 0; } void dll_remove(dll* deque, dll_node* node) { - if (node == deque->head) deque->head = deque->head->next; - if (node == deque->tail) deque->tail = deque->tail->prev; + if (!deque || !node) return; - if (node->prev) { - node->prev->next = node->next; - } + if (node == deque->head) deque->head = node->next; + if (node == deque->tail) deque->tail = node->prev; - if (node->next) { - node->next->prev = node->prev; - } + if (node->prev) node->prev->next = node->next; + if (node->next) node->next->prev = node->prev; - return; + deque->size--; + free(node); } dll_node* dll_next(dll_node* node) { @@ -150,4 +154,4 @@ dll_node* dll_prev(dll_node* node) { void* dll_node_get_value(dll_node* node) { return node->value; -} \ No newline at end of file +} diff --git a/src/elastisched/engine/src-crewrite/dll.h b/src/elastisched/engine/src-crewrite/dll.h index 2b6d2de..290b0a3 100644 --- a/src/elastisched/engine/src-crewrite/dll.h +++ b/src/elastisched/engine/src-crewrite/dll.h @@ -1,4 +1,7 @@ -#include +#ifndef ELASTISCHED_DLL_H +#define ELASTISCHED_DLL_H + +#include typedef struct dll_node dll_node; typedef struct dll dll; @@ -10,8 +13,8 @@ dll_node* dll_head(dll* deque); dll_node* dll_tail(dll* deque); void dll_append(dll* deque, void* e); void dll_prepend(dll* deque, void* e); -dll_node* dll_popleft(dll* deque); -dll_node* dll_popright(dll* deque); +void* dll_popleft(dll* deque); +void* dll_popright(dll* deque); void dll_remove(dll* deque, dll_node* node); dll_node* dll_next(dll_node* node); @@ -19,3 +22,5 @@ dll_node* dll_prev(dll_node* node); void* dll_node_get_value(dll_node* node); size_t dll_size(dll* deque); + +#endif diff --git a/src/elastisched/engine/src-crewrite/hash.h b/src/elastisched/engine/src-crewrite/hash.h index 3f84eee..5a8c833 100644 --- a/src/elastisched/engine/src-crewrite/hash.h +++ b/src/elastisched/engine/src-crewrite/hash.h @@ -1,3 +1,32 @@ -#include +#ifndef ELASTISCHED_HASH_H +#define ELASTISCHED_HASH_H -uint64_t string_hash(char* stri) \ No newline at end of file +#include +#include +#include + +static inline uint64_t mix64_hash(uint64_t x) { + x += 0x9e3779b97f4a7c15ULL; + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + return x ^ (x >> 31); +} + +static inline bool alpha_meets_threshold(size_t num_items, size_t num_buckets) { + if (num_buckets == 0) return false; + return (num_items * 4) >= (num_buckets * 3); +} + +static inline uint64_t string_hash(const char* str) { + const uint64_t fnv_offset = 1469598103934665603ULL; + const uint64_t fnv_prime = 1099511628211ULL; + uint64_t hash = fnv_offset; + if (!str) return hash; + for (const unsigned char* p = (const unsigned char*)str; *p; p++) { + hash ^= (uint64_t)(*p); + hash *= fnv_prime; + } + return hash; +} + +#endif diff --git a/src/elastisched/engine/src-crewrite/interval.c b/src/elastisched/engine/src-crewrite/interval.c index 37fc703..9a4fc51 100644 --- a/src/elastisched/engine/src-crewrite/interval.c +++ b/src/elastisched/engine/src-crewrite/interval.c @@ -1,5 +1,21 @@ #include "interval.h" +static sec_t node_max(Node* node) { + return node ? node->max : 0; +} + +static void update_max(Node* node) { + if (!node || !node->interval) return; + sec_t left_max = node_max(node->left); + sec_t right_max = node_max(node->right); + sec_t interval_high = node->interval->high; + + sec_t max = interval_high; + if (left_max > max) max = left_max; + if (right_max > max) max = right_max; + node->max = max; +} + bool interval_eq(const Interval* U, const Interval* V) { return (U->low == V->low) && (U->high == V->high); } @@ -64,7 +80,37 @@ IntervalMap* mk_intmap(Node* root) { } void intmap_insert(IntervalMap* map, Interval* key, void* value) { - return; + if (!map || !key) return; + if (!interval_is_valid(key)) return; + + if (!map->root) { + map->root = mk_leaf_node(NULL, key, value, key->high, BLACK); + return; + } + + Node* curr = map->root; + Node* parent = NULL; + while (curr) { + parent = curr; + if (key->low < curr->interval->low) { + curr = curr->left; + } else { + curr = curr->right; + } + } + + Node* node = mk_leaf_node(parent, key, value, key->high, BLACK); + if (!node) return; + + if (key->low < parent->interval->low) { + parent->left = node; + } else { + parent->right = node; + } + + for (Node* n = parent; n; n = n->parent) { + update_max(n); + } } void intmap_delete(IntervalMap* map) { @@ -72,7 +118,46 @@ void intmap_delete(IntervalMap* map) { } void intmap_free(IntervalMap* map) { - return; + if (!map) return; + if (!map->root) { + free(map); + return; + } + + size_t capacity = 32; + size_t top = 0; + Node** stack = malloc(capacity * sizeof(Node*)); + if (!stack) { + free(map); + return; + } + stack[top++] = map->root; + + while (top > 0) { + Node* node = stack[--top]; + if (node->left) { + if (top == capacity) { + capacity *= 2; + Node** new_stack = realloc(stack, capacity * sizeof(Node*)); + if (!new_stack) break; + stack = new_stack; + } + stack[top++] = node->left; + } + if (node->right) { + if (top == capacity) { + capacity *= 2; + Node** new_stack = realloc(stack, capacity * sizeof(Node*)); + if (!new_stack) break; + stack = new_stack; + } + stack[top++] = node->right; + } + free(node); + } + + free(stack); + free(map); } void intmap_left_rotate(IntervalMap* map, Node* x) { @@ -110,4 +195,3 @@ void intmap_right_rotate(IntervalMap* map, Node* y) { x->right = y; y->parent = x; } - diff --git a/src/elastisched/engine/src-crewrite/job.c b/src/elastisched/engine/src-crewrite/job.c index 1dabd44..ee8a28a 100644 --- a/src/elastisched/engine/src-crewrite/job.c +++ b/src/elastisched/engine/src-crewrite/job.c @@ -1,5 +1,37 @@ #include "job.h" +#include +#include +#include bool job_is_rigid(Job *job) { + if (!job) return false; return job->duration == interval_length(&job->schedulable_time_range); -} \ No newline at end of file +} + +char* job_to_string(Job* job) { + if (!job) return NULL; + const char* name = job->id.name ? job->id.name : "(null)"; + + int needed = snprintf(NULL, 0, + "Job{id=%s, duration=%" PRIu32 ", schedulable=[%" PRIu32 ",%" PRIu32 "], scheduled=[%" PRIu32 ",%" PRIu32 "]}", + name, + (uint32_t)job->duration, + (uint32_t)job->schedulable_time_range.low, + (uint32_t)job->schedulable_time_range.high, + (uint32_t)job->scheduled_time_range.low, + (uint32_t)job->scheduled_time_range.high); + + if (needed < 0) return NULL; + size_t size = (size_t)needed + 1; + char* buf = malloc(size); + if (!buf) return NULL; + snprintf(buf, size, + "Job{id=%s, duration=%" PRIu32 ", schedulable=[%" PRIu32 ",%" PRIu32 "], scheduled=[%" PRIu32 ",%" PRIu32 "]}", + name, + (uint32_t)job->duration, + (uint32_t)job->schedulable_time_range.low, + (uint32_t)job->schedulable_time_range.high, + (uint32_t)job->scheduled_time_range.low, + (uint32_t)job->scheduled_time_range.high); + return buf; +} diff --git a/src/elastisched/engine/src-crewrite/job.h b/src/elastisched/engine/src-crewrite/job.h index 3d00593..4673e6b 100644 --- a/src/elastisched/engine/src-crewrite/job.h +++ b/src/elastisched/engine/src-crewrite/job.h @@ -26,4 +26,4 @@ typedef struct JobVec { bool job_is_rigid(Job* job); char* job_to_string(Job* job); -#endif \ No newline at end of file +#endif diff --git a/src/elastisched/engine/src-crewrite/map.c b/src/elastisched/engine/src-crewrite/map.c index 1bb7d18..5c181f3 100644 --- a/src/elastisched/engine/src-crewrite/map.c +++ b/src/elastisched/engine/src-crewrite/map.c @@ -1,198 +1,237 @@ #include "map.h" +#include "hash.h" +#include /** * @brief Struct which manages map buckets - * in chaining hash-map. - * - * Note: that size may not always equal - * num_items since there may be collisions. + * in chaining hash-map. + * + * Note: size is number of occupied buckets; num_items is total entries. */ typedef struct bucket_vec { - size_t size; /// num of occupied data - size_t capacity; /// length of data + size_t size; + size_t capacity; size_t num_items; dll** data; } bucket_vec; struct map { - uint64_t (*hash_fn)(void* e); - int (*cmp_fn)(void* u, void* v); + uint64_t (*hash_fn)(const void* e); + int (*cmp_fn)(const void* u, const void* v); void (*free_fn)(void* e); bucket_vec* buckets; }; -map* mk_map(uint64_t (*hash_fn)(void* e), - int (*cmp_fn)(void* u, void* v), - void (*free_fn)(void* e)) -{ - map* dict = malloc(sizeof(map)); - if (!dict) return NULL; +static bucket_vec* mk_buckets(size_t capacity) { + bucket_vec* buckets = malloc(sizeof(bucket_vec)); + if (!buckets) return NULL; - dict->buckets = malloc(INITIAL_MAP_CAPACITY * sizeof(bucket_vec)); - if (!dict->buckets) { - free(dict); + buckets->data = calloc(capacity, sizeof(dll*)); + if (!buckets->data) { + free(buckets); return NULL; } - dict->buckets->size = 0; - dict->buckets->num_items = 0; - dict->buckets->capacity = INITIAL_MAP_CAPACITY; - dict->buckets->data = NULL; - - dict->hash_fn = hash_fn; - dict->cmp_fn = cmp_fn; - dict->free_fn = free_fn; - - return dict; + buckets->size = 0; + buckets->num_items = 0; + buckets->capacity = capacity; + return buckets; } -void map_free(map* dict) { - return; +static void free_item_value(void* value, void (*free_fn)(void*)) { + item* kv = (item*)value; + if (free_fn) free_fn(kv->value); + free(kv); } -void _map_insert(map* dict, void* key, void* value) { +static size_t bucket_index(map* dict, void* key) { size_t h = (size_t)dict->hash_fn(key); - size_t insert_index = mix64_hash(h) & (dict->buckets->capacity - 1); - dll* deque = dict->buckets->data[insert_index]; - - if (deque) { - item* kv = malloc(sizeof(item)); - if (!kv) return; - - kv->key = key; kv->value = value; - dll_append(deque, (void*)kv); - } else { - item* kv = malloc(sizeof(item)); - if (!kv) return; - - kv->key = key; kv->value = value; - dll* bucket = mk_dll(); - dll_append(bucket, (void*)kv); - *(&deque) = bucket; + return mix64_hash(h) & (dict->buckets->capacity - 1); +} + +static bool map_insert_bucket(map* dict, void* key, void* value) { + size_t index = bucket_index(dict, key); + dll* deque = dict->buckets->data[index]; + + item* kv = malloc(sizeof(item)); + if (!kv) return false; + kv->key = key; + kv->value = value; + + if (!deque) { + deque = mk_dll(); + if (!deque) { + free(kv); + return false; + } + dict->buckets->data[index] = deque; dict->buckets->size++; } + dll_append(deque, (void*)kv); dict->buckets->num_items++; - return; + return true; +} + +static dll_node* map_find_node(map* dict, void* key) { + size_t index = bucket_index(dict, key); + dll* deque = dict->buckets->data[index]; + if (!deque) return NULL; + + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + if (dict->cmp_fn(key, curr_item->key) == 0) return curr; + curr = dll_next(curr); + } + return NULL; } -void rebuild_map(map* dict) { - map* temp_dict = malloc(sizeof(map)); - if (!temp_dict) { +static void map_rebuild(map* dict, size_t new_capacity) { + bucket_vec* new_buckets = mk_buckets(new_capacity); + if (!new_buckets) { fprintf(stderr, "error: malloc failed during map rebuild\n"); return; } - temp_dict->hash_fn = dict->hash_fn; - temp_dict->cmp_fn = dict->cmp_fn; - temp_dict->free_fn = dict->free_fn; - - for (size_t i = 0; i < dict->buckets->capacity; i++) { - dll* deque = dict->buckets->data[i]; - if (deque) { - dll_node* curr = dll_head(deque); - while (curr) { - item* curr_item = (item*)dll_node_get_value(curr); - _map_insert(temp_dict, (void*)curr_item->key, - (void*)curr_item->value); - curr = dll_next(curr); - } + bucket_vec* old_buckets = dict->buckets; + dict->buckets = new_buckets; + + for (size_t i = 0; i < old_buckets->capacity; i++) { + dll* deque = old_buckets->data[i]; + if (!deque) continue; - dll_free(deque, NULL); + while (dll_size(deque)) { + item* kv = (item*)dll_popleft(deque); + map_insert_bucket(dict, kv->key, kv->value); + free(kv); } + + free(deque); } - dict = temp_dict; - return; + free(old_buckets->data); + free(old_buckets); } -void map_insert(map* dict, void* key, void* value) { - if (map_in(dict, key)) return; +map* mk_map(uint64_t (*hash_fn)(const void* e), + int (*cmp_fn)(const void* u, const void* v), + void (*free_fn)(void* e)) +{ + if (!hash_fn || !cmp_fn) return NULL; + map* dict = malloc(sizeof(map)); + if (!dict) return NULL; - if (alpha_meets_threshold(dict->buckets->num_items, - dict->buckets->size)) { - dict->buckets->capacity *= 2; - rebuild_map(dict); + dict->buckets = mk_buckets(INITIAL_MAP_CAPACITY); + if (!dict->buckets) { + free(dict); + return NULL; } - _map_insert(dict, key, value); - return; + + dict->hash_fn = hash_fn; + dict->cmp_fn = cmp_fn; + dict->free_fn = free_fn; + + return dict; } -bool map_in(map* dict, void* key) { - size_t h = (size_t)dict->hash_fn(key); - size_t index = mix64_hash(h) & (dict->buckets->capacity - 1); - dll* deque = dict->buckets->data[index]; +void map_free(map* dict) { + if (!dict) return; - if (deque) { - dll_node* curr = dll_head(deque); - while (curr) { - item* curr_item = (item*)dll_node_get_value(curr); - if (dict->cmp_fn(key, curr_item->key) == 0) { - return true; - } - curr = dll_next(curr); + for (size_t i = 0; i < dict->buckets->capacity; i++) { + dll* deque = dict->buckets->data[i]; + if (!deque) continue; + + while (dll_size(deque)) { + item* kv = (item*)dll_popleft(deque); + free_item_value(kv, dict->free_fn); } + + free(deque); } - return false; + + free(dict->buckets->data); + free(dict->buckets); + free(dict); } -void* map_get(map* dict, void* key) { - size_t h = (size_t)dict->hash_fn(key); - size_t index = mix64_hash(h) & (dict->buckets->capacity - 1); - dll* deque = dict->buckets->data[index]; +void map_insert(map* dict, void* key, void* value) { + if (!dict) return; - if (deque) { - dll_node* curr = dll_head(deque); - while (curr) { - item* curr_item = (item*)dll_node_get_value(curr); - if (dict->cmp_fn(key, curr_item->key) == 0) { - return curr_item->value; - } - curr = dll_next(curr); - } + dll_node* existing = map_find_node(dict, key); + if (existing) { + item* kv = (item*)dll_node_get_value(existing); + if (dict->free_fn) dict->free_fn(kv->value); + kv->value = value; + return; } - return NULL; + + if (alpha_meets_threshold(dict->buckets->num_items, dict->buckets->capacity)) { + map_rebuild(dict, dict->buckets->capacity * 2); + } + + map_insert_bucket(dict, key, value); +} + +bool map_in(map* dict, void* key) { + return map_find_node(dict, key) != NULL; +} + +void* map_get(map* dict, void* key) { + dll_node* node = map_find_node(dict, key); + if (!node) return NULL; + item* kv = (item*)dll_node_get_value(node); + return kv->value; } void map_delete(map* dict, void* key) { - size_t h = (size_t)dict->hash_fn(key); - size_t index = mix64_hash(h) & (dict->buckets->capacity - 1); + if (!dict) return; + size_t index = bucket_index(dict, key); dll* deque = dict->buckets->data[index]; - - if (deque) { - dll_node* curr = dll_head(deque); - while (curr) { - item* curr_item = (item*)dll_node_get_value(curr); - if (dict->cmp_fn(key, curr_item->key) == 0) { - dll_remove(deque, curr); - return; - // TODO: need to free something here + if (!deque) return; + + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + if (dict->cmp_fn(key, curr_item->key) == 0) { + free_item_value(curr_item, dict->free_fn); + dll_remove(deque, curr); + dict->buckets->num_items--; + if (dll_size(deque) == 0) { + dict->buckets->data[index] = NULL; + dict->buckets->size--; + free(deque); } - curr = dll_next(curr); + return; } + curr = dll_next(curr); } - return; } size_t map_size(map* dict) { - return dict->buckets->num_items; + return dict ? dict->buckets->num_items : 0; } vec_items* map_items(map* dict) { + if (!dict) return NULL; vec_items* items = malloc(sizeof(vec_items)); if (!items) return NULL; + items->size = 0; + items->capacity = 0; + items->data = NULL; + for (size_t i = 0; i < dict->buckets->capacity; i++) { dll* deque = dict->buckets->data[i]; - if (deque) { - dll_node* curr = dll_head(deque); - while (curr) { - item* curr_item = (item*)dll_node_get_value(curr); - vec_pushback(items, curr_item); - curr = dll_next(curr); - } + if (!deque) continue; + + dll_node* curr = dll_head(deque); + while (curr) { + item* curr_item = (item*)dll_node_get_value(curr); + vec_pushback(items, curr_item); + curr = dll_next(curr); } } return items; -} \ No newline at end of file +} diff --git a/src/elastisched/engine/src-crewrite/map.h b/src/elastisched/engine/src-crewrite/map.h index 57b3689..67ef20b 100644 --- a/src/elastisched/engine/src-crewrite/map.h +++ b/src/elastisched/engine/src-crewrite/map.h @@ -1,5 +1,5 @@ -#ifndef MAP_H -#define MAP_H +#ifndef ELASTISCHED_MAP_H +#define ELASTISCHED_MAP_H #include #include @@ -23,8 +23,8 @@ typedef struct vec_items { item** data; } vec_items; -map* mk_map(uint64_t (*hash_fn)(void* e), - int (*cmp_fn)(void* u, void* v), +map* mk_map(uint64_t (*hash_fn)(const void* e), + int (*cmp_fn)(const void* u, const void* v), void (*free_fn)(void* e)); void map_free(map* dict); @@ -36,4 +36,4 @@ void map_delete(map* dict, void* key); size_t map_size(map* dict); vec_items* map_items(map* dict); -#endif \ No newline at end of file +#endif diff --git a/src/elastisched/engine/src-crewrite/policy.c b/src/elastisched/engine/src-crewrite/policy.c index 9d72f81..afdfdb4 100644 --- a/src/elastisched/engine/src-crewrite/policy.c +++ b/src/elastisched/engine/src-crewrite/policy.c @@ -1,5 +1,13 @@ #include "policy.h" -bool policy_is_splittable(Policy* policy) { return (policy->scheduling_policies & 1u) != 0; } -bool policy_is_overlappable(Policy* policy) { return ((policy->scheduling_policies & 2u) >> 1) != 0; } -bool policy_is_invisible(Policy* policy) { return ((policy->scheduling_policies & 4u) >> 2) != 0; } \ No newline at end of file +bool policy_is_splittable(Policy* policy) { + return policy && (policy->scheduling_policies & 1u) != 0; +} + +bool policy_is_overlappable(Policy* policy) { + return policy && ((policy->scheduling_policies & 2u) >> 1) != 0; +} + +bool policy_is_invisible(Policy* policy) { + return policy && ((policy->scheduling_policies & 4u) >> 2) != 0; +} diff --git a/src/elastisched/engine/src-crewrite/policy.h b/src/elastisched/engine/src-crewrite/policy.h index ace4d64..8618490 100644 --- a/src/elastisched/engine/src-crewrite/policy.h +++ b/src/elastisched/engine/src-crewrite/policy.h @@ -3,6 +3,7 @@ #include #include +#include #include "constants.h" /** diff --git a/src/elastisched/engine/src-crewrite/schedule.c b/src/elastisched/engine/src-crewrite/schedule.c index d1f6dfd..e019232 100644 --- a/src/elastisched/engine/src-crewrite/schedule.c +++ b/src/elastisched/engine/src-crewrite/schedule.c @@ -1,10 +1,12 @@ #include "schedule.h" void schedule_add_job(Schedule* schedule, const Job* job) { + if (!schedule || !schedule->scheduled_jobs || !job) return; vec_pushback(schedule->scheduled_jobs, *job); return; } void schedule_clear(Schedule* schedule) { + if (!schedule || !schedule->scheduled_jobs) return; vec_clear(schedule->scheduled_jobs); -} \ No newline at end of file +} diff --git a/src/elastisched/engine/src-crewrite/schedule.h b/src/elastisched/engine/src-crewrite/schedule.h index 37114b2..925f5ea 100644 --- a/src/elastisched/engine/src-crewrite/schedule.h +++ b/src/elastisched/engine/src-crewrite/schedule.h @@ -1,5 +1,5 @@ -#ifndef SCHEDULE_H -#define SCHEDULE_H +#ifndef ELASTISCHED_SCHEDULE_H +#define ELASTISCHED_SCHEDULE_H #include "job.h" #include "utils.h" @@ -12,4 +12,4 @@ typedef struct Schedule { void schedule_add_job(Schedule* schedule, const Job* job); void schedule_clear(Schedule* schedule); -#endif \ No newline at end of file +#endif diff --git a/src/elastisched/engine/src-crewrite/simulated_annealer.h b/src/elastisched/engine/src-crewrite/simulated_annealer.h index e69de29..9254323 100644 --- a/src/elastisched/engine/src-crewrite/simulated_annealer.h +++ b/src/elastisched/engine/src-crewrite/simulated_annealer.h @@ -0,0 +1,4 @@ +#ifndef ELASTISCHED_SIMULATED_ANNEALER_H +#define ELASTISCHED_SIMULATED_ANNEALER_H + +#endif diff --git a/src/elastisched/engine/src-crewrite/tag.c b/src/elastisched/engine/src-crewrite/tag.c index 46175d0..b0b5ad6 100644 --- a/src/elastisched/engine/src-crewrite/tag.c +++ b/src/elastisched/engine/src-crewrite/tag.c @@ -1,4 +1,5 @@ #include "tag.h" +#include "hash.h" bool tag_eq(const Tag* U, const Tag* V) { return strcmp(U->name, V->name) == 0; @@ -10,14 +11,29 @@ int tag_cmp(const Tag* U, const Tag* V) { return (c > 0) - (c < 0); } +uint64_t tag_hash(const Tag* U) { + return string_hash(U ? U->name : NULL); +} + +int tag_cmp_void(const void* U, const void* V) { + return tag_cmp((const Tag*)U, (const Tag*)V); +} + +uint64_t tag_hash_void(const void* U) { + return tag_hash((const Tag*)U); +} + TagContainer* mk_tag_container(size_t capacity) { TagContainer* tag_set = malloc(sizeof(TagContainer)); if (!tag_set) return NULL; - Tag* data = malloc(capacity * sizeof(Tag)); - if (!data) { - free(tag_set); - return NULL; + Tag* data = NULL; + if (capacity != 0) { + data = malloc(capacity * sizeof(Tag)); + if (!data) { + free(tag_set); + return NULL; + } } tag_set->size = 0; @@ -28,12 +44,13 @@ TagContainer* mk_tag_container(size_t capacity) { } void tag_container_free(TagContainer* container) { + if (!container) return; free((void*)container->data); free(container); - return; } bool tag_container_resize(TagContainer* container) { + if (!container) return false; size_t new_capacity = (container->capacity == 0) ? INITIAL_TAGCONTAINER_CAPACITY : container->capacity * 2; Tag* new_data = realloc(container->data, new_capacity * sizeof(Tag)); @@ -125,7 +142,6 @@ TagContainer* ts_union(TagContainer* U, TagContainer* V) { v_ptr++; break; } - set_union->size++; } while (u_ptr < U->size) set_union->data[insert_ind++] = U->data[u_ptr++]; @@ -143,7 +159,7 @@ TagContainer* ts_intersection(TagContainer* U, TagContainer* V) { TagContainer* anchor = U->size > V->size ? V : U; TagContainer* ot = U->size > V->size ? U : V; size_t insert_ind = 0; - for (int i = 0; i < anchor->size; i++) { + for (size_t i = 0; i < anchor->size; i++) { if (ts_in(ot, anchor->data[i])) { set_intersection->data[insert_ind++] = anchor->data[i]; } @@ -168,4 +184,4 @@ bool tv_pushback(TagContainer* vec, Tag tag) { vec->data[vec->size++] = tag; return true; -} \ No newline at end of file +} diff --git a/src/elastisched/engine/src-crewrite/tag.h b/src/elastisched/engine/src-crewrite/tag.h index 6eb5f65..4cf5903 100644 --- a/src/elastisched/engine/src-crewrite/tag.h +++ b/src/elastisched/engine/src-crewrite/tag.h @@ -55,6 +55,8 @@ bool tag_eq(const Tag* U, const Tag* V); int tag_cmp(const Tag* U, const Tag* V); uint64_t tag_hash(const Tag* U); +int tag_cmp_void(const void* U, const void* V); +uint64_t tag_hash_void(const void* U); /** * @brief constructs a TagContainer with capacity ```capacity``` diff --git a/src/elastisched/engine/src-crewrite/topo_sort.c b/src/elastisched/engine/src-crewrite/topo_sort.c index 1731d23..49e8860 100644 --- a/src/elastisched/engine/src-crewrite/topo_sort.c +++ b/src/elastisched/engine/src-crewrite/topo_sort.c @@ -1,7 +1,57 @@ #include "topo_sort.h" +#include "hash.h" +#include -#define id_hash tag_hash -#define id_cmp tag_cmp +#define id_hash tag_hash_void +#define id_cmp tag_cmp_void + +static DependencyViolationContainer* mk_violation_container(size_t capacity) { + DependencyViolationContainer* container = malloc(sizeof(DependencyViolationContainer)); + if (!container) return NULL; + container->dependency_violations = NULL; + container->size = 0; + container->capacity = 0; + + if (capacity == 0) return container; + container->dependency_violations = calloc(capacity, sizeof(DependencyViolation)); + if (!container->dependency_violations) { + free(container); + return NULL; + } + container->capacity = capacity; + return container; +} + +static bool violation_container_push(DependencyViolationContainer* container, + DependencyViolation violation) { + if (container->size == container->capacity) { + size_t new_capacity = container->capacity == 0 ? 4 : container->capacity * 2; + DependencyViolation* new_data = realloc(container->dependency_violations, + new_capacity * sizeof(DependencyViolation)); + if (!new_data) return false; + container->dependency_violations = new_data; + container->capacity = new_capacity; + } + + container->dependency_violations[container->size++] = violation; + return true; +} + +static void free_tag_container(void* value) { + TagContainer* container = (TagContainer*)value; + if (!container) return; + free(container->data); + free(container); +} + +static void free_violation_container(DependencyViolationContainer* container) { + if (!container) return; + for (size_t i = 0; i < container->size; i++) { + tag_container_free(container->dependency_violations[i].violated_dependencies); + } + free(container->dependency_violations); + free(container); +} DependencyCheckResult* check_dependency_violations(const Schedule* schedule) { DependencyCheckResult* result = malloc(sizeof(DependencyCheckResult)); @@ -10,57 +60,156 @@ DependencyCheckResult* check_dependency_violations(const Schedule* schedule) { result->violations = NULL; result->has_cyclic_dependencies = false; + if (!schedule || !schedule->scheduled_jobs || schedule->scheduled_jobs->size == 0) { + return result; + } + JobVec* scheduled_jobs = schedule->scheduled_jobs; - if (scheduled_jobs->size == 0) return result; + map* index_map = mk_map(&id_hash, &id_cmp, free); + if (!index_map) return result; - map* id_to_job = mk_map(&id_hash, &id_cmp, NULL); for (size_t i = 0; i < scheduled_jobs->size; i++) { - map_insert(id_to_job, - (void*)&scheduled_jobs->data[i].id, - (void*)&scheduled_jobs->data[i]); + size_t* index = malloc(sizeof(size_t)); + if (!index) continue; + *index = i; + map_insert(index_map, (void*)&scheduled_jobs->data[i].id, (void*)index); + } + + DependencyViolationContainer* violations = mk_violation_container(4); + if (!violations) { + map_free(index_map); + return result; + } + + for (size_t i = 0; i < scheduled_jobs->size; i++) { + Job* job = &scheduled_jobs->data[i]; + DependencyContainer* deps = job->dependency_set; + if (!deps || deps->size == 0) continue; + + TagContainer* violated = mk_tag_container(0); + if (!violated) continue; + + for (size_t j = 0; j < deps->size; j++) { + ID* dep_id = &deps->data[j]; + size_t* dep_index = (size_t*)map_get(index_map, (void*)dep_id); + if (!dep_index) continue; + if (*dep_index > i) { + tv_pushback(violated, *dep_id); + } + } + + if (violated->size > 0) { + DependencyViolation violation = { + .job_id = job->id, + .violated_dependencies = violated + }; + if (!violation_container_push(violations, violation)) { + tag_container_free(violated); + } + } else { + tag_container_free(violated); + } + } + + if (violations->size > 0) { + result->has_violations = true; + result->violations = violations; + } else { + free_violation_container(violations); } - map* adj_list = mk_map(&id_hash, &id_cmp, NULL); - map* in_degree = mk_map(&id_hash, &id_cmp, NULL); + map_free(index_map); + + map* adj_list = mk_map(&id_hash, &id_cmp, free_tag_container); + map* in_degree = mk_map(&id_hash, &id_cmp, free); + if (!adj_list || !in_degree) { + map_free(adj_list); + map_free(in_degree); + return result; + } for (size_t i = 0; i < scheduled_jobs->size; i++) { ID* job_id = &scheduled_jobs->data[i].id; - JobVec* job_vec = malloc(sizeof(JobVec)); - size_t* in_degree = malloc(sizeof(size_t)); - map_insert(adj_list, (void*)job_id, (void*)job_vec); - map_insert(in_degree, (void*)job_id, (void*)in_degree); + TagContainer* neighbors = mk_tag_container(0); + size_t* degree = calloc(1, sizeof(size_t)); + if (!neighbors || !degree) { + free_tag_container(neighbors); + free(degree); + continue; + } + map_insert(adj_list, (void*)job_id, (void*)neighbors); + map_insert(in_degree, (void*)job_id, (void*)degree); } for (size_t i = 0; i < scheduled_jobs->size; i++) { - ID* job_this = &scheduled_jobs->data[i].id; - DependencyContainer* job_dependencies = scheduled_jobs->data[i].dependency_set; - for (size_t j = 0; j < job_dependencies->size; j++) { - ID* job_ot = &job_dependencies->data[j]; - if (map_in(id_to_job, (void*)job_ot)) { - map_insert(adj_list, (void*)job_ot, (void*)job_this); - map_insert(in_degree, (void*)job_this, - *(size_t*)map_get(in_degree, (void*)job_this) + 1); - } + Job* job = &scheduled_jobs->data[i]; + ID* job_id = &job->id; + DependencyContainer* deps = job->dependency_set; + if (!deps) continue; + + for (size_t j = 0; j < deps->size; j++) { + ID* dep_id = &deps->data[j]; + if (!map_in(adj_list, (void*)dep_id)) continue; + TagContainer* neighbors = (TagContainer*)map_get(adj_list, (void*)dep_id); + if (!neighbors) continue; + tv_pushback(neighbors, *job_id); + + size_t* degree = (size_t*)map_get(in_degree, (void*)job_id); + if (degree) (*degree)++; } } dll* queue = mk_dll(); - DependencyContainer* topo_order = mk_tag_container(8); - vec_items* items = map_items(in_degree); + TagContainer* topo_order = mk_tag_container(scheduled_jobs->size); + if (!queue || !topo_order) { + dll_free(queue, NULL); + tag_container_free(topo_order); + map_free(adj_list); + map_free(in_degree); + return result; + } - for (size_t i = 0; i < items->size; i++) { - if (*(size_t*)items->data[i]->value == 0) { - dll_append(queue, items->data[i]->key); + vec_items* items = map_items(in_degree); + if (items) { + for (size_t i = 0; i < items->size; i++) { + if (*(size_t*)items->data[i]->value == 0) { + dll_append(queue, items->data[i]->key); + } } } while (dll_size(queue)) { - ID* curr_id = (ID*)dll_node_get_value(dll_popleft(queue)); - vec_pushback(topo_order, *curr_id); + ID* curr_id = (ID*)dll_popleft(queue); + if (!curr_id) continue; + tv_pushback(topo_order, *curr_id); - for (size_t i = 0; i < ((JobVec*)map_get(adj_list, (void*)curr_id))->size; i++) { - + TagContainer* neighbors = (TagContainer*)map_get(adj_list, (void*)curr_id); + if (!neighbors) continue; + + for (size_t i = 0; i < neighbors->size; i++) { + ID* neighbor_id = &neighbors->data[i]; + size_t* degree = (size_t*)map_get(in_degree, (void*)neighbor_id); + if (!degree || *degree == 0) continue; + (*degree)--; + if (*degree == 0) { + dll_append(queue, neighbor_id); + } } } -} \ No newline at end of file + + if (topo_order->size < scheduled_jobs->size) { + result->has_cyclic_dependencies = true; + } + + if (items) { + free(items->data); + free(items); + } + dll_free(queue, NULL); + tag_container_free(topo_order); + map_free(adj_list); + map_free(in_degree); + + return result; +} diff --git a/src/elastisched/engine/src-crewrite/topo_sort.h b/src/elastisched/engine/src-crewrite/topo_sort.h index 1e97c8e..c17306a 100644 --- a/src/elastisched/engine/src-crewrite/topo_sort.h +++ b/src/elastisched/engine/src-crewrite/topo_sort.h @@ -1,4 +1,8 @@ +#ifndef ELASTISCHED_TOPO_SORT_H +#define ELASTISCHED_TOPO_SORT_H +#include +#include #include "constants.h" #include "dll.h" #include "map.h" @@ -13,6 +17,8 @@ typedef struct DependencyViolation { typedef struct DependencyViolationContainer { DependencyViolation* dependency_violations; + size_t size; + size_t capacity; } DependencyViolationContainer; typedef struct DependencyCheckResult { @@ -23,3 +29,4 @@ typedef struct DependencyCheckResult { DependencyCheckResult* check_dependency_violations(const Schedule* schedule); +#endif diff --git a/src/elastisched/engine/src-crewrite/utils.h b/src/elastisched/engine/src-crewrite/utils.h index cdbb6d7..d4ebe92 100644 --- a/src/elastisched/engine/src-crewrite/utils.h +++ b/src/elastisched/engine/src-crewrite/utils.h @@ -1,3 +1,6 @@ +#ifndef ELASTISCHED_UTILS_H +#define ELASTISCHED_UTILS_H + #include #include #include "constants.h" @@ -5,4 +8,6 @@ size_t min(size_t u, size_t v); size_t max(size_t u, size_t v); bool is_sorted(const void* base, size_t count, size_t elem_size, - int (*cmp)(const void*, const void*)); \ No newline at end of file + int (*cmp)(const void*, const void*)); + +#endif diff --git a/src/elastisched/engine/src-crewrite/vec.h b/src/elastisched/engine/src-crewrite/vec.h index 9d5d378..b7a4b76 100644 --- a/src/elastisched/engine/src-crewrite/vec.h +++ b/src/elastisched/engine/src-crewrite/vec.h @@ -1,6 +1,12 @@ +#ifndef ELASTISCHED_VEC_H +#define ELASTISCHED_VEC_H + +#include +#include "constants.h" + /** * vec.h - * + * * Macros for working with arbitrary C vectors */ @@ -22,4 +28,6 @@ #define vec_clear(vec) \ do { \ vec->size = 0; \ - } while (0) \ No newline at end of file + } while (0) + +#endif From 7cd03ac17a6d6e19eb2f0bfcf2671468c0794539 Mon Sep 17 00:00:00 2001 From: Chris Su Date: Thu, 15 Jan 2026 13:18:52 -0500 Subject: [PATCH 06/35] core scheduler header file --- src/elastisched/engine/src-crewrite/engine.h | 290 +++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 src/elastisched/engine/src-crewrite/engine.h diff --git a/src/elastisched/engine/src-crewrite/engine.h b/src/elastisched/engine/src-crewrite/engine.h new file mode 100644 index 0000000..61f99fa --- /dev/null +++ b/src/elastisched/engine/src-crewrite/engine.h @@ -0,0 +1,290 @@ +#ifndef ENGINE_H +#define ENGINE_H + +#include +#include +#include +#include +#include +#include + +#include "vec.h" +#include "set.h" +#include "map.h" + +#include "constants.h" +#include "utils.h" + +/** + * ============================================================================ + * =================== ELASTISCHED CORE SCHEDULING ENGINE =================== + * ============================================================================ + * + * @note this used to be written in C++, but it was re-written in C since what I + * got out of C++ was too much machinery for something that ended up being quite + * simple to implement. + * + * The scheduling engine uses simulated annealing (see: + * https://en.wikipedia.org/wiki/Simulated_annealing) to find schedules which + * minimize some cost function. + * + * Cost functions are intentionally non-opinionated, since preference learning + * (see ```learning```) should eventually learn user preferences which cannot be + * directly implemented by code. + * + * We call these implemented by the scheduling engine as "primitive costs + * functions", and exist to ensure validity, safety, and general optimality of + * the scheduled blobs / recurrences. + * + * Currently, we implement the following costs: + * + * - illegal schedule cost + * - overlap cost + * - split cost + * + * ============================================================================ + * Illegal Schedule Cost + * ============================================================================ + * + * Illegal schedule cost is returned whenever a schedule is deemed illegal by + * the scheduler. A schedule is considered illegal if any of the following are + * true: + * + * - a blob's scheduled tr overlaps a blob's schedulable tr + * - a non overlappable blob overlaps with another blob + * - a blob's dependencies are not met + * + * @note currently, the frontend / backend of this application will not include + * dependencies outside of the lookahead range to be scheduled. This implies + * that if a dependency, ```dep```, for a blob, ```b```, is after the lookahead + * range, then the dependency will ```dep``` will not be scheduled before + * ```b```. + * + * However, the cost function will not add illegal schedule cost for missing + * dependency(ies), returning the schedule as normal. + * + * Illegal schedules are intentionally given a large constant + * (```ILLEGAL_SCHEDULE_COST```) instead of failing for two reasons: + * + * 1. Even if the schedule settles on an illegal schedule, there might be + * *legal* schedules which the scheduler has yet to find, so failing early is + * not the correct behavior. + * + * 2. Even if the schedule cannot find an illegal schedule, it should still + * return a schedule, and the frontend/backend should handle this case + * appropriately. It is not the responsibility for the scheduler to handle what + * to do in the case of illegal schedules. + * + * ============================================================================ + * Overlap Cost + * ============================================================================ + * + * Overlap cost exists to ensure that the scheduler reduces the amount of + * overlap between two overlappable blobs as much as possible. This is because + * if free time exists on the calendar, the scheduler should try to utilize this + * free time first before attempting to overlap blobs. + * + * ============================================================================ + * Split Cost + * ============================================================================ + * + * Split cost exists to ensure that the scheduler reduces the amount of + * splitting that happens for a splittable blob. This is because lots of + * splitting reduces efficiency and solution quality of the engine since more + * blobs that need to be scheduled now exists within the lookahead range. The + * hope is that implementing split cost encourages the scheduler to first look + * for optima which splits as little as possible. + * + */ + +typedef uint32_t sec_t; + +typedef struct interval { + sec_t low; + sec_t high; +} interval_t; + +typedef struct tag tag_t; +typedef tag_t jid_t; /// an dependency ID is a Tag with an empty description +typedef struct policy policy_t; + +typedef struct schedule schedule_t; +typedef struct jobs jobs_t; +typedef struct pair pair_t; + +typedef struct container { + size_t size; + size_t capacity; + void** data; +} container; + +typedef container set; +typedef container vec; +typedef set tag_set_t; +typedef vec tag_vec_t; + +typedef struct job { + sec_t duration; + interval_t schedulable_tr; + interval_t scheduled_tr; + jid_t *id; + policy_t *policy; + set *dependencies; + set *tags; +} job_t; + +pair_t *mk_pair(void *U, void *V); +void pair_free(pair_t *pair); + +//================================= +//=========== CONSTANTS =========== +//================================= +static const sec_t MINUTE = (sec_t)60; +static const sec_t HOUR_TO_MINUTES = (sec_t)60; +static const sec_t DAY_TO_HOURS = (sec_t)24; +static const sec_t WEEK_TO_DAYS = (sec_t)7; + +static const sec_t HOUR = ((sec_t)60 * MINUTE); +static const sec_t DAY = ((sec_t)24 * HOUR); +static const sec_t WEEK = ((sec_t)7 * DAY); + +static const double ILLEGAL_SCHEDULE_COST = 1e12; + +static const double EPSILON = 1e-8; +static const unsigned int DEFAULT_RNG_SEED = 1337; + +//========================================== +//=========== INTERNAL CONSTANTS =========== +//========================================== + +/** + * @brief Internal constants + * + * These are constants used by elastisched's core scheduling + * engine. However, they are intentionally exposed to the + * user since these intrinsics might be useful in optimizing + * scheduling calls. + */ +static const size_t ELASTISCHED_INITIAL_TAGCONTAINER_CAPACITY = 8; +static const size_t ELASTISCHED_INITIAL_CONTAINER_CAPACITY = 256; +static const size_t ELASTISCHED_INITIAL_MAP_CAPACITY = 32; +static const size_t ELASTISCHED_INTERNAL_DLL_SZ = 3 * sizeof(size_t); + +// POLICY UTILS +/** + * @brief creates a scheduling policy configuration struct + * + * @param is_splittable + * @param is_overlappable + * @param is_invisible + * @param max_splits + * @param min_split_duration + * @return policy_t* + */ +policy_t *mk_policy(bool is_splittable, bool is_overlappable, bool is_invisible, + uint8_t max_splits, sec_t min_split_duration); +bool policy_is_splittable(policy_t *policy); +bool policy_is_overlappable(policy_t *policy); +bool policy_is_invisible(policy_t *policy); +uint8_t policy_max_splits(policy_t *policy); +sec_t min_split_duration(policy_t *policy); + +schedule_t *mk_schedule(); +void schedule_free(schedule_t *schedule); +void schedule_add_job(schedule_t *schedule, const job_t *job); +void schedule_clear(schedule_t *schedule); + +/** + * TAGS + * + * Definitions for working with the Tag and TagContainer structs. + * + * TagContainer is a simple container struct which allows for + * functions to operate on it and implement Sets and Vectors. + * + * A set of tags in practice does not get very large nor updated + * often so we opt for a simple vector-based sorted set. + * + * Two tags are equivalent if they share the same name, + * not necessarily the same description. + * + * This allows for O(logn) search time and membership + * checking, O(n) set difference and O(n) time to add + * to the set. + */ + +tag_t *mk_tag(char *name, char *description); +void tag_free(tag_t *tag); +bool tag_eq(const tag_t *U, const tag_t *V); +int tag_cmp(const tag_t *U, const tag_t *V); +uint64_t tag_hash(const tag_t *U); + +tag_set_t *mk_tag_container(size_t capacity); +void tag_container_free(container *container); + +/** + * @brief Helper function for adding tag into a set + * + * @param set + * @param tag + * @return true if no memory failures, false otherwise + */ +bool ts_add(tag_set_t *set, tag_t tag); + +/** + * @brief Membership checking in O(log|```set```|) + * + * @param set + * @param tag + * @return true if ```tag``` is in ```set``` + */ +bool ts_in(tag_set_t *set, tag_t tag); + +/** + * @brief returns a new set containing the set union + * + * @param U + * @param V + * @return TagContainer* + * + * @note runtime of this is O(|U| + |V|) + */ +tag_set_t *ts_union(tag_set_t *U, tag_set_t *V); + +/** + * @brief returns a new set containing the set intersection + * + * @param U + * @param V + * @return TagContainer* + * + * @note runtime of this is O(min(|U|, |V|)*log(max(|U|, |V|))) + */ +tag_set_t *ts_intersection(tag_set_t *U, tag_set_t *V); + +/** + * @brief ensures that the tag set internals meet the appropriate + * invariants. + * + * @param set + * @return: true if the invariants are met + */ +bool ts_is_valid(tag_set_t *set); + +/** + * @brief pushback tag to tagvec + * + * @param vec + * @param tag + * @return: true if no memory failures, false otherwise + */ +bool tv_pushback(tag_vec_t *vec, tag_t tag); + +schedule_t *generate_random_schedule_neighbor(schedule_t *schedule, + sec_t granularity, + unsigned int seed); +pair_t *_schedule_jobs(jobs_t jobs, sec_t granularity, double initial_temp, + double final_temp, uint64_t num_iters); +pair_t *schedule_jobs(jobs_t jobs, sec_t granularity); + +#endif \ No newline at end of file From bc6732eae0849a425c8fd19bccfe151496d11435 Mon Sep 17 00:00:00 2001 From: Chris Su Date: Fri, 16 Jan 2026 13:40:02 -0500 Subject: [PATCH 07/35] chore: simplify c backend --- .../engine/src-crewrite/constants.h | 37 -- .../engine/src-crewrite/container.c | 82 ++++ .../engine/src-crewrite/container.h | 63 +++ .../engine/src-crewrite/cost_function.h | 4 - src/elastisched/engine/src-crewrite/dll.c | 5 + src/elastisched/engine/src-crewrite/dll.h | 1 + src/elastisched/engine/src-crewrite/engine.c | 423 ++++++++++++++++++ src/elastisched/engine/src-crewrite/engine.h | 42 +- .../src-crewrite/{cost_function.c => graph.c} | 0 src/elastisched/engine/src-crewrite/graph.h | 0 .../engine/src-crewrite/interval.c | 197 -------- .../engine/src-crewrite/interval.h | 48 -- src/elastisched/engine/src-crewrite/job.c | 37 -- src/elastisched/engine/src-crewrite/job.h | 29 -- src/elastisched/engine/src-crewrite/map.c | 63 +-- src/elastisched/engine/src-crewrite/map.h | 13 +- src/elastisched/engine/src-crewrite/policy.c | 13 - src/elastisched/engine/src-crewrite/policy.h | 24 - .../engine/src-crewrite/schedule.c | 12 - .../engine/src-crewrite/schedule.h | 15 - .../engine/src-crewrite/simulated_annealer.h | 4 - src/elastisched/engine/src-crewrite/tag.c | 187 -------- src/elastisched/engine/src-crewrite/tag.h | 161 ------- src/elastisched/engine/src-crewrite/utils.c | 22 - src/elastisched/engine/src-crewrite/utils.h | 23 +- src/elastisched/engine/src-crewrite/vec.h | 33 -- 26 files changed, 652 insertions(+), 886 deletions(-) delete mode 100644 src/elastisched/engine/src-crewrite/constants.h create mode 100644 src/elastisched/engine/src-crewrite/container.c create mode 100644 src/elastisched/engine/src-crewrite/container.h delete mode 100644 src/elastisched/engine/src-crewrite/cost_function.h create mode 100644 src/elastisched/engine/src-crewrite/engine.c rename src/elastisched/engine/src-crewrite/{cost_function.c => graph.c} (100%) create mode 100644 src/elastisched/engine/src-crewrite/graph.h delete mode 100644 src/elastisched/engine/src-crewrite/interval.c delete mode 100644 src/elastisched/engine/src-crewrite/interval.h delete mode 100644 src/elastisched/engine/src-crewrite/job.c delete mode 100644 src/elastisched/engine/src-crewrite/job.h delete mode 100644 src/elastisched/engine/src-crewrite/policy.c delete mode 100644 src/elastisched/engine/src-crewrite/policy.h delete mode 100644 src/elastisched/engine/src-crewrite/schedule.c delete mode 100644 src/elastisched/engine/src-crewrite/schedule.h delete mode 100644 src/elastisched/engine/src-crewrite/simulated_annealer.h delete mode 100644 src/elastisched/engine/src-crewrite/tag.c delete mode 100644 src/elastisched/engine/src-crewrite/tag.h delete mode 100644 src/elastisched/engine/src-crewrite/utils.c delete mode 100644 src/elastisched/engine/src-crewrite/vec.h diff --git a/src/elastisched/engine/src-crewrite/constants.h b/src/elastisched/engine/src-crewrite/constants.h deleted file mode 100644 index 8edff34..0000000 --- a/src/elastisched/engine/src-crewrite/constants.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ELASTISCHED_CONSTANTS_H -#define ELASTISCHED_CONSTANTS_H - -#include -#include - -typedef uint32_t sec_t; - -static const sec_t MINUTE = (sec_t)60; -static const sec_t HOUR_TO_MINUTES = (sec_t)60; -static const sec_t DAY_TO_HOURS = (sec_t)24; -static const sec_t WEEK_TO_DAYS = (sec_t)7; - -static const sec_t HOUR = ((sec_t)60 * MINUTE); -static const sec_t DAY = ((sec_t)24 * HOUR); -static const sec_t WEEK = ((sec_t)7 * DAY); - -static const sec_t AFTERNOON_START = 17; - -static const double FRIDAY_HOURLY_COST_FACTOR = 2.0; -static const double SATURDAY_HOURLY_COST_FACTOR = 3.0; - -static const double EXP_DOWNFACTOR = 0.1; -static const double HOURLY_COST_FACTOR = 1.0; - -static const double ILLEGAL_SCHEDULE_COST = 1e12; - -static const double EPSILON = 1e-5; -static const unsigned int DEFAULT_RNG_SEED = 1337; - -static const size_t INITIAL_TAGCONTAINER_CAPACITY = 8; -static const size_t INITIAL_CONTAINER_CAPACITY = 256; -static const size_t INITIAL_MAP_CAPACITY = 32; - -static const size_t ELASTISCHED_INTERNAL_DLL_SZ = 3 * sizeof(size_t); - -#endif diff --git a/src/elastisched/engine/src-crewrite/container.c b/src/elastisched/engine/src-crewrite/container.c new file mode 100644 index 0000000..2c8a41c --- /dev/null +++ b/src/elastisched/engine/src-crewrite/container.c @@ -0,0 +1,82 @@ +#include "container.h" + +container_t* mk_container(size_t capacity) { + container_t* container = malloc(sizeof(container_t)); + if (!container) return NULL; + + void** data = NULL; + if (capacity != 0) { + data = malloc(capacity * sizeof(void*)); + if (!data) { + free(container); + return NULL; + } + } + + container->size = 0; + container->capacity = (!container->capacity) ? INITIAL_CONTAINER_CAPACITY : capacity; + container->data = data; + + return container; +} + +void container_free(container_t* container, void (*free_fn)(void*)) { + if (!container) return; + if (free_fn) { + for (size_t i = 0; i < container->size; ++i) { + free_fn(container->data[i]); + } + } + + free((void*)container->data); + free(container); +} + +bool container_resize(container_t* container, size_t* capacity) { + if (!container) return false; + if (capacity && *capacity < container->size) return false; + + /* if capacity is not NULL, use capacity. Otherwise, check + * if container->capacity is 0. If true, use + * ```INITIAL_CONTAINER_CAPACITY```, otherwise, double + * container capacity. */ + size_t new_capacity = capacity ? + *capacity : + ((container->capacity == 0) ? + INITIAL_CONTAINER_CAPACITY : container->capacity * 2); + + void** new_data = realloc(container->data, new_capacity * sizeof(void*)); + if (!new_data) return false; + + container->data = new_data; + container->capacity = new_capacity; + return true; +} + +bool container_append(container_t* container, void* e) { + if (!container) return false; + if (container->size == container->capacity) { + if (!container_resize(container, NULL)) return false; + } + container->data[container->size++] = e; + return true; +} + +bool container_insert(container_t* container, void* e, size_t insert_ind) { + if (!container) return false; + if (insert_ind > container->size) return false; + + if (container->size == container->capacity) { + if (!container_resize(container, NULL)) return false; + } + + if (insert_ind < container->size) { + memmove(&container->data[insert_ind + 1], + &container->data[insert_ind], + (container->size - insert_ind) * sizeof(void*)); + } + + container->data[insert_ind] = e; + container->size++; + return true; +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/container.h b/src/elastisched/engine/src-crewrite/container.h new file mode 100644 index 0000000..a0e6763 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/container.h @@ -0,0 +1,63 @@ +#ifndef CONTAINER_H +#define CONTAINER_H + +#include +#include +#include + +/** + * @brief Abstract data container which also adds (optional) safe dynamic array semantics + * + * container_t is the primary struct of interest here. container.h provides a + * mostly safe (and simple) API for interacting with the container_t struct. + */ + +#define INITIAL_CONTAINER_CAPACITY 16 + +typedef struct container { + size_t size; + size_t capacity; + void** data; +} container_t; + +container_t* mk_container(size_t capacity); + +/** + * @brief frees a container, optionally the elements if free_fn is not NULL + * + * @param container + */ +void container_free(container_t* container, void (*free_fn)(void*)); + +/** + * @brief Resize the container + * + * @param container + * @param capacity optional value which the function will use if it is not NULL. + * @return true if resize was successful + * @return false otherwise + */ +bool container_resize(container_t* container, size_t* capacity); + +/** + * @brief Appends to a container. Runs in O(1) amortized work. + * + * @param container + * @param e + * @return true if container[size-1] = e + * @return false otherwise (this can include memory failures, bad inputs, etc) + */ +bool container_append(container_t* container, void* e); + +/** + * @brief inserts to a container. Runs in O(n) work. + * + * @param container + * @param e + * @param insert_ind + * @return true if container[insert_index] = e + * @return false otherwise + */ +bool container_insert(container_t* container, void* e, size_t insert_ind); + +#endif diff --git a/src/elastisched/engine/src-crewrite/cost_function.h b/src/elastisched/engine/src-crewrite/cost_function.h deleted file mode 100644 index 10422a2..0000000 --- a/src/elastisched/engine/src-crewrite/cost_function.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef ELASTISCHED_COST_FUNCTION_H -#define ELASTISCHED_COST_FUNCTION_H - -#endif diff --git a/src/elastisched/engine/src-crewrite/dll.c b/src/elastisched/engine/src-crewrite/dll.c index e04cfda..8a60065 100644 --- a/src/elastisched/engine/src-crewrite/dll.c +++ b/src/elastisched/engine/src-crewrite/dll.c @@ -155,3 +155,8 @@ dll_node* dll_prev(dll_node* node) { void* dll_node_get_value(dll_node* node) { return node->value; } + +void dll_node_free(dll_node* node) { + free(node); + return; +} diff --git a/src/elastisched/engine/src-crewrite/dll.h b/src/elastisched/engine/src-crewrite/dll.h index 290b0a3..13ab9af 100644 --- a/src/elastisched/engine/src-crewrite/dll.h +++ b/src/elastisched/engine/src-crewrite/dll.h @@ -20,6 +20,7 @@ void dll_remove(dll* deque, dll_node* node); dll_node* dll_next(dll_node* node); dll_node* dll_prev(dll_node* node); void* dll_node_get_value(dll_node* node); +void dll_node_free(dll_node* node); size_t dll_size(dll* deque); diff --git a/src/elastisched/engine/src-crewrite/engine.c b/src/elastisched/engine/src-crewrite/engine.c new file mode 100644 index 0000000..c410719 --- /dev/null +++ b/src/elastisched/engine/src-crewrite/engine.c @@ -0,0 +1,423 @@ +#include "engine.h" + +/** + * @brief Scheduling policy struct, which + * defines the configurations for how a job + * may be scheduled. Each job may have its own policy. + */ +typedef struct policy { + uint8_t max_splits; /// how many times a splittable job may be split + sec_t min_split_duration; /// minimum duration that a splittable job can be split into + uint8_t scheduling_policies; /// overloaded bitfield: bit 0 = is_splittable, bit 1 = is_overlappable, bit 2 = is_invisible +} policy_t; + +policy_t *mk_policy(bool is_splittable, bool is_overlappable, bool is_invisible, + uint8_t max_splits, sec_t min_split_duration) { + policy_t* policy = malloc(sizeof(policy_t)); + if (!policy) return NULL; + + policy->max_splits = max_splits; + policy->min_split_duration = min_split_duration; + + policy->scheduling_policies = 0; + if (is_splittable) { + policy->scheduling_policies |= 1u; + } + if (is_overlappable) { + policy->scheduling_policies |= 2u; + } + if (is_invisible) { + policy->scheduling_policies |= 4u; + } + + return policy; +}; + +uint8_t policy_max_splits(policy_t *policy) { return policy->max_splits; }; + +sec_t min_split_duration(policy_t *policy) { return policy->min_split_duration; }; + +bool policy_is_splittable(policy_t* policy) { + return policy && (policy->scheduling_policies & 1u) != 0; } + +bool policy_is_overlappable(policy_t* policy) { + return policy && ((policy->scheduling_policies & 2u) >> 1) != 0; } + +bool policy_is_invisible(policy_t* policy) { + return policy && ((policy->scheduling_policies & 4u) >> 2) != 0; } + + +bool job_is_rigid(job_t *job) { + if (!job) return false; + return job->duration == interval_length(job->schedulable_tr); +} + + +typedef enum { + RED, + BLACK } +Color; + +typedef struct Node Node; + +struct Node { + Node* left; + Node* right; + Node* parent; + Interval* interval; + void* value; + sec_t max; + Color color; +}; + +typedef struct IntervalMap { + Node* root; +} IntervalMap; + +static sec_t node_max(Node* node) { + return node ? node->max : 0; +} + +static void update_max(Node* node) { + if (!node || !node->interval) return; + sec_t left_max = node_max(node->left); + sec_t right_max = node_max(node->right); + sec_t interval_high = node->interval->high; + + sec_t max = interval_high; + if (left_max > max) max = left_max; + if (right_max > max) max = right_max; + node->max = max; +} + +Node* mk_leaf_node(Node* parent, Interval* interval, + void* value, sec_t max, Color color +) { + Node* node = malloc(sizeof(Node)); + if (!node) return NULL; + + node->parent = parent; + node->interval = interval; + node->value = value; + node->max = max; + node->color = color; + + node->left = NULL; + node->right = NULL; + + return node; +} + +Node* mk_node(Node* left, Node* right, Node* parent, + Interval* interval, void* value, sec_t max, Color color +) { + Node* node = malloc(sizeof(Node)); + if (!node) return NULL; + + node->left = left; + node->right = right; + node->parent = parent; + node->interval = interval; + node->value = value; + node->max = max; + node->color = color; + + return node; +} + +IntervalMap* mk_intmap(Node* root) { + IntervalMap* map = malloc(sizeof(IntervalMap)); + if (!map) return NULL; + + map->root = root; + return map; +} + +void intmap_insert(IntervalMap* map, Interval* key, void* value) { + if (!map || !key) return; + if (!interval_is_valid(key)) return; + + if (!map->root) { + map->root = mk_leaf_node(NULL, key, value, key->high, BLACK); + return; + } + + Node* curr = map->root; + Node* parent = NULL; + while (curr) { + parent = curr; + if (key->low < curr->interval->low) { + curr = curr->left; + } else { + curr = curr->right; + } + } + + Node* node = mk_leaf_node(parent, key, value, key->high, BLACK); + if (!node) return; + + if (key->low < parent->interval->low) { + parent->left = node; + } else { + parent->right = node; + } + + for (Node* n = parent; n; n = n->parent) { + update_max(n); + } +} + +void intmap_delete(IntervalMap* map) { + return; +} + +void intmap_free(IntervalMap* map) { + if (!map) return; + if (!map->root) { + free(map); + return; + } + + size_t capacity = 32; + size_t top = 0; + Node** stack = malloc(capacity * sizeof(Node*)); + if (!stack) { + free(map); + return; + } + stack[top++] = map->root; + + while (top > 0) { + Node* node = stack[--top]; + if (node->left) { + if (top == capacity) { + capacity *= 2; + Node** new_stack = realloc(stack, capacity * sizeof(Node*)); + if (!new_stack) break; + stack = new_stack; + } + stack[top++] = node->left; + } + if (node->right) { + if (top == capacity) { + capacity *= 2; + Node** new_stack = realloc(stack, capacity * sizeof(Node*)); + if (!new_stack) break; + stack = new_stack; + } + stack[top++] = node->right; + } + free(node); + } + + free(stack); + free(map); +} + +void intmap_left_rotate(IntervalMap* map, Node* x) { + Node* y = x->right; + x->right = y->left; + if (y->left) y->left->parent = x; + + y->parent = x->parent; + if (!x->parent) { + map->root = y; + } else if (x == x->parent->left) { + x->parent->left = y; + } else { + x->parent->right = y; + } + y->left = x; + x->parent = y; +} + +void intmap_right_rotate(IntervalMap* map, Node* y) { + Node* x = y->left; + + y->left = x->right; + if (x->right) x->right->parent = y; + + x->parent = y->parent; + if (!y->parent) { + map->root = x; + } else if (y == y->parent->left) { + y->parent->left = x; + } else { + y->parent->right = x; + } + + x->right = y; + y->parent = x; +} + +/** + * + * + * Definitions for working with the Tag and TagContainer structs. + * + * TagContainer is a simple container struct which allows for + * functions to operate on it and implement Sets and Vectors. + * + * TagSet in practice does not get very large nor updated + * often so we opt for a simple vector-based sorted set. + * + * Two tags are equivalent if they share the same name, + * not necessarily the same description. + * + * This allows for O(logn) search time and membership + * checking, O(n) set difference and O(n) time to add + * to the set. + */ + +struct tag { + char* name; + char* description; +}; + +bool tag_eq(const tag_t* U, const tag_t* V) { + return strcmp(U->name, V->name) == 0; +} + +int tag_cmp(const tag_t* U, const tag_t* V) { + int c = strcmp(U->name, V->name); + + return (c > 0) - (c < 0); +} + +uint64_t tag_hash(const tag_t* U) { + return string_hash(U ? U->name : NULL); +} + +int tag_cmp_void(const void* U, const void* V) { + return tag_cmp((const tag_t*)U, (const tag_t*)V); +} + +uint64_t tag_hash_void(const void* U) { + return tag_hash((const tag_t*)U); +} + +size_t ts_insert_index(tag_set* set, tag_t tag) { + size_t l = 0; + size_t r = set->size; + + while (l < r) { + size_t mid = l + (r - l) / 2; + int _cmp = tag_cmp(&tag, &(set->data)[mid]); + if (_cmp == 0) return mid; + if (_cmp < 0) r = mid; + else l = mid + 1; + } + return l; +} + +void ts_insert(tag_set* set, tag_t tag, size_t insert_ind) { + if (insert_ind < set->size) { + memmove(&set->data[insert_ind + 1], + &set->data[insert_ind], + (set->size - insert_ind) * sizeof(tag_t)); + } + set->data[insert_ind] = tag; + set->size++; +} + +bool ts_add(tag_tContainer* set, tag_t tag) { + size_t insert_index = ts_insert_index(set, tag); + if (insert_index < set->size && + tag_eq(&tag, &set->data[insert_index])) return true; + + if (set->size == set->capacity && + !tag_container_resize(set)) + return false; + + ts_insert(set, tag, insert_index); + return true; +} + +bool ts_in(tag_tContainer* set, tag_t tag) { + size_t insert_index = ts_insert_index(set, tag); + return (insert_index < set->size && + tag_eq(&tag, &set->data[insert_index])); +} + +tag_tContainer* ts_union(tag_tContainer* U, tag_tContainer* V) { + if (U->size == 0) { + tag_tContainer* set_union = mk_tag_container(V->size); + if (!set_union) return NULL; + memcpy(set_union->data, V->data, V->size * sizeof(tag_t)); + set_union->size = V->size; + return set_union; + } + if (V->size == 0) { + tag_tContainer* set_union= mk_tag_container(U->size); + if (!set_union) return NULL; + memcpy(set_union->data, U->data, U->size * sizeof(tag_t)); + set_union->size = U->size; + return set_union; + } + + size_t u_ptr = 0; + size_t v_ptr = 0; + size_t insert_ind = 0; + tag_tContainer* set_union = mk_tag_container(U->size + V->size); + if (!set_union) return NULL; + + while (u_ptr < U->size && v_ptr < V->size) { + tag_t* curr_u_tag = &U->data[u_ptr]; + tag_t* curr_v_tag = &V->data[v_ptr]; + switch (tag_cmp(curr_u_tag, curr_v_tag)) { + case -1: + set_union->data[insert_ind++] = *curr_u_tag; + u_ptr++; + break; + case 0: + set_union->data[insert_ind++] = *curr_u_tag; + u_ptr++; v_ptr++; + break; + case 1: + set_union->data[insert_ind++] = *curr_v_tag; + v_ptr++; + break; + } + } + + while (u_ptr < U->size) set_union->data[insert_ind++] = U->data[u_ptr++]; + while (v_ptr < V->size) set_union->data[insert_ind++] = V->data[v_ptr++]; + + set_union->size = insert_ind; + + return set_union; +}; + +tag_tContainer* ts_intersection(tag_tContainer* U, tag_tContainer* V) { + tag_tContainer* set_intersection = mk_tag_container(min(U->size, V->size)); + if (!set_intersection) return NULL; + + tag_tContainer* anchor = U->size > V->size ? V : U; + tag_tContainer* ot = U->size > V->size ? U : V; + size_t insert_ind = 0; + for (size_t i = 0; i < anchor->size; i++) { + if (ts_in(ot, anchor->data[i])) { + set_intersection->data[insert_ind++] = anchor->data[i]; + } + } + + set_intersection->size = insert_ind; + return set_intersection; +} + +int _tag_cmp(const void* u, const void* v) { + return tag_cmp((tag_t*)u, (tag_t*)v); +} + +bool ts_is_valid(tag_tContainer* set) { + return set->size <= set->capacity && + is_sorted((void*)set->data, set->size, sizeof(tag_t), &_tag_cmp); +} + +bool tv_pushback(tag_tContainer* vec, tag_t tag) { + if (vec->size == vec->capacity && + !tag_container_resize(vec)) return false; + + vec->data[vec->size++] = tag; + return true; +} diff --git a/src/elastisched/engine/src-crewrite/engine.h b/src/elastisched/engine/src-crewrite/engine.h index 61f99fa..49fc51e 100644 --- a/src/elastisched/engine/src-crewrite/engine.h +++ b/src/elastisched/engine/src-crewrite/engine.h @@ -8,12 +8,12 @@ #include #include -#include "vec.h" -#include "set.h" +#include "container.h" #include "map.h" +#include "dll.h" +#include "graph.h" +#include "hash.h" -#include "constants.h" -#include "utils.h" /** * ============================================================================ @@ -99,12 +99,18 @@ typedef uint32_t sec_t; +#ifndef INTERVAL_T +#define INTERVAL_T typedef struct interval { sec_t low; sec_t high; } interval_t; +#endif typedef struct tag tag_t; +typedef container_t tag_set; +typedef container_t vec; + typedef tag_t jid_t; /// an dependency ID is a Tag with an empty description typedef struct policy policy_t; @@ -112,16 +118,6 @@ typedef struct schedule schedule_t; typedef struct jobs jobs_t; typedef struct pair pair_t; -typedef struct container { - size_t size; - size_t capacity; - void** data; -} container; - -typedef container set; -typedef container vec; -typedef set tag_set_t; -typedef vec tag_vec_t; typedef struct job { sec_t duration; @@ -148,11 +144,6 @@ static const sec_t HOUR = ((sec_t)60 * MINUTE); static const sec_t DAY = ((sec_t)24 * HOUR); static const sec_t WEEK = ((sec_t)7 * DAY); -static const double ILLEGAL_SCHEDULE_COST = 1e12; - -static const double EPSILON = 1e-8; -static const unsigned int DEFAULT_RNG_SEED = 1337; - //========================================== //=========== INTERNAL CONSTANTS =========== //========================================== @@ -165,10 +156,12 @@ static const unsigned int DEFAULT_RNG_SEED = 1337; * user since these intrinsics might be useful in optimizing * scheduling calls. */ -static const size_t ELASTISCHED_INITIAL_TAGCONTAINER_CAPACITY = 8; -static const size_t ELASTISCHED_INITIAL_CONTAINER_CAPACITY = 256; -static const size_t ELASTISCHED_INITIAL_MAP_CAPACITY = 32; -static const size_t ELASTISCHED_INTERNAL_DLL_SZ = 3 * sizeof(size_t); + +static const double ILLEGAL_SCHEDULE_COST = 1e12; + +static const double EPSILON = 1e-8; +static const unsigned int DEFAULT_RNG_SEED = 1337; + // POLICY UTILS /** @@ -219,9 +212,6 @@ bool tag_eq(const tag_t *U, const tag_t *V); int tag_cmp(const tag_t *U, const tag_t *V); uint64_t tag_hash(const tag_t *U); -tag_set_t *mk_tag_container(size_t capacity); -void tag_container_free(container *container); - /** * @brief Helper function for adding tag into a set * diff --git a/src/elastisched/engine/src-crewrite/cost_function.c b/src/elastisched/engine/src-crewrite/graph.c similarity index 100% rename from src/elastisched/engine/src-crewrite/cost_function.c rename to src/elastisched/engine/src-crewrite/graph.c diff --git a/src/elastisched/engine/src-crewrite/graph.h b/src/elastisched/engine/src-crewrite/graph.h new file mode 100644 index 0000000..e69de29 diff --git a/src/elastisched/engine/src-crewrite/interval.c b/src/elastisched/engine/src-crewrite/interval.c deleted file mode 100644 index 9a4fc51..0000000 --- a/src/elastisched/engine/src-crewrite/interval.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "interval.h" - -static sec_t node_max(Node* node) { - return node ? node->max : 0; -} - -static void update_max(Node* node) { - if (!node || !node->interval) return; - sec_t left_max = node_max(node->left); - sec_t right_max = node_max(node->right); - sec_t interval_high = node->interval->high; - - sec_t max = interval_high; - if (left_max > max) max = left_max; - if (right_max > max) max = right_max; - node->max = max; -} - -bool interval_eq(const Interval* U, const Interval* V) { - return (U->low == V->low) && (U->high == V->high); -} - -bool interval_overlaps(const Interval* U, const Interval* V) { - return !(U->high < V->low || V->high < U->low); -} - -bool interval_contains(const Interval* U, const Interval* V) { - return (U->low <= V->low && V->high <= U->high); -} - -sec_t interval_length(const Interval* interval) { - return interval->high - interval->low; -} - -bool interval_is_valid(const Interval* interval) { - return interval->high >= interval->low; -} - -Node* mk_leaf_node(Node* parent, Interval* interval, - void* value, sec_t max, Color color -) { - Node* node = malloc(sizeof(Node)); - if (!node) return NULL; - - node->parent = parent; - node->interval = interval; - node->value = value; - node->max = max; - node->color = color; - - node->left = NULL; - node->right = NULL; - - return node; -} - -Node* mk_node(Node* left, Node* right, Node* parent, - Interval* interval, void* value, sec_t max, Color color -) { - Node* node = malloc(sizeof(Node)); - if (!node) return NULL; - - node->left = left; - node->right = right; - node->parent = parent; - node->interval = interval; - node->value = value; - node->max = max; - node->color = color; - - return node; -} - -IntervalMap* mk_intmap(Node* root) { - IntervalMap* map = malloc(sizeof(IntervalMap)); - if (!map) return NULL; - - map->root = root; - return map; -} - -void intmap_insert(IntervalMap* map, Interval* key, void* value) { - if (!map || !key) return; - if (!interval_is_valid(key)) return; - - if (!map->root) { - map->root = mk_leaf_node(NULL, key, value, key->high, BLACK); - return; - } - - Node* curr = map->root; - Node* parent = NULL; - while (curr) { - parent = curr; - if (key->low < curr->interval->low) { - curr = curr->left; - } else { - curr = curr->right; - } - } - - Node* node = mk_leaf_node(parent, key, value, key->high, BLACK); - if (!node) return; - - if (key->low < parent->interval->low) { - parent->left = node; - } else { - parent->right = node; - } - - for (Node* n = parent; n; n = n->parent) { - update_max(n); - } -} - -void intmap_delete(IntervalMap* map) { - return; -} - -void intmap_free(IntervalMap* map) { - if (!map) return; - if (!map->root) { - free(map); - return; - } - - size_t capacity = 32; - size_t top = 0; - Node** stack = malloc(capacity * sizeof(Node*)); - if (!stack) { - free(map); - return; - } - stack[top++] = map->root; - - while (top > 0) { - Node* node = stack[--top]; - if (node->left) { - if (top == capacity) { - capacity *= 2; - Node** new_stack = realloc(stack, capacity * sizeof(Node*)); - if (!new_stack) break; - stack = new_stack; - } - stack[top++] = node->left; - } - if (node->right) { - if (top == capacity) { - capacity *= 2; - Node** new_stack = realloc(stack, capacity * sizeof(Node*)); - if (!new_stack) break; - stack = new_stack; - } - stack[top++] = node->right; - } - free(node); - } - - free(stack); - free(map); -} - -void intmap_left_rotate(IntervalMap* map, Node* x) { - Node* y = x->right; - x->right = y->left; - if (y->left) y->left->parent = x; - - y->parent = x->parent; - if (!x->parent) { - map->root = y; - } else if (x == x->parent->left) { - x->parent->left = y; - } else { - x->parent->right = y; - } - y->left = x; - x->parent = y; -} - -void intmap_right_rotate(IntervalMap* map, Node* y) { - Node* x = y->left; - - y->left = x->right; - if (x->right) x->right->parent = y; - - x->parent = y->parent; - if (!y->parent) { - map->root = x; - } else if (y == y->parent->left) { - y->parent->left = x; - } else { - y->parent->right = x; - } - - x->right = y; - y->parent = x; -} diff --git a/src/elastisched/engine/src-crewrite/interval.h b/src/elastisched/engine/src-crewrite/interval.h deleted file mode 100644 index cfe60b7..0000000 --- a/src/elastisched/engine/src-crewrite/interval.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef INTERVAL_H -#define INTERVAL_H - -#include -#include -#include "constants.h" - -typedef struct Interval { - sec_t low; - sec_t high; -} Interval; - -typedef enum { - RED, - BLACK } -Color; - -typedef struct Node Node; - -struct Node { - Node* left; - Node* right; - Node* parent; - Interval* interval; - void* value; - sec_t max; - Color color; -}; - -typedef struct IntervalMap { - Node* root; -} IntervalMap; - -bool interval_eq(const Interval* U, const Interval* V); -bool interval_overlaps(const Interval* U, const Interval* V); -bool interval_contains(const Interval* U, const Interval* V); -sec_t interval_length(const Interval* interval); -bool interval_is_valid(const Interval* interval); - -Node* mk_leaf_node(Node* parent, Interval* interval, - void* value, sec_t max, Color color); -Node* mk_node(Node* left, Node* right, Node* parent, - Interval* interval, void* value, sec_t max, Color color); - -IntervalMap* mk_intmap(Node* root); -void intmap_insert(IntervalMap* map, Interval* key, void* value); -void intmap_free(IntervalMap* map); -#endif diff --git a/src/elastisched/engine/src-crewrite/job.c b/src/elastisched/engine/src-crewrite/job.c deleted file mode 100644 index ee8a28a..0000000 --- a/src/elastisched/engine/src-crewrite/job.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "job.h" -#include -#include -#include - -bool job_is_rigid(Job *job) { - if (!job) return false; - return job->duration == interval_length(&job->schedulable_time_range); -} - -char* job_to_string(Job* job) { - if (!job) return NULL; - const char* name = job->id.name ? job->id.name : "(null)"; - - int needed = snprintf(NULL, 0, - "Job{id=%s, duration=%" PRIu32 ", schedulable=[%" PRIu32 ",%" PRIu32 "], scheduled=[%" PRIu32 ",%" PRIu32 "]}", - name, - (uint32_t)job->duration, - (uint32_t)job->schedulable_time_range.low, - (uint32_t)job->schedulable_time_range.high, - (uint32_t)job->scheduled_time_range.low, - (uint32_t)job->scheduled_time_range.high); - - if (needed < 0) return NULL; - size_t size = (size_t)needed + 1; - char* buf = malloc(size); - if (!buf) return NULL; - snprintf(buf, size, - "Job{id=%s, duration=%" PRIu32 ", schedulable=[%" PRIu32 ",%" PRIu32 "], scheduled=[%" PRIu32 ",%" PRIu32 "]}", - name, - (uint32_t)job->duration, - (uint32_t)job->schedulable_time_range.low, - (uint32_t)job->schedulable_time_range.high, - (uint32_t)job->scheduled_time_range.low, - (uint32_t)job->scheduled_time_range.high); - return buf; -} diff --git a/src/elastisched/engine/src-crewrite/job.h b/src/elastisched/engine/src-crewrite/job.h deleted file mode 100644 index 4673e6b..0000000 --- a/src/elastisched/engine/src-crewrite/job.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef JOB_H -#define JOB_H - -#include "constants.h" -#include "utils.h" -#include "interval.h" -#include "policy.h" -#include "tag.h" - -typedef struct Job { - sec_t duration; - Interval schedulable_time_range; - Interval scheduled_time_range; - ID id; - Policy policy; - DependencyContainer* dependency_set; - TagContainer* tag_set; -} Job; - -typedef struct JobVec { - Job* data; - size_t size; - size_t capacity; -} JobVec; - -bool job_is_rigid(Job* job); -char* job_to_string(Job* job); - -#endif diff --git a/src/elastisched/engine/src-crewrite/map.c b/src/elastisched/engine/src-crewrite/map.c index 5c181f3..48d396e 100644 --- a/src/elastisched/engine/src-crewrite/map.c +++ b/src/elastisched/engine/src-crewrite/map.c @@ -9,10 +9,8 @@ * Note: size is number of occupied buckets; num_items is total entries. */ typedef struct bucket_vec { - size_t size; - size_t capacity; size_t num_items; - dll** data; + container_t* data; } bucket_vec; struct map { @@ -26,15 +24,25 @@ static bucket_vec* mk_buckets(size_t capacity) { bucket_vec* buckets = malloc(sizeof(bucket_vec)); if (!buckets) return NULL; - buckets->data = calloc(capacity, sizeof(dll*)); - if (!buckets->data) { + container_t* data = malloc(sizeof(container_t)); + if (!data) { free(buckets); return NULL; } - buckets->size = 0; + dll** bucket_data = calloc(capacity, sizeof(dll*)); + if (!bucket_data) { + free(data); + free(buckets); + return NULL; + } + + data->size = 0; + data->capacity = capacity; + data->data = (void**)bucket_data; + buckets->num_items = 0; - buckets->capacity = capacity; + buckets->data = data; return buckets; } @@ -46,12 +54,12 @@ static void free_item_value(void* value, void (*free_fn)(void*)) { static size_t bucket_index(map* dict, void* key) { size_t h = (size_t)dict->hash_fn(key); - return mix64_hash(h) & (dict->buckets->capacity - 1); + return mix64_hash(h) & (dict->buckets->data->capacity - 1); } static bool map_insert_bucket(map* dict, void* key, void* value) { size_t index = bucket_index(dict, key); - dll* deque = dict->buckets->data[index]; + dll* deque = (dll*)dict->buckets->data->data[index]; item* kv = malloc(sizeof(item)); if (!kv) return false; @@ -64,8 +72,8 @@ static bool map_insert_bucket(map* dict, void* key, void* value) { free(kv); return false; } - dict->buckets->data[index] = deque; - dict->buckets->size++; + dict->buckets->data->data[index] = deque; + dict->buckets->data->size++; } dll_append(deque, (void*)kv); @@ -75,7 +83,7 @@ static bool map_insert_bucket(map* dict, void* key, void* value) { static dll_node* map_find_node(map* dict, void* key) { size_t index = bucket_index(dict, key); - dll* deque = dict->buckets->data[index]; + dll* deque = (dll*)dict->buckets->data->data[index]; if (!deque) return NULL; dll_node* curr = dll_head(deque); @@ -97,8 +105,8 @@ static void map_rebuild(map* dict, size_t new_capacity) { bucket_vec* old_buckets = dict->buckets; dict->buckets = new_buckets; - for (size_t i = 0; i < old_buckets->capacity; i++) { - dll* deque = old_buckets->data[i]; + for (size_t i = 0; i < old_buckets->data->capacity; i++) { + dll* deque = (dll*)old_buckets->data->data[i]; if (!deque) continue; while (dll_size(deque)) { @@ -110,6 +118,7 @@ static void map_rebuild(map* dict, size_t new_capacity) { free(deque); } + free(old_buckets->data->data); free(old_buckets->data); free(old_buckets); } @@ -139,7 +148,7 @@ void map_free(map* dict) { if (!dict) return; for (size_t i = 0; i < dict->buckets->capacity; i++) { - dll* deque = dict->buckets->data[i]; + dll* deque = (dll*)dict->buckets->data->data[i]; if (!deque) continue; while (dll_size(deque)) { @@ -150,6 +159,7 @@ void map_free(map* dict) { free(deque); } + free(dict->buckets->data->data); free(dict->buckets->data); free(dict->buckets); free(dict); @@ -166,8 +176,8 @@ void map_insert(map* dict, void* key, void* value) { return; } - if (alpha_meets_threshold(dict->buckets->num_items, dict->buckets->capacity)) { - map_rebuild(dict, dict->buckets->capacity * 2); + if (alpha_meets_threshold(dict->buckets->num_items, dict->buckets->data->capacity)) { + map_rebuild(dict, dict->buckets->data->capacity * 2); } map_insert_bucket(dict, key, value); @@ -187,7 +197,7 @@ void* map_get(map* dict, void* key) { void map_delete(map* dict, void* key) { if (!dict) return; size_t index = bucket_index(dict, key); - dll* deque = dict->buckets->data[index]; + dll* deque = (dll*)dict->buckets->data->data[index]; if (!deque) return; dll_node* curr = dll_head(deque); @@ -198,8 +208,8 @@ void map_delete(map* dict, void* key) { dll_remove(deque, curr); dict->buckets->num_items--; if (dll_size(deque) == 0) { - dict->buckets->data[index] = NULL; - dict->buckets->size--; + dict->buckets->data->data[index] = NULL; + dict->buckets->data->size--; free(deque); } return; @@ -212,23 +222,26 @@ size_t map_size(map* dict) { return dict ? dict->buckets->num_items : 0; } -vec_items* map_items(map* dict) { +vec_items_t* map_items(map* dict) { if (!dict) return NULL; - vec_items* items = malloc(sizeof(vec_items)); + vec_items_t* items = malloc(sizeof(vec_items_t)); if (!items) return NULL; items->size = 0; items->capacity = 0; items->data = NULL; - for (size_t i = 0; i < dict->buckets->capacity; i++) { - dll* deque = dict->buckets->data[i]; + for (size_t i = 0; i < dict->buckets->data->capacity; i++) { + dll* deque = (dll*)dict->buckets->data->data[i]; if (!deque) continue; dll_node* curr = dll_head(deque); while (curr) { item* curr_item = (item*)dll_node_get_value(curr); - vec_pushback(items, curr_item); + if (!container_append(items, curr_item)) { + container_free(items, NULL); + return NULL; + } curr = dll_next(curr); } } diff --git a/src/elastisched/engine/src-crewrite/map.h b/src/elastisched/engine/src-crewrite/map.h index 67ef20b..dccbd9b 100644 --- a/src/elastisched/engine/src-crewrite/map.h +++ b/src/elastisched/engine/src-crewrite/map.h @@ -5,9 +5,8 @@ #include #include -#include "constants.h" #include "dll.h" -#include "vec.h" +#include "container.h" typedef struct map map; typedef struct map set; @@ -17,11 +16,9 @@ typedef struct item { void* value; } item; -typedef struct vec_items { - size_t size; - size_t capacity; - item** data; -} vec_items; +#define INITIAL_MAP_CAPACITY 32 + +typedef container_t vec_items_t; map* mk_map(uint64_t (*hash_fn)(const void* e), int (*cmp_fn)(const void* u, const void* v), @@ -34,6 +31,6 @@ bool map_in(map* dict, void* key); void* map_get(map* dict, void* key); void map_delete(map* dict, void* key); size_t map_size(map* dict); -vec_items* map_items(map* dict); +vec_items_t* map_items(map* dict); #endif diff --git a/src/elastisched/engine/src-crewrite/policy.c b/src/elastisched/engine/src-crewrite/policy.c deleted file mode 100644 index afdfdb4..0000000 --- a/src/elastisched/engine/src-crewrite/policy.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "policy.h" - -bool policy_is_splittable(Policy* policy) { - return policy && (policy->scheduling_policies & 1u) != 0; -} - -bool policy_is_overlappable(Policy* policy) { - return policy && ((policy->scheduling_policies & 2u) >> 1) != 0; -} - -bool policy_is_invisible(Policy* policy) { - return policy && ((policy->scheduling_policies & 4u) >> 2) != 0; -} diff --git a/src/elastisched/engine/src-crewrite/policy.h b/src/elastisched/engine/src-crewrite/policy.h deleted file mode 100644 index 8618490..0000000 --- a/src/elastisched/engine/src-crewrite/policy.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef POLICY_H -#define POLICY_H - -#include -#include -#include -#include "constants.h" - -/** - * @brief Scheduling policy struct, which - * defines the configurations for how a job - * may be scheduled. Each job may have its own policy. - */ -typedef struct Policy { - uint8_t max_splits; /// how many times a splittable job may be split - time_t min_split_duration; /// minimum duration that a splittable job can be split into - uint8_t scheduling_policies; /// overloaded bitfield: bit 0 = is_splittable, bit 1 = is_overlappable, bit 2 = is_invisible -} Policy; - -bool policy_is_splittable(Policy* policy); -bool policy_is_overlappable(Policy* policy); -bool policy_is_invisible(Policy* policy); - -#endif diff --git a/src/elastisched/engine/src-crewrite/schedule.c b/src/elastisched/engine/src-crewrite/schedule.c deleted file mode 100644 index e019232..0000000 --- a/src/elastisched/engine/src-crewrite/schedule.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "schedule.h" - -void schedule_add_job(Schedule* schedule, const Job* job) { - if (!schedule || !schedule->scheduled_jobs || !job) return; - vec_pushback(schedule->scheduled_jobs, *job); - return; -} - -void schedule_clear(Schedule* schedule) { - if (!schedule || !schedule->scheduled_jobs) return; - vec_clear(schedule->scheduled_jobs); -} diff --git a/src/elastisched/engine/src-crewrite/schedule.h b/src/elastisched/engine/src-crewrite/schedule.h deleted file mode 100644 index 925f5ea..0000000 --- a/src/elastisched/engine/src-crewrite/schedule.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef ELASTISCHED_SCHEDULE_H -#define ELASTISCHED_SCHEDULE_H - -#include "job.h" -#include "utils.h" -#include "vec.h" - -typedef struct Schedule { - JobVec* scheduled_jobs; -} Schedule; - -void schedule_add_job(Schedule* schedule, const Job* job); -void schedule_clear(Schedule* schedule); - -#endif diff --git a/src/elastisched/engine/src-crewrite/simulated_annealer.h b/src/elastisched/engine/src-crewrite/simulated_annealer.h deleted file mode 100644 index 9254323..0000000 --- a/src/elastisched/engine/src-crewrite/simulated_annealer.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef ELASTISCHED_SIMULATED_ANNEALER_H -#define ELASTISCHED_SIMULATED_ANNEALER_H - -#endif diff --git a/src/elastisched/engine/src-crewrite/tag.c b/src/elastisched/engine/src-crewrite/tag.c deleted file mode 100644 index b0b5ad6..0000000 --- a/src/elastisched/engine/src-crewrite/tag.c +++ /dev/null @@ -1,187 +0,0 @@ -#include "tag.h" -#include "hash.h" - -bool tag_eq(const Tag* U, const Tag* V) { - return strcmp(U->name, V->name) == 0; -} - -int tag_cmp(const Tag* U, const Tag* V) { - int c = strcmp(U->name, V->name); - - return (c > 0) - (c < 0); -} - -uint64_t tag_hash(const Tag* U) { - return string_hash(U ? U->name : NULL); -} - -int tag_cmp_void(const void* U, const void* V) { - return tag_cmp((const Tag*)U, (const Tag*)V); -} - -uint64_t tag_hash_void(const void* U) { - return tag_hash((const Tag*)U); -} - -TagContainer* mk_tag_container(size_t capacity) { - TagContainer* tag_set = malloc(sizeof(TagContainer)); - if (!tag_set) return NULL; - - Tag* data = NULL; - if (capacity != 0) { - data = malloc(capacity * sizeof(Tag)); - if (!data) { - free(tag_set); - return NULL; - } - } - - tag_set->size = 0; - tag_set->capacity = capacity; - tag_set->data = data; - - return tag_set; -} - -void tag_container_free(TagContainer* container) { - if (!container) return; - free((void*)container->data); - free(container); -} - -bool tag_container_resize(TagContainer* container) { - if (!container) return false; - size_t new_capacity = (container->capacity == 0) ? - INITIAL_TAGCONTAINER_CAPACITY : container->capacity * 2; - Tag* new_data = realloc(container->data, new_capacity * sizeof(Tag)); - if (!new_data) return false; - container->data = new_data; - container->capacity = new_capacity; - return true; -} - -size_t ts_insert_index(TagContainer* set, Tag tag) { - size_t l = 0; - size_t r = set->size; - - while (l < r) { - size_t mid = l + (r - l) / 2; - int _cmp = tag_cmp(&tag, &(set->data)[mid]); - if (_cmp == 0) return mid; - if (_cmp < 0) r = mid; - else l = mid + 1; - } - return l; -} - -void ts_insert(TagContainer* set, Tag tag, size_t insert_ind) { - if (insert_ind < set->size) { - memmove(&set->data[insert_ind + 1], - &set->data[insert_ind], - (set->size - insert_ind) * sizeof(Tag)); - } - set->data[insert_ind] = tag; - set->size++; -} - -bool ts_add(TagContainer* set, Tag tag) { - size_t insert_index = ts_insert_index(set, tag); - if (insert_index < set->size && - tag_eq(&tag, &set->data[insert_index])) return true; - - if (set->size == set->capacity && - !tag_container_resize(set)) - return false; - - ts_insert(set, tag, insert_index); - return true; -} - -bool ts_in(TagContainer* set, Tag tag) { - size_t insert_index = ts_insert_index(set, tag); - return (insert_index < set->size && - tag_eq(&tag, &set->data[insert_index])); -} - -TagContainer* ts_union(TagContainer* U, TagContainer* V) { - if (U->size == 0) { - TagContainer* set_union = mk_tag_container(V->size); - if (!set_union) return NULL; - memcpy(set_union->data, V->data, V->size * sizeof(Tag)); - set_union->size = V->size; - return set_union; - } - if (V->size == 0) { - TagContainer* set_union= mk_tag_container(U->size); - if (!set_union) return NULL; - memcpy(set_union->data, U->data, U->size * sizeof(Tag)); - set_union->size = U->size; - return set_union; - } - - size_t u_ptr = 0; - size_t v_ptr = 0; - size_t insert_ind = 0; - TagContainer* set_union = mk_tag_container(U->size + V->size); - if (!set_union) return NULL; - - while (u_ptr < U->size && v_ptr < V->size) { - Tag* curr_u_tag = &U->data[u_ptr]; - Tag* curr_v_tag = &V->data[v_ptr]; - switch (tag_cmp(curr_u_tag, curr_v_tag)) { - case -1: - set_union->data[insert_ind++] = *curr_u_tag; - u_ptr++; - break; - case 0: - set_union->data[insert_ind++] = *curr_u_tag; - u_ptr++; v_ptr++; - break; - case 1: - set_union->data[insert_ind++] = *curr_v_tag; - v_ptr++; - break; - } - } - - while (u_ptr < U->size) set_union->data[insert_ind++] = U->data[u_ptr++]; - while (v_ptr < V->size) set_union->data[insert_ind++] = V->data[v_ptr++]; - - set_union->size = insert_ind; - - return set_union; -}; - -TagContainer* ts_intersection(TagContainer* U, TagContainer* V) { - TagContainer* set_intersection = mk_tag_container(min(U->size, V->size)); - if (!set_intersection) return NULL; - - TagContainer* anchor = U->size > V->size ? V : U; - TagContainer* ot = U->size > V->size ? U : V; - size_t insert_ind = 0; - for (size_t i = 0; i < anchor->size; i++) { - if (ts_in(ot, anchor->data[i])) { - set_intersection->data[insert_ind++] = anchor->data[i]; - } - } - - set_intersection->size = insert_ind; - return set_intersection; -} - -int _tag_cmp(const void* u, const void* v) { - return tag_cmp((Tag*)u, (Tag*)v); -} - -bool ts_is_valid(TagContainer* set) { - return set->size <= set->capacity && - is_sorted((void*)set->data, set->size, sizeof(Tag), &_tag_cmp); -} - -bool tv_pushback(TagContainer* vec, Tag tag) { - if (vec->size == vec->capacity && - !tag_container_resize(vec)) return false; - - vec->data[vec->size++] = tag; - return true; -} diff --git a/src/elastisched/engine/src-crewrite/tag.h b/src/elastisched/engine/src-crewrite/tag.h deleted file mode 100644 index 4cf5903..0000000 --- a/src/elastisched/engine/src-crewrite/tag.h +++ /dev/null @@ -1,161 +0,0 @@ -#ifndef TAG_H -#define TAG_H - -#include -#include -#include -#include - -#include "constants.h" -#include "utils.h" - - -/** - * Tag.h - * - * Definitions for working with the Tag and TagContainer structs. - * - * TagContainer is a simple container struct which allows for - * functions to operate on it and implement Sets and Vectors. - * - * TagSet in practice does not get very large nor updated - * often so we opt for a simple vector-based sorted set. - * - * Two tags are equivalent if they share the same name, - * not necessarily the same description. - * - * This allows for O(logn) search time and membership - * checking, O(n) set difference and O(n) time to add - * to the set. - */ - - -typedef struct Tag { - char* name; - char* description; -} Tag; - -typedef struct TagContainer { - Tag* data; - size_t size; - size_t capacity; -} TagContainer; - -typedef Tag ID; /// an dependency ID is a Tag with an empty description -typedef TagContainer DependencyContainer; - -/** - * @brief returns True if the names of ```U``` and ```V``` are the same - */ -bool tag_eq(const Tag* U, const Tag* V); - -/** - * @brief comparator for Tag type - */ -int tag_cmp(const Tag* U, const Tag* V); - -uint64_t tag_hash(const Tag* U); -int tag_cmp_void(const void* U, const void* V); -uint64_t tag_hash_void(const void* U); - -/** - * @brief constructs a TagContainer with capacity ```capacity``` - * - * @param capacity number of tags that the TagContainer can hold - * @return TagContainer* - */ -TagContainer* mk_tag_container(size_t capacity); - -/** - * @brief frees a TagContainer and its data - * - * @param container - */ -void tag_container_free(TagContainer* container); - -/** - * @brief resizes a container by doubling capacity and copying memory - * - * @param container - * @return true if resize successful, false otherwise - */ -bool tag_container_resize(TagContainer* container); - -/** - * @brief Find the insert index - * - * @param set the set to look in. - * Internally, ```set```->data should be sorted. - * @param tag the tag to look for - * @return first index, i, such that tag <= set[j], i<=j v ? v : u; -} - -size_t max(size_t u, size_t v) { - return u > v ? u: v; -} - -bool is_sorted(const void* base, size_t count, size_t elem_size, - int (*cmp)(const void*, const void*)) { - if (count < 2) return true; - - const char* bytes = (const char*)base; - for (size_t i = 1; i < count; i++) { - const void* prev = bytes + (i - 1) * elem_size; - const void* curr = bytes + i * elem_size; - if (cmp(prev, curr) > 0) return false; - } - return true; -} diff --git a/src/elastisched/engine/src-crewrite/utils.h b/src/elastisched/engine/src-crewrite/utils.h index d4ebe92..0edfe2d 100644 --- a/src/elastisched/engine/src-crewrite/utils.h +++ b/src/elastisched/engine/src-crewrite/utils.h @@ -3,11 +3,26 @@ #include #include -#include "constants.h" -size_t min(size_t u, size_t v); -size_t max(size_t u, size_t v); +size_t min(size_t u, size_t v) { + return u > v ? v : u; +} + +size_t max(size_t u, size_t v) { + return u > v ? u: v; +} + bool is_sorted(const void* base, size_t count, size_t elem_size, - int (*cmp)(const void*, const void*)); + int (*cmp)(const void*, const void*)) { + if (count < 2) return true; + + const char* bytes = (const char*)base; + for (size_t i = 1; i < count; i++) { + const void* prev = bytes + (i - 1) * elem_size; + const void* curr = bytes + i * elem_size; + if (cmp(prev, curr) > 0) return false; + } + return true; +} #endif diff --git a/src/elastisched/engine/src-crewrite/vec.h b/src/elastisched/engine/src-crewrite/vec.h deleted file mode 100644 index b7a4b76..0000000 --- a/src/elastisched/engine/src-crewrite/vec.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ELASTISCHED_VEC_H -#define ELASTISCHED_VEC_H - -#include -#include "constants.h" - -/** - * vec.h - * - * Macros for working with arbitrary C vectors - */ - -#define vec_realloc(vec) \ - do { \ - vec->data = realloc(vec->data, vec->capacity * sizeof(*vec->data)); \ - } while (0) - -#define vec_pushback(vec, e) \ - do { \ - if (vec->size >= vec->capacity) { \ - if (vec->size == 0) vec->capacity = INITIAL_CONTAINER_CAPACITY; \ - else vec->capacity *= 2; \ - vec->data = realloc(vec->data, vec->capacity * sizeof(*vec->data)); \ - } \ - vec->data[vec->size++] = e; \ - } while (0) - -#define vec_clear(vec) \ - do { \ - vec->size = 0; \ - } while (0) - -#endif From 87bbf53752c30f6fb01c6fbf892b128219ad4a3c Mon Sep 17 00:00:00 2001 From: Chris Su Date: Fri, 16 Jan 2026 15:24:16 -0500 Subject: [PATCH 08/35] finalized data structures --- src/elastisched/engine/src-crewrite/engine.c | 175 +++++++++++-------- src/elastisched/engine/src-crewrite/engine.h | 50 +++--- src/elastisched/engine/src-crewrite/graph.c | 0 src/elastisched/engine/src-crewrite/graph.h | 0 src/elastisched/engine/src-crewrite/map.c | 2 +- 5 files changed, 125 insertions(+), 102 deletions(-) delete mode 100644 src/elastisched/engine/src-crewrite/graph.c delete mode 100644 src/elastisched/engine/src-crewrite/graph.h diff --git a/src/elastisched/engine/src-crewrite/engine.c b/src/elastisched/engine/src-crewrite/engine.c index c410719..918bd97 100644 --- a/src/elastisched/engine/src-crewrite/engine.c +++ b/src/elastisched/engine/src-crewrite/engine.c @@ -1,5 +1,35 @@ #include "engine.h" +interval_t* mk_interval(sec_t low, sec_t high) { + interval_t* interval = malloc(sizeof(interval_t)); + if (!interval) return NULL; + + interval->low = low; interval->high = high; + return interval; +} + +void interval_free(interval_t* interval) { free(interval); } + +bool interval_eq(const interval_t* U, const interval_t* V) { + return (U->low == V->low) && (U->high == V->high); +} + +bool interval_overlaps(const interval_t* U, const interval_t* V) { + return !(U->high < V->low || V->high < U->low); +} + +bool interval_contains(const interval_t* U, const interval_t* V) { + return (U->low <= V->low && V->high <= U->high); +} + +sec_t interval_length(const interval_t* interval) { + return interval->high - interval->low; +} + +bool interval_is_valid(const interval_t* interval) { + return interval->high >= interval->low; +} + /** * @brief Scheduling policy struct, which * defines the configurations for how a job @@ -49,36 +79,35 @@ bool policy_is_invisible(policy_t* policy) { bool job_is_rigid(job_t *job) { if (!job) return false; - return job->duration == interval_length(job->schedulable_tr); + return job->duration == interval_length(&job->schedulable_tr); } - typedef enum { RED, BLACK } Color; -typedef struct Node Node; +typedef struct node node_t; -struct Node { - Node* left; - Node* right; - Node* parent; - Interval* interval; +struct node { + node_t* left; + node_t* right; + node_t* parent; + interval_t* interval; void* value; sec_t max; Color color; }; -typedef struct IntervalMap { - Node* root; -} IntervalMap; +typedef struct interval_tMap { + node_t* root; +} interval_tMap; -static sec_t node_max(Node* node) { +static sec_t node_max(node_t* node) { return node ? node->max : 0; } -static void update_max(Node* node) { +static void update_max(node_t* node) { if (!node || !node->interval) return; sec_t left_max = node_max(node->left); sec_t right_max = node_max(node->right); @@ -90,10 +119,10 @@ static void update_max(Node* node) { node->max = max; } -Node* mk_leaf_node(Node* parent, Interval* interval, +node_t* mk_leaf_node(node_t* parent, interval_t* interval, void* value, sec_t max, Color color ) { - Node* node = malloc(sizeof(Node)); + node_t* node = malloc(sizeof(node_t)); if (!node) return NULL; node->parent = parent; @@ -108,10 +137,10 @@ Node* mk_leaf_node(Node* parent, Interval* interval, return node; } -Node* mk_node(Node* left, Node* right, Node* parent, - Interval* interval, void* value, sec_t max, Color color +node_t* mk_node(node_t* left, node_t* right, node_t* parent, + interval_t* interval, void* value, sec_t max, Color color ) { - Node* node = malloc(sizeof(Node)); + node_t* node = malloc(sizeof(node_t)); if (!node) return NULL; node->left = left; @@ -125,15 +154,15 @@ Node* mk_node(Node* left, Node* right, Node* parent, return node; } -IntervalMap* mk_intmap(Node* root) { - IntervalMap* map = malloc(sizeof(IntervalMap)); +interval_tMap* mk_intmap(node_t* root) { + interval_tMap* map = malloc(sizeof(interval_tMap)); if (!map) return NULL; map->root = root; return map; } -void intmap_insert(IntervalMap* map, Interval* key, void* value) { +void intmap_insert(interval_tMap* map, interval_t* key, void* value) { if (!map || !key) return; if (!interval_is_valid(key)) return; @@ -142,8 +171,8 @@ void intmap_insert(IntervalMap* map, Interval* key, void* value) { return; } - Node* curr = map->root; - Node* parent = NULL; + node_t* curr = map->root; + node_t* parent = NULL; while (curr) { parent = curr; if (key->low < curr->interval->low) { @@ -153,7 +182,7 @@ void intmap_insert(IntervalMap* map, Interval* key, void* value) { } } - Node* node = mk_leaf_node(parent, key, value, key->high, BLACK); + node_t* node = mk_leaf_node(parent, key, value, key->high, BLACK); if (!node) return; if (key->low < parent->interval->low) { @@ -162,16 +191,16 @@ void intmap_insert(IntervalMap* map, Interval* key, void* value) { parent->right = node; } - for (Node* n = parent; n; n = n->parent) { + for (node_t* n = parent; n; n = n->parent) { update_max(n); } } -void intmap_delete(IntervalMap* map) { +void intmap_delete(interval_tMap* map) { return; } -void intmap_free(IntervalMap* map) { +void intmap_free(interval_tMap* map) { if (!map) return; if (!map->root) { free(map); @@ -180,7 +209,7 @@ void intmap_free(IntervalMap* map) { size_t capacity = 32; size_t top = 0; - Node** stack = malloc(capacity * sizeof(Node*)); + node_t** stack = malloc(capacity * sizeof(node_t*)); if (!stack) { free(map); return; @@ -188,11 +217,11 @@ void intmap_free(IntervalMap* map) { stack[top++] = map->root; while (top > 0) { - Node* node = stack[--top]; + node_t* node = stack[--top]; if (node->left) { if (top == capacity) { capacity *= 2; - Node** new_stack = realloc(stack, capacity * sizeof(Node*)); + node_t** new_stack = realloc(stack, capacity * sizeof(node_t*)); if (!new_stack) break; stack = new_stack; } @@ -201,7 +230,7 @@ void intmap_free(IntervalMap* map) { if (node->right) { if (top == capacity) { capacity *= 2; - Node** new_stack = realloc(stack, capacity * sizeof(Node*)); + node_t** new_stack = realloc(stack, capacity * sizeof(node_t*)); if (!new_stack) break; stack = new_stack; } @@ -214,8 +243,8 @@ void intmap_free(IntervalMap* map) { free(map); } -void intmap_left_rotate(IntervalMap* map, Node* x) { - Node* y = x->right; +void intmap_left_rotate(interval_tMap* map, node_t* x) { + node_t* y = x->right; x->right = y->left; if (y->left) y->left->parent = x; @@ -231,8 +260,8 @@ void intmap_left_rotate(IntervalMap* map, Node* x) { x->parent = y; } -void intmap_right_rotate(IntervalMap* map, Node* y) { - Node* x = y->left; +void intmap_right_rotate(interval_tMap* map, node_t* y) { + node_t* x = y->left; y->left = x->right; if (x->right) x->right->parent = y; @@ -251,13 +280,8 @@ void intmap_right_rotate(IntervalMap* map, Node* y) { } /** - * - * - * Definitions for working with the Tag and TagContainer structs. - * - * TagContainer is a simple container struct which allows for - * functions to operate on it and implement Sets and Vectors. - * + * @brief + * * TagSet in practice does not get very large nor updated * often so we opt for a simple vector-based sorted set. * @@ -296,13 +320,13 @@ uint64_t tag_hash_void(const void* U) { return tag_hash((const tag_t*)U); } -size_t ts_insert_index(tag_set* set, tag_t tag) { +size_t ts_insert_index(tag_set_t* set, tag_t tag) { size_t l = 0; size_t r = set->size; while (l < r) { size_t mid = l + (r - l) / 2; - int _cmp = tag_cmp(&tag, &(set->data)[mid]); + int _cmp = tag_cmp(&tag, (tag_t*)(set->data)[mid]); if (_cmp == 0) return mid; if (_cmp < 0) r = mid; else l = mid + 1; @@ -310,45 +334,54 @@ size_t ts_insert_index(tag_set* set, tag_t tag) { return l; } -void ts_insert(tag_set* set, tag_t tag, size_t insert_ind) { +void ts_insert(tag_set_t* set, tag_t tag, size_t insert_ind) { + void** e_ref = malloc(sizeof(void*)); + if (!e_ref) return; + *e_ref = (void*)&tag; + if (insert_ind < set->size) { memmove(&set->data[insert_ind + 1], &set->data[insert_ind], (set->size - insert_ind) * sizeof(tag_t)); } - set->data[insert_ind] = tag; + + set->data[insert_ind] = e_ref; set->size++; } -bool ts_add(tag_tContainer* set, tag_t tag) { +bool ts_add(tag_set_t* set, tag_t tag) { + void** e_ref = malloc(sizeof(void*)); + if (!e_ref) false; + *e_ref = (void*)&tag; + size_t insert_index = ts_insert_index(set, tag); if (insert_index < set->size && - tag_eq(&tag, &set->data[insert_index])) return true; + tag_eq(&tag, (tag_t*)set->data[insert_index])) return true; if (set->size == set->capacity && - !tag_container_resize(set)) + !container_resize(set, NULL)) return false; ts_insert(set, tag, insert_index); return true; } -bool ts_in(tag_tContainer* set, tag_t tag) { +bool ts_in(tag_set_t* set, tag_t tag) { size_t insert_index = ts_insert_index(set, tag); return (insert_index < set->size && - tag_eq(&tag, &set->data[insert_index])); + tag_eq(&tag, (tag_t*)set->data[insert_index])); } -tag_tContainer* ts_union(tag_tContainer* U, tag_tContainer* V) { +tag_set_t* ts_union(tag_set_t* U, tag_set_t* V) { if (U->size == 0) { - tag_tContainer* set_union = mk_tag_container(V->size); + tag_set_t* set_union = mk_container(V->size); if (!set_union) return NULL; memcpy(set_union->data, V->data, V->size * sizeof(tag_t)); set_union->size = V->size; return set_union; } if (V->size == 0) { - tag_tContainer* set_union= mk_tag_container(U->size); + tag_set_t* set_union= mk_container(U->size); if (!set_union) return NULL; memcpy(set_union->data, U->data, U->size * sizeof(tag_t)); set_union->size = U->size; @@ -358,23 +391,23 @@ tag_tContainer* ts_union(tag_tContainer* U, tag_tContainer* V) { size_t u_ptr = 0; size_t v_ptr = 0; size_t insert_ind = 0; - tag_tContainer* set_union = mk_tag_container(U->size + V->size); + tag_set_t* set_union = mk_container(U->size + V->size); if (!set_union) return NULL; while (u_ptr < U->size && v_ptr < V->size) { - tag_t* curr_u_tag = &U->data[u_ptr]; - tag_t* curr_v_tag = &V->data[v_ptr]; + tag_t* curr_u_tag = (tag_t*)U->data[u_ptr]; + tag_t* curr_v_tag = (tag_t*)V->data[v_ptr]; switch (tag_cmp(curr_u_tag, curr_v_tag)) { case -1: - set_union->data[insert_ind++] = *curr_u_tag; + set_union->data[insert_ind++] = (void*)curr_u_tag; u_ptr++; break; case 0: - set_union->data[insert_ind++] = *curr_u_tag; + set_union->data[insert_ind++] = (void*)curr_u_tag; u_ptr++; v_ptr++; break; case 1: - set_union->data[insert_ind++] = *curr_v_tag; + set_union->data[insert_ind++] = (void*)curr_v_tag; v_ptr++; break; } @@ -388,15 +421,15 @@ tag_tContainer* ts_union(tag_tContainer* U, tag_tContainer* V) { return set_union; }; -tag_tContainer* ts_intersection(tag_tContainer* U, tag_tContainer* V) { - tag_tContainer* set_intersection = mk_tag_container(min(U->size, V->size)); +tag_set_t* ts_intersection(tag_set_t* U, tag_set_t* V) { + tag_set_t* set_intersection = mk_container(min(U->size, V->size)); if (!set_intersection) return NULL; - tag_tContainer* anchor = U->size > V->size ? V : U; - tag_tContainer* ot = U->size > V->size ? U : V; + tag_set_t* anchor = U->size > V->size ? V : U; + tag_set_t* ot = U->size > V->size ? U : V; size_t insert_ind = 0; for (size_t i = 0; i < anchor->size; i++) { - if (ts_in(ot, anchor->data[i])) { + if (ts_in(ot, *(tag_t*)anchor->data[i])) { set_intersection->data[insert_ind++] = anchor->data[i]; } } @@ -409,15 +442,7 @@ int _tag_cmp(const void* u, const void* v) { return tag_cmp((tag_t*)u, (tag_t*)v); } -bool ts_is_valid(tag_tContainer* set) { +bool ts_is_valid(tag_set_t* set) { return set->size <= set->capacity && is_sorted((void*)set->data, set->size, sizeof(tag_t), &_tag_cmp); -} - -bool tv_pushback(tag_tContainer* vec, tag_t tag) { - if (vec->size == vec->capacity && - !tag_container_resize(vec)) return false; - - vec->data[vec->size++] = tag; - return true; -} +} \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/engine.h b/src/elastisched/engine/src-crewrite/engine.h index 49fc51e..0292eb7 100644 --- a/src/elastisched/engine/src-crewrite/engine.h +++ b/src/elastisched/engine/src-crewrite/engine.h @@ -11,8 +11,8 @@ #include "container.h" #include "map.h" #include "dll.h" -#include "graph.h" #include "hash.h" +#include "utils.h" /** @@ -98,18 +98,16 @@ */ typedef uint32_t sec_t; +typedef double cost_t; -#ifndef INTERVAL_T -#define INTERVAL_T typedef struct interval { sec_t low; sec_t high; } interval_t; -#endif typedef struct tag tag_t; -typedef container_t tag_set; typedef container_t vec; +typedef vec tag_set_t; typedef tag_t jid_t; /// an dependency ID is a Tag with an empty description typedef struct policy policy_t; @@ -118,7 +116,6 @@ typedef struct schedule schedule_t; typedef struct jobs jobs_t; typedef struct pair pair_t; - typedef struct job { sec_t duration; interval_t schedulable_tr; @@ -158,12 +155,21 @@ static const sec_t WEEK = ((sec_t)7 * DAY); */ static const double ILLEGAL_SCHEDULE_COST = 1e12; - static const double EPSILON = 1e-8; static const unsigned int DEFAULT_RNG_SEED = 1337; +//========================================== +//========================================== +//========================================== + +interval_t* mk_interval(sec_t low, sec_t high); +void interval_free(interval_t* interval); +bool interval_eq(const interval_t* U, const interval_t* V); +bool interval_overlaps(const interval_t* U, const interval_t* V); +bool interval_contains(const interval_t* U, const interval_t* V); +sec_t interval_length(const interval_t* interval); +bool interval_is_valid(const interval_t* interval); -// POLICY UTILS /** * @brief creates a scheduling policy configuration struct * @@ -182,11 +188,6 @@ bool policy_is_invisible(policy_t *policy); uint8_t policy_max_splits(policy_t *policy); sec_t min_split_duration(policy_t *policy); -schedule_t *mk_schedule(); -void schedule_free(schedule_t *schedule); -void schedule_add_job(schedule_t *schedule, const job_t *job); -void schedule_clear(schedule_t *schedule); - /** * TAGS * @@ -261,20 +262,17 @@ tag_set_t *ts_intersection(tag_set_t *U, tag_set_t *V); */ bool ts_is_valid(tag_set_t *set); -/** - * @brief pushback tag to tagvec - * - * @param vec - * @param tag - * @return: true if no memory failures, false otherwise - */ -bool tv_pushback(tag_vec_t *vec, tag_t tag); +schedule_t *mk_schedule(); +void schedule_free(schedule_t *schedule); +void schedule_add_job(schedule_t *schedule, const job_t *job); +void schedule_clear(schedule_t *schedule); + +cost_t schedule_cost_illegal(schedule_t *schedule, sec_t granularity); +cost_t schedule_cost_overlap(schedule_t *schedule, sec_t granularity); +cost_t schedule_cost_split(schedule_t *schedule, sec_t granularity); -schedule_t *generate_random_schedule_neighbor(schedule_t *schedule, - sec_t granularity, - unsigned int seed); -pair_t *_schedule_jobs(jobs_t jobs, sec_t granularity, double initial_temp, - double final_temp, uint64_t num_iters); +schedule_t *generate_random_schedule_neighbor(schedule_t *schedule, sec_t granularity, unsigned int seed); +pair_t *_schedule_jobs(jobs_t jobs, sec_t granularity, double initial_temp, double final_temp, uint64_t num_iters); pair_t *schedule_jobs(jobs_t jobs, sec_t granularity); #endif \ No newline at end of file diff --git a/src/elastisched/engine/src-crewrite/graph.c b/src/elastisched/engine/src-crewrite/graph.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/elastisched/engine/src-crewrite/graph.h b/src/elastisched/engine/src-crewrite/graph.h deleted file mode 100644 index e69de29..0000000 diff --git a/src/elastisched/engine/src-crewrite/map.c b/src/elastisched/engine/src-crewrite/map.c index 48d396e..a372fd5 100644 --- a/src/elastisched/engine/src-crewrite/map.c +++ b/src/elastisched/engine/src-crewrite/map.c @@ -147,7 +147,7 @@ map* mk_map(uint64_t (*hash_fn)(const void* e), void map_free(map* dict) { if (!dict) return; - for (size_t i = 0; i < dict->buckets->capacity; i++) { + for (size_t i = 0; i < dict->buckets->data->capacity; i++) { dll* deque = (dll*)dict->buckets->data->data[i]; if (!deque) continue; From 378337ad7b9f952da7fb9d05a8b5d98002001d54 Mon Sep 17 00:00:00 2001 From: Chris Su Date: Wed, 7 Jan 2026 01:02:25 -0500 Subject: [PATCH 09/35] DST agnostic --- frontend/css/styles.css | 162 ++++++++++ frontend/index.html | 43 ++- frontend/js/app.js | 33 +- frontend/js/dom.js | 2 + frontend/js/forms.js | 364 +++++++++++++++++++++-- frontend/js/render.js | 288 ++++++++++-------- frontend/js/utils.js | 6 +- integrations/TODO.txt | 3 + src/elastisched/api/recurrence_router.py | 36 ++- src/elastisched/api/schedule_router.py | 8 + 10 files changed, 784 insertions(+), 161 deletions(-) create mode 100644 integrations/TODO.txt diff --git a/frontend/css/styles.css b/frontend/css/styles.css index 758cb67..115c375 100644 --- a/frontend/css/styles.css +++ b/frontend/css/styles.css @@ -8,6 +8,11 @@ --ocean: #3a6e7a; --rose: #c07d6b; --shadow: 0 14px 30px rgba(27, 26, 22, 0.18); + --palette-sand: #f1d7aa; + --palette-sage: #c8d6c3; + --palette-mist: #c1d4da; + --palette-clay: #e0c1b3; + --palette-moss: #d6ccb4; } * { @@ -338,11 +343,114 @@ body { gap: 8px; } +.tag-field.hidden { + display: none; +} + .tag-label { font-size: 13px; color: var(--muted); } +.recurrence-extras { + grid-column: 1 / -1; + display: grid; + gap: 12px; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); +} + +.color-field { + grid-column: 1 / -1; + display: grid; + gap: 10px; +} + +.color-label { + font-size: 13px; + color: var(--muted); +} + +.color-swatches { + display: inline-grid; + grid-auto-flow: column; + grid-auto-columns: 28px; + gap: 4px; + align-items: center; +} + +.color-swatch { + display: inline-grid; + place-items: center; + width: 28px; + height: 28px; + padding: 0; + position: relative; + border-radius: 4px; + border: 1px solid rgba(27, 26, 22, 0.2); + background: transparent; + cursor: pointer; + overflow: hidden; +} + +.color-swatch input { + position: absolute; + opacity: 0; + pointer-events: none; +} + +.color-swatch .swatch { + position: absolute; + inset: 0; + border-radius: 3px; + border: none; + box-shadow: none; +} + +.color-swatch .swatch::after { + content: ""; + position: absolute; + right: 4px; + bottom: 4px; + width: 6px; + height: 6px; + border-radius: 2px; + background: rgba(27, 26, 22, 0.7); + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.7); + opacity: 0; +} + +.color-swatch input:checked + .swatch { + box-shadow: 0 0 0 2px rgba(27, 26, 22, 0.6); +} + +.color-swatch input:checked + .swatch::after { + opacity: 1; +} + +.color-swatch .swatch-default { + background: linear-gradient(135deg, #f7d89b, #cbd8cc, #c7d7db); +} + +.color-swatch .swatch-sand { + background: var(--palette-sand); +} + +.color-swatch .swatch-sage { + background: var(--palette-sage); +} + +.color-swatch .swatch-mist { + background: var(--palette-mist); +} + +.color-swatch .swatch-clay { + background: var(--palette-clay); +} + +.color-swatch .swatch-moss { + background: var(--palette-moss); +} + .tag-input-row { display: flex; gap: 8px; @@ -377,12 +485,15 @@ body { .tag-pill { position: relative; + display: inline-flex; + align-items: center; padding: 4px 10px; border-radius: 999px; border: 1px solid #e0d3c1; background: #f8efe2; font-size: 12px; color: var(--ink); + line-height: 1.2; } .tag-pill::after { @@ -540,6 +651,10 @@ body { display: none; } +.weekly-slots:not(.per-slot) .weekly-slot .slot-tags { + display: none; +} + .weekly-slots-header { display: flex; align-items: center; @@ -572,10 +687,32 @@ body { display: none; } +.weekly-slot .slot-tags { + display: none; +} + .weekly-slots.per-slot .slot-meta { display: grid; } +.weekly-slots.per-slot .slot-tags { + display: grid; +} + +.slot-tag-field { + grid-column: 1 / -1; + display: grid; + gap: 8px; + grid-template-columns: 1fr; +} + +.slot-tag-field .tag-label, +.slot-tag-field .tag-input-row, +.slot-tag-field .tag-suggestions, +.slot-tag-field .tag-list { + grid-column: 1 / -1; +} + .weekly-slot.single-slot .slot-meta { display: grid; } @@ -1171,6 +1308,31 @@ body { border: 1.5px solid rgba(255, 255, 255, 0.7); } +.day-block.palette-sand { + background: var(--palette-sand); + border-color: rgba(255, 255, 255, 0.7); +} + +.day-block.palette-sage { + background: var(--palette-sage); + border-color: rgba(255, 255, 255, 0.7); +} + +.day-block.palette-mist { + background: var(--palette-mist); + border-color: rgba(255, 255, 255, 0.7); +} + +.day-block.palette-clay { + background: var(--palette-clay); + border-color: rgba(255, 255, 255, 0.7); +} + +.day-block.palette-moss { + background: var(--palette-moss); + border-color: rgba(255, 255, 255, 0.7); +} + .day-block .event-time { display: block; font-size: 11px; diff --git a/frontend/index.html b/frontend/index.html index 0f146f2..cff31c7 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -130,6 +130,12 @@ Repeats annually on the default start date. +
+ +
-
+
Tags
+
+ Color +
+ + + + + + +
+
+
+ Slot tags +
+ + +
+
+
+