From e3b3f7d92cb834344bac6281dfea6734ef11c0a3 Mon Sep 17 00:00:00 2001 From: Paul Irofti Date: Thu, 20 Mar 2025 19:32:25 +0200 Subject: [PATCH] WIP: started working on CCubes kernel --- Makefile | 19 ++ ccubes.cl | 286 +++++++++++++++++++ cl_setup.c | 373 +++++++++++++++++++++++++ cl_setup.h | 35 +++ clccubes.h | 26 ++ config.c | 136 +++++++++ config.h | 18 ++ logging.c | 126 +++++++++ logging.h | 74 +++++ queue.h | 568 ++++++++++++++++++++++++++++++++++++++ real.h | 20 ++ test_ccubes.c | 140 ++++++++++ tree.h | 748 ++++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 2569 insertions(+) create mode 100644 Makefile create mode 100644 ccubes.cl create mode 100644 cl_setup.c create mode 100644 cl_setup.h create mode 100644 clccubes.h create mode 100644 config.c create mode 100644 config.h create mode 100644 logging.c create mode 100644 logging.h create mode 100644 queue.h create mode 100644 real.h create mode 100644 test_ccubes.c create mode 100644 tree.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..da0c8f2 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +NAME = test_ompcl + +SRCS = $(shell find . -maxdepth 1 -type f -name '*.c') +OBJS = $(SRCS:.c=.o) + +LDLIBS += -lOpenCL + +.PHONY: all clean test + +all: ${NAME} + +${NAME}: ${OBJS} + ${CC} ${LDFLAGS} $^ ${LDLIBS} -o $@ + +test: + ./${NAME} + +clean: + @${RM} ${NAME} ${OBJS} diff --git a/ccubes.cl b/ccubes.cl new file mode 100644 index 0000000..05e1a14 --- /dev/null +++ b/ccubes.cl @@ -0,0 +1,286 @@ +#ifdef USE_DOUBLE +#pragma OPENCL EXTENSION cl_khr_fp64 : enable +typedef double real; +#define R_ZERO 1e-14 +#else +typedef float real; +#define R_ZERO 1e-10 +#endif + +#define BITS_PER_WORD 32 + +#define ROW_DIM 0 +#define COL_DIM 1 + +// #pragma OPENCL EXTENSION cl_amd_printf : enable +// #pragma OPENCL EXTENSION cl_khr_select_fprounding_mode : enable +// #pragma OPENCL SELECT_ROUNDING_MODE rtz + +#ifdef RANGE_DEBUG +#define RANGE_CHECK(lower, upper, value, str) do { \ + if ((value) < (lower) || (value) > (upper)) { \ + printf("%s", (str)); \ + return; \ + } \ +} while(0); +#else +#define RANGE_CHECK(lower, upper, value, str) +#endif + +unsigned long int +nchoosek(int n, int k) +{ + if (k == 0 || k == n) return 1; + if (k == 1) return n; + + unsigned long int result = 1; + + if (k > n - k) { + k = n - k; + } + + for (int i = 0; i < k; i++) { + result = result * (n - i) / (i + 1); + } + + return result; +} + +/* + * + * PROBLEM: CCubes + * + * INPUT: + * k - current input + * ninputs - number of inputs + * posrows - positive output rows (the ON set) + * negrows - negative output rows (the OFF set) + * pichart_words - words needed per PI chart columns + * implicant_words - words needed per PI representation + * nofvalues (ninputs x 1) - number of values + * nofpi (ninputs x 1) - number of prime implicants + * ON_set (posrows x ninputs) - ON set + * OFF_set (ninputs x negrows) - OFF set + * + * OUTPUT: + * x (n x 1) - solution (L \ b) + * + * NOTE: Both input and output must be allocated before calling this funciton. + */ +__kernel void +ccubes_task(int k, int ninputs, + int posrows, + int negrows, + int pichart_words, + int implicant_words, + __global const real *nofvalues, + __global const real *nofpi, + __global const real *ON_set, + __global const real *OFF_set, + __global const unsigned int *p_implicants_pos, + __global const unsigned int *p_implicants_val, + __global const int *last_index, + __global const int *p_covered, + __global const int *p_pichart_pos, + ) +{ + /* work-item?: task in nchoosek(ninputs, k) */ + /* work-group?: k in 1 to ninputs */ + /* total work: tasks in nchoosek for k in 1 to ninputs */ + + size_t task = get_global_id(0); + + int prevfoundPI = 0; + + int tempk[k]; /* max is tempk[ninputs] */ + int x = 0; + int start_point = task; + + // fill the combination for the current task + for (int i = 0; i < k; i++) { + while (nchoosek(ninputs - (x + 1), k - (i + 1)) <= start_point) { + start_point -= nchoosek(ninputs - (x + 1), k - (i + 1)); + x++; + } + tempk[i] = x; + x++; + } + + // allocate vectors of decimal row numbers for the positive and negative rows + int decpos[posrows]; + int decneg[negrows]; + + // create the vector of multiple bases, useful when calculating the decimal representation + // of a particular combination of columns, for each row + int mbase[k]; + mbase[0] = 1; // the first number is _always_ equal to 1, irrespective of the number of values in a certain input + + // calculate the vector of multiple bases, for example if we have k = 3 (three inputs) with + // 2, 3 and 2 values then mbase will be [1, 2, 6] from: 1, 1 * 2 = 2, 2 * 3 = 6 + for (int i = 1; i < k; i++) { + mbase[i] = mbase[i - 1] * nofvalues[tempk[i - 1]]; + } + + // calculate decimal numbers, using mbase, fills in decpos and decneg + for (int r = 0; r < posrows; r++) { + decpos[r] = 0; + for (int c = 0; c < k; c++) { + decpos[r] += ON_set[tempk[c] * posrows + r] * mbase[c]; + } + } + + for (int r = 0; r < negrows; r++) { + decneg[r] = 0; + for (int c = 0; c < k; c++) { + decneg[r] += OFF_set[tempk[c] * negrows + r] * mbase[c]; + } + } + + + int possible_rows[posrows]; + + bool possible_cover[posrows]; + possible_cover[0] = true; // bool flag, to be set with false if found among the OFF set + + int found = 0; + + // identifies all unique decimal rows, for the selected combination of k inputs + for (int r = 0; r < posrows; r++) { + int prev = 0; + bool unique = true; // bool flag, assume the row is unique + while (prev < found && unique) { + unique = decpos[possible_rows[prev]] != decpos[r]; + prev++; + } + + if (unique) { + possible_rows[found] = r; + possible_cover[found] = true; + found++; + } + } + + if (found > 0) { + // some of the ON set numbers are possible PIs (not found in the OFF set) + int frows[found]; + + // verify if this is a possible PI + // (if the same decimal number is not found in the OFF set) + for (int i = found - 1; i >= 0; i--) { + int j = 0; + while (j < negrows && possible_cover[i]) { + if (decpos[possible_rows[i]] == decneg[j]) { + possible_cover[i] = false; + found--; + } + j++; + } + + if (possible_cover[i]) { + frows[found - i - 1] = possible_rows[i]; + } + } + // Rprintf("task: %d; rows: %d\n", task, found); + + for (int f = 0; f < found; f++) { + + + // create a temporary vector of length k, containing the values from the initial ON set + // plus 1 (because 0 now signals a minimization, it becomes 1, and 1 becomes 2 etc. + int tempc[k]; + + // using bit shifting, store the fixed bits and value bits + unsigned int fixed_bits[implicant_words]; + unsigned int value_bits[implicant_words]; + + for (int i = 0; i < implicant_words; i++) { + fixed_bits[i] = 0U; + value_bits[i] = 0U; + } + + for (int c = 0; c < k; c++) { + int value = ON_set[tempk[c] * posrows + frows[f]]; + tempc[c] = value + 1; + + int word_index = tempk[c] / BITS_PER_WORD; + int bit_index = tempk[c] % BITS_PER_WORD; + + fixed_bits[word_index] |= 1U << bit_index; + value_bits[word_index] |= (unsigned int)value << (bit_index * value_bit_width); + } + + // check if the current PI is not redundant + bool redundant = false; + + int i = 0; + while (i < prevfoundPI && !redundant) { + // /* + // - ck contains the complexity level for each of the previously found non-redundant PIs + // - indx is a matrix containing the indexes of the columns where the values were stored + // - a redundant PI is one for which all values from a previous PI are exactly the same: + // 0 0 1 2 0, let's say previously found PI + // which means a corresponding ck = 2 and a corresponding indx = [3, 4] + // 0 0 1 2 1 is redundant because on both columns 3 and 4 the values are equal + // therefore sumeq = 2 and it will be equal to v = 2 when reaching the complexity level ck = 2 + // */ + + bool is_subset = true; // Assume it's a subset unless proven otherwise + + for (int w = 0; w < implicant_words; w++) { + // If the new PI has values on positions outside the existing PI’s fixed positions, it’s not a subset + if ((fixed_bits[w] & p_implicants_pos[i * implicant_words + w]) != p_implicants_pos[i * implicant_words + w]) { + is_subset = false; + break; + } + + // then compare the value bits, if one or more values on those positions are different, it’s not a subset + if ((value_bits[w] & p_implicants_val[i * implicant_words + w]) != p_implicants_val[i * implicant_words + w]) { + is_subset = false; + break; + } + } + + redundant = is_subset; + + i++; + } + + if (redundant) continue; + + bool coverage[posrows]; + int covsum = 0; + unsigned int pichart_values[pichart_words]; + for (int w = 0; w < pichart_words; w++) { + pichart_values[w] = 0U; + } + + for (int r = 0; r < posrows; r++) { + coverage[r] = decpos[r] == decpos[frows[f]]; + if (coverage[r]) { + int word_index = r / BITS_PER_WORD; + int bit_index = r % BITS_PER_WORD; + pichart_values[word_index] |= (1U << bit_index); + } + covsum += coverage[r]; + } + + // verify row dominance + int rd = 0; + while (rd < last_index[covsum - 1] && !redundant) { + + bool dominated = true; + for (int w = 0; w < pichart_words; w++) { + if ((pichart_values[w] & p_pichart_pos[p_covered[rd] * pichart_words + w]) != pichart_values[w]) { + dominated = false; + break; + } + } + + redundant = dominated; + rd++; + } + + if (redundant) continue; + } + } +} diff --git a/cl_setup.c b/cl_setup.c new file mode 100644 index 0000000..58ccee7 --- /dev/null +++ b/cl_setup.c @@ -0,0 +1,373 @@ +#include +#include +#include + +#include "logging.h" + +#include "cl_setup.h" +#define CL_DEBUG + +cl_int +cl_init(struct cl_uctx *puctx) +{ + /* OpenCL specific variables */ + size_t dataBytes; + + cl_int result = CL_SUCCESS; + cl_uint nplat = 0, ndevices = 0, i = 0, matched_plat; + cl_platform_id *platforms = NULL, platform = NULL; + cl_device_id *devices = NULL; + cl_context_properties props[3] = {CL_CONTEXT_PLATFORM, 0, 0}; + cl_context ctx = NULL; + + + if (puctx == NULL || puctx->platform_name == NULL) { + return CL_INVALID_VALUE; + } + if (puctx->device_type > CL_DEVICE_TYPE_GPU) { + puctx->device_type = CL_DEVICE_TYPE_ALL; + } + + /* + * Initialize OpenCL. + */ + result = clGetPlatformIDs(0, NULL, &nplat); + if (result != CL_SUCCESS) { + log_warn("cl", "Failed getting the number of platforms"); + goto err; + } + if (nplat < 0) { + log_warn("cl", "No platforms found"); + goto err; + } + platforms = calloc(nplat, sizeof platforms[0]); + if (platforms == NULL) { + log_warn("cl", "Failed to allocate platforms"); + goto err; + } + result = clGetPlatformIDs(nplat, platforms, NULL); + if (result != CL_SUCCESS) { + log_warn("cl", "Failed fetching the platforms"); + goto err; + } + + log_debug("cl", "Found %d platforms", nplat); + for (i = 0; i < nplat; i++) { + char platname[100]; + result = clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, + sizeof(platname), platname, NULL); + if (result != CL_SUCCESS) { + log_warn("cl", "Failed fetching platform info"); + goto err; + } + log_debug("cl", "Checking %s == %s", + puctx->platform_name, platname); + if (!strcmp(platname, puctx->platform_name)) { + platform = platforms[i]; + matched_plat = i; + break; + } + } + if (platform == NULL) { + log_warn("cl", "No matching platform found"); + result = CL_DEVICE_NOT_FOUND; + goto err; + } + +#ifdef CL_DEBUG + printf("-----------------------------------------------------------\n"); + printf(" PLATFORM INFORMATION (the number of platforms = %d) \n", + nplat); + for(i = 0 ; i < nplat; i++) + { + char *long_str; + char str[1024]; + size_t str_size; + + printf("-------------------------------------------------------" + "----\n"); + printf( " PLATFORM ID : %d " + "\n", i); + printf("-------------------------------------------------------" + "----\n"); + + clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, sizeof(str), + str, NULL); + printf("Platform name : %s\n", str); + + clGetPlatformInfo(platforms[i], CL_PLATFORM_VERSION, + sizeof(str), str, NULL); + printf("Platform version : %s\n", str); + + clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, 0, + NULL, &str_size); + long_str = (char *)malloc(str_size); + clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, + str_size, long_str, NULL); + printf("Platform extensions : %s\n", long_str); + + printf("-------------------------------------------------------" + "----\n\n"); + + free(long_str); + } +#endif + + props[1] = (cl_context_properties)platform; + + ctx = clCreateContextFromType(props, CL_DEVICE_TYPE_ALL, NULL, NULL, + &result); + if(result != CL_SUCCESS) { + log_warn("cl", "Failed to create context"); + goto err; + } + + /* fetch the list of devices associated with context */ + result = clGetContextInfo(ctx, CL_CONTEXT_DEVICES, 0, NULL, + &dataBytes); + if(result != CL_SUCCESS) { + log_warn("cl", "Failed to fetch devices size!"); + goto err; + } + devices = (cl_device_id *)malloc(dataBytes); + if (devices == NULL) { + log_warn("cl", "devices malloc() failed!"); + goto err; + } + result |= clGetContextInfo(ctx, CL_CONTEXT_DEVICES, dataBytes, + devices, NULL); + if (result != CL_SUCCESS) { + log_warn("cl", "clGetContextInfo() failed with %d!", result); + goto err; + } + + +#ifdef CL_DEBUG + result = clGetDeviceIDs(platforms[matched_plat], CL_DEVICE_TYPE_ALL, + 0, NULL, &ndevices); + if (result != CL_SUCCESS) { + log_warn("cl", "clGetDeviceIDs() failed!"); + goto err; + } + printf("-----------------------------------------------------------\n"); + printf(" DEVICE INFORMATION (the number of devices = %d) \n", + ndevices); + for(i = 0 ; i < ndevices ; i++) + { + char str[1024]; + size_t int_info; + + printf("-------------------------------------------------------" + "----\n"); + printf(" DEVICE ID : %d " + "\n",i); + printf("-------------------------------------------------------" + "----\n"); + + clGetDeviceInfo(devices[i], CL_DEVICE_NAME, sizeof(str), str, + NULL); + printf("Device Name : %s\n",str); + + clGetDeviceInfo(devices[i], CL_DEVICE_VERSION, sizeof(str), str, + NULL); + printf("Device Version : %s\n",str); + + clGetDeviceInfo(devices[i], CL_DEVICE_GLOBAL_MEM_SIZE, + sizeof(int_info), &int_info, NULL); + printf("Size of global memory : %lu (MB) \n", + int_info/1024/1024); + + clGetDeviceInfo(devices[i], CL_DEVICE_LOCAL_MEM_SIZE, + sizeof(int_info), &int_info, NULL); + printf("Size of local memory : %lu (KB) \n", int_info/1024); + + clGetDeviceInfo(devices[i], CL_DEVICE_MAX_CLOCK_FREQUENCY, + sizeof(int_info), &int_info, NULL); + printf("Max clock frequency : %4.2lf (GHz) \n", + int_info/1024.0); + + printf("-------------------------------------------------------" + "----\n\n"); + } +#endif + + /* + * XXX: Very AMD-centric, should make it more flexible... + * Intel has the first device as the CPU and no GPU support + */ + + /* + * GPU + */ + puctx->gpu_queue = clCreateCommandQueueWithProperties(ctx, devices[0], 0, + &result); + if (result != CL_SUCCESS) { + log_warn("cl", "GPU: clGetContextInfo() failed!"); + goto err; + } + + /* + * CPU + */ +#if 0 + puctx->cpu_queue = clCreateCommandQueue(ctx, devices[1], 0, + &result); + if (result != CL_SUCCESS) { + log_warn("cl", "CPU: clGetContextInfo() failed!"); + goto err; + } +#endif + + puctx->devices = devices; + puctx->ctx = ctx; + + free(platforms); + + return CL_SUCCESS; + +err: + if (platforms) + free(platforms); + if (devices) + free(devices); + if (puctx->cpu_queue) + clReleaseCommandQueue(puctx->cpu_queue); + if (puctx->gpu_queue) + clReleaseCommandQueue(puctx->gpu_queue); + if (ctx) + clReleaseContext(ctx); + + + return result; +} + +void +cl_clean_up(struct cl_uctx uctx) +{ + if (uctx.devices) { + free(uctx.devices); + } + if (uctx.gpu_queue) { + clReleaseCommandQueue(uctx.gpu_queue); + } + if (uctx.cpu_queue) { + clReleaseCommandQueue(uctx.cpu_queue); + } + if (uctx.ctx) { + clReleaseContext(uctx.ctx); + } +} + +cl_int +cl_build(struct cl_uctx uctx, cl_device_type dev, + char *kern_fname, cl_program *pprogram) +{ + cl_program program = NULL; + cl_int result = CL_SUCCESS; + + FILE *kern_file = NULL; + char *kern_src = NULL; + size_t srcsz = 0; + + int type; + +#ifdef INTEL_KERNEL_DEBUG + char build_options[100] = "-g -s F:\\obj\\vs\\debug\\Bin\\Debug\\"; + strcat(&build_options[32], kern_fname); +#else + char *build_options = NULL; +#endif + + if (kern_fname == NULL || pprogram == NULL) + return CL_INVALID_VALUE; + + /* XXX: AMD-centric, should probably be passed as param */ + /* Decide the target based on device type */ + if (dev == CL_DEVICE_TYPE_GPU) { + type = 0; + } else { + type = 1; + } + + /* + * Compile and link the OpenCL kernel. + */ + + /* Read-in the source code */ + kern_file = fopen(kern_fname, "rb"); + if (kern_file == NULL) { + log_warn("cl", "Failed to open kernel source file %s!", + kern_fname); + result = CL_INVALID_VALUE; + goto err; + } + fseek(kern_file, 0, SEEK_END); + srcsz = ftell(kern_file); + fseek(kern_file, 0, SEEK_SET); + kern_src = (char *)malloc(srcsz + 1); + if (kern_src == NULL) { + log_warn("cl", "kern_src malloc() failed!"); + result = CL_INVALID_VALUE; + goto err; + } + fread(kern_src, 1, srcsz, kern_file); + kern_src[srcsz] = 0; + + log_info("cl", "FILE DUMP BEGINS"); + log_info("cl", "%s", kern_src); + log_info("cl", "FILE DUMP ENDS"); + + program = clCreateProgramWithSource(uctx.ctx, 1, + (const char **)&kern_src, &srcsz, &result); + if (result != CL_SUCCESS) { + log_warn("cl", "clCreateProgamWithSource() failed!"); + goto err; + } + + /* Build the kernel */ + result = clBuildProgram(program, 1, &uctx.devices[type], + build_options, NULL, NULL); + if (result != CL_SUCCESS) { + /* Print out the build log in case of failure */ + char programLog[10000] = {0}; + log_warn("cl", "clBuildProgram() failed!"); + clGetProgramBuildInfo(program, uctx.devices[type], + CL_PROGRAM_BUILD_LOG, 10000, programLog, 0); + log_warn("cl", "%s\n", programLog); + goto err; + } + + *pprogram = program; + +err: + if (kern_file) + fclose(kern_file); + if (kern_src) + free(kern_src); + if (result != CL_SUCCESS && program) + clReleaseProgram(program); + return result; +} + +cl_int +cl_get_kern(cl_program program, char *kname, cl_kernel *pkern) +{ + cl_int result = CL_SUCCESS; + + if (program == NULL || kname == NULL || pkern == NULL) + return CL_INVALID_VALUE; + + *pkern = clCreateKernel(program, kname, &result); + +#if 0 + clGetKernelWorkGroupInfo(ckKernel[0], cdDevice[did], + CL_KERNEL_WORK_GROUP_SIZE, sizeof(int_info), &int_info, NULL); + printf("GPU Maximum Work group size : %d\n", int_info); + clGetKernelWorkGroupInfo(ckKernel[0], cdDevice[did], + CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(int_info), + &int_info, NULL); + printf("GPU Preferred Work group size : %d\n", int_info); +#endif + + return result; +} diff --git a/cl_setup.h b/cl_setup.h new file mode 100644 index 0000000..dce2b04 --- /dev/null +++ b/cl_setup.h @@ -0,0 +1,35 @@ +#ifndef SETUP_H__ +#define SETUP_H__ + +#define CL_TARGET_OPENCL_VERSION 220 /* For Sinaia cluster */ + +#include + +/* + * OpenCL user context. + */ +struct cl_uctx { + cl_context ctx; /* internal context */ + cl_device_id *devices; /* device in use */ + + cl_command_queue gpu_queue; /* command queue */ + cl_command_queue cpu_queue; /* command queue */ + + cl_device_type device_type; /* desired device type */ + char platform_name[100]; /* desired platform */ + + size_t reduce_gws, reduce_lws; /* norm global and local workspace */ + + /* BLAS kernels */ + cl_kernel gemm_NN_kernel, gemm_TN_kernel; + cl_kernel frob_stage1_kernel, frob_stage2_kernel; +}; + +cl_int cl_init(struct cl_uctx *uctx); +void cl_clean_up(struct cl_uctx uctx); + +cl_int cl_build(struct cl_uctx uctx, cl_device_type dev, char *kern_name, + cl_program *pprogram); +cl_int cl_get_kern(cl_program program, char *kname, cl_kernel *pkern); + +#endif diff --git a/clccubes.h b/clccubes.h new file mode 100644 index 0000000..9234044 --- /dev/null +++ b/clccubes.h @@ -0,0 +1,26 @@ +#ifndef CLccubes_H__ +#define CLccubes_H__ + +#include "cl_setup.h" + +#ifndef _MSC_VER +#include +#else +#include +#endif + +#define ROW_DIM 0 +#define COL_DIM 1 + +struct ccubes_context { + struct cl_uctx *clctx; + + cl_kernel ccubes_task; +}; + +int +clccubes(struct ccubes_context *ccubesctx, cl_mem alpha0, cl_mem G, size_t signals, + size_t atoms, uint32_t sparsity, uint32_t pcoding, cl_mem gamma, cl_uint + num_events_in_wait_list, const cl_event *event_wait_list, cl_event *ev_ccubes); + +#endif diff --git a/config.c b/config.c new file mode 100644 index 0000000..42c299f --- /dev/null +++ b/config.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2012, Marius Barbu + * Copyright (c) 2013, Paul Irofti + */ + +#include /* sscanf/snprintf */ +#include /* stricmp */ +#include + +#include "tree.h" +#include "config.h" + +#if _MSC_VER +#define snprintf _snprintf +#endif + +struct ConfigMap { + char *name; + char *value; + RB_ENTRY(ConfigMap) entry; +}; + +int +cfg_cmp(struct ConfigMap *cfg1, struct ConfigMap *cfg2) +{ + return (strcmp(cfg1->name, cfg2->name)); +} + +RB_HEAD(cfgtree, ConfigMap) cfg_head = RB_INITIALIZER(&cfg_head); +RB_GENERATE(cfgtree, ConfigMap, entry, cfg_cmp); + +void +config_set_string(char *name, char *value) +{ + struct ConfigMap *dup = NULL; + struct ConfigMap *cfg = malloc(sizeof *cfg); + + if (cfg == NULL || name == NULL || value == NULL) + return; + + cfg->name = strdup(name); + if (cfg->name == NULL) + return; + cfg->value = strdup(value); + if (cfg->value == NULL) + return; + if ((dup = RB_INSERT(cfgtree, &cfg_head, cfg))) { + dup->value = cfg->value; + free(cfg->name); + free(cfg); + } +} + +void +config_set_int(char *name, int value) +{ + char buff[20]; + + snprintf(buff, sizeof buff, "%d", value); + + config_set_string(name, buff); +} + +void +config_set_bool(char *name, int value) +{ + char buff[20]; + + snprintf(buff, sizeof buff, "%d", value ? 1 : 0); + + config_set_string(name, buff); +} + +char* +config_get_string(char *name, char *def) +{ + struct ConfigMap find, *found = NULL; + find.name = (char *)name; + + found = RB_FIND(cfgtree, &cfg_head, &find); + if (found == NULL) { + return def; + } + + return found->value; +} + +int +config_get_int(char *name, int def) +{ + int val = def; + struct ConfigMap find, *found = NULL; + + find.name = (char *)name; + found = RB_FIND(cfgtree, &cfg_head, &find); + if (found == NULL) { + return def; + } + + sscanf(found->value, "%i", &val); + + return val; +} + +int +config_get_bool(char *name, int def) +{ + struct ConfigMap find, *found = NULL; + + find.name = (char *)name; + found = RB_FIND(cfgtree, &cfg_head, &find); + if (found == NULL) { + return def; + } + + if (strcmp(found->value, "0") == 0 || + strcmp(found->value, "false") == 0) { + return 0; + } + + return 1; +} + +void +config_destroy() +{ + struct ConfigMap *var, *nxt; + for (var = RB_MIN(cfgtree, &cfg_head); var != NULL; var = nxt) { + nxt = RB_NEXT(cfgtree, &cfg_head, var); + RB_REMOVE(cfgtree, &cfg_head, var); + free(var->name); + free(var->value); + free(var); + } +} + diff --git a/config.h b/config.h new file mode 100644 index 0000000..89f169f --- /dev/null +++ b/config.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2012, Marius Barbu + * Copyright (c) 2013, Paul Irofti + */ +#ifndef CONFIG_H__ +#define CONFIG_H__ + +void config_set_string(char *name, char *value); +void config_set_int(char *name, int value); +void config_set_bool(char *name, int value); + +char* config_get_string(char *name, char *def); +int config_get_int(char *name, int def); +int config_get_bool(char *name, int def); + +void config_destroy(); + +#endif /* CONFIG_H__ */ diff --git a/logging.c b/logging.c new file mode 100644 index 0000000..86b678c --- /dev/null +++ b/logging.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2012, Marius Barbu + * Copyright (c) 2013, Paul Irofti + */ + +#include /* FILE */ +#include /* abort */ +#include +#include /* strrchr */ + +#include "tree.h" + +#include "config.h" +#include "logging.h" + +static char *levelName[] = { + "INFO", + "DEBUG", + "WARN", + "ERROR" +}; + +static FILE *logFile; + +static char* +nice_path(char *name) +{ + if (strrchr(name, '/')) { + name = strrchr(name, '/') + 1; + } else if (strrchr(name, '\\')) { + name = strrchr(name, '\\') + 1; + } + + return name; +} + +enum LogLevel +log_level(char *id) +{ + char name[20] = { 0 }; + size_t len = 0; + int cfgGlobal, cfgId; + + if (id == NULL) + return LOG_LEVEL_ERROR; + + len = strlen(id); + if (len == 0 || len > 15) + return LOG_LEVEL_ERROR; + + strcpy(name, "log:"); + strcat(name, id); + cfgGlobal = config_get_int("log", LOG_LEVEL_WARN); + cfgId = config_get_int(name, cfgGlobal); + + if (cfgId > LOG_LEVEL_ERROR) { + cfgId = LOG_LEVEL_ERROR; + } + + return cfgId; +} + +static void +log_message_raw_v(enum LogLevel l, char *id, char *fmt, va_list argp) +{ + if (l < log_level(id)) { + return; + } + + if (logFile == 0) { + char *name = config_get_string("out", 0); + if (name) { + logFile = fopen(name, "a+t"); + if (logFile == 0) { + perror(name); + } + } + + if (logFile == 0) { + logFile = stdout; + } + } + + vfprintf(logFile, fmt, argp); +} + +void +log_message(enum LogLevel l, char *where, int line, char *id, + char *fmt, ...) +{ + va_list argp; + + if (l < log_level(id)) { + return; + } + +#ifdef NON_DIFFABLE_OUTPUT + log_message_raw(l, id, "%s - %s:%d - %s: ", levelName[l], + nice_path(where), line, id); +#endif + log_message_raw(l, id, "%s - %s: ", levelName[l], id); + va_start(argp, fmt); + log_message_raw_v(l, id, fmt, argp); + va_end(argp); + log_message_raw(l, id, "\n"); + if (l >= LOG_LEVEL_ERROR) { + fflush(logFile); + /*exit(1);*/ + } + fflush(logFile); +} + +void +log_message_raw(enum LogLevel l, char *id, char *fmt, ...) +{ + va_list argp; + if (l < log_level(id)) { + return; + } + + + va_start(argp, fmt); + log_message_raw_v(l, id, fmt, argp); + va_end(argp); +} + diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..99799f3 --- /dev/null +++ b/logging.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012, Marius Barbu + * Copyright (c) 2013, Paul Irofti + */ +#ifndef LOGGING_H__ +#define LOGGING_H__ + +enum LogLevel { + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_WARN, + LOG_LEVEL_ERROR, + LOG_LEVEL_DISABLE, +}; + +enum LogLevel log_level(char *id); +void log_message_raw(enum LogLevel l, char *id, char *fmt, ...); +void log_message(enum LogLevel l, char *where, int line, char *id, + char *fmt, ...); + +#define log_info(id, ...) \ + do { \ + int enabled = log_level(id) <= LOG_LEVEL_INFO; \ + if (enabled) { \ + log_message(LOG_LEVEL_INFO, __FILE__, __LINE__, \ + id, __VA_ARGS__); \ + } \ + } while (0) + +#define log_info_raw(id, ...) \ + do { \ + int enabled = log_level(id) <= LOG_LEVEL_INFO; \ + if (enabled) { \ + log_message_raw(LOG_LEVEL_INFO, \ + id, __VA_ARGS__); \ + } \ + } while (0) + +#define log_debug(id, ...) \ + do { \ + int enabled = log_level(id) <= LOG_LEVEL_DEBUG; \ + if (enabled) { \ + log_message(LOG_LEVEL_DEBUG, __FILE__, __LINE__, \ + id, __VA_ARGS__); \ + } \ + } while (0) + +#define log_debug_raw(id, ...) \ + do { \ + int enabled = log_level(id) <= LOG_LEVEL_DEBUG; \ + if (enabled) { \ + log_message_raw(LOG_LEVEL_DEBUG, \ + id, __VA_ARGS__); \ + } \ + } while (0) + + +#define log_warn(id, ...) \ + log_message(LOG_LEVEL_WARN, __FILE__, __LINE__, \ + id, __VA_ARGS__) + +#define log_warn_raw(id, ...) \ + log_message_raw(LOG_LEVEL_WARN, \ + id, __VA_ARGS__) + +#define log_error(id, ...) \ + log_message(LOG_LEVEL_ERROR, __FILE__, __LINE__, \ + id, __VA_ARGS__) + +#define log_error_raw(id, ...) \ + log_message(LOG_LEVEL_ERROR, \ + id, __VA_ARGS__) + +#endif /* LOGGING_H__ */ diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..fb23a72 --- /dev/null +++ b/queue.h @@ -0,0 +1,568 @@ +/* $OpenBSD: queue.h,v 1.35 2012/01/11 00:06:48 bluhm Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_NEXT(head, elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + _Q_INVALIDATE((elm)->field.sle_next); \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_NEXT(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = CIRCLEQ_LAST(head, headname); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/real.h b/real.h new file mode 100644 index 0000000..56069c7 --- /dev/null +++ b/real.h @@ -0,0 +1,20 @@ +#ifndef REAL_H__ +#define REAL_H__ + +#include + +#ifdef USE_DOUBLE +typedef double real; +#define R_ZERO 1e-8 +#define rabs fabs +#define rsqrt sqrt +#define clAmdBlasGemmEx clAmdBlasDgemmEx +#else +typedef float real; +#define R_ZERO 1e-6 +#define rabs fabsf +#define rsqrt sqrtf +#define clAmdBlasGemmEx clAmdBlasSgemmEx +#endif + +#endif diff --git a/test_ccubes.c b/test_ccubes.c new file mode 100644 index 0000000..66f8d22 --- /dev/null +++ b/test_ccubes.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2013, 2014, Paul Irofti + */ + +#include +#include + +#include "real.h" +#include "cl_setup.h" + +#include "clccubes.h" + +#include "config.h" +#include "logging.h" + +int +parse_args(char *argv[], int argc, + uint32_t *signals, uint32_t *atoms, uint32_t *problem_size, + uint32_t *sparsity, uint32_t *iters, uint32_t *patoms, uint32_t *pcoding, + real *rmse) +{ + int i = 1; + while (i < argc) { + if (strcmp(argv[i], "-signals") == 0) { + *signals = strtoul(argv[++i], NULL, 0); + } else if (strcmp(argv[i], "-atoms") == 0) { + *atoms = strtoul(argv[++i], NULL, 0); + } else if (strcmp(argv[i], "-problem") == 0) { + *problem_size = strtoul(argv[++i], NULL, 0); + } else if (strcmp(argv[i], "-sparsity") == 0) { + *sparsity = strtoul(argv[++i], NULL, 0); + } else if (strcmp(argv[i], "-iterations") == 0) { + *iters = strtoul(argv[++i], NULL, 0); + } else if (strcmp(argv[i], "-patoms") == 0) { + *patoms = strtoul(argv[++i], NULL, 0); + } else if (strcmp(argv[i], "-pcoding") == 0) { + *pcoding = strtoul(argv[++i], NULL, 0); + } else if (strcmp(argv[i], "-rmse") == 0) { + sscanf(argv[++i], "%f", rmse); + } else { + printf("Unrecognized option %s\n", argv[i]); + return 1; + } + i++; + } + + return 0; +} + +void +usage(char *program) +{ + printf("USAGE: %s [-signals sigs] [-atoms atoms] " + "[-problem problem_size] [-sparsity s] [-iterations iters] " + "[-patoms p] [-pcoding c]\n", + program); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int rc = 0; + + real *zeroes = NULL; + uint64_t start, stop; + char log[100] = {0}; + char *program; + + struct ccubes_context ccubesctx; + cl_program ccubes_program; + + + /* Inputs */ + + /* Outputs */ + + /* Rounding */ + + /* Logging */ + config_set_int("log", LOG_LEVEL_WARN); + config_set_int("log:ccubes", LOG_LEVEL_WARN); + config_set_int("log:test", LOG_LEVEL_WARN); + config_set_int("cl", LOG_LEVEL_DEBUG); + +#if 0 + rc = parse_args(argv, argc, + &signals, &atoms, &problem_size, &sparsity, &iters, + &patoms, &pcoding, &target_error); + if (rc != 0) { + usage(argv[0]); + } + + if (strstr(argv[0], "test_ccubes") != NULL) + program = "ccubes"; + sprintf(log, "log-%s-p%d-n%d-m%d-k%d-i%d-pa%d", program, + problem_size, atoms, signals, sparsity, iters, patoms); + config_set_string("out", log); +#endif + + log_debug("test", "Starting ccubes..."); + + /* + * CCubes + */ + ccubesctx.clctx = malloc(sizeof *ccubesctx.clctx); + + ccubesctx.clctx->device_type = CL_DEVICE_TYPE_GPU; + strcpy(ccubesctx.clctx->platform_name, "NVIDIA Corporation\0"); + /* strcpy(ccubesctx.clctx->platform_name, "Advanced Micro Devices, Inc.\0"); */ + /*strcpy(ccubesctx.clctx->platform_name, "Intel(R) Corporation\0");*/ + rc = cl_init(ccubesctx.clctx); + if (rc != CL_SUCCESS) { + printf("[%d] Failed to initialize the OpenCL framework\n", + rc); + goto err; + } + + + rc = cl_build(*ccubesctx.clctx, CL_DEVICE_TYPE_GPU, + "ccubes.cl", &ccubes_program); + if (rc != CL_SUCCESS) { + log_warn("test", "Failed building ccubes.cl (%d)", rc); + goto err; + } + + rc = cl_get_kern(ccubes_program, "ccubes_task", + &ccubesctx.ccubes_task); + if (rc != CL_SUCCESS) { + log_warn("test", "Failed fetching ccubes_task (%d)", rc); + goto err; + } + + fflush(stdout); +err: + clReleaseProgram(ccubes_program); + + cl_clean_up(*ccubesctx.clctx); + + return rc; +} diff --git a/tree.h b/tree.h new file mode 100644 index 0000000..80d0f53 --- /dev/null +++ b/tree.h @@ -0,0 +1,748 @@ +/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */