perliol
СОДЕРЖАНИЕ
НАЗВАНИЕ
perliol — C-API для реализации PerlIO в слоях.
СИНТАКСИС
/* Defining a layer ... */
#include <perliol.h> ОПИСАНИЕ
Этот документ описывает поведение и реализацию абстракции PerlIO, описанной в perlapio, когда USE_PERLIO определён.
История и предыстория
Абстракция PerlIO была введена в perl5.003_02, но оставалась просто абстракцией до perl5.7.0. Однако в это время ряд расширений perl переключился на её использование, поэтому API в основном фиксирован для поддержания совместимости (исходного кода).
Целью реализации является предоставление API PerlIO гибким и независимым от платформы способом. Это также попытка использования подхода «Объектно-ориентированный C с vтаблицами», который может быть применён к Perl 6.
Основная структура
PerlIO представляет собой стек слоёв.
Нижние уровни стека работают с низкоуровневыми системными вызовами (дескрипторы файлов в C), получая и выводя байты, более высокие уровни стека буферизуют, фильтруют и иначе обрабатывают ввод-вывод и возвращают символы (или байты) Perl. Термины выше и ниже используются для обозначения относительного расположения слоёв стека.
Слой содержит «vтаблицу», таблицу операций ввода-вывода (на уровне C — таблицу указателей функций) и флаги состояния. Функции в vтаблице реализуют такие операции, как «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 или подобных 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» можно заменить (во время открытия или даже динамически) слоем «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
-
Уровень выполняет преобразование, подобное 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для класса и наличием функции(й) в таблице. Однако классу, который обычно предоставляет этот интерфейс, может потребоваться отказаться от него в конкретном экземпляре. Уровень "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* для аргумента, переданного слою.Если слой открывает или берёт во владение дескриптор файла, этот слой отвечает за приведение флага 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) и когда поток дублируется с помощью '&' в
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);Надмножество
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, приведенный к типу.
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 системы не позволяет перлу доступ к "быстрым функциям получения", и которые не различают
O_TEXTиO_BINARY. - "stdio"
-
Слой, который предоставляет API PerlIO через схему слоёв, но реализует его, вызывая системные функции stdio. Это (на данный момент) значение по умолчанию, если stdio системы обеспечивает достаточный доступ для функций перла "быстрое получение", и которые не различают
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" в качестве "базового"), который выполняет операции "чтения" путём mmap() файла. Улучшение производительности незначительно на современных системах, поэтому он в основном служит примером концепции. Вероятно, он будет исключён из ядра в какой-то момент. Слой "mmap" представляет собой разумную модель для минималистичного "производного" слоя. - "pending"
-
Внутреннее производное от "perlio", которое может использоваться для предоставления функции Unread() для слоёв, у которых нет буфера или которые не хотят этим заниматься. (По сути,
Fill()извлекает себя из стека и таким образом возобновляет чтение со слоя ниже.) - "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 для подробностей.
TODO
Задачи, которые необходимо выполнить для улучшения данной документации.
-
Объяснить, как создать действительный дескриптор файла без прохождения через 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, но с примерами там, где есть неясные моменты, будет легче для человека, который ещё не является экспертом в PerlIO.
© 1993–2020 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.30.3/perliol