Программы C и C ++, позволяющие увеличить время доступа к файлам

https://github.com/speedrun-program/load-extender

Это пара программ для увеличения времени загрузки: одна для Windows, другая для Linux. Версия для Windows написана на C ++, а версия для Linux написана на C, поэтому она может работать с LD_PRELOAD.

Я в основном заинтересован в том, чтобы убедиться, что я не делаю ничего, что могло бы вызвать сбой, поскольку я знаю, что у этих языков много неопределенного поведения, но я постараюсь исправить любые другие проблемы, на которые также указали. Я уверен, что есть проблемы с тем, как это написано, потому что у меня очень ограниченный опыт программирования, и это первое, что я когда-либо писал на C / C ++.

Вот код для Linux:

fopen_interceptor.c:

#define _GNU_SOURCE

#include <stdio.h>
#include <dlfcn.h>
#include <time.h>
#include <pthread.h>
#include "khash.h"
// thank you for making this hash map, attractivechaos.
// here's his github: https://github.com/attractivechaos

// these are global so fopen and khash_destruction can see them
static pthread_mutex_t lock;
static unsigned char LOCK_SUCCESS = 0; // used so it won't try to destroy lock if it failed to be made
static unsigned char SETUP_SUCCEEDED = 0; // if this is 0 then the fopen interception function skips to calling the real fopen function
static const int khStrInt = 32;
KHASH_MAP_INIT_STR(khStrInt, unsigned int*)
static khash_t(khStrInt) *my_khash_map;

#include "khash_setup_header.h" // included here so it recognizes kh_khStrInt_t

static void __attribute__((constructor)) khash_setup();
static void khash_setup()
{
    my_khash_map = kh_init(khStrInt);
    if (pthread_mutex_init(&lock, NULL) != 0)
    {
        printf("mutex init failedn");
        return;
    }
    LOCK_SUCCESS = 1; // LOCK_SUCCESS only changed here
    FILE *my_file;
    FILE *(*original_fopen)(const char*, const char*);
    original_fopen = dlsym(RTLD_NEXT, "fopen");
    my_file = original_fopen("./files_and_delays.txt", "r");
    if (my_file == NULL)
    {
        printf("error opening files_and_delays.txtn");
        return;
    }
    // longest_name, longest_sequence, longest_delay, and line_count made here so they can be printed in fopen_interceptor_test
    size_t longest_name = 1; // starts at 1 for null character
    size_t longest_sequence = 3; // starts at 3 for index holding array length, index holding current position, and first number in delay sequence
    unsigned char longest_delay = 1; // starts at 1 for null character
    unsigned int line_count = 1; // starts at 1 because there's always at least 1 line. khash map largest value is UINT_MAX
    unsigned char khash_succeeded = prepare_to_setup(my_khash_map, my_file, &longest_name, &longest_sequence, &longest_delay, &line_count);
    fclose(my_file);
    if (khash_succeeded == 0) // khash failed to be made. Error message is printed in khash_setup_header in prepare_to_setup.
    {
        return;
    }
    SETUP_SUCCEEDED = 1; // SETUP_SUCCEEDED only changed here
}

static void __attribute__((destructor)) khash_destruction();
static void khash_destruction()
{
    khiter_t khash_map_iter;
    for (khash_map_iter = 0; khash_map_iter < kh_end(my_khash_map); ++khash_map_iter)  // kh_destroy doesn't free keys and values, they need to be freed like this
    {
        if (kh_exist(my_khash_map, khash_map_iter))
        {
            free((char*)kh_key(my_khash_map, khash_map_iter));
            kh_key(my_khash_map, khash_map_iter) = NULL;
            free((unsigned int*)kh_val(my_khash_map, khash_map_iter));
            kh_val(my_khash_map, khash_map_iter) = NULL;
        }
    }
    kh_destroy(khStrInt, my_khash_map);
    if (LOCK_SUCCESS == 1)
    {
        pthread_mutex_destroy(&lock);
    }
}

FILE *fopen(const char *path, const char *mode)
{
    if (SETUP_SUCCEEDED == 1) // skip to calling original fopen if setup failed
    {
        short slash_index = -1;
        for (unsigned short i = 0; path[i] != ''; i++) // look for start of filename by finding last occurence of "https://codereview.stackexchange.com/" character
        {
            if (path[i] == "https://codereview.stackexchange.com/")
            {
                slash_index = i % SHRT_MAX; // will cause key to not be found, but prevents unsigned int overflow
            }
        }
        // +1 so "https://codereview.stackexchange.com/" isn't included. If "https://codereview.stackexchange.com/" not found in path, slash_index stays -1 and entire path argument is checked as key since "path + -1 + 1" is the same as just "path"
        khiter_t khash_map_iter = kh_get(khStrInt, my_khash_map, path + slash_index + 1);
        if (khash_map_iter != kh_end(my_khash_map)) // key found
        {
            unsigned int *delay_sequence = kh_val(my_khash_map, khash_map_iter);
            pthread_mutex_lock(&lock); // don't let other threads change delay sequence when it's being read or changed
            if (delay_sequence[2] == UINT_MAX) // reset all delay sequences
            {
                unsigned int *sequence_to_reset;
                kh_foreach_value(my_khash_map, sequence_to_reset, {sequence_to_reset[1] = 2;});
                pthread_mutex_unlock(&lock);
            }
            else if (delay_sequence[1] < delay_sequence[0])
            {
                if (delay_sequence[delay_sequence[1]] == UINT_MAX) // reset delay sequence
                {
                    delay_sequence[1] = 2;
                }
                unsigned int sleep_time = delay_sequence[delay_sequence[1]];
                delay_sequence[1] += 1;
                pthread_mutex_unlock(&lock);
                long time_nanoseconds = (sleep_time % 1000) * 1000000;
                struct timespec ts = {sleep_time / 1000, time_nanoseconds};
                nanosleep(&ts, NULL);
            }
            else // delay sequence already finished
            {
                pthread_mutex_unlock(&lock);
            }
        }
    }
    FILE *(*original_fopen)(const char*, const char*);
    original_fopen = dlsym(RTLD_NEXT, "fopen");
    return (*original_fopen)(path, mode);
}

fopen_interceptor_test.c:

#define _GNU_SOURCE

#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include "khash.h"
// thank you for making this hash map, attractivechaos.
// here's his github: https://github.com/attractivechaos

// these are global so fopen and khash_destruction can see them
static pthread_mutex_t lock;
static unsigned char LOCK_SUCCESS = 0; // used so it won't try to destroy lock if it failed to be made
static unsigned char SETUP_SUCCEEDED = 0; // if this is 0 then the fopen interception function skips to calling the real fopen function
static const int khStrInt = 32;
KHASH_MAP_INIT_STR(khStrInt, unsigned int*)
static khash_t(khStrInt) *my_khash_map;

#include "khash_setup_header.h" // included here so it recognizes kh_khStrInt_t

