You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
4.7 KiB
191 lines
4.7 KiB
//! -pipe -std=gnu17 -O3 -msse -fwhole-program -flto -fno-strict-aliasing -Wall -Wstrict-aliasing -Werror -Wl,-flto -Wl,-O3 -o
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#define expect(expr, msg) ({ __auto_type _expect__expr = (expr); \
|
|
if(__builtin_expect(!_expect__expr, 0)) { perror("Fatal error: `" #expr "` failed: " msg); exit(1); } \
|
|
_expect__expr; })
|
|
|
|
#define report(...) ({ fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); false; })
|
|
#define throw(...) return report(__VA_ARGS__)
|
|
|
|
typedef struct mappedfd {
|
|
int fd;
|
|
size_t len;
|
|
|
|
void* origin;
|
|
} mmap_t;
|
|
|
|
bool filesz(int fd, off_t* restrict size)
|
|
{
|
|
if(fd<0) return false;
|
|
struct stat st;
|
|
if(fstat(fd, &st)) return false;
|
|
*size = st.st_size;
|
|
return true;
|
|
}
|
|
|
|
bool map_stdin(mmap_t* restrict map, size_t sz)
|
|
{
|
|
size_t size;
|
|
if(! (size = sz)) {
|
|
off_t osz;
|
|
if(!filesz(STDIN_FILENO, &osz)) return false;
|
|
size = (size_t)osz;
|
|
}
|
|
void* origin = mmap(NULL, size, PROT_READ, MAP_PRIVATE, STDIN_FILENO, 0);
|
|
if(origin == MAP_FAILED) return false;
|
|
*map = (mmap_t) {
|
|
.fd = STDIN_FILENO,
|
|
.len = size,
|
|
.origin = origin,
|
|
};
|
|
return true;
|
|
}
|
|
|
|
bool map_input(const char* file, mmap_t *restrict map)
|
|
{
|
|
int fd = open(file, O_RDONLY);
|
|
if(fd<0) return false;
|
|
|
|
off_t _sz;
|
|
if(!filesz(fd, &_sz)) return (close(fd), false);
|
|
size_t sz = (size_t)_sz;
|
|
|
|
void* origin = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if(origin == MAP_FAILED) return (close(fd), false);
|
|
|
|
*map = (mmap_t){
|
|
.fd = fd,
|
|
.len = sz,
|
|
.origin = origin,
|
|
};
|
|
|
|
return true;
|
|
}
|
|
|
|
bool map_stdout(mmap_t* restrict map, size_t size)
|
|
{
|
|
char link[64];
|
|
int fd;
|
|
snprintf(link, sizeof(link), "/proc/%d/fd/1", getpid());
|
|
|
|
fd = open(link, O_RDWR);
|
|
if(fd < 0) return report("failed to open fd 1 for rdwr (path was `%s')", link);
|
|
|
|
//dup2(fd, STDOUT_FILENO);
|
|
//close(fd);
|
|
// !!! normal stdout dead beyond this point
|
|
if(ftruncate(fd, size)<0) return (close(fd), report("failed to truncate stdout (temp %d) to size %lu", fd, size));
|
|
if(dup2(fd, STDOUT_FILENO)<0) return (close(fd), report("failed to dup2 %d -> %d", fd, STDOUT_FILENO));
|
|
close(fd);
|
|
|
|
void* origin = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, STDOUT_FILENO, 0);
|
|
if(origin == MAP_FAILED) return report("failed to map stdout as write + shared at size %lu", size);
|
|
|
|
*map = (mmap_t){
|
|
.fd = STDOUT_FILENO,
|
|
.len = size,
|
|
.origin = origin,
|
|
};
|
|
return true;
|
|
}
|
|
|
|
void map_close(mmap_t map)
|
|
{
|
|
munmap(map.origin, map.len);
|
|
if(map.fd >= 0) close(map.fd);
|
|
}
|
|
|
|
const unsigned char VALID[255] = {
|
|
['0' ... '9'] = 1,
|
|
['\n'] = 2,
|
|
};
|
|
|
|
inline bool is_number(const char* str)
|
|
{
|
|
register unsigned char vl;
|
|
while( (vl = (unsigned char)*str++) ) if(VALID[vl] != 1) return false;
|
|
return true;
|
|
}
|
|
|
|
size_t get_width(const char* str)
|
|
{
|
|
|
|
size_t i=0;
|
|
unsigned char v;
|
|
while( (v = VALID[(unsigned char) *str++]) ) {
|
|
i+=1;
|
|
if(v == 2) break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
bool process(const mmap_t* imap, const mmap_t* omap, size_t *restrict osize)
|
|
{
|
|
const char* input = imap->origin;
|
|
char* output = omap->origin;
|
|
uintptr_t _start = (uintptr_t)output;
|
|
size_t size = omap->len;
|
|
|
|
size_t width = get_width(input) - 1; //XXX: This isn't working, because of newlines and bullshit like that. Idk how to fix it...
|
|
size_t height = size / (width + 1);
|
|
fprintf(stderr, "size: %lu, w: %lu, h: %lu\n", size, width, height);
|
|
#define getat(ar, x, y) ((ar)[(((y) * (width+1)) + (x))])
|
|
assert(size % (width + 1)== 0);
|
|
|
|
for(size_t x = 0;x<width;x++) {
|
|
for(size_t y = 0; y<height; y++)
|
|
*output++ = getat(input, x, y);
|
|
*output++ = '\n';
|
|
}
|
|
*osize = (size_t)(((uintptr_t)output) - _start);
|
|
return true;
|
|
#undef getat
|
|
}
|
|
|
|
mmap_t open_input(char*const* argv)
|
|
{
|
|
const char* in = argv[1];
|
|
mmap_t input;
|
|
if(!in) expect(map_stdin(&input, 0), "failed to map stdin with no size hint");
|
|
else if(is_number(in)) expect(map_stdin(&input, strtoul(in, NULL, 10)), "failed to map stdin at that size");
|
|
else expect(map_input(in, &input), "failed to map input file");
|
|
return input;
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
int rc = 0;
|
|
|
|
mmap_t input;
|
|
mmap_t output;
|
|
|
|
input = open_input(argv); //expect(map_stdin(&input), "failed to map stdin");
|
|
expect(map_stdout(&output, input.len), "failed to map stdout");
|
|
|
|
expect(input.len == output.len, "invalid i/o lengths");
|
|
size_t fout;
|
|
expect(process(&input, &output, &fout), "failed to process");
|
|
fprintf(stderr, "output size %lu -> %lu\n", output.len, fout);
|
|
map_close(input);
|
|
|
|
if(msync(output.origin, fout, MS_SYNC)) perror("failed to sync. output may be corrupted");
|
|
munmap(output.origin, output.len);
|
|
if(ftruncate(output.fd, fout)) perror("failed to truncate file to correct output size");
|
|
close(output.fd);
|
|
|
|
return rc;
|
|
}
|
|
|