perliol - Perl 的分层 IO 实现的 C API。
/* Defining a layer ... */
#include <perliol.h>
本文档描述了在定义 USE_PERLIO
时,perlapio 中描述的 PerlIO 抽象的行为和实现。
PerlIO 抽象在 perl5.003_02 中引入,但直到 perl5.7.0 才成为一种抽象。然而,在那段时间里,许多 perl 扩展切换到使用它,因此该 API 主要被修复以保持(源代码)兼容性。
该实现的目的是以灵活且与平台无关的方式提供 PerlIO API。它也是一种“面向对象 C,带有虚表”方法的尝试,该方法可应用于 Raku。
PerlIO 是一个分层堆栈。
堆栈的低层级与低层级操作系统调用(C 中的文件描述符)一起使用,获取输入和输出字节,堆栈的较高层级缓冲、筛选,并以其他方式操作 I/O,并返回字符(或字节)到 Perl。术语上方和下方用于指代堆栈层级的相对位置。
层级包含一个“vtable”,即 I/O 操作表(在 C 级别中是一个函数指针表),以及状态标志。vtable 中的函数实现“open”、“read”和“write”等操作。
例如,当请求 I/O(例如“read”)时,请求首先使用每层级的“read”函数从 Perl 向下通过堆栈,然后在底部从操作系统服务请求输入,然后将结果返回到堆栈,最后解释为 Perl 数据。
请求不一定总是会一直向下传递到操作系统:这就是 PerlIO 缓冲发挥作用的地方。
当执行 open() 并指定要部署的额外 PerlIO 层级时,指定的层级将“推入”到已存在的默认堆栈之上。一种查看方式是“操作系统位于左侧”和“Perl 位于右侧”。
此默认堆栈中包含哪些确切层级取决于很多因素:您的操作系统、Perl 版本、Perl 编译时配置和 Perl 运行时配置。有关更多信息,请参阅 PerlIO、perlrun 中的“PERLIO” 和 open。
binmode() 的操作方式类似于 open():默认情况下,指定的层级被推入到现有堆栈之上。
但是,请注意,即使指定的层级对于 open() 和 binmode() 被“推入到顶部”,但这并不意味着效果仅限于“顶部”:PerlIO 层级可以非常“活跃”,并检查和影响堆栈中更深层的层级。例如,有一个名为“raw”的层级,它会重复“弹出”层级,直到到达第一个声明自己能够处理二进制数据的层级。被“推入”的层级按照从左到右的顺序进行处理。
sysopen()(毫不奇怪)在堆栈中比 open() 处于更低级别。例如,在 Unix 或类似 Unix 的系统中,sysopen() 直接在文件描述符级别上操作:就 PerlIO 层级而言,它仅使用“unix”层级,这是 Unix 文件描述符之上的一个相当薄的包装器。
最初讨论修改 IO 流行为时,使用术语“discipline”来表示已添加的实体。我认为这是源自“sfio”中的术语,而“sfio”又借用了 Unix 终端上的“line disciplines”。但是,本文档(和 C 代码)使用术语“layer”。
我希望,鉴于实现情况,这是一个自然的术语,并且应该避免“discipline”在之前用法中固有的含义,因为这些用法与“layer”有很大不同。
基本数据结构是 PerlIOl
typedef struct _PerlIO PerlIOl;
typedef struct _PerlIO_funcs PerlIO_funcs;
typedef PerlIOl *PerlIO;
struct _PerlIO
{
PerlIOl * next; /* Lower layer */
PerlIO_funcs * tab; /* Functions for this layer */
U32 flags; /* Various flags for state */
};
PerlIOl *
是指向该结构的指针,而应用程序级别 PerlIO *
是指向 PerlIOl *
的指针 - 即指向该结构的指针的指针。这允许应用程序级别 PerlIO *
保持不变,而底层的实际 PerlIOl *
发生变化。(比较 Perl 的 SV *
,它在标量的类型发生变化时保持不变,而其 sv_any
字段发生变化。)然后,IO 流通常表示为指向此“layer”链表的指针。
需要注意的是,由于 PerlIO *
中的双重间接寻址,&(perlio->next)
“是”一个 PerlIO *
,因此至少在某种程度上,一层可以使用下一层的“标准”API。
一个“layer”由两部分组成
“layer 类”的函数和属性。
特定句柄的每个实例的数据。
通过 PerlIOl
的“tab”(表示表格)成员访问函数和属性。函数(layer“类”的方法)是固定的,并由 PerlIO_funcs
类型定义。它们与公共 PerlIO_xxxxx
函数大体相同
struct _PerlIO_funcs
{
Size_t fsize;
char * name;
Size_t size;
IV kind;
IV (*Pushed)(pTHX_ PerlIO *f,
const char *mode,
SV *arg,
PerlIO_funcs *tab);
IV (*Popped)(pTHX_ PerlIO *f);
PerlIO * (*Open)(pTHX_ PerlIO_funcs *tab,
PerlIO_list_t *layers, IV n,
const char *mode,
int fd, int imode, int perm,
PerlIO *old,
int narg, SV **args);
IV (*Binmode)(pTHX_ PerlIO *f);
SV * (*Getarg)(pTHX_ PerlIO *f, CLONE_PARAMS *param, int flags)
IV (*Fileno)(pTHX_ PerlIO *f);
PerlIO * (*Dup)(pTHX_ PerlIO *f,
PerlIO *o,
CLONE_PARAMS *param,
int flags)
/* Unix-like functions - cf sfio line disciplines */
SSize_t (*Read)(pTHX_ PerlIO *f, void *vbuf, Size_t count);
SSize_t (*Unread)(pTHX_ PerlIO *f, const void *vbuf, Size_t count);
SSize_t (*Write)(pTHX_ PerlIO *f, const void *vbuf, Size_t count);
IV (*Seek)(pTHX_ PerlIO *f, Off_t offset, int whence);
Off_t (*Tell)(pTHX_ PerlIO *f);
IV (*Close)(pTHX_ PerlIO *f);
/* Stdio-like buffered IO functions */
IV (*Flush)(pTHX_ PerlIO *f);
IV (*Fill)(pTHX_ PerlIO *f);
IV (*Eof)(pTHX_ PerlIO *f);
IV (*Error)(pTHX_ PerlIO *f);
void (*Clearerr)(pTHX_ PerlIO *f);
void (*Setlinebuf)(pTHX_ PerlIO *f);
/* Perl's snooping functions */
STDCHAR * (*Get_base)(pTHX_ PerlIO *f);
Size_t (*Get_bufsiz)(pTHX_ PerlIO *f);
STDCHAR * (*Get_ptr)(pTHX_ PerlIO *f);
SSize_t (*Get_cnt)(pTHX_ PerlIO *f);
void (*Set_ptrcnt)(pTHX_ PerlIO *f,STDCHAR *ptr,SSize_t cnt);
};
结构的前几个成员为兼容性检查“名称”提供函数表大小,为每个实例数据提供 malloc
大小,以及一些作为整体类的属性的标志(例如它是否是一个缓冲层),然后遵循分为四个基本组的函数
打开和设置函数
基本 IO 操作
Stdio 类缓冲选项。
支持 Perl 传统“快速”访问缓冲区的函数。
一个层不必实现所有函数,但必须存在整个表。未实现的槽可以为 NULL(在调用时将导致错误),或者可以用存根填充以“继承”来自“基类”的行为。这种“继承”对于该层的全部实例是固定的,但由于该层选择填充表的存根,因此可以进行有限的“多重继承”。
每个实例数据保存在 PerlIOl 基本结构之外的内存中,方法是将 PerlIOl 作为该层的结构的第一个成员,如下所示
typedef struct
{
struct _PerlIO base; /* Base "class" info */
STDCHAR * buf; /* Start of buffer */
STDCHAR * end; /* End of valid part of buffer */
STDCHAR * ptr; /* Current position in buffer */
Off_t posn; /* Offset of buf into the file */
Size_t bufsiz; /* Real size of buffer */
IV oneword; /* Emergency buffer */
} PerlIOBuf;
通过这种方式(对于 perl 的标量),指向 PerlIOBuf 的指针可以视为指向 PerlIOl 的指针。
table perlio unix
| |
+-----------+ +----------+ +--------+
PerlIO ->| |--->| next |--->| NULL |
+-----------+ +----------+ +--------+
| | | buffer | | fd |
+-----------+ | | +--------+
| | +----------+
上述内容试图展示层方案如何在简单的情况下工作。应用程序的 PerlIO *
指向表示打开(已分配)句柄的表中的一个条目。例如,表中的前三个槽对应于 stdin
、stdout
和 stderr
。该表反过来指向该句柄的当前“顶部”层 - 在这种情况下是通用缓冲层“perlio”的一个实例。该层反过来指向下一层 - 在这种情况下是低级“unix”层。
上述内容大致相当于“stdio”缓冲流,但具有更大的灵活性
如果 Unix 级别 read
/write
/lseek
不适用于(例如)套接字,则可以在打开时(甚至动态地)用“套接字”层替换“unix”层。
不同的句柄可以有不同的缓冲方案。“top”层可以是“mmap”层,如果使用mmap
读取磁盘文件比使用read
更快。可以通过不使用缓冲层来简单地实现“无缓冲”流。
可以插入额外的层来处理数据流。这是将方案包含在 perl 5.7.0+ 中的主要需求 - 我们需要一种机制来允许在 perl 的内部编码(至少在概念上是 UTF-8 的 Unicode)和系统使用的“本机”格式之间转换数据。这由通常位于缓冲层之上的“:encoding(xxxx)”层提供。
可以添加一个执行“\n”到 CRLF 转换的层。此层可以在任何平台上使用,而不仅仅是通常执行此类操作的平台。
通用标志位是O_XXXXX
样式标志位和从传递给PerlIO_open()
的模式字符串推断出的状态位的混合。
文件结尾。
允许写入,即以“w”或“r+”或“a”等方式打开。
允许读取,即以“r”或“w+”(甚至“a+” - 呕)方式打开。
发生错误(对于PerlIO_error()
)。
打开模式建议截断文件。
所有写入都应追加。
层执行类似 Win32 的“\n”映射到 CR、LF 以进行输出,以及 CR、LF 映射到“\n”以进行输入。通常,提供的“crlf”层是唯一需要关注此问题的层。如果为层的类设置了PERLIO_K_CANCRLF
位,则PerlIO_binmode()
将处理此标志,而不是添加/删除层。
写入此层的数据应采用 UTF-8 编码;此层提供的数据应被视为 UTF-8 编码。可以通过“:utf8”虚拟层在任何层上设置。还可以在“:encoding”层上设置。
层未缓冲 - 即,应为对该层的每次写入而写入到下一层。
此层的缓冲区当前保存写入到该层但未发送到下一层的数据。
此层当前的缓冲区保存了从下层读取的未消耗数据。
层是行缓冲的。每当看到“\n”时,应将写入数据传递到下一层。然后应处理“\n”之外的任何数据。
文件已unlink()
,或应在close()
时删除。
句柄已打开。
此层的此实例支持“快速gets
”接口。通常根据类的PERLIO_K_FASTGETS
和表中函数的存在来设置。但是,通常提供该接口的类可能需要在特定实例中避免它。“挂起”层需要在被推送到不支持该接口的层之上时执行此操作。(Perl 的sv_gets()
不希望在一次“获取”期间流的快速gets
行为发生变化。)
Size_t fsize;
函数表的大小。将其与 PerlIO 代码“知道”的值进行比较,作为兼容性检查。未来版本可能能够容忍针对旧版标头编译的层。
char * name;
Perl 应在 open() 上调用的 open() 方法的层的名称。例如,如果层称为 APR,您将调用
open $fh, ">:APR", ...
并且 Perl 知道它必须调用由 APR 层实现的 PerlIOAPR_open() 方法。
Size_t size;
每个实例数据结构的大小,例如
sizeof(PerlIOAPR)
如果此字段为零,则PerlIO_pushed
不会分配任何内容,并假定层的 Pushed 函数将执行任何必需的层堆栈操作 - 用于避免虚拟层的分配/释放开销。如果字段非零,则它必须至少为PerlIOl
的大小,PerlIO_pushed
将为层的 data 结构分配内存并将新层链接到流的堆栈上。(如果层的 Pushed 方法返回错误指示,则该层将再次弹出。)
IV kind;
PERLIO_K_BUFFERED
该层已缓冲。
PERLIO_K_RAW
该层可接受在 binmode(FH) 堆栈中 - 即它不会(或将自身配置为不会)转换通过它的字节。
PERLIO_K_CANCRLF
层可以在“\n”和 CRLF 行尾之间进行转换。
PERLIO_K_FASTGETS
层允许缓冲区窥探。
PERLIO_K_MULTIARG
当层的 open() 接受比通常更多的参数时使用。额外的参数不应出现在 MODE
参数之前。当使用此标志时,由层来验证参数。
IV (*Pushed)(pTHX_ PerlIO *f,const char *mode, SV *arg);
唯一绝对强制的方法。当层被压入堆栈时调用。如果这是在后打开发生,则 mode
参数可以为 NULL。如果传递了参数字符串,则 arg
将为非 NULL
。在大多数情况下,这应该调用 PerlIOBase_pushed()
以将 mode
转换为适当的 PERLIO_F_XXXXX
标志,以及层本身执行的任何操作。如果层不期望参数,则它既不需要保存传递给它的参数,也不需要提供 Getarg()
(它可能 Perl_warn
参数意外)。
成功时返回 0。失败时返回 -1,并应设置 errno。
IV (*Popped)(pTHX_ PerlIO *f);
当层从堆栈中弹出时调用。通常在调用 Close()
后弹出层。但如果程序在流上动态管理层,则可以在不关闭的情况下弹出层。在这种情况下,Popped()
应释放未直接保存在层的结构中的任何资源(缓冲区、转换表...)。它还应将已从下层读取并缓冲的任何未消耗数据 Unread()
回该层,以便可以重新提供给现在位于其上方的任何内容。
成功和失败时返回 0。如果 Popped()
返回 true,则 perlio.c 假设层已弹出自身,或者层非常特殊,需要出于其他原因保留。在大多数情况下,它应该返回 false。
PerlIO * (*Open)(...);
Open()
方法有许多参数,因为它结合了 perl 的 open
、PerlIO_open
、perl 的 sysopen
、PerlIO_fdopen
和 PerlIO_reopen
的功能。完整的原型如下
PerlIO * (*Open)(pTHX_ PerlIO_funcs *tab,
PerlIO_list_t *layers, IV n,
const char *mode,
int fd, int imode, int perm,
PerlIO *old,
int narg, SV **args);
Open 应该(可能间接)调用 PerlIO_allocate()
来分配表中的一个槽,并通过调用 PerlIO_push
将其与打开文件的层信息关联。层是所有用于 PerlIO *
的层的数组,以及传递给它们的任何参数,n 是被调用层的该数组中的索引。宏 PerlIOArg
将返回一个(可能为 NULL
)SV *,用于传递给该层的参数。
当层打开或获取文件描述符的所有权时,该层负责将文件描述符的 close-on-exec 标志置于正确状态。对于编号小于或等于 PL_maxsysfd
的文件描述符,该标志应清除,而对于编号更高的任何文件描述符,该标志应设置。为了线程安全,当层打开一个新文件描述符时,如果可能,它应该在最初设置 close-on-exec 标志的情况下打开它。
mode 字符串是一个“类似 fopen()
”的字符串,它将匹配正则表达式 /^[I#]?[rwa]\+?[bt]?$/
。
'I'
前缀在通过特殊的 PerlIO_fdopen
调用创建 stdin
..stderr
时使用;'#'
前缀表示这是 sysopen
,并且应将 imode 和 perm 传递给 PerlLIO_open3
;'r'
表示read,'w'
表示write,'a'
表示append。'+'
后缀表示允许同时读写/追加。'b'
后缀表示文件应为二进制文件,'t'
表示它是文本文件。(几乎所有层都应在二进制模式下执行 IO,并忽略 b/t 位。应推送 :crlf
层来处理区别。)
如果 old 不为 NULL
,则这是 PerlIO_reopen
。Perl 本身不使用此功能(尚未使用?),并且语义有点模糊。
如果 fd 不为负,则它是数字文件描述符 fd,它将以与提供的模式字符串兼容的方式打开,因此该调用等效于 PerlIO_fdopen
。在这种情况下,nargs 将为零。文件描述符可以设置或清除 close-on-exec 标志;负责获取该标志的层有责任将其置于正确状态。
如果 nargs 大于零,则它给出传递给 open
的参数数量,否则它将为 1,例如,如果调用了 PerlIO_open
。在简单的情况下,SvPV_nolen(*args) 是要打开的路径名。
如果一个层提供了 Open()
,它通常应该调用下一层的 Open()
方法(如果存在),然后在成功后将自身推到顶部。PerlIOBase_open
被提供来执行此操作,因此在大多数情况下,您不必编写自己的 Open()
方法。如果未定义此方法,其他层可能难以在打开期间将其推到该层之上。
如果执行了 PerlIO_push
并且打开失败,它必须 PerlIO_pop
自身,因为如果不这样做,该层将不会被删除,并且可能会导致严重问题。
失败时返回 NULL
。
IV (*Binmode)(pTHX_ PerlIO *f);
可选。当 :raw
层被推入(显式或作为 binmode(FH) 的结果)时使用。如果不存在,该层将被弹出。如果存在,应将该层配置为二进制(或弹出自身)并返回 0。如果它返回 -1 表示错误,binmode
将失败,该层仍保留在堆栈中。
SV * (*Getarg)(pTHX_ PerlIO *f,
CLONE_PARAMS *param, int flags);
可选。如果存在,应返回一个 SV *,表示当该层被推入时传递给该层的字符串参数。例如:":encoding(ascii)"
将返回一个值 "ascii" 的 SvPV。(在大多数情况下,可以忽略 param 和 flags 参数)
Dup
使用 Getarg
检索最初传递给 Pushed
的参数,因此如果您的层对 Pushed
有额外的参数并且将被 Dup
,您必须实现此函数。
IV (*Fileno)(pTHX_ PerlIO *f);
返回句柄的 Unix/Posix 数字文件描述符。通常 PerlIOBase_fileno()
(它只询问下一层)就足以满足此要求。
出错时返回 -1,其中包括该层无法提供此类文件描述符的情况。
PerlIO * (*Dup)(pTHX_ PerlIO *f, PerlIO *o,
CLONE_PARAMS *param, int flags);
XXX:需要更多文档。
当一个线程被生成(在这种情况下,param 将为非 NULL)以及当一个流通过 open
中的“&”被复制时,用作“克隆”过程的一部分。
类似于 Open
,成功时返回 PerlIO*,失败时返回 NULL
。
SSize_t (*Read)(pTHX_ PerlIO *f, void *vbuf, Size_t count);
基本读取操作。
通常会调用 Fill
并操作指针(可能通过 API)。PerlIOBuf_read()
可能适用于提供“快速获取”方法的派生类。
返回实际读取的字节数,或在出错时返回 -1。
SSize_t (*Unread)(pTHX_ PerlIO *f,
const void *vbuf, Size_t count);
ungetc()
的 stdio 超集。应安排将来的读取查看 vbuf
中的字节。如果没有明显更好的实现,则 PerlIOBase_unread()
通过在调用层之上推送“假”的“挂起”层来提供此函数。
返回未读字符数。
SSize_t (*Write)(PerlIO *f, const void *vbuf, Size_t count);
基本写入操作。
返回写入的字节数,或在发生错误时返回 -1。
IV (*Seek)(pTHX_ PerlIO *f, Off_t offset, int whence);
定位文件指针。通常应调用其自身的 Flush
方法,然后调用下一层的 Seek
方法。
成功时返回 0,失败时返回 -1。
Off_t (*Tell)(pTHX_ PerlIO *f);
返回文件指针。可能基于层缓存的位置概念以避免开销。
获取文件指针失败时返回 -1。
IV (*Close)(pTHX_ PerlIO *f);
关闭流。通常应调用 PerlIOBase_close()
来刷新自身并关闭下层,然后释放未直接保存在数据结构中的任何数据结构(缓冲区、转换表,...)。
成功时返回 0,失败时返回 -1。
IV (*Flush)(pTHX_ PerlIO *f);
应使流的状态与下层保持一致。也就是说,应写入任何缓冲的写入数据,并针对从下层读取但实际上未消耗的数据调整下层的文件位置。(或许应将此类数据 Unread()
到下层。)
成功时返回 0,失败时返回 -1。
IV (*Fill)(pTHX_ PerlIO *f);
应从下层填充此层的缓冲区(用于读取)。当“子类化”PerlIOBuf 层时,您希望使用其 _read 方法并提供自己的填充方法,该方法填充 PerlIOBuf 的缓冲区。
成功时返回 0,失败时返回 -1。
IV (*Eof)(pTHX_ PerlIO *f);
返回文件结束指示符。PerlIOBase_eof()
通常就足够了。
文件结束时返回 0,未文件结束时返回 1,发生错误时返回 -1。
IV (*Error)(pTHX_ PerlIO *f);
返回错误指示符。PerlIOBase_error()
通常就足够了。
如果存在错误(通常在设置 PERLIO_F_ERROR
时),则返回 1,否则返回 0。
void (*Clearerr)(pTHX_ PerlIO *f);
清除文件结束和错误指示符。应调用 PerlIOBase_clearerr()
来设置 PERLIO_F_XXXXX
标志,这可能就足够了。
void (*Setlinebuf)(pTHX_ PerlIO *f);
将流标记为行缓冲。PerlIOBase_setlinebuf()
设置 PERLIO_F_LINEBUF 标志,通常就足够了。
STDCHAR * (*Get_base)(pTHX_ PerlIO *f);
分配(如果尚未分配)此层的读取缓冲区,并返回指向它的指针。如果失败,则返回 NULL。
Size_t (*Get_bufsiz)(pTHX_ PerlIO *f);
返回上一个 Fill()
放入缓冲区的字节数。
STDCHAR * (*Get_ptr)(pTHX_ PerlIO *f);
返回此层缓冲区中当前的读取指针。
SSize_t (*Get_cnt)(pTHX_ PerlIO *f);
返回当前缓冲区中剩余的字节数。
void (*Set_ptrcnt)(pTHX_ PerlIO *f,
STDCHAR *ptr, SSize_t cnt);
调整读取指针和字节计数,以匹配 ptr
和/或 cnt
。应用程序(或上层)必须确保它们一致。(偏执狂允许检查。)
要询问下一层,请使用 PerlIONext(PerlIO *f)。
要检查 PerlIO* 是否有效,请使用 PerlIOValid(PerlIO *f)。(它真正要做的只是检查指针是否为非 NULL,以及该指针后面的指针是否为非 NULL。)
PerlIOBase(PerlIO *f) 返回“基”指针,或者换句话说,返回 PerlIOl*
指针。
PerlIOSelf(PerlIO* f, type) 返回转换为类型的 PerlIOBase。
Perl_PerlIO_or_Base(PerlIO* f, callback, base, failure, args) 要么使用 args 从层 f 的函数中调用回调(只需使用 IO 函数的名称,例如“读取”),或者如果没有这样的回调,则使用相同的 args 调用回调的基本版本,或者如果 f 无效,则将 errno 设置为 EBADF 并返回失败。
Perl_PerlIO_or_fail(PerlIO* f, callback, failure, args) 要么使用 args 从层 f 的函数中调用回调,或者如果没有这样的回调,则将 errno 设置为 EINVAL。或者如果 f 无效,则将 errno 设置为 EBADF 并返回失败。
Perl_PerlIO_or_Base_void(PerlIO* f, callback, base, args) 要么使用 args 从层 f 的函数中调用回调,或者如果没有这样的回调,则使用相同的 args 调用回调的基本版本,或者如果 f 无效,则将 errno 设置为 EBADF。
Perl_PerlIO_or_fail_void(PerlIO* f, callback, args) 要么使用 args 从层 f 的函数中调用回调,或者如果没有这样的回调,则将 errno 设置为 EINVAL。或者如果 f 无效,则将 errno 设置为 EBADF。
如果你发现实现文档不清楚或不够充分,请查看现有的 PerlIO 层实现,其中包括
C 实现
Perl 核心中的perlio.c 和perliol.h 实现“unix”、“perlio”、“stdio”、“crlf”、“utf8”、“byte”、“raw”、“pending”层,以及适用的“mmap”和“win32”层。(“win32”目前尚未完成且未使用,要查看 Win32 中使用的是什么,请参阅 "PerlIO 中的查询文件句柄的层" 。)
PerlIO::encoding、PerlIO::scalar、PerlIO::via 在 Perl 核心。
PerlIO::gzip 和 APR::PerlIO (mod_perl 2.0) 在 CPAN 上。
Perl 实现
PerlIO::via::QuotedPrint 在 Perl 核心和 PerlIO::via::* 在 CPAN 上。
如果您正在创建一个 PerlIO 层,您可能想要偷懒,换句话说,只实现您感兴趣的方法。您可以用“空白”方法替换其他方法
PerlIOBase_noop_ok
PerlIOBase_noop_fail
(什么都不做,分别返回零和 -1) 或对于某些方法,您可以使用 NULL 方法假定默认行为。Open 方法在“父”层中寻找帮助。下表总结了行为
method behaviour with NULL
Clearerr PerlIOBase_clearerr
Close PerlIOBase_close
Dup PerlIOBase_dup
Eof PerlIOBase_eof
Error PerlIOBase_error
Fileno PerlIOBase_fileno
Fill FAILURE
Flush SUCCESS
Getarg SUCCESS
Get_base FAILURE
Get_bufsiz FAILURE
Get_cnt FAILURE
Get_ptr FAILURE
Open INHERITED
Popped SUCCESS
Pushed SUCCESS
Read PerlIOBase_read
Seek FAILURE
Set_cnt FAILURE
Set_ptrcnt FAILURE
Setlinebuf PerlIOBase_setlinebuf
Tell FAILURE
Unread PerlIOBase_unread
Write FAILURE
FAILURE Set errno (to EINVAL in Unixish, to LIB$_INVARG in VMS)
and return -1 (for numeric return values) or NULL (for
pointers)
INHERITED Inherited from the layer below
SUCCESS Return 0 (for numeric return values) or a pointer
文件 perlio.c
提供以下层
一个基本非缓冲层,它调用 Unix/POSIX read()
、write()
、lseek()
、close()
。无缓冲。即使在区分 O_TEXT 和 O_BINARY 的平台上,此层始终为 O_BINARY。
一个非常完整的通用缓冲层,它提供了整个 PerlIO API。它还旨在用作其他层的“基类”。(例如,它的 Read()
方法是根据 Get_cnt()
/Get_ptr()
/Set_ptrcnt()
方法实现的)。
在“unix”上的“perlio”为通过 PerlIO API 看到的 stdio 提供了完整的替换。当系统的 stdio 不允许 perl 的“快速获取”访问并且不区分 O_TEXT
和 O_BINARY
时,这是 USE_PERLIO 的默认值。
一个通过层方案提供 PerlIO API 的层,但通过调用系统的 stdio 来实现它。如果系统的 stdio 提供足够的访问权限以允许 perl 的“快速获取”访问并且不区分 O_TEXT
和 O_BINARY
,则这是(当前的)默认值。
使用“perlio”作为基类的派生层。它提供类似于 Win32 的“\n”到 CR、LF 转换。既可应用于“perlio”之上,也可作为缓冲层本身。如果系统区分 O_TEXT
和 O_BINARY
打开,则“crlf”高于“unix”是默认值。(在某些时候,“unix”将在该平台上替换为“本机”Win32 IO 层,因为 Win32 的读/写层有各种缺点。)“crlf”层是转换数据的层的合理模型。
如果 Configure 检测到 mmap()
函数,则提供此层(以“perlio”为“base”),该层通过 mmap()ing 文件执行“read”操作。在现代系统上,性能提升很小,因此它主要作为概念验证而存在。它很可能在某个时候从核心解绑。“mmap”层是极简主义“派生”层的合理模型。
“perlio”的“内部”派生,可用于为没有缓冲或无法处理的层提供 Unread() 函数。(基本上,此层的 Fill()
会将自身从堆栈中弹出,然后从下层恢复读取。)
一个永远不存在于层堆栈中的虚拟层。相反,当“push”时,它实际上会弹出堆栈并移除自身,然后调用堆栈中所有层的 Binmode 函数表项 - 通常这(通过 PerlIOBase_binmode)会移除任何未设置 PERLIO_K_RAW
位的层。层可以通过定义自己的 Binmode 项来修改该行为。
另一个虚拟层。当 push 时,它会弹出自身并在堆栈顶部(现在再次成为顶部)的层上设置 PERLIO_F_UTF8
标志。
此外,perlio.c 还提供许多 PerlIOBase_xxxx()
函数,这些函数旨在用于不需要对特定方法执行任何特殊操作的类的表槽中。
层可以通过扩展模块提供。当遇到未知层时,PerlIO 代码将执行等效于
use PerlIO 'layer';
其中 layer 是未知层。PerlIO.pm 然后将尝试
require PerlIO::layer;
如果经过该过程后该层仍然未定义,则open
将失败。
以下扩展层与 Perl 捆绑在一起
use Encoding;
使该层可用,尽管PerlIO.pm“知道”在哪里可以找到它。它是一个作为参数调用的层的示例,如下所示
open( $fh, "<:encoding(iso-8859-7)", $pathname );
提供从标量读取数据和向标量写入数据的支持。
open( $fh, "+<:scalar", \$scalar );
当句柄如此打开时,读取会从$scalar的字符串值获取字节,并且写入会更改该值。在这两种情况下,$scalar中的位置都从零开始,但可以通过seek
更改,并通过tell
确定。
请注意,在如下调用 open() 时,此层是隐含的
open( $fh, "+<", \$scalar );
提供允许将层实现为 Perl 代码。例如
use PerlIO::via::StripHTML;
open( my $fh, "<:via(StripHTML)", "index.html" );
有关详细信息,请参见PerlIO::via。
需要完成以改进此文档的事项。
解释如何在不通过 open() 的情况下创建有效的 fh(即应用层)。例如,如果文件不是通过 Perl 打开的,但我们希望取回一个 fh,就像它是由 Perl 打开的一样。
PerlIO_apply_layera 如何适应,它的文档在哪里,它是否已公开?
当前,示例可能如下所示
PerlIO *foo_to_PerlIO(pTHX_ char *mode, ...)
{
char *mode; /* "w", "r", etc */
const char *layers = ":APR"; /* the layer name */
PerlIO *f = PerlIO_allocate(aTHX);
if (!f) {
return NULL;
}
PerlIO_apply_layers(aTHX_ f, mode, layers);
if (f) {
PerlIOAPR *st = PerlIOSelf(f, PerlIOAPR);
/* fill in the st struct, as in _open() */
st->file = file;
PerlIOBase(f)->flags |= PERLIO_F_OPEN;
return f;
}
return NULL;
}
修复/添加标记为 XXX 的位置中的文档。
未指定该层对错误的处理方式。例如,何时应显式设置 $!,何时应将错误处理仅委托给顶层。
可能会提供有关使用 SETERRNO() 的一些提示,或指向可以找到它们的位置。
我认为提供一些具体示例将有助于更容易理解 API。当然,我同意 API 必须简洁,但由于没有第二份更像指南的文档,我认为从 API 文档开始会更容易,但在不清楚的地方包含示例,对于还不是 PerlIO 大师的人来说。