static void __attribute__((constructor)) khash_setup();
static void khash_setup()
{
    my_khash_map = kh_init(khStrInt);
    if (pthread_mutex_init(&lock, NULL) != 0)
    {
        printf("mutex init failedn");
        return;
    }
    LOCK_SUCCESS = 1; // LOCK_SUCCESS only changed here
    FILE *my_file;
    /*FILE *(*original_fopen)(const char*, const char*);
    original_fopen = dlsym(RTLD_NEXT, "fopen");
    my_file = original_fopen("./files_and_delays.txt", "r");*/
    my_file = fopen("./files_and_delays.txt", "r");
    if (my_file == NULL)
    {
        printf("error opening files_and_delays.txtn");
        return;
    }
    size_t longest_name = 1; // starts at 1 for null character
    size_t longest_sequence = 3; // starts at 3 for index holding array length, index holding current position, and first number in delay sequence
    unsigned char longest_delay = 1; // starts at 1 for null character
    unsigned int line_count = 1; // starts at 1 because there's always at least 1 line. khash map largest value is UINT_MAX
    unsigned char khash_succeeded = prepare_to_setup(my_khash_map, my_file, &longest_name, &longest_sequence, &longest_delay, &line_count);
    fclose(my_file);
    if (khash_succeeded == 0) // khash failed to be made. Error message is printed in khash_setup_header in prepare_to_setup.
    {
        return;
    }
    printf("nlongest filename (plus null character and whitespace in file): %zun"
    "longest delay sequence (+2 for current delay and sequence length): %zun"
    "most digits in delay (+1 for null character, max delay is UINT_MAX): %un"
    "lines in file (overestimates slightly): %un"
    "khash map size (lines should be less than 70%% of this): %zun", longest_name, longest_sequence, longest_delay, line_count, kh_n_buckets(my_khash_map));
    SETUP_SUCCEEDED = 1; // SETUP_SUCCEEDED only changed here
}

static void __attribute__((destructor)) khash_destruction();
static void khash_destruction()
{
    khiter_t khash_map_iter;
    for (khash_map_iter = 0; khash_map_iter < kh_end(my_khash_map); ++khash_map_iter)  // kh_destroy doesn't free keys and values, they need to be freed like this
    {
        if (kh_exist(my_khash_map, khash_map_iter))
        {
            free((char*)kh_key(my_khash_map, khash_map_iter));
            kh_key(my_khash_map, khash_map_iter) = NULL;
            free((unsigned int*)kh_val(my_khash_map, khash_map_iter));
            kh_val(my_khash_map, khash_map_iter) = NULL;
        }
    }
    kh_destroy(khStrInt, my_khash_map);
    if (LOCK_SUCCESS == 1)
    {
        pthread_mutex_destroy(&lock);
    }
}

static void fopen_test(const char *path, const char *mode)
{
    if (SETUP_SUCCEEDED == 1) // skip to calling original fopen if setup failed
    {
        short slash_index = -1;
        for (unsigned short i = 0; path[i] != ''; i++) // look for start of filename by finding last occurence of "https://codereview.stackexchange.com/" character
        {
            if (path[i] == "https://codereview.stackexchange.com/")
            {
                slash_index = i % SHRT_MAX; // will cause key to not be found, but prevents unsigned int overflow
            }
        }
        // +1 so "https://codereview.stackexchange.com/" isn't included. If "https://codereview.stackexchange.com/" not found in path, slash_index stays -1 and entire path argument is checked as key since "path + -1 + 1" is the same as just "path"
        khiter_t khash_map_iter = kh_get(khStrInt, my_khash_map, path + slash_index + 1);
        if (khash_map_iter != kh_end(my_khash_map)) // key found
        {
            unsigned int *delay_sequence = kh_val(my_khash_map, khash_map_iter);
            printf("%s successfully found in khash mapn", path + slash_index + 1);
            pthread_mutex_lock(&lock); // don't let other threads change delay sequence when it's being read or changed
            if (delay_sequence[2] == UINT_MAX) // reset all delay sequences
            {
                unsigned int *sequence_to_reset;
                kh_foreach_value(my_khash_map, sequence_to_reset, {sequence_to_reset[1] = 2;});
                pthread_mutex_unlock(&lock);
                printf("all delay sequences resetn");
            }
            else if (delay_sequence[1] < delay_sequence[0])
            {
                if (delay_sequence[delay_sequence[1]] == UINT_MAX) // reset delay sequence
                {
                    delay_sequence[1] = 2;
                    printf("%s delay sequence resetn", path + slash_index + 1);
                }
                unsigned int sleep_time = delay_sequence[delay_sequence[1]];
                delay_sequence[1] += 1;
                pthread_mutex_unlock(&lock);
                long time_nanoseconds = (sleep_time % 1000) * 1000000;
                struct timespec ts = {sleep_time / 1000, time_nanoseconds};
                //nanosleep(&ts, NULL);
                printf("sleep for %ld second(s) and %ld millisecond(s)n", ts.tv_sec, ts.tv_nsec / 1000000);
            }
            else // delay sequence already finished
            {
                pthread_mutex_unlock(&lock);
                printf("%s delay sequence already finishedn", path + slash_index + 1);
            }
        }
        else // key not found
        {
            printf("%s not found in khash mapn", path + slash_index + 1);
        }
    }
    /*FILE *(*original_fopen)(const char*, const char*);
    original_fopen = dlsym(RTLD_NEXT, "fopen");
    return (*original_fopen)(path, mode);*/
}

static size_t find_longest_input_length(FILE *test_input) // find how much space to give to array used to hold input
{
    size_t longest_input = 1;
    size_t current_input = 1; // starts at 1 for null character
    char ch="";
    while (ch != EOF && current_input < SIZE_MAX)
    {
        ch = fgetc(test_input);
        if (ch == 'n' || ch == EOF)
        {
            if (current_input > longest_input)
            {
                longest_input = current_input;
            }
            current_input = 1;
        }
        else
        {
            current_input += 1;
        }
    }
    if (current_input == SIZE_MAX)
    {
        return SIZE_MAX; // a test path was too big
    }
    return longest_input;
}

static void print_khash()
{
    printf("n---------- khash map current state ----------n");
    const char *key_to_print;
    unsigned int *sequence_to_print;
    kh_foreach(my_khash_map, key_to_print, sequence_to_print,
    {
        printf("%s / ", key_to_print);
        for (unsigned int i = 0; i < sequence_to_print[0]; i++)
        {
            if (sequence_to_print[i] == UINT_MAX) // it's a reset point
            {
                printf("RESET"); // it doesn't need a space because the line always ends here
            }
            else
            {
                printf("%u ", sequence_to_print[i]);
            }
        }
        printf("n");
    });
    printf("---------------------------------------------nn");
}

static void test_all_inputs(FILE *test_input, size_t longest_input)
{
    char *input_array = (char*)malloc(longest_input); // find_longest_input_length accounts for null character.  Always greater than 0, shouldn't return NULL
    if (input_array == NULL)
    {
        printf("malloc failed in test_all_inputsn");
        return;
    }
    size_t ch_position = 0;
    char ch="";
    while (ch != EOF)
    {
        ch = fgetc(test_input);
        if (ch == 'n' || ch == EOF)
        {
            input_array[ch_position] = '';
            ch_position = 0;
            if (ch != EOF) // avoids extra test
            {
                printf("testing fopen input: %sn", input_array);
                fopen_test(input_array, "r");
                print_khash();
            }
        }
        else
        {
            input_array[ch_position] = ch;
            ch_position += 1;
        }
    }
    free((char*)input_array);
    input_array = NULL;
}

int main()
{
    FILE *test_input;
    test_input = fopen("./test_input.txt", "r");
    if (test_input == NULL)
    {
        printf("error opening test_input.txtn");
        return 1;
    }
    size_t longest_input = find_longest_input_length(test_input);
    if (longest_input == SIZE_MAX) // check_file will return SIZE_MAX if a test path is too big
    {
        printf("a test path is too bign");
        fclose(test_input);
        return 1;
    }
    if (fseek(test_input, 0, SEEK_SET) != 0)
    {
        printf("fseek on test_input.txt failedn");
        fclose(test_input);
        return 1;
    }
    printf("nlongest test path (+1 for null character): %zun", longest_input);
    printf("ntest startn");
    print_khash();
    test_all_inputs(test_input, longest_input);
    fclose(test_input);
    printf("test finishnn");
    return 0;
}

