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 реализуют операции, такие как «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 * под ним меняются. (Сравните со скалярами Perl, SV * которые остаются неизменными, в то время как поле 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не выполняет malloc и предполагает, что функция 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было выполнено, а open завершился неудачей, он должен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 при ошибке.
- Ошибка
-
IV (*Error)(pTHX_ PerlIO *f);Возвращает индикатор ошибки.
PerlIOBase_error()обычно достаточно.Возвращает 1, если произошла ошибка (обычно, когда
PERLIO_F_ERRORустановлено), 0 в противном случае. - Очистить ошибку
-
void (*Clearerr)(pTHX_ PerlIO *f);Очищает индикаторы конца файла и ошибки. Следует вызвать
PerlIOBase_clearerr()для установки флаговPERLIO_F_XXXXX, что может быть достаточно. - Установить построчную буферизацию
-
void (*Setlinebuf)(pTHX_ PerlIO *f);Помечает поток как построчно буферизованный.
PerlIOBase_setlinebuf()устанавливает флаг PERLIO_F_LINEBUF, что обычно достаточно. - Получить базовый указатель
-
STDCHAR * (*Get_base)(pTHX_ PerlIO *f);Выделяет (если еще не сделано) буфер чтения для этого уровня и возвращает указатель на него. Возвращает NULL при ошибке.
- Получить размер буфера
-
Size_t (*Get_bufsiz)(pTHX_ PerlIO *f);Возвращает количество байтов, которые были последними
Fill()помещены в буфер. - Получить указатель чтения
-
STDCHAR * (*Get_ptr)(pTHX_ PerlIO *f);Возвращает текущий указатель чтения относительно буфера этого уровня.
- Получить количество байтов
-
SSize_t (*Get_cnt)(pTHX_ PerlIO *f);Возвращает количество байтов, оставшихся для чтения в текущем буфере.
- Установить указатель и количество байтов
-
void (*Set_ptrcnt)(pTHX_ PerlIO *f, STDCHAR *ptr, SSize_t cnt);Корректирует указатель чтения и количество байтов для соответствия
ptrи/илиcnt. Приложение (или уровень выше) должно гарантировать их согласованность. (Проверка разрешена для предосторожности.)
Функции
Для запроса следующего уровня вниз используйте PerlIONext(PerlIO *f).
Для проверки валидности PerlIO* используйте PerlIOValid(PerlIO *f). (Всё это в основном проверяет, что указатель не NULL и что указатель за ним также не NULL.)
PerlIOBase(PerlIO *f) возвращает базовый указатель, то есть PerlIOl* указатель.
PerlIOSelf(PerlIO* f, type) возвращает PerlIOBase, приведённый к типу.
Perl_PerlIO_or_Base(PerlIO* f, callback, base, failure, args) либо вызывает 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 системы не позволяет перлу доступ к "быстрым получениям", и которые не различают
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() для слоёв, у которых нет буфера или нет желания. (В основном, этот слой удаляет себя со стека и, таким образом, возобновляет чтение со слоя ниже.)
- "raw"
-
Слои, которые никогда не существуют в стеке слоёв. Вместо этого, при "нажатии" он фактически удаляет себя со стека, а затем вызывает функцию таблицы Binmode на всех слоях в стеке - обычно это (через PerlIOBase_binmode) удаляет все слои, которые не имеют
PERLIO_K_RAWбит установленным. Слои могут изменить это поведение, определив свою собственную запись Binmode. - "utf8"
-
Ещё один псевдо-слой. При нажатии удаляет себя и устанавливает флаг
PERLIO_F_UTF8на слое, который был (и теперь снова является) вершиной стека.
Кроме того, perlio.c также предоставляет ряд PerlIOBase_xxxx() функций, предназначенных для использования в слотах таблицы классов, которым не нужно делать ничего особенного для конкретного метода.
Дополнительные слои
Слои могут быть доступны модулями расширения. Когда встречается неизвестный слой, код PerlIO выполнит эквивалент:
use PerlIO '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, но имеет примеры там, где вещи неясны, для человека, который ещё не является гуру 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.32.0/perliol