genops.c

其中定义了许多公共子函数,为具体跳表函数的实现提供基础功能

int _IO_switch_to_get_mode (FILE *fp)

此函数在同一个文件描述符变为get模式时可用,也就是准备从文件想缓冲区写入东西时可用

fileops.c

跳表函数的核心实现

int _IO_new_file_overflow (FILE *f, int ch)

此函数用于刷新和维护输出缓冲区

流程:

1.错误检查

2.从读切换到write,或第一次write都会进入此分支

​ 2.1 若没有缓冲区,则设置缓冲区

​ 2.2 backup的处理,目前还没学过

​ 2.3 调整读写缓冲区指针,基本思想为:将writeptr与writebase更新为readptr,防止readbase和readptr有数据未读取完成;将writeend更新为bufend,尽可能利用缓冲区;将readptr,readbase,readend放到同一个位置。(感觉这操纵有点迷,自己想维护readbase到readptr之间的数据,后面又将他们放到同一个位置,这不前后矛盾吗,可能由于版本迭代之后逻辑比较混乱)

​ 2.4 调整flag,置为最近putting过

​ 2.5 不是宽字符并且是行缓冲或无缓冲就将写缓冲区的end设置为ptr(不知道什么功能会用上)

3.通过判断ch参数来决定函数功能

​ 3.1 ch = eof,就是刷新缓冲区,将writebase到writeptr中的数据写入文件之中

​ 3.2 缓冲区满了之后刷新缓冲区,第一次调用是不会走到这一步的

​ 3.3 & 3.4 将ch放入到缓冲区,该刷新的时候刷新,一个字符一个字符判断

int_IO_new_file_overflow (FILE *f, int ch)
{
/*错误检查*/
if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
{
f->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return EOF;
}
/*从读切换到write,或第一次write都会进入此分支*/
/* If currently reading or no buffer allocated. */
if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
{
/* Allocate a buffer if needed. */
if (f->_IO_write_base == NULL)
{
_IO_doallocbuf (f);
_IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
}
/* Otherwise must be currently reading.
If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,
logically slide the buffer forwards one block (by setting the
read pointers to all point at the beginning of the block). This
makes room for subsequent output.
Otherwise, set the read pointers to _IO_read_end (leaving that
alone, so it can continue to correspond to the external position). */
if (__glibc_unlikely (_IO_in_backup (f)))
{
size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;
_IO_free_backup_area (f);
f->_IO_read_base -= MIN (nbackup,
f->_IO_read_base - f->_IO_buf_base);
f->_IO_read_ptr = f->_IO_read_base;
}

if (f->_IO_read_ptr == f->_IO_buf_end)
f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
f->_IO_write_ptr = f->_IO_read_ptr;
f->_IO_write_base = f->_IO_write_ptr;
f->_IO_write_end = f->_IO_buf_end;
f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;

f->_flags |= _IO_CURRENTLY_PUTTING;
if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
f->_IO_write_end = f->_IO_write_ptr;
}
if (ch == EOF)
return _IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base);
if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
if (_IO_do_flush (f) == EOF)
return EOF;
*f->_IO_write_ptr++ = ch;
if ((f->_flags & _IO_UNBUFFERED)
|| ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
if (_IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base) == EOF)
return EOF;
return (unsigned char) ch;
}
#define _IO_do_flush(_f) \
((_f)->_mode <= 0 \
? _IO_do_write(_f, (_f)->_IO_write_base, \
(_f)->_IO_write_ptr-(_f)->_IO_write_base) \
: _IO_wdo_write(_f, (_f)->_wide_data->_IO_write_base, \
((_f)->_wide_data->_IO_write_ptr \
- (_f)->_wide_data->_IO_write_base)))

static size_t new_do_write (FILE *fp, const char *data, size_t to_do)

1.通过判断readend和writebase来判断读操作是否成功,若不成功调整文件偏移,使写操作在正确位置。
2.调整_cur_column,格式化所需要,目前知道干什么的就行
3.更新写缓冲区的位置为整个缓冲区大小或0

/*
注意:int
_IO_new_do_write (FILE *fp, const char *data, size_t to_do)
此函数使上述函数的核心实现,上述函数仅仅做了一个封装,成功返回0,不成功返回EOF
*/
static size_t
new_do_write (FILE *fp, const char *data, size_t to_do)
{
size_t count;
if (fp->_flags & _IO_IS_APPENDING)
/* On a system without a proper O_APPEND implementation,
you would need to sys_seek(0, SEEK_END) here, but is
not needed nor desirable for Unix- or Posix-like systems.
Instead, just indicate that offset (before and after) is
unpredictable. */
fp->_offset = _IO_pos_BAD;
else if (fp->_IO_read_end != fp->_IO_write_base)
{
off64_t new_pos
= _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
if (new_pos == _IO_pos_BAD)
return 0;
fp->_offset = new_pos;
}
count = _IO_SYSWRITE (fp, data, to_do);
if (fp->_cur_column && count)
fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;
_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
fp->_IO_write_end = (fp->_mode <= 0
&& (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
? fp->_IO_buf_base : fp->_IO_buf_end);
return count;
}

size_t _IO_default_xsputn (FILE *f, const void *data, size_t n)

此函数用于读取不足一个缓冲区的字符向文件中,但是需要考虑行缓冲无缓冲等情况需要封装一个函数来进行

1.缓冲区不够直接通过overflow的方式直接传递,直接就需要考虑缓冲区刷新问题

2.缓冲区足够,先填满缓冲区,多出来的第一个按1的方式处理。

在f->_IO_write_ptr < f->_IO_write_end,若填不满缓冲区不会刷新,(这就是c语言说的缓冲区刷新时机的原理)

size_t
_IO_default_xsputn (FILE *f, const void *data, size_t n)
{
const char *s = (char *) data;
size_t more = n;
if (more <= 0)
return 0;
for (;;)
{
/* Space available. */
if (f->_IO_write_ptr < f->_IO_write_end)
{
size_t count = f->_IO_write_end - f->_IO_write_ptr;
if (count > more)
count = more;
if (count > 20)
{
f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
s += count;
}
else if (count)
{
char *p = f->_IO_write_ptr;
ssize_t i;
for (i = count; --i >= 0; )
*p++ = *s++;
f->_IO_write_ptr = p;
}
more -= count;
}
if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)
break;
more--;
}
return n - more;
}