khash_setup_header.h:

#include <stdint.h>

static unsigned char TOO_BIG_DELAY_SEEN = 0; // used so user won't be alerted more than once that they entered a delay that's too big

// checks line count to know how much space to give to khash
// updates longest_name and longest_sequence to know how much space to give to arrays used to hold key and value
static void check_file(FILE *my_file, size_t *longest_name, size_t *longest_sequence, unsigned char *longest_delay, unsigned int *line_count, unsigned char digits_in_uint)
{
    unsigned char checking_sequence = 0; // 0 if checking a filename's length, 1 if checking a delay sequence's length
    size_t current_name = 1; // starts at 1 for null character
    size_t current_sequence = 3; // starts at 3 for index holding array length, index holding current position, and first number in delay sequence
    unsigned char current_delay = 1; // starts at 1 for null character
    char ch="";
    // longest_sequence can't be bigger than UINT_MAX because the size is recorded in the array, and the array uses unsigned ints
    while (ch != EOF && current_name < SIZE_MAX && current_sequence < (SIZE_MAX / sizeof(unsigned int)) && *line_count < UINT_MAX && current_sequence < UINT_MAX)
    {
        ch = fgetc(my_file);
        if (ch == EOF || ch == 'n')
        {
            if (checking_sequence == 1)
            {
                if (current_delay > *longest_delay)
                {
                    *longest_delay = current_delay;
                }
                if (current_sequence > *longest_sequence)
                {
                    *longest_sequence = current_sequence;
                }
                checking_sequence = 0;
                current_sequence = 3;
                current_delay = 1;
            }
            *line_count += 1; // if for some reason there are a lot of hash collisions, the user can put empty lines in files_and_delays.txt to try to fix it
            current_name = 1;
        }
        else if (ch == "https://codereview.stackexchange.com/")
        {
            if (checking_sequence == 0)
            {
                if (current_name > *longest_name)
                {
                    *longest_name = current_name;
                }
                checking_sequence = 1;
            }
            else
            {
                if (current_delay > *longest_delay)
                {
                    *longest_delay = current_delay;
                }
                current_sequence += 1;
                current_delay = 1;
            }
        }
        else if (checking_sequence == 0)
        {
            current_name += 1;
        }
        // ignores non-digits and leading zeros. maximum space is digits_in_uint + 1. +1 is for null character
        else if (((ch >= 49 && ch <= 57) || (ch == 48 && current_delay > 1)) && current_delay < digits_in_uint + 1)
        {
            current_delay += 1;
        }
    }
    if (current_name == SIZE_MAX || current_sequence == (SIZE_MAX / sizeof(unsigned int)) || current_sequence == UINT_MAX)
    {
        *line_count = UINT_MAX; // something was too big
    }
}

// reads the next filename and writes it in name_array
static size_t read_name(FILE *my_file, char *ch, char *name_array)
{
    size_t ch_position = 0;
    *ch = fgetc(my_file);
    while (*ch != EOF && *ch != 'n' && *ch != "https://codereview.stackexchange.com/")
    {
        name_array[ch_position] = *ch;
        ch_position += 1;
        *ch = fgetc(my_file);
    }
    name_array[ch_position] = '';
    if (*ch == "https://codereview.stackexchange.com/") // filename given and line didn't end abruptly
    {
        for (; ch_position > 0 && (name_array[ch_position - 1] == ' ' || name_array[ch_position - 1] == 't'); ch_position--)
        {
            name_array[ch_position - 1] = ''; // remove whitespace between "https://codereview.stackexchange.com/" and end of filename if the user has it written like that
        }
    }
    return ch_position + 1; // +1 for null character
}

static unsigned int my_atoi(char *delay_array, unsigned int max_power_of_ten, unsigned char digits_in_uint, unsigned char number_position)
{
    unsigned char delay_too_big = 0;
    unsigned int current_power_of_ten = 1;
    unsigned int accumulator = 0;
    if (delay_array[0] == '') // no digits found, assume delay is 0
    {
        return 0;
    }
    // delay has more digits than UINT_MAX or it has as many digits and the most significant digit is bigger in the delay than in UINT_MAX
    else if (delay_array[0] == '+' || (number_position == digits_in_uint && delay_array[0] - 48 > UINT_MAX / max_power_of_ten))
    {
        delay_too_big = 1;
    }
    else
    {
        number_position -= 1;
        for (; number_position > 0; number_position--) // sum everything except most significant digit
        {
            accumulator += ((delay_array[number_position] - 48) * current_power_of_ten);
            current_power_of_ten *= 10;
        }
        // checking that adding final value won't make accumulator overflow
        // delay_array[0] won't be bigger than UINT_MAX / max_power_of_ten here because the other else if block checks for that
        if (current_power_of_ten == max_power_of_ten && UINT_MAX - ((delay_array[0] - 48) * max_power_of_ten) - 1 < accumulator)
        {
            delay_too_big = 1;
        }
        else
        {
            accumulator += ((delay_array[0] - 48) * current_power_of_ten);
        }
    }
    if (delay_too_big == 1)
    {
        if (TOO_BIG_DELAY_SEEN == 0)
        {
            printf("nWARNING: maximum delay time is %un", UINT_MAX - 1);
            TOO_BIG_DELAY_SEEN = 1; // TOO_BIG_DELAY_SEEN only changed here
        }
        return UINT_MAX - 1;
    }
    return accumulator;
}

// reads the next delay sequence and writes it in sequence_array
// also returns how many numbers are in the sequence so the right amount will be copied into the array given to khash
// there can't be more than UINT_MAX delays in one sequence
static unsigned int read_sequence(FILE *my_file, char *ch, char *delay_array, unsigned int *sequence_array, unsigned int max_power_of_ten, unsigned char digits_in_uint)
{
    unsigned char number_position = 0; // next digit in current delay time. Also used in my_atoi to see if delay is too big
    size_t sequence_position = 2; // starts at 2 because first two indexes are used to store sequence length and what the next delay is
    delay_array[0] = ''; // initializing to use in condition
    while (*ch != EOF && *ch != 'n')
    {
        *ch = fgetc(my_file);
        if (*ch == EOF || *ch == 'n')
        {
            delay_array[number_position] = '';
            unsigned int last_delay = my_atoi(delay_array, max_power_of_ten, digits_in_uint, number_position); // atoi can't be used because delay_array might have wrong input
            if (last_delay != 0) // no point adding 0 as final delay time
            {
                sequence_array[sequence_position] = last_delay;
                sequence_position += 1;
            }
        }
        else if (*ch == "https://codereview.stackexchange.com/") // end of current delay, about to read next delay
        {
            delay_array[number_position] = '';
            sequence_array[sequence_position] = my_atoi(delay_array, max_power_of_ten, digits_in_uint, number_position); // atoi can't be used because delay_array might have wrong input
            sequence_position += 1;
            number_position = 0;
        }
        // ch is a digit and the digit isn't a leading zero and the maximum delay isn't reached
        else if (((*ch >= 49 && *ch <= 57) || (*ch == 48 && number_position > 0)) && delay_array[0] != '+')
        {
            if (number_position != digits_in_uint) // if it's already equal to digits_in_uint then the delay is too big
            {
                delay_array[number_position] = *ch;
                number_position += 1;
            }
            else
            {
                delay_array[0] = '+'; // '+' used to check if the delay is too big in my_atoi
            }
        }
        else if (*ch == '-')
        {
            sequence_array[sequence_position] = UINT_MAX; // used for saying reset should happen
            sequence_position += 1;
            break; // no point checking delay times beyond where reset happens
        }
    }
    while (*ch != EOF && *ch != 'n') // finish reading line if it's not already read
    {
        *ch = fgetc(my_file);
    }
    if (sequence_array[sequence_position - 1] != UINT_MAX) // numbers entered and not a reset-all-sequences file
    {
        for (unsigned int i = sequence_position - 1; i > 1 && sequence_array[i] == 0; i--) // don't copy pointless zeros
        {
            sequence_position -= 1;
        }
    }
    return sequence_position;
}

