perliol
СОДЕРЖАНИЕ
ИМЯ
perliol - C API для реализации Perl's IO в слоях.
СИНОПСИС
/* Defining a layer ... */
#include <perliol.h> ОПИСАНИЕ
В этом документе описано поведение и реализация абстракции PerlIO, описанной в perlapio, когда USE_PERLIO определена.
История и предыстория
Абстракция PerlIO была введена в perl5.003_02, но оставалась просто абстракцией до perl5.7.0. Однако за это время ряд расширений perl перешли к ее использованию, поэтому API в основном зафиксирован для поддержания совместимости (исходного кода).
Цель реализации — предоставить API PerlIO гибким и независимым от платформы способом. Это также опыт «объектно-ориентированного C с vtable», который может быть применён к Raku.
Основная структура
PerlIO — это стек слоёв.
Нижние уровни стека работают с низкоуровневыми системными вызовами (дескрипторы файлов в C), получая и отправляя байты, более высокие уровни стека буферизуют, фильтруют и иначе обрабатывают ввод/вывод и возвращают символы (или байты) в Perl. Термины «выше» и «ниже» используются для обозначения относительного расположения слоёв стека.
Слой содержит «vtable», таблицу операций ввода/вывода (на уровне C — таблицу указателей функций), и флаги состояния. Функции в vtable реализуют такие операции, как «open», «read» и «write».
При запросе ввода/вывода, например, «read», запрос идёт из Perl сначала вниз по стеку, используя функции «read» каждого слоя, затем внизу запрос отправляется системным службам, а результат возвращается вверх по стеку, в конечном итоге интерпретируясь как данные Perl.
Запросы не обязательно всегда доходят до операционной системы: именно здесь проявляется буферизация PerlIO.
Когда вы выполняете open() и указываете дополнительные слои PerlIO для развертывания, указанные вами слои «добавляются» сверху уже существующего стандартного стека. Один из способов взглянуть на это — «операционная система слева», а «Perl справа».
Какие именно слои находятся в этом стандартном стеке, зависит от многих факторов: вашей операционной системы, версии Perl, конфигурации Perl во время компиляции и конфигурации Perl во время выполнения. Для получения дополнительной информации см. PerlIO, "PERLIO" в perlrun и open.
binmode() работает аналогично open(): по умолчанию указанные слои добавляются сверху существующего стека.
Однако обратите внимание, что даже если указанные слои «добавляются сверху» для open() и binmode(), это не означает, что их влияние ограничивается «верхним» уровнем: слои PerlIO могут быть очень «активными» и проверять и влиять на слои, расположенные глубже в стеке. Например, существует слой «raw», который многократно «удаляет» слои, пока не достигнет первого слоя, который заявил о своей способности обрабатывать двоичные данные. «Добавленные» слои обрабатываются в порядке слева направо.
sysopen() работает (как можно предположить) на более низком уровне в стеке, чем open(). Например, в Unix или подобных системах sysopen() работает непосредственно на уровне дескрипторов файлов: в терминах слоёв PerlIO он использует только слой «unix», который является довольно тонким обёрткой над дескрипторами файлов Unix.
Слои против дисциплин
Первоначальная дискуссия о возможности изменения поведения потоков ввода/вывода использовала термин «дисциплина» для сущностей, которые добавлялись. Полагаю, это произошло из-за использования этого термина в «sfio», который в свою очередь позаимствовал его из «линейных дисциплин» на терминалах Unix. Однако в этом документе (и коде C) используется термин «слой».
Надеюсь, это естественный термин, учитывая реализацию, и он должен избежать коннотаций, присущих более ранним использованиям «дисциплины» для вещей, которые довольно сильно отличаются.
Структуры данных
Основная структура данных — это 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 */
}; A PerlIOl * — указатель на структуру, а уровень PerlIO * — указатель на PerlIOl * — т.е. указатель на указатель на структуру. Это позволяет уровню PerlIO * оставаться постоянным, в то время как фактический PerlIOl * снизу меняется. (Сравните с SV * perl, который остаётся постоянным, в то время как поле sv_any меняется по мере изменения типа скалярной переменной.) Поток ввода/вывода в общем случае представляется как указатель на этот связанный список «слоёв».
Следует отметить, что из-за двойного косвенного обращения в PerlIO *, &(perlio->next) «является» PerlIO *, и поэтому, по крайней мере, один слой может использовать «стандартный» API на следующем слое снизу.
«Слой» состоит из двух частей:
-
Функции и атрибуты «класса слоя».
-
Данные на экземпляр для конкретной дескриптор.
Функции и атрибуты
К функциям и атрибутам можно получить доступ через член «tab» (для таблицы) PerlIOl. Функции (методы «класса» слоя) фиксированы и определяются типом 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 данных на экземпляр и некоторые флаги, которые являются атрибутами всего класса (например, является ли он буферизующим слоем), затем следуют функции, которые можно разделить на четыре основные группы:
-
Функции открытия и настройки
-
Основные операции ввода/вывода
-
Параметры буферизации класса 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» можно заменить (во время открытия или даже динамически) на слой «socket». -
Разные дескрипторы могут иметь разные схемы буферизации. «Верхний» слой может быть слоем «mmap», если чтение файлов на диске было бы быстрее с использованием
mmap, чемread. «Небуферизованный» поток можно реализовать, просто не имея буферизующего слоя. -
Можно вставить дополнительные слои для обработки данных по мере их прохождения. Именно в этом была основная потребность в схеме в perl 5.7.0+ — нам нужен был механизм, позволяющий преобразовывать данные между внутренней кодировкой perl (по крайней мере, концептуально, Unicode как UTF-8) и «родном» формате, используемом системой. Это обеспечивается слоем «:encoding(xxxx)», который обычно располагается над буферизующим слоем.
-
Можно добавить слой, который выполняет перевод «\n» в CRLF. Этот слой можно использовать на любой платформе, а не только на тех, которые обычно это делают.
Флаги состояния на экземпляр
Общие флаги состояния представляют собой гибрид флагов в стиле O_XXXXX , выведенных из строки режима, переданной в PerlIO_open(), и битов состояния для типичных буферизующих слоёв.
- PERLIO_F_EOF
-
Конец файла.
- PERLIO_F_CANWRITE
-
Запись разрешена, т.е. открыто как "w" или "r+" или "a" и т.д.
- PERLIO_F_CANREAD
-
Чтение разрешено, т.е. открыто как "r" или "w+" (или даже "a+" - уф).
- PERLIO_F_ERROR
-
Произошла ошибка (для
PerlIO_error()). - PERLIO_F_TRUNCATE
-
Файл предполагается обрезать в соответствии с режимом открытия.
- PERLIO_F_APPEND
-
Все записи должны быть добавлениями.
- PERLIO_F_CRLF
-
Слой выполняет преобразование «\n» в CR,LF для вывода и CR,LF в «\n» для ввода, подобно Win32. Обычно слой «crlf» — единственный слой, который должен этим заниматься.
PerlIO_binmode()будет изменять этот флаг вместо добавления/удаления слоев, если для класса слоев установлен битPERLIO_K_CANCRLF. - PERLIO_F_UTF8
-
Данные, записываемые в этот слой, должны быть закодированы в UTF-8; данные, предоставленные этим слоем, должны рассматриваться как закодированные в UTF-8. Может быть установлен на любом слое с помощью фиктивного слоя «:utf8». Также установлен на слое «:encoding».
- PERLIO_F_UNBUF
-
Слой не буферизован — т.е. запись в следующий слой должна выполняться для каждой записи в этот слой.
- PERLIO_F_WRBUF
-
Буфер этого слоя в настоящее время содержит данные, записанные в него, но ещё не отправленные в следующий слой.
- PERLIO_F_RDBUF
-
Буфер этого слоя в настоящее время содержит непрочитанные данные, считанные из нижнего слоя.
- PERLIO_F_LINEBUF
-
Слой имеет буферизацию по строкам. Данные записи должны быть переданы в следующий слой всякий раз, когда встречается «\n». Любые данные после «\n» должны затем обрабатываться.
- PERLIO_F_TEMP
-
Файл был
unlink()рован или должен быть удалён приclose(). - PERLIO_F_OPEN
-
Дескриптор файла открыт.
- PERLIO_F_FASTGETS
-
Этот экземпляр этого слоя поддерживает интерфейс «быстрый
gets». Обычно устанавливается на основеPERLIO_K_FASTGETSдля класса и наличием функции(й) в таблице. Однако классу, который обычно предоставляет этот интерфейс, может потребоваться его отключение для конкретного экземпляра. Слой «pending» должен это сделать, когда он расположен над слоем, который не поддерживает этот интерфейс. (Perl'ssv_gets()не ожидает изменения поведения быстрыхgetsпотоков во время одного вызова «get».)
Подробное описание методов
- fsize
-
Size_t fsize;Размер таблицы функций. Он сравнивается со значением, известным коду PerlIO, для проверки совместимости. Будущие версии могут быть способны допускать слои, скомпилированные с использованием старой версии заголовков.
- name
-
char * name;Имя слоя, метод open() которого Perl должен вызвать при открытии. Например, если слой называется APR, вы вызовете:
open $fh, ">:APR", ...и Perl знает, что должен вызвать метод PerlIOAPR_open(), реализованный слоем APR.
- size
-
Size_t size;Размер структуры данных на экземпляр, например:
sizeof(PerlIOAPR)Если это поле равно нулю, то
PerlIO_pushedне выделяет память и предполагает, что функция Pushed слоя выполнит необходимые манипуляции со стеком слоя — используется для избежания накладных расходов malloc/free для фиктивных слоёв. Если поле не равно нулю, оно должно быть как минимум размеромPerlIOl,PerlIO_pushedвыделит память для структур данных слоя и подключит новый слой к стеку потока. (Если метод Pushed слоя возвращает ошибку, слой извлекается обратно.) - kind
-
IV kind;-
PERLIO_K_BUFFERED
Слой буферизован.
-
PERLIO_K_RAW
Слой допустим для использования в стеке binmode(FH) — то есть он не (или настроится так, чтобы не) преобразовывать байты, проходящие через него.
-
PERLIO_K_CANCRLF
Слой может преобразовывать между "\n" и CRLF-концами строк.
-
PERLIO_K_FASTGETS
Слой разрешает просмотр буфера.
-
PERLIO_K_MULTIARG
Используется, когда метод open() слоя принимает больше аргументов, чем обычно. Дополнительные аргументы должны находиться не перед аргументом
MODE. Когда используется этот флаг, слой должен проверить аргументы.
-
- Pushed
-
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.
- Popped
-
IV (*Popped)(pTHX_ PerlIO *f);Вызывается, когда слой извлекается из стека. Слой обычно извлекается после вызова
Close(). Но слой может быть извлечён без закрытия, если программа динамически управляет слоями в потоке. В таких случаяхPopped()должен освободить все ресурсы (буферы, таблицы преобразования и т. д.), не содержащиеся непосредственно в структуре слоя. Он также долженUnread()любые неиспользованные данные, которые были прочитаны и буферизованы из нижнего слоя обратно в этот слой, чтобы они могли быть повторно предоставлены тому, что сейчас выше.Возвращает 0 при успехе и при ошибке. Если
Popped()возвращает true, то perlio.c предполагает, что либо слой извлёк себя, либо слой является суперспециальным и его нужно сохранить по другим причинам. В большинстве случаев он должен возвращать false. - Open
-
PerlIO * (*Open)(...);Метод
Open()имеет множество аргументов, потому что он объединяет функции Perl'sopen,PerlIO_open, Perl'ssysopen,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. layers — это массив всех слоёв, предназначенных дляPerlIO *, и любых аргументов, переданных им, n — это индекс в этом массиве вызываемого слоя. МакросPerlIOArgвернёт (возможно,NULL) SV * для аргумента, переданного слою.Если слой открывает или получает права доступа к файловому дескриптору, этот слой отвечает за приведение флага закрытия при выполнении в правильное состояние. Флаг должен быть сброшен для файлового дескриптора с номером меньше или равным
PL_maxsysfd, и установлен для любого файлового дескриптора с бо́льшим номером. Для обеспечения потоковой безопасности, когда слой открывает новый файловый дескриптор, он, если это возможно, должен открыть его с флагом close-on-exec, изначально установленным.Строка mode — это строка типа "
fopen()", которая будет соответствовать регулярному выражению/^[I#]?[rwa]\+?[bt]?$/.Префикс
'I'используется при созданииstdin..stderrчерез специальные вызовыPerlIO_fdopen; префикс'#'означает, что этоsysopenи что imode и perm должны быть переданы вPerlLIO_open3;'r'означает чтение,'w'означает запись, а'a'означает добавление. Суффикс'+'означает, что разрешены как чтение, так и запись/добавление. Суффикс'b'означает, что файл должен быть двоичным, а't'означает, что он является текстовым. (Практически все слои должны выполнять ввод/вывод в двоичном режиме и игнорировать биты 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при ошибке. - Binmode
-
IV (*Binmode)(pTHX_ PerlIO *f);Необязательно. Используется, когда слой
:rawпомещается в стек (явно или в результате binmode(FH)). Если он отсутствует, слой будет извлечён. Если он присутствует, он должен настроить слой как двоичный (или извлечь себя) и вернуть 0. Если он возвращает -1 для ошибки,binmodeзавершится неудачей, а слой останется в стеке. - Getarg
-
SV * (*Getarg)(pTHX_ PerlIO *f, CLONE_PARAMS *param, int flags);Необязательно. Если он присутствует, он должен вернуть SV *, представляющий строковый аргумент, переданный слою, когда он был помещён в стек. Например, ":encoding(ascii)" вернёт SvPV со значением "ascii". (Аргументы param и flags можно игнорировать в большинстве случаев).
DupиспользуетGetargдля извлечения аргумента, первоначально переданногоPushed, поэтому вы должны реализовать эту функцию, если ваш слой имеет дополнительный аргумент дляPushedи когда-либо будетDup. - Fileno
-
IV (*Fileno)(pTHX_ PerlIO *f);Возвращает числовой файловый дескриптор Unix/Posix для объекта-обработчика. Обычно
PerlIOBase_fileno()(который просто спрашивает у следующего нижнего слоя) будет достаточным.Возвращает -1 при ошибке, что считается включающим случай, когда слой не может предоставить такой файловый дескриптор.
- Dup
-
PerlIO * (*Dup)(pTHX_ PerlIO *f, PerlIO *o, CLONE_PARAMS *param, int flags);XXX: Требуется более подробная документация.
Используется в рамках процесса "клонирования" при запуске потока (в этом случае param будет не NULL) и при дублировании потока с помощью '&' в
open.Аналогично
Open, возвращает PerlIO* при успехе,NULLпри ошибке. - Read
-
SSize_t (*Read)(pTHX_ PerlIO *f, void *vbuf, Size_t count);Базовая операция чтения.
Обычно вызывает
Fillи манипулирует указателями (возможно, через API).PerlIOBuf_read()может подойти для производных классов, которые предоставляют методы "быстрого получения".Возвращает фактическое количество прочитанных байтов или -1 при ошибке.
- Unread
-
SSize_t (*Unread)(pTHX_ PerlIO *f, const void *vbuf, Size_t count);Надстройка над stdio's
ungetc(). Должно обеспечить, чтобы последующие чтения видели байты вvbuf. Если нет очевидно лучшей реализации,PerlIOBase_unread()предоставляет функцию путём размещения "фиктивного" "ожидающего" слоя над вызывающим слоем.Возвращает количество непрочитанных символов.
- Write
-
SSize_t (*Write)(PerlIO *f, const void *vbuf, Size_t count);Базовая операция записи.
Возвращает количество записанных байтов или -1 при ошибке.
- Seek
-
IV (*Seek)(pTHX_ PerlIO *f, Off_t offset, int whence);Установить указатель файла. Обычно вызывает собственный метод
Flushи затем методSeekследующего нижнего слоя.Возвращает 0 при успехе, -1 при ошибке.
- Tell
-
Off_t (*Tell)(pTHX_ PerlIO *f);Возвращает указатель файла. Может быть основан на кэшированном значении позиции слоёв для избежания накладных расходов.
Возвращает -1 при ошибке получения указателя файла.
- Close
-
IV (*Close)(pTHX_ PerlIO *f);Закрыть поток. Обычно вызывает
PerlIOBase_close()для сброса и закрытия нижних слоёв, а затем освобождает все структуры данных (буферы, таблицы преобразования и т. д.), не содержащиеся непосредственно в структуре данных.Возвращает 0 при успехе, -1 при ошибке.
- Flush
-
IV (*Flush)(pTHX_ PerlIO *f);Должен согласовать состояние потока с нижними слоями. То есть любые буферизованные данные записи должны быть записаны, и позиция файла нижних слоёв должна быть скорректирована для данных, прочитанных снизу, но не фактически обработанных. (Возможно, должен
Unread()такие данные нижнему слою.)Возвращает 0 при успехе, -1 при ошибке.
- Fill
-
IV (*Fill)(pTHX_ PerlIO *f);Буфер для этого слоя должен быть заполнен (для чтения) из нижнего слоя. Когда вы "наследуете" слой PerlIOBuf, вы хотите использовать его метод _read и предоставить свой собственный метод заполнения, который заполняет буфер PerlIOBuf.
Возвращает 0 при успехе, -1 при ошибке.
- Eof
-
IV (*Eof)(pTHX_ PerlIO *f);Возвращает индикатор конца файла.
PerlIOBase_eof()обычно достаточно.Возвращает 0 при достижении конца файла, 1, если конец файла не достигнут, -1 при ошибке.
- Error
-
IV (*Error)(pTHX_ PerlIO *f);Возвращает индикатор ошибки.
PerlIOBase_error()обычно достаточно.Возвращает 1, если произошла ошибка (обычно при установке
PERLIO_F_ERROR), 0 в противном случае. - Clearerr
-
void (*Clearerr)(pTHX_ PerlIO *f);Сбросить индикаторы конца файла и ошибки. Должно вызвать
PerlIOBase_clearerr()для установки флаговPERLIO_F_XXXXX, что может быть достаточно. - Setlinebuf
-
void (*Setlinebuf)(pTHX_ PerlIO *f);Отметить поток как построчно буферизованный.
PerlIOBase_setlinebuf()устанавливает флаг PERLIO_F_LINEBUF и обычно этого достаточно. - Get_base
-
STDCHAR * (*Get_base)(pTHX_ PerlIO *f);Выделить (если ещё не сделано) буфер чтения для этого уровня и вернуть указатель на него. Вернуть NULL при ошибке.
- Get_bufsiz
-
Size_t (*Get_bufsiz)(pTHX_ PerlIO *f);Возвращает количество байтов, которые были последними
Fill()помещены в буфер. - Get_ptr
-
STDCHAR * (*Get_ptr)(pTHX_ PerlIO *f);Возвращает текущую позицию указателя чтения относительно буфера этого уровня.
- Get_cnt
-
SSize_t (*Get_cnt)(pTHX_ PerlIO *f);Возвращает количество байтов, оставшихся для чтения в текущем буфере.
- Set_ptrcnt
-
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) возвращает указатель «Base», или, другими словами, PerlIOl* указатель.
PerlIOSelf(PerlIO* f, type) возвращает PerlIOBase, преобразованный к типу.
Perl_PerlIO_or_Base(PerlIO* f, callback, base, failure, args) либо вызывает callback из функций слоя f (только по имени функции ввода-вывода, например, «Read») с args, либо, если такого callback нет, вызывает base версию callback с теми же args, либо, если f невалиден, устанавливает errno в EBADF и возвращает failure.
Perl_PerlIO_or_fail(PerlIO* f, callback, failure, args) либо вызывает callback функций слоя f с args, либо, если такого callback нет, устанавливает errno в EINVAL. Либо, если f невалиден, устанавливает errno в EBADF и возвращает failure.
Perl_PerlIO_or_Base_void(PerlIO* f, callback, base, args) либо вызывает callback функций слоя f с args, либо, если такого callback нет, вызывает base версию callback с теми же args, либо, если f невалиден, устанавливает errno в EBADF.
Perl_PerlIO_or_fail_void(PerlIO* f, callback, args) либо вызывает callback функций слоя f с args, либо, если такого callback нет, устанавливает errno в EINVAL. Либо, если f невалиден, устанавливает errno в EBADF.
Реализация слоёв PerlIO
Если вы считаете документ по реализации неясным или недостаточным, обратитесь к существующим реализациям слоёв PerlIO, которые включают:
-
Реализации на C
perlio.c и perliol.h в ядре Perl реализуют слои «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"
-
Базовый небуферизованный слой, который вызывает Unix/POSIX
read(),write(),lseek(),close(). Без буферизации. Даже на платформах, которые различают O_TEXT и O_BINARY, этот слой всегда является O_BINARY. - "perlio"
-
Очень полный универсальный буферизованный слой, который предоставляет весь API PerlIO. Он также предназначен для использования в качестве «базового класса» для других слоёв. (Например, его метод
Read()реализован через методыGet_cnt()/Get_ptr()/Set_ptrcnt()).«perlio» над «unix» обеспечивает полную замену stdio, как видно через API PerlIO. Это значение по умолчанию для USE_PERLIO, когда stdio системы не позволяет perl'овскому доступу «быстрых чтений», и которые не различают
O_TEXTиO_BINARY. - "stdio"
-
Слой, который предоставляет API PerlIO через схему слоёв, но реализует его, вызывая stdio системы. Это (в настоящее время) значение по умолчанию, если stdio системы обеспечивает достаточный доступ для поддержки «быстрых чтений» perl и которые не различают
O_TEXTиO_BINARY. - "crlf"
-
Слой, полученный с использованием «perlio» в качестве базового класса. Он предоставляет преобразование с Win32-подобного «\n» в CR,LF. Может быть применён как над «perlio», так и служить самим буферизованным слоем. «crlf» над «unix» является значением по умолчанию, если система различает
O_TEXTиO_BINARYоткрывания. (В какой-то момент «unix» будет заменён слоем ввода-вывода «родного» Win32 на этой платформе, так как слой чтения/записи Win32 имеет различные недостатки.) Слой «crlf» является разумной моделью слоя, который преобразует данные каким-либо образом. - "mmap"
-
Если Configure обнаруживает функции
mmap(), предоставляется этот слой (с «perlio» в качестве «базового»), который выполняет операции «чтения» путём mmap() файла. Улучшение производительности незначительно на современных системах, поэтому он в основном существует в качестве концептуального доказательства. Вероятно, он будет вынесен из ядра в какой-то момент. Слой «mmap» является разумной моделью для минималистского «производного» слоя. - "pending"
-
«Внутренний» производный слой «perlio», который может использоваться для предоставления функции Unread() для слоёв, у которых нет буфера или которые не хотят его иметь. (В основном, этот слой удаляет себя со стека и, таким образом, возобновляет чтение со слоя ниже.)
- "raw"
-
Слой-заглушка, который никогда не существует в стеке слоёв. Вместо этого, когда он «находится» в стеке, он фактически удаляет его, удаляя себя, затем вызывает функцию таблицы Binmode для всех слоёв в стеке — обычно это (через PerlIOBase_binmode) удаляет все слои, у которых не установлен бит
PERLIO_K_RAW. Слои могут изменить это поведение, определив свою собственную запись Binmode. - "utf8"
-
Ещё одна заглушка. При «нажатии» он удаляет себя и устанавливает флаг
PERLIO_F_UTF8на слой, который был (и теперь снова является) вершиной стека.
Кроме того, perlio.c также предоставляет ряд PerlIOBase_xxxx() функций, которые предназначены для использования в слотах таблиц классов, которым не нужно делать ничего особенного для конкретного метода.
Дополнительные слои
Слои могут быть доступны модулями расширения. При встрече неизвестного слоя код PerlIO выполнит эквивалент:
use PerlIO 'layer'; Где layer — неизвестный слой. PerlIO.pm затем попытается:
require PerlIO::layer; Если после этого процесса слой всё ещё не определён, open потерпит неудачу.
Следующие слои расширения поставляются с perl:
- ":encoding"
-
use Encoding;делает этот слой доступным, хотя PerlIO.pm «знает», где его найти. Это пример слоя, который принимает аргумент при вызове, поэтому:
open( $fh, "<:encoding(iso-8859-7)", $pathname ); - ":scalar"
-
Обеспечивает поддержку чтения данных из скаляра и записи данных в скаляр.
open( $fh, "+<:scalar", \$scalar );Когда обработчик открывается таким образом, чтения получают байты из строкового значения $scalar, а записи изменяют значение. В обоих случаях позиция в $scalar начинается с нуля, но может быть изменена с помощью
seek, и определена с помощьюtell.Обратите внимание, что этот слой подразумевается при вызове open(), например:
open( $fh, "+<", \$scalar ); - ":via"
-
Предоставлен для реализации слоёв с использованием кода Perl. Например:
use PerlIO::via::StripHTML; open( my $fh, "<:via(StripHTML)", "index.html" );См. PerlIO::via для получения дополнительных сведений.
Задачи
Вещи, которые необходимо сделать для улучшения этого документа.
-
Объяснить, как создать действительный дескриптор файла без прохождения через open() (т. е. применить слой). Например, если файл не открывается через perl, но мы хотим получить обратно дескриптор файла, как если бы он был открыт 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, который является API, но содержит примеры там, где что-то неясно, для человека, который ещё не является гуру PerlIO (ещё).
© 1993–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.36.0/perliol