Files
Sprinter-SDCC/libc/file/fread.c
T
snark13 9f8aa6fc28 libc: две версии библиотеки — sprinter.lib (fast) / sprinter_safe.lib
Симметрично libbgi (bgi256/bgi256_safe): fast = -DLIBC_NOCHECK,
дефолт sprinter-cc; safe линкуется по --safe (флаг уже существовал).

Под LIBC_NOCHECK вырезаны ТОЛЬКО параметр-валидации:
- fgetc/fputc: NULL-check в горячей asm-обёртке (~11Т на каждый байт);
- fgets/fputs/fread/fwrite: NULL ptr/fp и EBADF на неверное направление
  потока; ftell/fseek/ungetc/fclose: NULL fp; cputs: NULL s.
НЕ тронуты: критичный _fd_guard (9-й OPEN вешает DSS — в обеих
версиях), функциональная маршрутизация (консоль/направление/hold),
cold-path валидации (fopen/cbl_open/irq/settextmode — экономии ноль).

libc/Makefile — dual-build по образцу libbgi (build/fast + build/safe,
общий стейл-контроль).  Корневой Makefile: в TESTS добавлены bgitest,
bgi_img, accfill, cblstream — раньше не собирались корневым make и
выпадали из size-check при чистой пересборке.

Дельты fast vs старая (safe-семантика): filetest -481, fbench -384,
solidt -180, errno -71, остальные -3..-9; роста нет.  Проверено в MAME:
filetest fast и safe (--safe, sprinter_safe.lib) — прогоны идентичны.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 22:11:01 +03:00

87 lines
2.6 KiB
C

/*
* fread — читает nmemb элементов по size байт из потока в ptr
* (буферизованно, вариант B+).
*
* Порядок: hold-байт → хвост буфера (memcpy) → большой остаток
* (>= BUFSIZ) одним read() напрямую в ptr мимо буфера → мелкий
* остаток через дозаполнение буфера. Возвращает число ПОЛНЫХ
* элементов; короткое чтение ставит _F_EOF, ошибка — _F_ERROR.
* Консольный поток — посимвольно через getchar().
*/
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "_file.h"
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp)
{
#ifndef LIBC_NOCHECK
if (!ptr || !fp) return 0;
#endif
if (size == 0 || nmemb == 0) return 0;
uint8_t *p = (uint8_t *)ptr;
uint16_t total = size * nmemb;
uint16_t got = 0;
uint16_t n;
int r;
if (fp->hold != EOF) {
*p++ = (uint8_t)fp->hold;
fp->hold = EOF;
got = 1;
}
if (fp->flags & _F_CONIN) {
while (got < total) {
int c = getchar();
if (c == EOF) break;
*p++ = (uint8_t)c;
got++;
}
goto done;
}
#ifndef LIBC_NOCHECK
if (!(fp->flags & _F_READ)) { errno = EBADF; return 0; }
#endif
if (fp->flags & _F_DIROUT) {
if (_file_sync(fp)) return 0; /* сброс накопленной записи */
}
/* 1) хвост readahead-буфера */
if (fp->level) {
n = total - got;
if (n > fp->level) n = fp->level;
memcpy(p, fp->curp, n);
fp->curp += n;
fp->level -= n;
p += n;
got += n;
}
n = total - got;
if (n == 0) goto done;
if (n >= BUFSIZ || !_file_buf(fp)) {
/* 2а) большой остаток (или нет буфера) — напрямую мимо буфера */
r = read(fp->fd, p, n);
} else {
/* 2б) мелкий остаток — дозаполнить буфер и скопировать */
r = read(fp->fd, fp->buf, BUFSIZ);
if (r >= 0) {
fp->level = (uint16_t)r;
fp->curp = fp->buf;
if ((uint16_t)r < n) n = (uint16_t)r;
memcpy(p, fp->curp, n);
fp->curp += n;
fp->level -= n;
r = (int)n;
}
}
if (r < 0) { fp->flags |= _F_ERROR; goto done; }
got += (uint16_t)r;
if (got < total) fp->flags |= _F_EOF;
done:
return size == 1 ? got : got / size;
}