198 lines
4.7 KiB
C
198 lines
4.7 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <limits.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/mman.h>
|
|
|
|
#define eprintf(...) fprintf(stderr, __VA_ARGS__);
|
|
|
|
|
|
typedef struct _Config {
|
|
char ifile[PATH_MAX + 1];
|
|
char ofile[PATH_MAX + 1];
|
|
|
|
size_t size;
|
|
|
|
uint64_t x0;
|
|
uint64_t a;
|
|
uint64_t c;
|
|
uint64_t m;
|
|
|
|
char *pad;
|
|
} Config;
|
|
|
|
|
|
typedef struct _Context {
|
|
pthread_t id;
|
|
pthread_barrier_t *barrier;
|
|
|
|
size_t start;
|
|
size_t end;
|
|
size_t length;
|
|
|
|
char *pad;
|
|
char *input;
|
|
char *output;
|
|
} Context;
|
|
|
|
|
|
void *lcg_gen(void *arg) {
|
|
Config *cfg = (Config*) arg;
|
|
uint64_t x = cfg->x0;
|
|
uint64_t a = cfg->a;
|
|
uint64_t c = cfg->c;
|
|
uint64_t m = cfg->m;
|
|
for (size_t i = 0; i < cfg->size; i++) {
|
|
cfg->pad[i] = x;
|
|
x = (x * a + c) % m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void *worker(void *arg) {
|
|
Context *ctx = (Context*) arg;
|
|
printf("[worker %ld] start: %zu, end: %zu, len: %zu\n", ctx->id, ctx->start, ctx->end, ctx->length);
|
|
for (size_t i = ctx->start; i < ctx->end; i++) {
|
|
ctx->output[i] = ctx->input[i] ^ ctx->pad[i];
|
|
}
|
|
pthread_barrier_wait(ctx->barrier);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
Config cfg;
|
|
|
|
char current_opt;
|
|
extern char *optarg;
|
|
extern int optind, optopt, opterr;
|
|
while ((current_opt = (char) getopt(argc, argv, "hi:o:x:a:c:m:")) != -1) {
|
|
switch (current_opt) {
|
|
case 'x':
|
|
cfg.x0 = atoi(optarg);
|
|
break;
|
|
case 'a':
|
|
cfg.a = atoi(optarg);
|
|
break;
|
|
case 'c':
|
|
cfg.c = atoi(optarg);
|
|
break;
|
|
case 'm':
|
|
cfg.m = atoi(optarg);
|
|
break;
|
|
case 'i':
|
|
strncpy(cfg.ifile, optarg, PATH_MAX);
|
|
break;
|
|
case 'o':
|
|
strncpy(cfg.ofile, optarg, PATH_MAX);
|
|
break;
|
|
case 'h':
|
|
printf("< help text here >\n");
|
|
return EXIT_SUCCESS;
|
|
case ':':
|
|
eprintf("Option %c requires an argument!\n", optopt);
|
|
return EXIT_FAILURE;
|
|
case '?':
|
|
eprintf("Use \"%s -h\" to see help page\n", argv[0]);
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
int ifd;
|
|
int ofd;
|
|
if ((ifd = open(cfg.ifile, O_RDONLY)) == -1) {
|
|
perror("input file");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if ((ofd = open(cfg.ofile, O_WRONLY | O_CREAT, S_IREAD | S_IWRITE)) == -1) {
|
|
perror("output file");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
struct stat istat;
|
|
if (fstat(ifd, &istat) == -1) {
|
|
perror("input file");
|
|
return EXIT_FAILURE;
|
|
}
|
|
cfg.size = (size_t) istat.st_size;
|
|
|
|
char *input = mmap(NULL, cfg.size, PROT_READ, MAP_PRIVATE, ifd, 0);
|
|
if (input == MAP_FAILED) {
|
|
perror("input file");
|
|
return EXIT_FAILURE;
|
|
}
|
|
char *output = calloc(sizeof(char), cfg.size);
|
|
|
|
close(ifd);
|
|
|
|
if ((cfg.pad = calloc(sizeof(char), cfg.size)) == NULL) {
|
|
perror("pad");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
pthread_t th_lcg_gen;
|
|
if (pthread_create(&th_lcg_gen, NULL, lcg_gen, (void*) &cfg) != 0) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
pthread_join(th_lcg_gen, NULL);
|
|
|
|
int workers_count = get_nprocs();
|
|
Context **workers = calloc(sizeof(Context*), workers_count);
|
|
size_t blen = cfg.size / workers_count; // averrage len for one block
|
|
|
|
printf("[main] workers count: %d\n", workers_count);
|
|
|
|
pthread_barrier_t barrier;
|
|
if (pthread_barrier_init(&barrier, NULL, workers_count + 1) != 0) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
printf("[main] file size: %zu\n", cfg.size);
|
|
|
|
for (int i = 0; i < workers_count; i++) {
|
|
workers[i] = calloc(sizeof(Context), 1);
|
|
workers[i]->barrier = &barrier;
|
|
workers[i]->pad = cfg.pad;
|
|
workers[i]->input = input;
|
|
workers[i]->output = output;
|
|
workers[i]->start = i * blen;
|
|
if (i == workers_count - 1) {
|
|
workers[i]->end = cfg.size; // handle remainder after integer division
|
|
} else {
|
|
workers[i]->end = (i + 1) * blen;
|
|
}
|
|
workers[i]->length = workers[i]->end - workers[i]->start;
|
|
pthread_create(&workers[i]->id, NULL, worker, (void*) workers[i]);
|
|
}
|
|
|
|
pthread_barrier_wait(&barrier);
|
|
|
|
for (int i = 0; i < workers_count; i++) {
|
|
pthread_join(workers[i]->id, NULL);
|
|
}
|
|
|
|
pthread_barrier_destroy(&barrier);
|
|
munmap(input, cfg.size);
|
|
|
|
for (int i = 0; i < workers_count; i++) {
|
|
free(workers[i]);
|
|
}
|
|
free(workers);
|
|
free(cfg.pad);
|
|
|
|
write(ofd, output, cfg.size);
|
|
close(ofd);
|
|
free(output);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|