Я написал пару программ, позволяющих кому-либо отправлять произвольные двоичные файлы в определенном радиорежиме с ограниченным набором символов, где каждый символ представлен некоторым кодом Хаффмана. Одна программа — кодировщик, другая — декодер.
Программы прекрасно работают в Linux. Он работает исключительно через стандартный ввод и вывод. Я фаззил их, используя случайные файлы размером 1 ГиБ. Однако когда я пытаюсь скомпилировать его для Windows (используя cl.exe
или x86_64-w64-mingw32-gcc
(в WSL), программы отказываются выводить что-либо в любой выходной поток.
Я пытался удалить все выходные данные в stderr, пытался выполнить два вызова putchar()
с помощью '\r'
'\n'
(думая, что это проблема с LF и CRLF), fflush()
каждый раз, когда я putchar()
, но это все равно не сработает. Единственное, что сработало, — это полностью закомментировать основной while
цикл программы-кодировщика, в котором мне удалось получить исходную напечатанную строку FILE:
.
Поскольку цикл не закомментирован, я даже не получу первый printf()
с немедленным fflush(stdout)
после него. Однако программа будет остановлена. Ниже представлена одна из программ, которую я хочу отладить в первую очередь.
Для проверки работоспособности я попытался скомпилировать простую программу Hello World!
, которая использует и printf()
, и putchar()
, и она отлично работает в Windows, но моя конкретная программа - нет. Я в растерянности, где проверить в первую очередь.
js8file_enc.c
#include "js8file_enc.h"
// From stdin
bool fill_buffer(bool * bitbuf, size_t * buf_size, bool ** bitbuf_read_head_ptr){
// Justify remaining buffer back to the beginning
if (*buf_size > 0){
memmove(bitbuf, *bitbuf_read_head_ptr, *buf_size);
}
// Read from stdin (indeterminate length), keep track of buffer size
char buf[BUF_SIZE];
// The max bytes here is sizeof(buf) - ceil(*buf_size/8) expressed in a roundabout way.
// This will fill our byte buffer as much as possible for the bit buffer to be near-full in the next step.
size_t increase = fread(buf, 1, sizeof(buf) - ((*buf_size+8-1)/8), stdin);
// Extract bits from byte buffer
for(size_t i = 0; i < increase; i++){
for(int j = 0; j < 8; j++){
bitbuf[*buf_size] = (buf[i] >> (7 - j)) & 1;
(*buf_size)++;
}
}
*bitbuf_read_head_ptr = bitbuf;
return increase > 0;
}
int main(){
printf("FILE:");
bool bitbuf[BUF_SIZE*8];
bool * bitbuf_read_head = bitbuf;
size_t buf_size = 0; // In bits
while(true){
if (buf_size < 8){ // 8 bits, length of longest key
if (!fill_buffer(bitbuf, &buf_size, &bitbuf_read_head)){
// There may still be bits remaining (<8), encode the rest
while(buf_size > 0){
for(int i = buf_size; i > 0; i--){
if (LUT[i] == NULL){
continue;
}
uint8_t key = 0;
for(int j = 0; j < i; j++){
key |= bitbuf_read_head[j] << (i - j - 1);
}
if (LUT[i][key] != 0){
putchar(LUT[i][key]);
bitbuf_read_head += i;
buf_size -= i;
break;
}
}
}
break;
}
}
int max_key_size = (buf_size < 8) ? buf_size : 8;
// Produce keys in descending order, check for first match, write to stdout, then move buffer head.
// If there are no codewords of length n (that is, LUT[n] is NULL), then we skip to the next length.
for(int i = max_key_size; i > 0; i--){
if (LUT[i] == NULL){
continue;
}
uint8_t key = 0;
for(int j = 0; j < i; j++){
key |= bitbuf_read_head[j] << (i - j - 1);
}
if (LUT[i][key] != 0){
putchar(LUT[i][key]);
bitbuf_read_head += i;
buf_size -= i;
break;
}
}
}
fflush(stdout);
fprintf(stderr, "<EOF>\n");
fflush(stderr);
return 0;
}
js8file_enc.h
#include "ref.h"
// Indexed by codeword length (.bits)
const unsigned char * LUT[9] = {
[0] = NULL,
[1] = (unsigned char[0b10]){
[0b0] = ' ',
[0b1] = 'E'
},
[2] = NULL,
[3] = NULL,
[4] = (unsigned char[0b10000]){
[0b1101] = 'T',
[0b0011] = 'A'
},
[5] = (unsigned char[0b100000]){
[0b11111] = 'O',
[0b11100] = 'I',
[0b10111] = 'N',
[0b10100] = 'S',
[0b00011] = 'H',
[0b00000] = 'R'
},
[6] = (unsigned char[0b1000000]){
[0b111011] = 'D',
[0b110011] = 'L',
[0b110001] = 'C',
[0b101101] = 'U',
[0b101011] = 'M',
[0b001011] = 'W',
[0b001001] = 'F',
[0b000101] = 'G',
[0b000011] = 'Y'
},
[7] = (unsigned char[0b10000000]){
[0b1111011] = 'P',
[0b1111001] = 'B',
[0b1110100] = '.',
[0b1100101] = 'V',
[0b1100100] = 'K',
[0b1100001] = '-',
[0b1100000] = '+',
[0b1011001] = '?',
[0b1011000] = '!',
[0b1010101] = '"',
[0b1010100] = 'X',
[0b0010101] = '0',
[0b0010100] = 'J',
[0b0010001] = '1',
[0b0010000] = 'Q',
[0b0001001] = '2',
[0b0001000] = 'Z',
[0b0000101] = '3',
[0b0000100] = '5'
},
[8] = (unsigned char[0b100000000]){
[0b11110101] = '4',
[0b11110100] = '9',
[0b11110001] = '8',
[0b11110000] = '6',
[0b11101011] = '7',
[0b11101010] = '/'
}
};
ссылка.h
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
// DEFAULT SETTING: 524288
// 512 KiB, packed (bytes read from stdin)
// 4 MiB, unpacked (512 KiB worth of bits stored in bitbuf)
// Changed for debugging purposes
#define BUF_SIZE 524288
typedef const struct codeword{
const uint8_t key;
const uint8_t bits;
} codeword;
Обновлено: В первом ответе говорится, что размер стека Windows по умолчанию составляет 1 МБ. Я этого не знал, и я проверю это исправление.
🤔 А знаете ли вы, что...
C оставался популярным и актуальным языком программирования на протяжении многих десятилетий.
Эта строка в main()
:
bool bitbuf[BUF_SIZE * 8];
Где BUF_SIZE
определяется в ref.h
как 524288
. При запуске из стека будет выделено 4 мегабайта. Это приведет к сбою вашей программы еще до того, как она доберется до оператора printf("FILE:")
.
По умолчанию компилятор VC допускает размер стека только 1 МБ.
Решения:
Измените приведенную выше строку следующим образом:
bool* bitbuf = malloc(BUF_SIZE*8);
(Не забудьте поставить free
, когда закончите)
ИЛИ
Аналогично, char buf[BUF_SIZE];
в fill_buffer
занимает 512 КБ стека.