Files
Sprinter-SDCC/libc/file/fputc.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

101 lines
3.5 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.
/*
* fputc — записать один байт в поток (буферизованно, вариант B+).
*
* Горячий путь («поток уже копит запись и в буфере есть место»)
* написан на asm — см. пояснение в fgetc.c. Остальное — медленный
* C-путь fputc_slow: консольный putchar(), переключение направления
* (_file_sync откатывает readahead), сброс полного буфера, ленивое
* выделение, fallback без буфера (прямой write() 1 байта).
* ВНИМАНИЕ: успех fputc = «байт принят в буфер»; ошибка реальной
* записи может вылезти позже (fflush/fclose).
*
* Раскладка FILE: +0 fd, +2 flags, +3 buf, +5 curp, +7 level, +9 hold.
* asm рассчитывает на BUFSIZ == 512 (проверка уровня по старшему
* байту level < 2).
*/
#include <unistd.h>
#include <errno.h>
#include "_file.h"
#if BUFSIZ != 512
#error "asm-горячий путь fputc рассчитан на BUFSIZ == 512"
#endif
/* Медленный путь; зовётся из asm-обёртки. */
static int fputc_slow(int c, FILE *fp)
{
if (!fp) { errno = EBADF; return EOF; }
if (fp->flags & _F_CONOUT) {
putchar(c);
return (int)(uint8_t)c;
}
if (!(fp->flags & _F_WRITE)) { errno = EBADF; return EOF; }
if (!(fp->flags & _F_DIROUT)) {
if (_file_sync(fp)) return EOF; /* откат readahead/hold */
} else if (fp->level >= BUFSIZ) {
if (_file_sync(fp)) return EOF; /* буфер полон — сброс */
}
uint8_t *b = _file_buf(fp);
if (!b) {
/* Небуферизованный fallback. */
uint8_t ch = (uint8_t)c;
if (write(fp->fd, &ch, 1) != 1) {
fp->flags |= _F_ERROR;
return EOF;
}
return (int)ch;
}
if (!(fp->flags & _F_DIROUT)) {
fp->flags |= _F_DIROUT;
fp->curp = b;
}
*fp->curp++ = (uint8_t)c;
fp->level++;
return (int)(uint8_t)c;
}
int fputc(int c, FILE *fp) __naked
{
(void)c; (void)fp;
(void)fputc_slow; /* используется из asm */
__asm
;; HL = c, DE = fp.
ld a, d
or a, e
jr Z, _fp_slow ; NULL C-путь выставит errno
push de
pop iy ; IY = fp
ld a, 2 (iy) ; flags
and a, #0x08 ; _F_DIROUT уже копим запись?
jr Z, _fp_slow ; нет (или консоль/чтение) C-путь
ld a, 8 (iy) ; level_hi < 2 <=> level < 512
cp a, #2
jr NC, _fp_slow ; буфер полон сброс в C-пути
;; --- горячий путь: *curp++ = c, level++ ---
ld e, 5 (iy) ; DE = curp (fp больше не нужен в DE)
ld d, 6 (iy)
ld a, l
ld (de), a
inc de
ld 5 (iy), e
ld 6 (iy), d
ld a, 7 (iy) ; level++
inc a
ld 7 (iy), a
jr NZ, _fp_ret
inc 8 (iy)
_fp_ret:
ld e, l ; вернуть (uint8_t)c
ld d, #0
ret
_fp_slow:
jp _fputc_slow ; HL = c, DE = fp уже на месте
__endasm;
}