static unsigned char set_keys_and_values(kh_khStrInt_t *my_khash_map, FILE *my_file, size_t longest_name, size_t longest_sequence, unsigned char longest_delay, unsigned int line_count, unsigned int max_power_of_ten, unsigned char digits_in_uint)
{
    khiter_t khash_map_iter;
    char *name_array = (char*)malloc(longest_name); // array for filename text. Always greater than 0, shouldn't return NULL.
    unsigned int *sequence_array = (unsigned int*)malloc(longest_sequence * sizeof(unsigned int)); // array for delay sequence. Always greater than 0, shouldn't return NULL.
    char *delay_array = (char*)malloc(longest_delay); // array for delay text. Always greater than 0, shouldn't return NULL.
    if (name_array == NULL || sequence_array == NULL || delay_array == NULL)
    {
        return 0;
    }
    if ((size_t)(UINT_MAX / 2) < line_count) // khash resizes by powers of two, so if line_count is this big then khash can't resize any bigger
    {
        kh_resize(khStrInt, my_khash_map, UINT_MAX);
    }
    else
    {
        kh_resize(khStrInt, my_khash_map, (unsigned int)((line_count / 0.7) + 1)); // khash map will always be at 70% or less capacity
    }
    sequence_array[0] = 2; // first index stores length of array
    sequence_array[1] = 2; // second index stores what the next delay to use in the sequence is
    size_t chars_to_copy;
    unsigned int numbers_to_copy;
    char ch="";
    while (ch != EOF)
    {
        chars_to_copy = read_name(my_file, &ch, name_array); // accounts for null character
        if (ch != EOF && ch != 'n') // checks if the line had a delay sequence. If it didn't, moves on to next line
        {
            numbers_to_copy = read_sequence(my_file, &ch, delay_array, sequence_array, max_power_of_ten, digits_in_uint);
            if (numbers_to_copy > 2) // read_sequence returns 2 if either the sequence was pointless or the user didn't enter anything for it
            {
                int ret;
                sequence_array[0] = numbers_to_copy;
                char *key_for_khash = (char*)malloc(chars_to_copy); // always greater than 0, shouldn't return NULL
                unsigned int *value_for_khash = (unsigned int*)malloc(numbers_to_copy * sizeof(unsigned int)); // always greater than 0, shouldn't return NULL
                if (key_for_khash == NULL || value_for_khash == NULL)
                {
                    return 0;
                }
                for (size_t i = 0; i < chars_to_copy; i++)
                {
                    key_for_khash[i] = name_array[i];
                }
                for (unsigned int i = 0; i < numbers_to_copy; i++)
                {
                    value_for_khash[i] = sequence_array[i];
                }
                khash_map_iter = kh_put(khStrInt, my_khash_map, key_for_khash, &ret);
                kh_val(my_khash_map, khash_map_iter) = value_for_khash;
            }
        }
        sequence_array[0] = 2;
        sequence_array[1] = 2;
    }
    free((char*)name_array);
    name_array = NULL;
    free((unsigned int*)sequence_array);
    sequence_array = NULL;
    free((char*)delay_array);
    delay_array = NULL;
    return 1;
}

static unsigned char prepare_to_setup(kh_khStrInt_t *my_khash_map, FILE *my_file, size_t *longest_name, size_t *longest_sequence, unsigned char *longest_delay, unsigned int *line_count)
{
    unsigned int max_power_of_ten = 1; // biggest power of 10 less than UINT_MAX, used in my_atoi
    unsigned char digits_in_uint = 1; // how many digits are in UINT_MAX, used for helping to see if a delay is too big
    for (; UINT_MAX / max_power_of_ten > 9; max_power_of_ten *= 10)
    {
        digits_in_uint += 1;
    }
    check_file(my_file, longest_name, longest_sequence, longest_delay, line_count, digits_in_uint);
    if (fseek(my_file, 0, SEEK_SET) != 0)
    {
        printf("fseek on files_and_delays.txt failedn");
        return 0;
    }
    // longest_sequence can't be bigger than UINT_MAX because the size is recorded in the array, and the array uses unsigned ints
    if (*line_count == UINT_MAX) // check_file will return UINT_MAX if something is too big
    {
        printf("filename, delay, delay sequence, and/or number of files is too bign");
        return 0;
    }
    unsigned char khash_succeeded = set_keys_and_values(my_khash_map, my_file, *longest_name, *longest_sequence, *longest_delay, *line_count, max_power_of_ten, digits_in_uint);
    if (khash_succeeded == 0) // set_keys_and_values returns 0 if malloc fails
    {
        printf("malloc failed in set_keys_and_valuesn");
        return 0;
    }
    return 1;
}

Вот код для Windows:

load_extender_exe.cpp:

#include <tchar.h>
#include <iostream>
#include <string>
#include <Windows.h>
#include <easyhook.h>

void get_exit_input()
{
    std::wcout << "Press Enter to exit";
    std::wstring input;
    std::getline(std::wcin, input);
}

int _tmain(int argc, _TCHAR* argv[])
{
    WCHAR* dllToInject32 = NULL;
    WCHAR* dllToInject64 = NULL;
    LPCWSTR lpApplicationName = argv[0];
    DWORD lpBinaryType;
    if (GetBinaryType(lpApplicationName, &lpBinaryType) == 0 || (lpBinaryType != 0 && lpBinaryType != 6))
    {
        std::wcout << "ERROR: This exe wasn't identified as 32-bit or as 64-bit";
        get_exit_input();
        return 1;
    }
    else if (lpBinaryType == 0)
    {
        dllToInject32 = (WCHAR*)L"load_extender_32.dll";
    }
    else
    {
        dllToInject64 = (WCHAR*)L"load_extender_64.dll";
    }
    DWORD processId;
    std::wcout << "Enter the target process Id: ";
    std::cin >> processId;

    wprintf(L"Attempting to inject dllnn");

    // Inject dllToInject into the target process Id, passing 
    // freqOffset as the pass through data.
    NTSTATUS nt = RhInjectLibrary(
        processId,   // The process to inject into
        0,           // ThreadId to wake up upon injection
        EASYHOOK_INJECT_DEFAULT,
        dllToInject32, // 32-bit
        dllToInject64, // 64-bit
        NULL, // data to send to injected DLL entry point
        0 // size of data to send
    );

    if (nt != 0)
    {
        printf("RhInjectLibrary failed with error code = %dn", nt);
        PWCHAR err = RtlGetLastErrorString();
        std::wcout << err << "n";
        get_exit_input();
        return 1;
    }
    else
    {
        std::wcout << L"Library injected successfully.n";
    }

    get_exit_input();
    return 0;
}

load_extender_dll.cpp:

#include <string>
#include <mutex>
#include <Windows.h>
#include <easyhook.h>
#include "robin_hood.h"
// thank you for making this hash map, martinus.
// here's his github: https://github.com/martinus/robin-hood-hashing

namespace rh = robin_hood;

// included here so alias can be used
#include "rh_map_setup_header.h"

static class rh_map_handler // this is used to free all the unsigned int array memory when the program ends
{
public:
    rh::unordered_flat_map<std::wstring, unsigned int*> obj_map;

