Spec-Zone.ru › Perl 5.28

perliol

СОДЕРЖАНИЕ

  • ИМЯ
  • СИНОПСИС
  • ОПИСАНИЕ
    • История и контекст
    • Базовая структура
    • Слои против дисциплин
    • Структуры данных
    • Функции и атрибуты
    • Данные на экземпляр
    • Слои в действии.
    • Флаги на экземпляр
    • Подробности методов
    • Утилиты
    • Реализация слоев PerlIO
    • Основные слои
    • Расширенные слои
  • TODO

ИМЯ

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», который может быть применён к Perl 6.

Базовая структура

PerlIO — это стековая структура слоёв.

Нижние уровни стека работают с низкоуровневыми системными вызовами (дескрипторы файлов в C), получая и отправляя байты; верхние слои стека буферизуют, фильтруют и иным образом манипулируют вводом-выводом и возвращают символы (или байты) Perl.

Термины «выше» и «ниже» используются для обозначения относительного расположения слоёв стека.

Слой содержит «vtable» — таблицу операций ввода-вывода (на уровне C — таблицу указателей на функции) и флаги состояния. Функции в vtable реализуют операции, такие как «открытие», «чтение» и «запись».

Когда запрашивается ввод-вывод, например, «чтение», запрос сначала проходит по стеку вниз, используя функции «чтение» каждого слоя; затем внизу запрос отправляется в системные службы; затем результат возвращается вверх по стеку, наконец, интерпретируясь как данные 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 на следующем слое ниже.

«Слой» состоит из двух частей:

  1. Функции и атрибуты «класса слоя».

  2. Данные на экземпляр для конкретной обработки.

Функции и атрибуты

К функциям и атрибутам можно получить доступ через член «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 данных на экземпляр и некоторые флаги, которые являются атрибутами всего класса (например, является ли он слоем буферизации), затем следуют функции, которые можно разделить на четыре основные группы:

  1. Функции открытия и настройки

  2. Основные операции ввода-вывода

  3. Параметры буферизации класса stdio.

  4. Функции для поддержки традиционного «быстрого» доступа к буферу 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's sv_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's open, PerlIO_open, perl's 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 было выполнено, а 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.28.3/perliol

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API