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 ответ
Воспользуйтесь стандартной библиотекой
Вы изобретаете множество колес в своих программах. Стандартная библиотека, которую вы используете, уже многое сделает за вас. Даже в 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
Я бы все равно использовал
std::vector
чтобы удерживать время задержки, поскольку доступ к вектору выполняется быстрее, чем к списку, и мне кажется, что после чтения файла конфигурации вы никогда не измените задержки.— Г. Сон
Хорошо, в этом случае я буду использовать векторы.
— my_first_c_program