    ~rh_map_handler()
    {
        for (auto it = obj_map.begin(); it != obj_map.end();)
        {
            free((unsigned int*)it->second);
            it->second = NULL;
            it = obj_map.erase(it); // erasing so obj_map's destructor doesn't have to do as much
        }
    }
};

// my_map_handler is global so it will free all the unsigned int arrays when the program ends
// my_rh_map and mtx are global so the NtOpenFile hook function can see them
static rh_map_handler my_map_handler;
static rh::unordered_flat_map<std::wstring, unsigned int*>& my_rh_map = my_map_handler.obj_map;
static std::mutex mtx;

static unsigned char rh_map_setup()
{
    FILE* my_file = NULL;
    if (fopen_s(&my_file, ".\files_and_delays.txt", "r") != 0)
    {
        return 0;
    }
    size_t longest_name = 1; // starts at 1 for null character
    size_t longest_sequence = 3; // starts at 3 for index holding array length, index holding current position, and first number in delay sequence
    unsigned char longest_delay = 1; // starts at 1 for null character
    size_t line_count = 1; // starts at 1 because there's always at least 1 line
    unsigned char rh_map_succeeded = prepare_to_setup(my_rh_map, my_file, &longest_name, &longest_sequence, &longest_delay, &line_count);
    if (my_file != NULL) // this makes a warning go away
    {
        fclose(my_file);
    }
    if (rh_map_succeeded == 0) // rh map failed to be made. Error message is printed in rh_map_setup_header in prepare_to_setup.
    {
        return 0;
    }
    return 1;
}

static unsigned char SETUP_SUCCEEDED = rh_map_setup(); // if this is 0 then the hook function skips to calling the real function

static NTSTATUS WINAPI NtOpenFileHook(
    PHANDLE           FileHandle,
    ACCESS_MASK        DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    PIO_STATUS_BLOCK  IoStatusBlock,
    ULONG              ShareAccess,
    ULONG              OpenOptions) {
    if (SETUP_SUCCEEDED == 1) // skip to calling original NtOpenProcess if setup failed
    {
        std::wstring file_path = ObjectAttributes->ObjectName->Buffer;
        // +1 so '' isn't included. If '' not found in path, the whole wstring is checked because npos is -1
        std::wstring file_name = file_path.substr(file_path.rfind(L"\") + 1);
        rh::unordered_flat_map<std::wstring, unsigned int*>::const_iterator rh_map_iter = my_rh_map.find(file_name);
        if (rh_map_iter != my_rh_map.end()) // key found
        {
            unsigned int* delay_sequence = rh_map_iter->second;
            mtx.lock(); // don't let other threads change delay sequence when it's being read or changed
            if (delay_sequence[2] == UINT_MAX) // reset all delay sequences
            {
                for (auto& it : my_rh_map)
                {
                    it.second[1] = 2;
                }
                mtx.unlock();
            }
            else if (delay_sequence[1] < delay_sequence[0])
            {
                if (delay_sequence[delay_sequence[1]] == UINT_MAX) // reset delay sequence
                {
                    delay_sequence[1] = 2;
                }
                unsigned int sleep_time = delay_sequence[delay_sequence[1]];
                delay_sequence[1] += 1;
                mtx.unlock();
                Sleep(sleep_time);
            }
            else // delay sequence already finished
            {
                mtx.unlock();
            }
        }
    }
    return NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
}

extern "C" void __declspec(dllexport) __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO * inRemoteInfo);

void __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* inRemoteInfo) {
    HOOK_TRACE_INFO hHook1 = { NULL };
    LhInstallHook(
        GetProcAddress(GetModuleHandle(TEXT("ntdll")), "NtOpenFile"),
        NtOpenFileHook,
        NULL,
        &hHook1);

    ULONG ACLEntries[1] = { 0 };
    LhSetExclusiveACL(ACLEntries, 1, &hHook1);
    return;
}

load_extender_test.cpp:

#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <mutex>
#include "robin_hood.h"
// thank you for making this hash map, martinus.
// here's his github: https://github.com/martinus/robin-hood-hashing

namespace rh = robin_hood;

// included here so alias can be used
#include "rh_map_setup_header.h"

static class rh_map_handler // this is used to free all the unsigned int array memory when the program ends
{
public:
    rh::unordered_flat_map<std::wstring, unsigned int*> obj_map;

    ~rh_map_handler()
    {
        for (auto it = obj_map.begin(); it != obj_map.end();)
        {
            free((unsigned int*)it->second);
            it->second = NULL;
            it = obj_map.erase(it); // erasing so obj_map's destructor doesn't have to do as much
        }
    }
};

// my_map_handler is global so it will free all the unsigned int arrays when the program ends
// my_rh_map and mtx are global so the NtOpenFile hook function can see them
static rh_map_handler my_map_handler;
static rh::unordered_flat_map<std::wstring, unsigned int*>& my_rh_map = my_map_handler.obj_map;
static std::mutex mtx;

static unsigned char rh_map_setup()
{
    FILE* my_file = NULL;
    if (fopen_s(&my_file, ".\files_and_delays.txt", "r") != 0)
    {
        printf("error opening files_and_delays.txtn");
        return 0;
    }
    size_t longest_name = 1; // starts at 1 for null character
    size_t longest_sequence = 3; // starts at 3 for index holding array length, index holding current position, and first number in delay sequence
    unsigned char longest_delay = 1; // starts at 1 for null character
    size_t line_count = 1; // starts at 1 because there's always at least 1 line
    unsigned char rh_map_succeeded = prepare_to_setup(my_rh_map, my_file, &longest_name, &longest_sequence, &longest_delay, &line_count);
    if (my_file != NULL) // this makes a warning go away
    {
        fclose(my_file);
    }
    if (rh_map_succeeded == 0) // rh map failed to be made. Error message is printed in rh_map_setup_header in prepare_to_setup.
    {
        return 0;
    }
    printf("nlongest filename (plus null character and whitespace in file): %zun"
        "longest delay sequence (+2 for current delay and sequence length): %zun"
        "most digits in delay (+1 for null character, max delay is UINT_MAX): %un"
        "lines in file (overestimates slightly): %zun", longest_name, longest_sequence, longest_delay, line_count);
    return 1;
}

static unsigned char SETUP_SUCCEEDED = rh_map_setup(); // if this is 0 then the hook function skips to calling the real function

