perliol
СОДЕРЖАНИЕ
НАЗВАНИЕ
perliol - C API для реализации ввода-вывода Perl в слоях.
СИНТАКСИС
/* 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 реализуют такие операции, как «открытие», «чтение» и «запись».
Когда запрашивается ввод-вывод, например, «чтение», запрос идёт от Perl сначала вниз по стеку, используя функции «чтение» каждого слоя, затем внизу запрашивается ввод от системных служб, затем результат возвращается вверх по стеку, в конечном итоге интерпретируясь как данные 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 на следующем слое снизу.
«Слой» состоит из двух частей:
-
Функции и атрибуты «класса слоя».
-
Данные для каждого экземпляра конкретной обработки.
Функции и атрибуты
К функциям и атрибутам можно получить доступ через член «таб» (для таблицы) в 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» можно заменить (во время открытия или даже динамически) слоем «сокет». -
Разные обработчики могут иметь разные схемы буферизации. «Верхний» слой может быть слоем «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
-
Уровень выполняет преобразование, подобное Win32: "\n" отображается как CR,LF для вывода и CR,LF отображается как "\n" для ввода. Обычно слой "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для класса и наличием функции(ей) в таблице. Однако классу, который обычно предоставляет этот интерфейс, может потребоваться отказаться от него в конкретном экземпляре. Слой «ожидания» должен это сделать, когда он размещён над слоем, который не поддерживает интерфейс. (Perlsv_gets()не ожидает изменения поведения потока быстрогоgetsво время одного «получения».)
Подробное описание методов
- 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()имеет много аргументов, потому что он объединяет функции перловскихopen,PerlIO_open, перловских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. layers — массив всех слоев, предназначенных дляPerlIO *, и любых аргументов, переданных им, n — индекс в этом массиве для вызываемого слоя. МакросPerlIOArgвернет (возможно,NULL) SV * для аргумента, переданного слою.Когда слой открывает или берет на себя ответственность за дескриптор файла, этот слой отвечает за приведение флага close-on-exec дескриптора файла в нужное состояние. Флаг должен быть сброшен для дескриптора файла с номером меньше или равным
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: Требуются дополнительные документы.
Используется в процессе "клонирования" при запуске потока (в этом случае параметр будет не NULL) и при дублировании потока с помощью '&' в %%%CODE_BLOCK_137%%.
Аналогично
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);Надмножество
ungetc()stdio. Должен гарантировать, что будущие чтения будут видеть байты в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, отформатированный как type.
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, см. "Querying the layers of filehandles" в 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 системы не позволяет перлю "быстрый доступ к get", и которые не различают
O_TEXTиO_BINARY. - "stdio"
-
Слой, который предоставляет API PerlIO через схему слоёв, но реализует его, вызывая stdio системы. Это (в настоящее время) значение по умолчанию, если stdio системы обеспечивает достаточный доступ для перля к "быстрым get", и которые не различают
O_TEXTиO_BINARY. - "crlf"
-
Слой, полученный с использованием "perlio" в качестве базового класса. Он обеспечивает преобразование "\n" в CR,LF, подобное Win32. Может быть применён поверх "perlio" или служить буферизованным слоем. "crlf" поверх "unix" является значением по умолчанию, если система различает
O_TEXTиO_BINARYоткрытия. (В какой-то момент "unix" будет заменён на «родной» слой ввода-вывода Win32 на этой платформе, так как у слоя чтения/записи Win32 есть различные недостатки.) Слой "crlf" является разумной моделью слоя, который преобразует данные каким-либо образом. - "mmap"
-
Если Configure обнаруживает функции
mmap(), этот слой предоставляется (с "perlio" в качестве "базового"), который выполняет операции "read", mmap()ing файл. Улучшение производительности незначительно на современных системах, поэтому он в основном существует как демонстрация концепции. Вероятно, он будет отделён от ядра в какой-то момент. Слой "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 для получения подробной информации.
Задачи
Вещи, которые нужно сделать, чтобы улучшить этот документ.
-
Объясните, как создать действительный fh без прохождения через open() (т.е. применить слой). Например, если файл не открывается через 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 (пока).
© 1993–2023 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.38.0/perliol