Files
Sprinter-SDCC/libc/file/fgetc.c
T
snark13 48d552bf3a libc: FILE* v2 — буферизация потоков (вариант B+)
- единый ленивый буфер BUFSIZ=512 на чтение и запись с
  автопереключением направления (_F_DIROUT, _file_sync: запись
  сбрасывается write()-ом, readahead откатывается lseek-ом)
- статическая таблица OPEN_MAX=8 слотов вместо malloc для FILE;
  _fclosall через atexit — exit() сбрасывает несброшенную запись
- fread/fwrite: мелкое через буфер (memcpy), блоки >= BUFSIZ — мимо
  буфера одним syscall; горячие пути fgetc/fputc и сканер строк
  fgets (LDI до '\n') — на asm, SDCC на эти цепочки генерит ~90
  инструкций с IX-фреймом
- новое: ungetc (1 байт через hold, работает и на stdin),
  fprintf/vfprintf (vsprintf+fwrite), fflush(NULL) = все потоки
- фиксы stdio-review: fwrite ставит _F_ERROR при короткой записи
  (issue 3), fgets(n=1) возвращает пустую строку (issue 4)
- замер (MAME, HDD, 100 КБ): небуферизованная оценка ~144 с →
  fgetc 5 с (×29), fgets ~1 с; дизайн и отвергнутые варианты —
  docs/file-buffering-design.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:09:13 +03:00

102 lines
3.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* fgetc — прочитать один байт из потока (буферизованно, вариант B+).
*
* Горячий путь («в readahead-буфере есть байт») написан на asm —
* SDCC на эту цепочку проверок генерит ~90 инструкций с IX-фреймом,
* asm укладывается в ~30 без фрейма (замер fbench: ~176 мкс/байт
* у C-версии). Всё остальное — медленный C-путь fgetc_slow:
* hold-байт от ungetc, консольный getchar(), сброс накопленной
* записи, дозаполнение буфера read(fd, buf, BUFSIZ), fallback без
* буфера. Возвращает байт, EOF на конце файла (_F_EOF) или при
* ошибке (_F_ERROR).
*
* Раскладка FILE (см. stdio.h, байтовые смещения):
* +0 fd, +2 flags, +3 buf, +5 curp, +7 level, +9 hold
*/
#include <unistd.h>
#include <errno.h>
#include "_file.h"
/* Медленный путь; зовётся из asm-обёртки (метка _fgetc_slow видна в
* пределах этого модуля). */
static int fgetc_slow(FILE *fp)
{
if (!fp) { errno = EBADF; return EOF; }
if (fp->hold != EOF) {
int c = fp->hold;
fp->hold = EOF;
return c;
}
if (fp->flags & _F_CONIN) {
return getchar();
}
if (!(fp->flags & _F_READ)) { errno = EBADF; return EOF; }
if (fp->flags & _F_DIROUT) {
if (_file_sync(fp)) return EOF; /* сброс накопленной записи */
}
if (fp->level == 0) {
uint8_t *b = _file_buf(fp);
if (!b) {
/* Небуферизованный fallback. */
uint8_t ch;
int r = read(fp->fd, &ch, 1);
if (r == 0) { fp->flags |= _F_EOF; return EOF; }
if (r < 0) { fp->flags |= _F_ERROR; return EOF; }
return (int)ch;
}
int r = read(fp->fd, b, BUFSIZ);
if (r == 0) { fp->flags |= _F_EOF; return EOF; }
if (r < 0) { fp->flags |= _F_ERROR; return EOF; }
fp->level = (uint16_t)r;
fp->curp = b;
}
fp->level--;
return (int)*fp->curp++;
}
int fgetc(FILE *fp) __naked
{
(void)fp;
(void)fgetc_slow; /* используется из asm */
__asm
;; HL = fp.
ld a, h
or a, l
jr Z, _fg_slow ; NULL C-путь выставит errno
push hl
pop iy ; IY = fp (IY scratch в __sdcccall(1))
ld a, 7 (iy) ; level != 0 ?
or a, 8 (iy)
jr Z, _fg_slow ; буфер пуст/не наш режим
ld a, 2 (iy) ; flags
and a, #0x08 ; _F_DIROUT буфер копит запись?
jr NZ, _fg_slow
ld a, 9 (iy) ; hold == EOF (0xFFFF)?
and a, 10 (iy)
inc a
jr NZ, _fg_slow ; есть отложенный ungetc-байт
;; --- горячий путь: level--, вернуть *curp++ ---
ld a, 7 (iy)
sub a, #1
ld 7 (iy), a
jr NC, _fg_nb
dec 8 (iy)
_fg_nb:
ld l, 5 (iy) ; HL = curp
ld h, 6 (iy)
ld e, (hl) ; E = байт
ld d, #0 ; zero-extend в int
inc hl
ld 5 (iy), l ; curp++
ld 6 (iy), h
ret
_fg_slow:
jp _fgetc_slow ; HL = fp уже на месте
__endasm;
}