static void NtOpenFile_test(std::wstring file_path)
{
    if (SETUP_SUCCEEDED == 1) // skip to calling original NtOpenFile if setup failed
    {
        // +1 so '' isn't included. If '' not found in path, the whole wstring is checked because npos is -1
        std::wstring file_name = file_path.substr(file_path.rfind(L"\") + 1);
        rh::unordered_flat_map<std::wstring, unsigned int*>::const_iterator rh_map_iter = my_rh_map.find(file_name);
        if (rh_map_iter != my_rh_map.end()) // key found
        {
            unsigned int* delay_sequence = rh_map_iter->second;
            printf("%ls successfully found in hash mapn", file_name.c_str());
            mtx.lock(); // don't let other threads change delay sequence when it's being read or changed
            if (delay_sequence[2] == UINT_MAX) // reset all delay sequences
            {
                for (auto& it : my_rh_map)
                {
                    it.second[1] = 2;
                }
                mtx.unlock();
                printf("all delay sequences resetn");
            }
            else if (delay_sequence[1] < delay_sequence[0])
            {
                if (delay_sequence[delay_sequence[1]] == UINT_MAX) // reset delay sequence
                {
                    delay_sequence[1] = 2;
                    printf("%ls delay sequence resetn", file_name.c_str());
                }
                unsigned int sleep_time = delay_sequence[delay_sequence[1]];
                delay_sequence[1] += 1;
                mtx.unlock();
                //Sleep(sleep_time);
                printf("sleep for %u second(s) and %u millisecond(s)n", sleep_time / 1000, sleep_time % 1000);
            }
            else // delay sequence already finished
            {
                mtx.unlock();
                printf("%ls delay sequence already finishedn", file_name.c_str());
            }
        }
        else // key not found
        {
            printf("%ls not found in khash mapn", file_name.c_str());
        }
    }
    // NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
}

static size_t find_longest_input_length(FILE* test_input) // find how much space to give to array used to hold input
{
    size_t longest_input = 1;
    size_t current_input = 1; // starts at 1 for null character
    wchar_t ch = L'';
    while (ch != WEOF && current_input < SIZE_MAX)
    {
        ch = fgetwc(test_input);
        if (ch == L'n' || ch == WEOF)
        {
            if (current_input > longest_input)
            {
                longest_input = current_input;
            }
            current_input = 1;
        }
        else
        {
            current_input += 1;
        }
    }
    if (current_input == SIZE_MAX)
    {
        return SIZE_MAX; // a test path was too big
    }
    return longest_input;
}

static void print_rh_map()
{
    printf("n---------- khash map current state ----------n");
    for (auto& it : my_rh_map)
    {
        printf("%ls / ", it.first.c_str());
        unsigned int* sequence_to_print = it.second;
        for (unsigned int i = 0; i < sequence_to_print[0]; i++)
        {
            if (sequence_to_print[i] == UINT_MAX) // it's a reset point
            {
                printf("RESET"); // it doesn't need a space because the line always ends here
            }
            else
            {
                printf("%u ", sequence_to_print[i]);
            }
        }
        printf("n");
    }
    printf("---------------------------------------------nn");
}

static void test_all_inputs(FILE* test_input, size_t longest_input)
{
    wchar_t* input_array = (wchar_t*)malloc(longest_input * sizeof(wchar_t)); // find_longest_input_length accounts for null character.  Always greater than 0, shouldn't return NULL
    if (input_array == NULL)
    {
        printf("malloc failed in test_all_inputsn");
        return;
    }
    size_t ch_position = 0;
    wchar_t ch = L'';
    while (ch != WEOF)
    {
        ch = fgetwc(test_input);
        if (ch == L'n' || ch == WEOF)
        {
            input_array[ch_position] = L'';
            ch_position = 0;
            if (ch != WEOF) // avoids extra test
            {
                printf("testing NtOpenFile input: %lsn", input_array);
                NtOpenFile_test((std::wstring)input_array);
                print_rh_map();
            }
        }
        else
        {
            input_array[ch_position] = ch;
            ch_position += 1;
        }
    }
    free((wchar_t*)input_array);
    input_array = NULL;
}

static void get_exit_input()
{
    printf("Press Enter to exitn");
    std::wstring input;
    std::getline(std::wcin, input);
}

static int _tmain(int argc, _TCHAR* argv[])
{
    LPCWSTR lpApplicationName = argv[0];
    DWORD lpBinaryType;
    if (GetBinaryType(lpApplicationName, &lpBinaryType) == 0)
    {
        printf("nERROR: couldn't read if this file is 32 bit or 64 bitn");
        get_exit_input();
        return 1;
    }
    else if (lpBinaryType != 0)
    {
        printf("nError: this file was incorrectly identified as not 32-bitn");
        get_exit_input();
        return 1;
    }
    else
    {
        printf("nthis file was correctly identified as 32-bitn");
    }
    FILE* test_input;
    if (fopen_s(&test_input, ".\test_input.txt", "r") != 0)
    {
        printf("error opening test_input.txtn");
        get_exit_input();
        return 1;
    }
    size_t longest_input = find_longest_input_length(test_input);
    if (longest_input == SIZE_MAX) // check_file will return SIZE_MAX if a test path is too big
    {
        printf("a test path is too bign");
        if (test_input != NULL) // this makes a warning go away
        {
            fclose(test_input);
        }
        get_exit_input();
        return 1;
    }
    // test_input != NULL check makes warning go away
    if (test_input != NULL && fseek(test_input, 0, SEEK_SET) != 0)
    {
        printf("fseek on test_input.txt failedn");
        if (test_input != NULL) // this makes a warning go away
        {
            fclose(test_input);
        }
        get_exit_input();
        return 1;
    }
    printf("nlongest test path (+1 for null character): %zun", longest_input);
    printf("ntest startn");
    print_rh_map();
    test_all_inputs(test_input, longest_input);
    if (test_input != NULL) // this makes a warning go away
    {
        fclose(test_input);
    }
    get_exit_input();
    return 0;
}

rh_map_setup_header:

#include <stdint.h>

// printf calls are used for debugging in load_extender_test.exe

static unsigned char TOO_BIG_DELAY_SEEN = 0; // used so user won't be alerted more than once that they entered a delay that's too big

// checks line count to know how much space to give to robin_hood map
// updates longest_name and longest_sequence to know how much space to give to arrays used to hold key and value
static void check_file(FILE* my_file, size_t* longest_name, size_t* longest_sequence, unsigned char* longest_delay, size_t* line_count, unsigned char digits_in_uint, size_t map_max_size)
{
    unsigned char checking_sequence = 0; // 0 if checking a filename's length, 1 if checking a delay sequence's length
    size_t current_name = 1; // starts at 1 for null character
    size_t current_sequence = 3; // starts at 3 for index holding array length, index holding current position, and first number in delay sequence
    unsigned char current_delay = 1; // starts at 1 for null character
    wchar_t ch = L'';
    // longest_sequence can't be bigger than UINT_MAX because the size is recorded in the array, and the array uses unsigned ints
    while (ch != WEOF && current_name < SIZE_MAX && current_sequence < (SIZE_MAX / sizeof(unsigned int)) && *line_count < SIZE_MAX && current_sequence < UINT_MAX)
    {
        ch = fgetwc(my_file);
        if (ch == WEOF || ch == L'n')
        {
            if (checking_sequence == 1)
            {
                if (current_delay > * longest_delay)
                {
                    *longest_delay = current_delay;
                }
                if (current_sequence > * longest_sequence)
                {
                    *longest_sequence = current_sequence;
                }
                checking_sequence = 0;
                current_sequence = 3;
                current_delay = 1;
            }
            *line_count += 1; // if for some reason there are a lot of hash collisions, the user can put empty lines in files_and_delays.txt to try to fix it
            current_name = 1;
        }
        else if (ch == L"https://codereview.stackexchange.com/")
        {
            if (checking_sequence == 0)
            {
                if (current_name > * longest_name)
                {
                    *longest_name = current_name;
                }
                checking_sequence = 1;
            }
            else
            {
                if (current_delay > * longest_delay)
                {
                    *longest_delay = current_delay;
                }
                current_sequence += 1;
                current_delay = 1;
            }
        }
        else if (checking_sequence == 0)
        {
            current_name += 1;
        }
        // ignores non-digits and leading zeros. maximum space is digits_in_uint + 1. +1 is for null character
        else if (((ch >= 49 && ch <= 57) || (ch == 48 && current_delay > 1)) && current_delay < digits_in_uint + 1)
        {
            current_delay += 1;
        }
    }
    if (current_name == SIZE_MAX ||
        current_sequence == (SIZE_MAX / sizeof(unsigned int)) ||
        current_sequence == UINT_MAX
        || *line_count > map_max_size)
    {
        *line_count = SIZE_MAX; // something was too big
    }
}

// reads the next filename and writes it in name_array
static void read_name(FILE* my_file, wchar_t* ch, wchar_t* name_array)
{
    size_t ch_position = 0;
    *ch = fgetwc(my_file);
    while (*ch != WEOF && *ch != L'n' && *ch != L"https://codereview.stackexchange.com/")
    {
        name_array[ch_position] = *ch;
        ch_position += 1;
        *ch = fgetwc(my_file);
    }
    name_array[ch_position] = L'';
    if (*ch == L"https://codereview.stackexchange.com/") // filename given and line didn't end abruptly
    {
        for (; ch_position > 0 && (name_array[ch_position - 1] == L' ' || name_array[ch_position - 1] == L't'); ch_position--)
        {
            name_array[ch_position - 1] = L''; // remove whitespace between "https://codereview.stackexchange.com/" and end of filename if the user has it written like that
        }
    }
}

static unsigned int my_atoi(wchar_t* delay_array, unsigned int max_power_of_ten, unsigned char digits_in_uint, unsigned char number_position)
{
    unsigned char delay_too_big = 0;
    unsigned int current_power_of_ten = 1;
    unsigned int accumulator = 0;
    if (delay_array[0] == L'') // no digits found, assume delay is 0
    {
        return 0;
    }
    // delay has more digits than UINT_MAX or it has as many digits and the most significant digit is bigger in the delay than in UINT_MAX
    else if (delay_array[0] == L'+' || (number_position == digits_in_uint && delay_array[0] - 48 > UINT_MAX / max_power_of_ten))
    {
        delay_too_big = 1;
    }
    else
    {
        number_position -= 1;
        for (; number_position > 0; number_position--) // sum everything except most significant digit
        {
            accumulator += ((delay_array[number_position] - 48) * current_power_of_ten);
            current_power_of_ten *= 10;
        }
        // checking that adding final value won't make accumulator overflow
        // delay_array[0] won't be bigger than UINT_MAX / max_power_of_ten here because the other else if block checks for that
        if (current_power_of_ten == max_power_of_ten && UINT_MAX - ((delay_array[0] - 48) * max_power_of_ten) - 1 < accumulator)
        {
            delay_too_big = 1;
        }
        else
        {
            accumulator += ((delay_array[0] - 48) * current_power_of_ten);
        }
    }
    if (delay_too_big == 1)
    {
        if (TOO_BIG_DELAY_SEEN == 0)
        {
            printf("nWARNING: maximum delay time is %un", UINT_MAX - 1);
            TOO_BIG_DELAY_SEEN = 1; // TOO_BIG_DELAY_SEEN only changed here
        }
        return UINT_MAX - 1;
    }
    return accumulator;
}

// reads the next delay sequence and writes it in sequence_array
// also returns how many numbers are in the sequence so the right amount will be copied into the array given to robin_hood map
// there can't be more than UINT_MAX delays in one sequence
static unsigned int read_sequence(FILE* my_file, wchar_t* ch, wchar_t* delay_array, unsigned int* sequence_array, unsigned int max_power_of_ten, unsigned char digits_in_uint)
{
    unsigned char number_position = 0; // next digit in current delay time. Also used in my_atoi to see if delay is too big
    size_t sequence_position = 2; // starts at 2 because first two indexes are used to store sequence length and what the next delay is
    delay_array[0] = L''; // initializing to use in condition
    while (*ch != WEOF && *ch != L'n')
    {
        *ch = fgetwc(my_file);
        if (*ch == WEOF || *ch == L'n')
        {
            delay_array[number_position] = L'';
            unsigned int last_delay = my_atoi(delay_array, max_power_of_ten, digits_in_uint, number_position); // atoi can't be used because delay_array might have wrong input
            if (last_delay != 0) // no point adding 0 as final delay time
            {
                sequence_array[sequence_position] = last_delay;
                sequence_position += 1;
            }
        }
        else if (*ch == L"https://codereview.stackexchange.com/") // end of current delay, about to read next delay
        {
            delay_array[number_position] = L'';
            sequence_array[sequence_position] = my_atoi(delay_array, max_power_of_ten, digits_in_uint, number_position); // atoi can't be used because delay_array might have wrong input
            sequence_position += 1;
            number_position = 0;
        }
        // ch is a digit and the digit isn't a leading zero and the maximum delay isn't reached
        else if (((*ch >= 49 && *ch <= 57) || (*ch == 48 && number_position > 0)) && delay_array[0] != L'+')
        {
            if (number_position != digits_in_uint) // if it's already equal to digits_in_uint then the delay is too big
            {
                delay_array[number_position] = *ch;
                number_position += 1;
            }
            else
            {
                delay_array[0] = L'+'; // '+' used to check if the delay is too big in my_atoi
            }
        }
        else if (*ch == L'-')
        {
            sequence_array[sequence_position] = UINT_MAX; // used for saying reset should happen
            sequence_position += 1;
            break; // no point checking delay times beyond where reset happens
        }
    }
    while (*ch != WEOF && *ch != L'n') // finish reading line if it's not already read
    {
        *ch = fgetwc(my_file);
    }
    if (sequence_array[sequence_position - 1] != UINT_MAX) // numbers entered and not a reset-all-sequences file
    {
        for (unsigned int i = sequence_position - 1; i > 1 && sequence_array[i] == 0; i--) // don't copy pointless zeros
        {
            sequence_position -= 1;
        }
    }
    return sequence_position;
}

static unsigned char set_keys_and_values(rh::unordered_flat_map<std::wstring, unsigned int*>& my_rh_map, FILE* my_file, size_t longest_name, size_t longest_sequence, unsigned char longest_delay, size_t line_count, unsigned int max_power_of_ten, unsigned char digits_in_uint)
{
    wchar_t* name_array = (wchar_t*)malloc(longest_name * sizeof(wchar_t)); // array for filename text. Always greater than 0, shouldn't return NULL. Null character is accounted for.
    unsigned int* sequence_array = (unsigned int*)malloc(longest_sequence * sizeof(unsigned int)); // array for delay sequence. Always greater than 0, shouldn't return NULL.
    wchar_t* delay_array = (wchar_t*)malloc(longest_delay * sizeof(wchar_t)); // array for delay text. Always greater than 0, shouldn't return NULL.
    if (name_array == NULL || sequence_array == NULL || delay_array == NULL)
    {
        return 0;
    }
    my_rh_map.reserve(line_count);
    sequence_array[0] = 2; // first index stores length of array
    sequence_array[1] = 2; // second index stores what the next delay to use in the sequence is
    size_t chars_to_copy;
    unsigned int numbers_to_copy;
    wchar_t ch = L'';
    while (ch != WEOF)
    {
        read_name(my_file, &ch, name_array); // puts characters in name_array
        if (ch != WEOF && ch != L'n') // checks if the line had a delay sequence. If it didn't, moves on to next line
        {
            numbers_to_copy = read_sequence(my_file, &ch, delay_array, sequence_array, max_power_of_ten, digits_in_uint);
            if (numbers_to_copy > 2) // read_sequence returns 2 if either the sequence was pointless or the user didn't enter anything for it
            {
                sequence_array[0] = numbers_to_copy;
                unsigned int* value_for_rh = (unsigned int*)malloc(numbers_to_copy * sizeof(unsigned int)); // always greater than 0, shouldn't return NULL
                if (value_for_rh == NULL)
                {
                    return 0;
                }
                std::wstring key_for_rh(name_array); // null character added to name_array in read_name function
                for (unsigned int i = 0; i < numbers_to_copy; i++)
                {
                    value_for_rh[i] = sequence_array[i];
                }
                my_rh_map[key_for_rh] = value_for_rh;
            }
        }
        sequence_array[0] = 2;
        sequence_array[1] = 2;
    }
    free((wchar_t*)name_array);
    name_array = NULL;
    free((unsigned int*)sequence_array);
    sequence_array = NULL;
    free((wchar_t*)delay_array);
    delay_array = NULL;
    return 1;
}

static unsigned char prepare_to_setup(rh::unordered_flat_map<std::wstring, unsigned int*>& my_rh_map, FILE* my_file, size_t* longest_name, size_t* longest_sequence, unsigned char* longest_delay, size_t* line_count)
{
    unsigned int max_power_of_ten = 1; // biggest power of 10 less than UINT_MAX, used in my_atoi
    unsigned char digits_in_uint = 1; // how many digits are in UINT_MAX, used for helping to see if a delay is too big
    for (; UINT_MAX / max_power_of_ten > 9; max_power_of_ten *= 10)
    {
        digits_in_uint += 1;
    }
    size_t map_max_size = my_rh_map.max_size();
    check_file(my_file, longest_name, longest_sequence, longest_delay, line_count, digits_in_uint, map_max_size);
    if (fseek(my_file, 0, SEEK_SET) != 0)
    {
        printf("fseek on files_and_delays.txt failedn");
        return 0;
    }
    // longest_sequence can't be bigger than UINT_MAX because the size is recorded in the array, and the array uses unsigned ints
    if (*line_count == SIZE_MAX) // check_file will return UINT_MAX if something is too big
    {
        printf("filename, delay, delay sequence, and/or number of files is too bign");
        return 0;
    }
    unsigned char rh_succeeded = set_keys_and_values(my_rh_map, my_file, *longest_name, *longest_sequence, *longest_delay, *line_count, max_power_of_ten, digits_in_uint);
    if (rh_succeeded == 0) // set_keys_and_values returns 0 if malloc fails
    {
        printf("malloc failed in set_keys_and_valuesn");
        return 0;
    }
    return 1;
}
```

1 ответ
1

Воспользуйтесь стандартной библиотекой

Вы изобретаете множество колес в своих программах. Стандартная библиотека, которую вы используете, уже многое сделает за вас. Даже в C в Linux вы можете использовать функции POSIX для управления хеш-таблицами: hcreate() и hsearch(). Но даже лучше, вероятно, написать версии для Linux и Windows на C ++ и использовать std::unordered_map. С помощью C ++ вы также можете избежать всего ручного управления памятью, которое вы делаете.

Вы также читаете входные файлы посимвольно, что очень медленно. Почему бы не использовать std::getline() читать целые строки за раз, а затем разделять их по мере необходимости? Или убедитесь, что ваш ввод находится в таком формате, что вы можете использовать >> оператор для чтения каждого элемента. Вы даже переопределили парсинг чисел в my_atoi(). Гораздо лучше оставить это стандартной функцией, например std::stoi().

В вашем коде почти нет ничего, что должно быть низким уровнем. Сделайте все максимально простым и легким.

Сделайте свой код максимально независимым от платформы

Вам не понадобятся две совершенно разные реализации. Можно поделиться большим количеством кода. Единственное, что различается между Linux и Windows, — это вызовы функций, которые необходимо перехватывать, и способы их перехвата. Вы должны иметь возможность совместно использовать обработку входного файла, поиск в хэш-таблице и спящий режим. Так что fopen() хук должен выглядеть так просто:

static auto original_fopen = reinterpret_cast<FILE *(*)(const char *path, const char *mode)>(dlsym(RTLD_NEXT, "fopen"));

FILE *fopen(const char *path, const char *mode)
{
    delay_file(path);
    return original_fopen(path, mode);
}

А ловушка Windows должна выглядеть так:

static NTSTATUS WINAPI NtOpenFileHook(
    PHANDLE           FileHandle,
    ACCESS_MASK        DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    PIO_STATUS_BLOCK  IoStatusBlock,
    ULONG              ShareAccess,
    ULONG              OpenOptions)
{
    delay_file(ObjectAttributes->ObjectName->Buffer);
    return NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
}

Функция delay_file() следует позаботиться обо всем остальном. Тип строки, используемый для имен файлов, отличается в Linux и Windows, но, поскольку вы упомянули C ++ 17, вы должны иметь возможность использовать std::filesystem::path как тип, не беспокоясь об этом:

static std::unordered_map<std::filesystem::path, ...> delay_sequences;

delay_file(std::filesystem::path filename)
{
    if (auto it = delay_sequences.find(filename); it != delay_sequences.end()) {
        auto &sequence = it->second;
        ...
    }
}

Создать struct или же class управлять последовательностью задержки

Вы сохраняете последовательность задержки как массив целых чисел, но некоторые элементы этого массива являются особенными, и длина предположительно может варьироваться. Лучше создать для него правильную структуру данных, хотите ли вы сделать это на C или на C ++. С C ++ я бы написал что-то вроде:

struct delay_sequence {
    std::size_t index = 0;
    std::vector<std::chrono::milliseconds> delays;
}

Вектор delays содержит фактические значения задержки, используя std::chrono::duration тип. Преимущество в том, что вы можете передать его напрямую std::this_thread::sleep_for() спать на указанное время. А std::vector знает свою длину, поэтому вам не нужно хранить ее отдельно. Единственный другой необходимый элемент — это текущий индекс.

Избегайте ручной блокировки мьютекса

Вместо того, чтобы звонить вручную mutex.lock() и mutex.unlock(), использовать std::lock_guard возражаю сделать это за вас.

Альтернативно:

Рассмотрите возможность использования атомарных переменных

Индекс в массиве задержек — это просто счетчик, который нужно увеличивать или сбрасывать. Подумайте о том, чтобы сделать это std::atomic<std::size_t>, вот так:

struct delay_sequence {
    std::atomic<std::size_t> index;
    std::vector<std::chrono::milliseconds> delays;
}

А потом используйте это так:

auto &sequence = it->second;
auto index = sequence.index++;

if (index < sequence.delays.size()) {
    std::this_thread::sleep_for(sequence.delays[index]);
} else {
    // we've already finished, if you are worried about index wrapping, do:
    sequence.index--;
}

Вы также можете сбросить все счетчики атомарно:

for (auto &[filename, sequence]: delay_sequences) {
    sequence.index = 0;
}

Однако трюк с if (delay_sequence[...] == UINT_MAX) не так хорошо с атомиксом. У вас все еще может быть особая длительность (возможно, отрицательная?), Указывающая, что вы хотите сбросить один или все индексы, но я бы предпочел флаг для struct delay_sequence чтобы указать, хотите ли вы повторить последовательность, и другой флаг, чтобы указать, что вы хотите сбросить все индексы при открытии данного файла, например:

struct delay_sequence {
    std::atomic<std::size_t> index;
    std::vector<std::chrono::milliseconds> delays;
    bool repeat;
    bool reset_all;
};

...

auto &sequence = it->second;

if (sequence.reset_all) {
    for (auto &[filename, sequence]: delay_sequences) {
        sequence.index = 0;
    }
} else {
    auto index = sequence.index++;

    if (sequence.repeat) {
        index %= sequence.delays.size();
    }

    if (index < sequence.delays.size()) {
        std::this_thread::sleep_for(sequence.delays[index]);
    }
}

  • Спасибо, что нашли время посмотреть на это. Постараюсь поменять с вашими предложениями. Я также думаю, что должен использовать forward_lists вместо векторов, если я использую структуры. Я могу отслеживать текущую позицию в структуре и решить сбросить ее до начала forward_list, когда она достигнет конечной позиции. Так что я попробую так переписать.

    — my_first_c_program

  • 1

    Я бы все равно использовал std::vector чтобы удерживать время задержки, поскольку доступ к вектору выполняется быстрее, чем к списку, и мне кажется, что после чтения файла конфигурации вы никогда не измените задержки.

    — Г. Сон

  • Хорошо, в этом случае я буду использовать векторы.

    — my_first_c_program

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *