Streams
El agua puede fluir, arrastrarse, gotear o estrellarse, sé agua amigo mío. Bruce Lee
Funciones
Tipos y Constantes
Stream* | kSTDIN |
Stream* | kSTDOUT |
Stream* | kSTDERR |
Stream* | kDEVNULL |
- 1. Tipos de stream
- 1.1. File stream
- 1.2. Socket stream
- 1.3. Block stream
- 1.4. Memory stream
- 1.5. Standard stream
- 1.6. Null stream
- 1.7. Stream binario
- 1.8. Stream de texto
- 2. Tokens
- 3. Ventajas de los streams
- 3.1. Unificar la serialización
- 3.2. Mayor elegancia
- 3.3. Mayor productividad
- 3.4. Mayor rendimiento
- 3.5. Orden de bytes
- 4. Estado del stream
Un stream es un flujo de datos que corre desde un origen a un destino. Piensa en una llamada telefónica. Tenemos un origen (la persona que habla), un destino (la persona que escucha) y un canal (la propia línea). En programación, el stream es el equivalente a la línea telefónica, es la tubería que une la aplicación con un origen o destino de datos (Figura 1) y por la que circula información binaria, secuencias de bits. Al igual que ocurre con cualquier otro canal de comunicación, la información es volátil, disponible por un tiempo muy limitado. Una vez que llega al receptor, desaparece.
En esencia, son tres las operaciones elementales a realizar cuando trabajamos con streams: Crear el canal, leer datos y escribir datos.
- Utiliza stm_memory para crear un stream de lectura/escritura en memoria.
- Utiliza stm_read_r32 para leer un
float
desde el stream. - Utiliza stm_write_r32 para escribir un
float
en el stream. - Utiliza stm_close para cerrar el canal y liberar los recursos (destructor).
1. Tipos de stream
En realidad, es más correcto hablar de tipos de interlocutores (origen y destino) que de tipos de stream. Desde la perspectiva del programador, un stream es un tipo abstracto que presenta la misma funcionalidad independientemente de los extremos que conecta. Por tanto, al hablar de tipo de stream nos estamos refiriendo al tipo de constructor.
1.1. File stream
En File streams (Figura 2), el origen es la memoria del proceso y el destino un archivo en disco. Lo contrario también puede ocurrir: que el origen sea el archivo y el destino la memoria, dependerá de como creemos el canal. No será posible realizar operaciones de escritura en un archivo abierto para lectura o viceversa (Listado 1). Internamente, el stream se encargará del acceso al sistema de ficheros, como vimos en Archivos y directorios.
- Utiliza stm_from_file para abrir un archivo y leer de él.
- Utiliza stm_to_file para crear un archivo y escribir en él.
- Utiliza stm_append_file para añadir contenido a un archivo existente.
1 2 3 4 5 6 7 8 9 10 |
Stream *stm = stm_to_file("C:\Users\user\john\out.txt", NULL); if (stm != NULL) { stm_writef(stm, "One "); stm_writef(stm, "Two "); stm_writef(stm, "Three"); stm_writef(stm, "."); stm_close(&stm); // 'out.txt' is closed = "One Two Three." } |
1.2. Socket stream
Un socket es un canal de comunicación entre dos procesos a través de Internet (Figura 3). A diferencia de los file streams, los sockets permiten la comunicación bidireccional (full-duplex), es decir, ambos extremos pueden enviar y recibir información. La secuencia de intercambio de mensajes entre interlocutores está determinada por el protocolo (Listado 2), siendo HTTP, FTP, SMTP o LDAP algunos de los más utilizados para las transmisiones por Internet. Ver Sockets.
- Utiliza stm_socket para crear un canal de comunicación con un proceso remoto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
uint32_t ip = bsocket_url_ip("www.myserver.com", NULL); Socket *socket = bsocket_connect(ip, 80, 0, NULL); if (socket != NULL) { Stream *stm = stm_socket(socket); stm_writef(stm, "GET /mypage.html HTTP/1.1\r\n"); stm_writef(stm, "Host: www.myserver.com\r\n"); stm_writef(stm, "\r\n"); stm_lines(line, stm) bstd_printf(line); bstd_printf("\n"); stm_next(line, stm) // Socket will be closed too stm_close(&stm); } |
1.3. Block stream
Los block streams se utilizan para leer datos con formato a partir de un bloque de memoria genérico. (Figura 4). Esta zona de memoria se considera de solo lectura y no será modificada, por lo que no se permitirán operaciones de escritura en este tipo de streams. Cuando se llegue al final del bloque, se activará el estado ekSTEND.
- Utiliza stm_from_block para leer datos desde un bloque de memoria.
1.4. Memory stream
Los memory streams son canales de lectura/escritura que permiten implementar el modelo productor/consumidor (Figura 5). En primer lugar, la información llega al stream mediante operaciones de escritura y se almacena en un búfer interno de memoria. Posteriormente, dicha información puede ser leída por otra función, hebra o proceso. Tras cada lectura la información leída desaparecerá del canal. El concepto es similar al de las tuberías (IPC-Pipes), con la salvedad de que no existe un límite de tamaño para el búfer, sino que crecerá bajo demanda. Las operaciones de lectura y escritura se pueden ir simultaneando en función del protocolo establecido.
- Utiliza stm_memory para crear un stream en memoria.
- Utiliza stm_buffer para acceder al búfer interno.
A pesar que este tipo de stream soporta operaciones de lectura y escritura no se considera full-duplex. La lectura se hace sobre datos escritos previamente, pero no puede "contestar" al interlocutor. No es una "conversación".
1.5. Standard stream
La E/S Estándar puede ser gestionada mediante streams utilizando tres objetos predefinidos (Figura 6). Estos objetos se crean al iniciar el programa y se liberarán automáticamente al terminar.
- Utiliza kSTDIN para leer de la entrada estándar.
- Utiliza kSTDOUT para escribir en la salida estándar.
- Utiliza kSTDERR para escribir en la salida de errores.
1 2 3 4 5 |
real64_t value; const char_t *line; value = stm_read_r64(kSTDIN); line = stm_read_line(kSTDIN); stm_printf(kSTDOUT, "Value = %.4f", value); |
1.6. Null stream
En ocasiones puede ser útil disponer de un "sumidero" que ignore todas las operaciones de escritura (Figura 7). Piensa en tareas de depuración donde queramos activar o desactivar la salida de información pero que borrar o comentar el código sea engorroso. La idea es similar al /dev/null
de Unix.
- Utiliza kDEVNULL para escribir en un sumidero que ignorará todos los datos recibidos.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#if defined __ASSERTS__ Stream *stm = kSTDOUT; #else Stream *stm = kDEVNULL; #endif ... i_large_dump_func(obj1, stm); ... // More debug functions stm_printf(stm, "More debug data...\n"); ... i_other_dump_func(obj2, stm); |
No es posible leer desde kDEVNULL.
1.7. Stream binario
Por un stream siempre viajan datos binarios genéricos como flujo de bytes. Como estos datos sean interpretados depende de los interlocutores y de su protocolo de comunicación. Pero al hacer énfasis en "datos binarios" nos referimos a que los valores numéricos se escriben en el canal tal y como aparecen en los registros de la CPU utilizando el código binario, complemento a dos o IEEE754 (Figura 8). En tipos multi-byte debemos tener en cuenta el Orden de bytes. En stream.h
se definen varias funciones para leer y escribir tipos binarios.
- Utiliza stm_read_u32 para leer un entero sin signo de 32 bits.
- Utiliza stm_write_r64 para escribir un real de 64bits (
double
). - Utiliza stm_write_bool para escribir un booleano.
1.8. Stream de texto
Los streams de texto son un caso particular donde se asume que la información binaria representa códigos de caracteres Unicode (codepoints) (Figura 9) (Listado 3). Esto significa que el contenido del stream será legible directamente por un humano, pero requerirá un post-proceso (parsing) en destino para interpretar dichos textos y traducirlos a binario. No hay que hacer nada especial al crear un stream para indicar que es de tipo texto, tan solo hay que utilizar las funciones adecuadas.
- Utiliza stm_printf para escribir texto en un stream.
- Utiliza stm_read_char para leer un carácter desde un stream.
- Utiliza stm_read_line para leer una línea de texto desde un stream.
- Utiliza stm_col para obtener el número de columna del último carácter leído.
- Utiliza stm_row para obtener el número de fila del último carácter leído.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Stream *stm = stm_from_file("/home/fran/Desktop/text.txt", NULL); const char_t *line = stm_read_line(stm); while(line != NULL) { // Do something with 'line' textview_writef(text, line); textview_writef(text, "\n"); // Read next line line = stm_read_line(stm); } stm_close(&stm); |
stm_read_line y demás funciones de lectura nos devolverán el texto siempre en UTF8
. Pero si los datos dentro del stream estuvieran en otro formato, deberemos utilizar stm_set_read_utf, con el fin de realizar la conversión correctamente. Ver Codificaciones UTF.
Por otro lado, stm_printf también recibe el texto en UTF8, pero es posible que el receptor lo necesite en otro formato. Utilizaremos stm_set_write_utf para establecer la codificación de salida. Nosotros escribiremos en UTF8, pero al canal se enviará en UTF16 o UTF32.
Los streams no tienen porqué ser de texto o binarios "puros". Pueden combinar ambos tipos de representaciones.
2. Tokens
Al leer desde streams de texto, se hace necesaria una interpretación (parsing) del contenido con el fin de trasladar los datos a variables de memoria (en binario). El primer paso es fragmentar el texto en símbolos (o palabras) denominados tokens. Internamente, los streams incorporan un sencillo analizador léxico que reconoce los tokens propios del lenguaje C, muy comunes en infinidad de gramáticas y formatos de archivo (Figura 10). Está implementado como una máquina de estados finitos y nos facilitará enormemente el procesamiento de estos flujos de texto. En (Listado 4) vemos el código necesario para leer uno a uno todos los tokens de un archivo .c
. El resultado de procesar el archivo (Listado 5) lo tenemos en (Listado 6).
- Utiliza stm_read_token para leer un token.
- Utiliza stm_token_lexeme para obtener la cadena asociada al último token leído.
- Utiliza stm_read_r64_tok para leer un real64_t a partir de texto.
- Utiliza stm_token_col para obtener la columna del último token.
- Utiliza stm_token_row para obtener la fila del último token.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Stream *stm = stm_from_file("source.c", NULL); token_t token; while ((token = stm_read_token(lex)) != ekTEOF) { switch (token) { case ekTIDENT: // It's an IDENTIFIER ... case ekTREAL: // It's a REAL NUMBER ... } } |
1 2 3 4 5 6 7 |
void func(int a)
{
int i;
char *str = "Hello";
i = 5 + 2.5;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
Token Lexeme ----- ------ ekTIDENT "void" ekTIDENT "func" ekTOPENPAR "(" ekTIDENT "int" ekTIDENT "a" ekTCLOSPAR ")" ekTOPENCURL "{" ekTIDENT "int" ekTIDENT "i" ekTSCOLON ";" ekTIDENT "char" ekTASTERK "*" ekTIDENT "str" ekTEQUALS "=" ekTSTRING ""Hello"" ekTSCOLON ";" ekTIDENT "i" ekTEQUALS "=" ekTINTEGER "5" ekTPLUS "+" ekTREAL "2.5" ekTSCOLON ";" |
2.1. Identificadores
Un identificador es una "palabra" alfanumérica que debe comenzar por una letra o '_'
y contiene números, letras o '_'
. Se utiliza para nombrar variables, funciones, palabras reservadas, etc. No permiten espacios ni símbolos. (Listado 7) (Figura 11).
1 2 |
OK: while cos _reSult a56B _06_t aG h9 _12AcVb NO: 045 ?er "_5G _tg( |
Se pueden reservar ciertos identificadores para que actúen como palabras clave del lenguaje. Por ejemplo for
, while
o if
son palabras clave de C y no podrán ser utilizadas para el nombrado de variables o funciones. Al ser de propósito general, nuestro escáner no reconoce ningún tipo de palabra reservada, sino hay que etiquetarla expresamente tras leer el token (Listado 8).
1 2 3 4 5 6 7 8 9 10 |
while ((token = stm_read_token(stm)) != ekTEOF) { if (token == ekTIDENT) { const char_t *lex = stm_token_lexeme(stm, NULL); if (str_equ_c(lex, "while") == TRUE) token = ekTRESERVED; } } |
2.2. Cadenas
Una cadena de texto es una serie de caracteres Unicode puestos entre comillas ("")
(Figura 12). El analizador reconoce las secuencias de escape de C para representar códigos no imprimibles o caracteres no disponibles en el teclado (Listado 9).
- Utiliza stm_token_escapes para hacer efectivas las secuencias de escape al leer cadenas.
ekTSTRING
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
\a 07 Alert (Beep, Bell) (added in C89) \b 08 Backspace \f 0C Formfeed Page Break \n 0A Newline (Line Feed) \r 0D Carriage Return \t 09 Horizontal Tab \v 0B Vertical Tab \\ 5C Backslash \' 27 Single quotation mark \" 22 Double quotation mark \? 3F Question mark (used to avoid trigraphs) \nnn Octal number \xhh Hexadecimal number \Uhhhhhhhh Unicode code point \uhhhh Unicode code point |
2.3. Números
En el caso de tokens numéricos la cosa se complica un poco debido a las diferentes bases numéricas y a la representación exponencial de números reales (Figura 13). La resumimos brevemente, aunque es común a muchos lenguajes de programación (C incluido).
- Si el número empieza por
0
será considerado octal (base 8), por lo tanto, las cifras siguientes están limitadas a0-7
, pe:043, 001, 0777
. - Si el número empieza por
0x
se considerará hexadecimal (base 16) con cifras0-9 a-f A-F
, pe:0x4F, 0XAA5, 0x01EAC
. - En el momento que aparezca un punto decimal
'.'
se considerará número real. El punto inicial es válido, pe:.56
. - Un número entero o real permite notación exponencial con el carácter
'e' ('E')
, pe:12.4e2, .56e3, 1e4
.
2.4. Símbolos
Los símbolos son tokens de un solo carácter que representan casi la totalidad de signos de puntuación US-ASCII y que suelen utilizarse como operadores, separadores o limitadores dentro de las gramáticas (Listado 10) (Figura 14).
1 |
< > , . ; : ( ) [ ] { } + - * = $ % # & ' " ^ ! ? | / \ @ |
2.5. Comentarios
Por defecto los comentarios de C /* Comment */
o de C++ // Comment
son ignorados por stm_read_token.
- Utiliza stm_token_comments para que devuelva ekTSLCOM o ekTMLCOM si encuentra alguno.
- Utiliza stm_token_spaces para que devuelva ekTSPACE cuando encuentre espacios en blanco.
3. Ventajas de los streams
A pesar que es posible leer o escribir directamente en los canales E/S (Memoria, Archivos y directorios, Sockets, E/S Estándar), hacerlo a través de objetos Stream tiene ciertas ventajas. Por esto recomendamos utilizarlos en lugar de las API de bajo nivel por los siguientes motivos:
3.1. Unificar la serialización
Los streams ofrecen una interfaz uniforme, independientemente del origen y destino de los datos (Figura 15). Para la serialización de objetos tan solo debemos escribir un lector y un escritor, sin preocuparnos si objeto será guardado en disco, transmitido por Internet o almacenado temporalmente en memoria (Listado 11).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
typedef struct _product_t { type_t type; String *code; String *description; Image *image64; real32_t price; } Product; void product_write(Stream *stm, Product *product) { stm_write_enum(stm, product->type, type_t); str_write(stm, product->code); str_write(stm, product->description); image_write(stm, product->image64); stm_write_r32(stm, product->price); } void product_read(Stream *stm, Product *product) { product->type = stm_read_enum(stm, type_t); product->code = str_read(stm); product->description = str_read(stm); product->image64 = image_read(stm); product->price = stm_read_r32(stm); } |
3.2. Mayor elegancia
Los canales E/S solo trabajan con bloques de bytes. Los streams implementan funciones de alto nivel para textos y tipos binarios. Esto hará nuestro código mucho más legible (Listado 12).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void product_write(File *file, Product *product) { uint32_t size = str_len(product->description); const char_t *data = tc(product->description); bfile_write(file, (byte_t*)&product->id, sizeof(uint32_t), NULL, NULL); bfile_write(file, (byte_t*)&product->price, sizeof(real64_t), NULL, NULL); bfile_write(file, (byte_t*)&size, sizeof(uint32_t), NULL, NULL); bfile_write(file, (byte_t*)data, size, NULL, NULL); } void product_write(Stream *stream, Product *product) { stm_write_u32(stream, product->id); stm_write_r64(stream, product->price); str_write(stream, product->description); } |
3.3. Mayor productividad
Relacionada con la anterior, los streams pueden "parsear" cadenas de texto directamente. Se pueden obtener caracteres, líneas o tokens sin tener que escanear la entrada carácter a carácter (Listado 13).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
String *getline(File *file) { /* Potentially unsafe. */ /* Risk of buffer overflow. */ char_t buffer[MAXBUFF]; uint32_t i = 0; char_t c; bfile_read(file, (byte_t*)&c, 1, NULL, NULL); while (c != '\n') { buffer[i] = c; i += 1; bfile_read(file, (byte_t*)&c, 1, NULL, NULL); } buffer[i] = '\0'; return str_c(buffer); } String *getline(Stream *stream) { /* Totally safe. */ /* 'line' is managed by dynamic cache. */ const char_t *line = stm_read_line(stream); return str_c(line); } |
3.4. Mayor rendimiento
File streams y standard streams implementan una caché interna. Esto permite acceder menos veces al canal con un mayor volumen de datos, lo que implica mayor velocidad de proceso (Figura 16).
- Utiliza stm_flush para vaciar la caché y volcar los datos en el canal.
3.5. Orden de bytes
Al leer o escribir datos binarios de un canal E/S hay que prestar especial atención al orden de los bytes en tipos de datos de 16, 32 o 64 bits lo que se conoce como endianness. En máquinas litte endian, como es el caso de la familia de procesadores Intel x86/x64, el byte de menor orden se ubicará en la dirección de memoria más baja. En el caso de los big endian (Motorola 68000, PowerPC) ocurre al contrario, irá en en la más alta. Por ejemplo, si escribimos un entero de 32-bits en un archivo o socket desde una máquina little endian y lo leemos desde una big endian los datos estarán corrompidos al alterar el orden interno de bits (Figura 17). Los objetos Stream
ajustan automáticamente el endianness en cada operación de lectura/escritura. Por defecto se establece ekLITEND, salvo en sockets que será ekBIGEND por ser el convenio aceptado para las comunicaciones en red. No obstante, puede cambiarse si es necesario.
- Utiliza stm_set_write_endian para establecer el endianness del canal de salida. Los datos pasarán de CPU endian a Stream endian antes de ser escritos.
- Utiliza stm_set_read_endian para establecer el endianness del canal de entrada. Los datos pasarán de Stream endian a CPU endian en el momento de ser leídos.
El endianness no influye en las cadenas de texto UTF-8, pero sí en las UTF-16 y UTF-32.
4. Estado del stream
Un stream puede verse afectado por dos tipos de problemas. Por un lado la corrupción de datos que queda patente cuando leemos datos binarios desde el stream. Un claro ejemplo sería leer un booleano mediante stm_read_bool y obtener un valor de 129
cuando, evidentemente, este valor debería ser 0 (TRUE)
ó 1 (FALSE)
. Normalmente, un stream se corrompe debido a la falta de coordinación entre el escritor y el lector y, por lo general, se debe a un error de programación. Esta situación debe resolverse depurando y corrigiendo la serialización de objetos o revisando el protocolo de datos. Por otro lado, pueden existir errores "físicos" en el canal (archivo eliminado, perdida de conexión a Internet, permisos, etc). En ambos casos el stream quedará invalidado y serán ignoradas las operaciones subsecuentes de lectura o escritura que efectuemos sobre el mismo. También podemos preguntar la cantidad total de bytes leídos y/o escritos en el canal, en el caso que necesitemos saber si existe información disponible para lectura.
- Utiliza stm_state para conocer el estado actual del canal.
- Utiliza stm_file_err para obtener información extendida del error en streams de disco.
- Utiliza stm_sock_err para obtener información extendida del error en sockets.
- Utiliza stm_corrupt para marcar un stream como ekSTCORRUPT. En ocasiones es el propia aplicación la que detecta que el dato no es correcto (pe. fuera de rango).
- Utiliza stm_bytes_written para obtener el número total de bytes escritos en el stream.
- Utiliza stm_bytes_readed para obtener el número total de bytes leídos del stream.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
uint32_t nw = stm_bytes_written(stm); uint32_t nr = stm_bytes_readed(stm); if (nw - nr > 0) { if (stm_state(stm) == ekSTOK) { uint32_t v1 = stm_read_u32(stm); real32_t v2 = stm_read_r32(stm); ... } else { // Error in stream } } else { // No data in stream } |
kSTDIN
Stream* kSTDIN;
Stream conectado a la entrada estándar stdin
.
kSTDOUT
Stream* kSTDOUT;
Stream conectado a la salida estándar stdout
.
kSTDERR
Stream* kSTDERR;
Stream conectado a la salida de errores stderr
.
kDEVNULL
Stream* kDEVNULL;
Stream de escritura nulo. Será ignorado todo el contenido que se mande por este canal.
stm_from_block ()
Crea un stream de lectura a partir de un bloque de memoria ya existente.
Stream* stm_from_block(const byte_t *data, const uint32_t size);
data | Puntero al bloque de memoria. |
size | Tamaño en bytes del bloque de memoria. |
Retorna
El objeto stream.
Observaciones
El bloque original no será modificado (solo lectura). Cuando se alcance el final del bloque stm_state devolverá ekSTEND. Block stream.
stm_memory ()
Crea un stream de lectura/escritura en memoria.
Stream* stm_memory(const uint32_t size);
size | Tamaño inicial del búfer (en bytes). Crecerá si es necesario. |
Retorna
El objeto stream.
Observaciones
Puede utilizarse como tubería interna para el intercambio de información entre funciones o hilos. Se comporta como un búfer FIFO (First In Fist Out). Para acceso multi-hilo debe estar protegido con un Mutex. Memory stream.
stm_from_file ()
Crea un stream para leer desde un archivo en disco.
Stream* stm_from_file(const char_t *pathname, ferror_t *error);
pathname | Pathname del archivo. Filename y pathname. |
error | Código de error si la función falla. Puede ser |
Retorna
El objeto stream o NULL
si falla la apertura del archivo.
Observaciones
stm_to_file ()
Crea un stream para escribir datos a un archivo en disco.
Stream* stm_to_file(const char_t *pathname, ferror_t *error);
pathname | Pathname del archivo. Filename y pathname. |
error | Código de error si la función falla. Puede ser |
Retorna
El objeto stream o NULL
si falla la creación del archivo.
Observaciones
Si el archivo ya existe será sobrescrito. File stream.
stm_append_file ()
Crea un stream para escribir datos al final de un archivo existente.
Stream* stm_append_file(const char_t *pathname, ferror_t *error);
pathname | Pathname del archivo. Filename y pathname. |
error | Código de error si la función falla. Puede ser |
Retorna
El objeto stream o NULL
si falla la apertura del archivo.
Observaciones
Fallará si el archivo no existe (no lo crea). File stream.
stm_socket ()
Crea un stream a partir de un socket.
Stream* stm_socket(Socket *socket);
socket | Socket cliente o servidor. |
Retorna
El objeto stream.
Observaciones
Permite utilizar la funcionalidad de los streams para leer o escribir en un proceso remoto. El socket debe haber sido previamente creado con bsocket_connect (cliente) o bsocket_accept (servidor). El stream cerrará el socket en stm_close. No se debe llamar a bsocket_close. Ver Socket stream.
stm_close ()
Cierra el stream. Todos los recursos como descriptores de archivos o sockets serán liberados. Previamente al cierre, se escribirán en el canal los datos en caché stm_flush.
void stm_close(Stream **stm);
stm | El objeto stream. Será puesto a |
stm_get_write_endian ()
Obtiene el orden de bytes actual al escribir en el stream.
endian_t stm_get_write_endian(const Stream *stm);
stm | El objeto stream. |
Retorna
El Orden de bytes.
stm_get_read_endian ()
Obtiene el orden de bytes actual al leer del stream.
endian_t stm_get_read_endian(const Stream *stm);
stm | El objeto stream. |
Retorna
El Orden de bytes.
stm_set_write_endian ()
Establece el orden de bytes al escribir en el stream, a partir de ahora.
void stm_set_write_endian(Stream *stm, const endian_t endian);
stm | El objeto stream. |
endian | El Orden de bytes. |
Observaciones
Por defecto es ekLITEND, salvo en sockets que será ekBIGEND.
stm_set_read_endian ()
Establece el orden de bytes al leer del stream, a partir de ahora.
void stm_set_read_endian(Stream *stm, const endian_t endian);
stm | El objeto stream. |
endian | El Orden de bytes. |
Observaciones
Por defecto es ekLITEND, salvo en sockets que será ekBIGEND.
stm_get_write_utf ()
Obtiene la codificación UTF con la que se están escribiendo los textos en el stream.
unicode_t stm_get_write_utf(const Stream *stm);
stm | El objeto stream. |
Retorna
Observaciones
Ver Stream de texto.
stm_get_read_utf ()
Obtiene la codificación UTF con la que se están leyendo los textos en el stream.
unicode_t stm_get_read_utf(const Stream *stm);
stm | El objeto stream. |
Retorna
Observaciones
Ver Stream de texto.
stm_set_write_utf ()
Establece la codificación UTF al escribir textos en el stream, a partir de ahora.
void stm_set_write_utf(Stream *stm, const unicode_t format);
stm | El objeto stream. |
format |
Observaciones
Ver Stream de texto.
stm_set_read_utf ()
Establece la codificación UTF al leer textos en el stream, a partir de ahora.
void stm_set_read_utf(Stream *stm, const unicode_t format);
stm | El objeto stream. |
format |
Observaciones
Ver Stream de texto.
stm_is_memory ()
Obtiene si es un stream residente en memoria.
bool_t stm_is_memory(const Stream *stm);
stm | El objeto stream. |
Retorna
TRUE
si ha sido creado mediante stm_from_block o stm_memory.
stm_bytes_written ()
Obtiene el total de bytes escritos en el stream desde su creación.
uint64_t stm_bytes_written(const Stream *stm);
stm | El objeto stream. |
Retorna
El número total de bytes escritos.
stm_bytes_readed ()
Obtiene el total de bytes leídos desde el stream desde su creación.
uint64_t stm_bytes_readed(const Stream *stm);
stm | El objeto stream. |
Retorna
El número total de bytes leídos.
stm_col ()
Obtiene la columna en streams de texto.
uint32_t stm_col(const Stream *stm);
stm | El objeto stream. |
Retorna
Número de columna.
Observaciones
Cuando leemos caracteres en streams de texto con stm_read_char o derivadas, se contabilizan las columnas y filas de forma similar a como lo hacen los editores de texto. Esta información puede ser útil a la hora de mostrar avisos o mensajes de error. En streams mixtos (binarios + texto), la cuenta se detiene al leer datos binarios y continúa cuando se retoma la lectura del texto. Ver Stream de texto.
stm_row ()
Obtiene la fila en streams de texto.
uint32_t stm_row(const Stream *stm);
stm | El objeto stream. |
Retorna
Número de fila.
Observaciones
Ver stm_col.
stm_token_col ()
Obtiene la columna del último token leído.
uint32_t stm_token_col(const Stream *stm);
stm | El objeto stream. |
Retorna
Número de columna.
Observaciones
Solo tiene efecto tras llamar a stm_read_token o derivadas. Ver stm_col y Tokens.
stm_token_row ()
Obtiene la fila del último token leído.
uint32_t stm_token_row(const Stream *stm);
stm | El objeto stream. |
Retorna
Número de fila.
Observaciones
Solo tiene efecto tras llamar a stm_read_token o derivadas. Ver stm_col y Tokens.
stm_token_lexeme ()
Obtiene el lexema del último token leído.
const char_t* stm_token_lexeme(const Stream *stm);
stm | El objeto stream. |
Retorna
El lexema. Está almacenado en un buffer temporal y se perderá al leer el siguiente token. Si lo necesitas, haz una copia con str_c.
Observaciones
Solo tiene efecto tras llamar a stm_read_token o derivadas. Ver Tokens.
stm_token_escapes ()
Opción de secuencias de escape al leer tokens.
void stm_token_escapes(const Stream *stm, const bool_t active_escapes);
stm | El objeto stream. |
active_escapes |
|
Observaciones
Tendrá efecto en la siguiente llamada a stm_read_token. Ver Tokens.
stm_token_spaces ()
Opción de espacios en blanco al leer tokens.
void stm_token_spaces(const Stream *stm, const bool_t active_spaces);
stm | El objeto stream. |
active_spaces |
|
Observaciones
Tendrá efecto en la siguiente llamada a stm_read_token. Ver Tokens.
stm_token_comments ()
Opción de comentarios al leer tokens.
void stm_token_comments(const Stream *stm, const bool_t active_comments);
stm | El objeto stream. |
active_comments |
|
Observaciones
Tendrá efecto en la siguiente llamada a stm_read_token. Ver Tokens.
stm_state ()
Obtiene el estado actual del stream.
sstate_t stm_state(const Stream *stm);
stm | El objeto stream. |
Retorna
stm_file_err ()
Obtiene información adicional sobre el error, en streams de disco.
ferror_t stm_file_err(const Stream *stm);
stm | El objeto stream. |
Retorna
Error de archivo.
Observaciones
Solo es relevante en File stream con el estado ekSTBROKEN.
stm_sock_err ()
Obtiene información adicional sobre el error, en streams de red.
serror_t stm_sock_err(const Stream *stm);
stm | El objeto stream. |
Retorna
Error de socket.
Observaciones
Solo es relevante en Socket stream con el estado ekSTBROKEN.
stm_corrupt ()
Establece el estado del stream a ekSTCORRUPT.
void stm_corrupt(Stream *stm);
stm | El objeto stream. |
Observaciones
En ocasiones, es la aplicación la que detecta que los datos están corruptos ya que la semántica de los mismos no coincide con lo esperado.
stm_str ()
Crea un string con el contenido actual del búfer interno. Solo es válido para stream en memoria. stm_memory.
String* stm_str(const Stream *stm);
stm | El objeto stream. |
Retorna
El string con el contenido del búfer.
stm_buffer ()
Obtiene un puntero al contenido actual del búfer interno. Solo es válido para stream en memoria. stm_memory.
const byte_t* stm_buffer(const Stream *stm);
stm | El objeto stream. |
Retorna
Puntero al búfer interno.
Observaciones
Este puntero es de solo lectura. Escribir aquí tendrá consecuencias indeseadas.
stm_buffer_size ()
Obtiene el tamaño actual del búfer interno. Solo es válido para stream en memoria. stm_memory.
uint32_t stm_buffer_size(const Stream *stm);
stm | El objeto stream. |
Retorna
El tamaño del búfer interno (en bytes).
stm_write ()
Escribe bytes en el stream.
void stm_write(Stream *stm, const byte_t *data, const uint32_t size);
stm | El objeto stream. |
data | Puntero al bloque de datos a escribir. |
size | Número de bytes a escribir. |
Observaciones
El bloque se escribe tal cual llega, sin tener en cuenta el Orden de bytes ni las Codificaciones UTF.
stm_write_char ()
Escribe un carácter Unicode en el stream.
void stm_write_char(Stream *stm, const uint32_t codepoint);
stm | El objeto stream. |
codepoint | El código Unicode del carácter. |
Observaciones
La codificación se puede cambiar con stm_set_write_utf.
stm_printf ()
Escribe texto en el stream, utilizando el formato del printf
.
uint32_t stm_printf(Stream *stm, const char_t *format, ...);
1 |
stm_printf(stream, Code: %-10s Price %5.2f\n", code, price);
|
stm | El objeto stream. |
format | Cadena con el formato tipo-printf con un número variable de parámetros. |
... | Argumentos o variables del printf. |
Retorna
El número de bytes escritos.
Observaciones
El carácter nulo final ('\0'
) no será escrito. La codificación se puede cambiar con stm_set_write_utf.
stm_writef ()
Escribe una cadena C UTF8 en el stream.
uint32_t stm_writef(Stream *stm, const char_t *str);
stm | El objeto stream. |
str | Cadena C UTF8 terminada en carácter nulo |
Retorna
El número de bytes escritos.
Observaciones
El carácter nulo final ('\0'
) no será escrito. Está función es más rápida que stm_printf cuando la cadena es constante y no necesita formato. Para objetos String utiliza str_writef. La codificación se puede cambiar con stm_set_write_utf.
stm_write_bool ()
Escribe una variable bool_t
en el stream.
void stm_write_bool(Stream *stm, const bool_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros".
stm_write_i8 ()
Escribe una variable int8_t
en el stream.
void stm_write_i8(Stream *stm, const int8_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros".
stm_write_i16 ()
Escribe una variable int16_t
en el stream.
void stm_write_i16(Stream *stm, const int16_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_i32 ()
Escribe una variable int32_t
en el stream.
void stm_write_i32(Stream *stm, const int32_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_i64 ()
Escribe una variable int64_t
en el stream.
void stm_write_i64(Stream *stm, const int64_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_u8 ()
Escribe una variable uint8_t
en el stream.
void stm_write_u8(Stream *stm, const uint8_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros".
stm_write_u16 ()
Escribe una variable uint16_t
en el stream.
void stm_write_u16(Stream *stm, const uint16_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_u32 ()
Escribe una variable uint32_t
en el stream.
void stm_write_u32(Stream *stm, const uint32_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_u64 ()
Escribe una variable uint64_t
en el stream.
void stm_write_u64(Stream *stm, const uint64_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_r32 ()
Escribe una variable real32_t
en el stream.
void stm_write_r32(Stream *stm, const real32_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_r64 ()
Escribe una variable real64_t
en el stream.
void stm_write_r64(Stream *stm, const real64_t value);
stm | El objeto stream. |
value | Variable a escribir. |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_write_enum ()
Escribe una variable enum
en el stream.
void stm_write_enum(Stream *stm, const type value, type);
stm | El objeto stream. |
value | Variable a escribir. |
type | El tipo del |
Observaciones
Es una escritura binaria. No utilizar en streams de texto "puros". Orden de bytes.
stm_read ()
Lee bytes desde el stream.
uint32_t stm_read(Stream *stm, byte_t *data, const uint32_t size);
stm | El objeto stream. |
data | Puntero al búfer donde se escribirán los datos leídos. |
size | La cantidad de bytes a leer (tamaño del búfer). |
Retorna
El número de bytes realmente leídos.
stm_read_char ()
Lee un carácter de texto desde el stream.
uint32_t stm_read_char(Stream *stm);
stm | El objeto stream. |
Retorna
El código Unicode del carácter.
Observaciones
La codificación del texto de entrada puede ajustarse con stm_set_read_utf. Actualizará el contador de filas y columnas. Ver stm_col.
stm_read_chars ()
Lee varios caracteres desde el stream.
const char_t* stm_read_chars(Stream *stm, const uint32_t n);
stm | El objeto stream. |
n | La cantidad de caracteres a leer. |
Retorna
Puntero a la cadena C UTF8 leída. Terminará con el carácter nulo '\0'
.
Observaciones
El puntero devuelto es temporal y será sobrescrito en la próxima lectura. Si es necesario, haz una copia con str_c. La codificación del texto de entrada puede ajustarse con stm_set_read_utf. Actualizará el contador de filas y columnas. Ver stm_col.
stm_read_line ()
Lee caracteres del stream hasta alcanzar un final de línea '\n'
.
const char_t* stm_read_line(Stream *stm);
stm | El objeto stream. |
Retorna
Puntero a la cadena C UTF8, terminada con el carácter nulo '\0'
. Los caracteres '\n'
o '\r\n'
no serán incluidos en el resultado. Se devolverá NULL
cuando se alcance el final del stream.
Observaciones
El puntero devuelto es temporal y será sobrescrito en la próxima lectura. Si es necesario, haz una copia con str_c. La codificación del texto de entrada puede ajustarse con stm_set_read_utf. Actualizará el contador de filas y columnas. Ver stm_col.
stm_read_trim ()
Lee la siguiente secuencia de caracteres eliminando los espacios en blanco.
const char_t* stm_read_trim(Stream *stm);
stm | El objeto stream. |
Retorna
Puntero a la cadena C UTF8 leída. Terminará con el carácter nulo '\0'
.
Observaciones
Útil para leer cadenas desde streams de texto. Ignorará todos los espacios en blanco iniciales y leerá caracteres hasta encontrar el primer blanco (' ', '\\t', '\\n', '\\v', '\\f', '\\r'
). Si necesitas mayor control sobre los tokens utiliza stm_read_token. El puntero devuelto es temporal y será sobrescrito en la próxima lectura. Si es necesario, haz una copia con str_c. La codificación del texto de entrada puede ajustarse con stm_set_read_utf. Actualizará el contador de filas y columnas. Ver stm_col.
stm_read_token ()
Obtiene el siguiente token en Stream de texto.
token_t stm_read_token(Stream *stm);
stm | El objeto stream. |
Retorna
El tipo de token obtenido.
Observaciones
Para obtener la cadena de texto asociada al token, utiliza stm_token_lexeme. Ver Tokens.
stm_read_i8_tok ()
Lee el siguiente token con stm_read_token y, si es un número entero, lo convierte a int8_t.
int8_t stm_read_i8_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
En caso que no se pueda leer un token de tipo ekTINTEGER (con o sin ekTMINUS) o que el valor numérico esté fuera de rango, se devolverá 0 y se marcará el stream como corrupto con stm_corrupt.
stm_read_i16_tok ()
Lee el siguiente token y lo convierte a int16_t.
int16_t stm_read_i16_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
Ver stm_read_i8_tok.
stm_read_i32_tok ()
Lee el siguiente token y lo convierte a int32_t.
int32_t stm_read_i32_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
Ver stm_read_i8_tok.
stm_read_i64_tok ()
Lee el siguiente token y lo convierte a int64_t.
int64_t stm_read_i64_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
Ver stm_read_i8_tok.
stm_read_u8_tok ()
Lee el siguiente token con stm_read_token y, si es un número entero, lo convierte a uint8_t.
uint8_t stm_read_u8_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
En caso que no se pueda leer un token de tipo ekTINTEGER o que el valor numérico esté fuera de rango, se devolverá 0 y se marcará el stream como corrupto con stm_corrupt.
stm_read_u16_tok ()
Lee el siguiente token y lo convierte a uint16_t.
uint16_t stm_read_u16_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
Ver stm_read_u8_tok.
stm_read_u32_tok ()
Lee el siguiente token y lo convierte a uint32_t.
uint32_t stm_read_u32_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
Ver stm_read_u8_tok.
stm_read_u64_tok ()
Lee el siguiente token y lo convierte a uint64_t.
uint64_t stm_read_u64_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
Ver stm_read_u8_tok.
stm_read_r32_tok ()
Lee el siguiente token con stm_read_token y, si es un número real, lo convierte a real32_t.
real32_t stm_read_r32_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
En caso que no se pueda leer un token de tipo ekTINTEGER o ekTREAL (con o sin ekTMINUS), se devolverá 0 y se marcará el stream como corrupto con stm_corrupt.
stm_read_r64_tok ()
Lee el siguiente token y lo convierte a real64_t.
real64_t stm_read_r64_tok(Stream *stm);
stm | El objeto stream. |
Retorna
El valor numérico del token.
Observaciones
Ver stm_read_r32_tok.
stm_read_bool ()
Lee una variable bool_t
desde el stream.
bool_t stm_read_bool(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerá 1 byte. Si el valor es diferente de 0 o 1, se marcará el stream como corrupto con stm_corrupt. Ver Stream binario.
stm_read_i8 ()
Lee una variable int8_t
desde el stream.
int8_t stm_read_i8(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerá 1 byte. Ver Stream binario.
stm_read_i16 ()
Lee una variable int16_t
desde el stream.
int16_t stm_read_i16(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 2 bytes. Ver Stream binario.
stm_read_i32 ()
Lee una variable int32_t
desde el stream.
int32_t stm_read_i32(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 4 bytes. Ver Stream binario.
stm_read_i64 ()
Lee una variable int64_t
desde el stream.
int64_t stm_read_i64(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 8 bytes. Ver Stream binario.
stm_read_u8 ()
Lee una variable uint8_t
desde el stream.
uint8_t stm_read_u8(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerá 1 byte. Ver Stream binario.
stm_read_u16 ()
Lee una variable uint16_t
desde el stream.
uint16_t stm_read_u16(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 2 bytes. Ver Stream binario.
stm_read_u32 ()
Lee una variable uint32_t
desde el stream.
uint32_t stm_read_u32(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 4 bytes. Ver Stream binario.
stm_read_u64 ()
Lee una variable uint64_t
desde el stream.
uint64_t stm_read_u64(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 8 bytes. Ver Stream binario.
stm_read_r32 ()
Lee una variable real32_t
desde el stream.
real32_t stm_read_r32(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 4 bytes. Ver Stream binario.
stm_read_r64 ()
Lee una variable real64_t
desde el stream.
real64_t stm_read_r64(Stream *stm);
stm | El objeto stream. |
Retorna
Variable leída.
Observaciones
Se leerán 8 bytes. Ver Stream binario.
stm_read_enum ()
Lee una variable enum
desde el stream.
type stm_read_enum(Stream *stm, type);
stm | El objeto stream. |
type | El tipo del |
Retorna
Variable leída.
Observaciones
Se leerán 4 bytes. Ver Stream binario.
stm_skip ()
Salta e ignora los próximos bytes del stream.
void stm_skip(Stream *stm, const uint32_t size);
stm | El objeto stream. |
size | La cantidad de bytes a saltar. |
stm_skip_bom ()
Salta la posible secuencia Byte Order Mark ""
que se encuentra al comienzo de algunos streams de texto.
void stm_skip_bom(Stream *stm);
stm | El objeto stream. |
Observaciones
Esta función no tendrá efecto si no existe dicha secuencia al inicio del stream. El BOM es habitual en streams provenientes de algunos servidores Web.
stm_skip_token ()
Salta el siguiente token del stream. Si el token no corresponde al indicado, se marcará el stream como corrupto.
void stm_skip_token(Stream *stm, const token_t token);
1 2 3 4 5 6 |
void stm_skip_token(Stream *stm, const token_t token) { token_t tok = stm_read_token(stm); if (tok != token) stm_corrupt(stm); } |
stm | El objeto stream. |
token | Token esperado. |
stm_flush ()
Escribe en el canal la información existente en la caché.
void stm_flush(Stream *stm);
stm | El objeto stream. |
Observaciones
Para mejorar el rendimiento, las operaciones de escritura en streams de disco o E/S estándar se almacenan en una caché interna. Esta función fuerza la escritura en el canal y limpia el búfer. Será útil con protocolos full-duplex donde el receptor espera contestación para continuar.
stm_pipe ()
Conecta dos streams, leyendo datos de uno y escribiéndolos en otro.
void stm_pipe(Stream *from, Stream *to, const uint32_t n);
from | El objeto stream de entrada (para leer). |
to | El objeto stream de salida (para escribir). |
n | La cantidad de bytes a trasvasar. |
Observaciones
El trasvase se realizará sobre datos "en bruto", sin tener en cuenta Orden de bytes o Codificaciones UTF. Si tienes claro que esto no afecta, es mucho más rápido que utilizar operaciones atómicas de lectura/escritura.
stm_lines ()
Itera sobre todas las líneas en un Stream de texto. Debes usar stm_next para cerrar el bucle.
void stm_lines(const char_t *line, Stream *stm);
1 2 3 4 5 6 |
uint32_t i = 1; Stream *stm = stm_from_file("/home/john/friends.txt", NULL); stm_lines(line, stm) bstd_printf("Friend %d, name %s\n", i++, line); stm_next(line, stm) stm_close(&stm); |
line | Nombre de la variable que albergará temporalmente la línea. Utiliza una caché interna del stream, por lo que deberás hacer una copia con str_c si necesitas conservarla. |
stm | Stream. |
stm_next ()
Cierra un bucle abierto por stm_lines.
void stm_next(const char_t *line, Stream *stm);
line | Nombre de la variable línea. |
stm | Stream. |