Serie ficheros virtuales

  

 

 

 

C Ficheros virtuales

 

 

 

 

A.I.2 Estructuras

 

 A.I.2.1 Definición

 A.I.2.2 Almacenamiento y recuperación de información en estructuras

 A.I.2.3 Tamaño

 A.I.2.4 Orden. La función SRRCAL_lLexi

 

                                                             _________

 

 

Aunque la aplicación se basa en una utilización de los punteros genéricos que se han visto en el capítulo anterior, casi siempre se usará para manejar punteros a estructuras, pues los ficheros virtuales surgen de su serialización

 

Es una situación que veremos en las distintas definiciones de las bases de datos de los ejemplos, que siempre parten de un soporte de estructura compuesta de una o varias subestructuras de claves y otra de datos.

 

Así pues, nos iremos encontrando con unas definiciones similares a las del siguiente tipo resumen:

 

              struct   sMuestra {struct sK sDk;  struct sD sDd;}  sDmuestra;

 

 

cuyos matices vamos a exponer en este capítulo.

 

                                                             _________

 

A.I.2.1 Definición

 

Cuando se dispone de uno o varios tipos de elementos que recogen un conjunto de información relacionada, nos encontramos ante una estructura.

 

Los elementos que integran la estructura se denominan miembros. Estos miembros pueden ser cualquier tipo de datos, en particular pueden ser a su vez estructuras.

 

Al salvarse a disco, las estructuras grabadas se denominan registros, y quedan recogidas en ficheros.

 

Los ficheros virtuales también se basan en registros, pero la grabación de estos se efectúa en la memoria en vez de en soporte físico, de la manera que se expondrá en los siguientes capítulos.

 

 

La declaración formal de una estructura en ANSI C es:

 

struct [nombre de estructura]

{

 [tipo de dato miembro 1] [nombre de miembro 1];

 [tipo de dato miembro 2] [nombre de miembro 2];

 ...

 [tipo de dato miembro n] [nombre de miembro n];

};

 

 

Y la definición de variables del tipo correspondiente al de la estructura definida viene dada como

 

struct [nombre de estructura]

       [variable estructura 1], [variable estructura2],..., [variable estructura m];

 

 

También pueden reunirse declaración y definición como sigue:

 

struct [nombre de estructura]

{

 [tipo de dato miembro 1] [nombre de miembro 1];

 [tipo de dato miembro 2] [nombre de miembro 2];

 ...

 [tipo de dato miembro n] [nombre de miembro n];

} [variable estructura 1], [variable estructura2], ..., [variable estructura m];

 

                                                             _________

 

Ejemplo:

En una aplicación de cálculo binomial de opciones se emplean las siguientes estructuras, en donde se aplican los dos formatos de declaración y definición indicados:

 

// Ds del fichero virtual de evolución de subyacente/primas

 

struct sDsArbolEvolucion

{

 struct sDsK sDk; // Claves

 struct sDsD sDd; // Datos

} sDsArbol;

 

 

// Estructura de claves de acceso a los nodos (i,j)

 

struct sDsK

{

 long li; // Indice i del nodo

 long lj; // Indice j del nodo

};

 

 

// Estructura de datos de subyacente/primas en cada nodo (i,j)

 

struct sDsD

{

 double dS; // Evolución del valor de subyacente

 double dC; // Evolución del valor de prima Call

 double dP; // Evolución del valor de prima Put

};

 

 

Esta definición de estructuras permite dar soporte a la evolución de un árbol binomial con nodos del tipo (i,j) siguiente:

 

 

             3,1 . . .

 

      2,1

 

1,1        3,2 . . .

 

     2,2

 

           3,3 . . .

 

                                                             _________

 

 

A.I.2.2 Almacenamiento y recuperación de información en estructuras

 

El almacenamiento y recuperación puede hacerse directamente mediante el operador punto:

 

...

 

// Pasa índices de acceso al nodo del árbol

 

sDsArbol.sDk.li = li;

sDsArbol.sDk.lj = lj;

 

...

 

// Pasa valores recuperados

 

dC = sDsArbol.sDd.dC;

dP = sDsArbol.sDd.dP;

 

...

 

Este sistema es el que se utilizará normalmente en los ejemplos del libro.

 

 

También se puede acceder a los datos a partir de un puntero, con el operador flecha:

 

(Ejemplo eII_1)

 

 

struct sDsArbolEvolucion *p_sDsArbol;

 

p_sDsArbol = (sDsArbolEvolucion *) malloc(sizeof(sDsArbolEvolucion));

 

...

 

p_sDsArbol -> sDk.li = li;

p_sDsArbol -> sDk.lj = lj;

 

...

 

liw = p_sDsArbol -> sDk.li;

ljw = p_sDsArbol -> sDk.lj;

 

...

 

                                                             _________

 

Que podemos ver en concreto con una imagen bajo debug del ejemplo:

 

 

 

 

Como para cualquier otro tipo de datos, se puede crear una serie de estructuras. Sin embargo la aplicación de ficheros virtuales aporta una perspectiva alternativa al permitir utilizar las estructuras como soporte de los mismos, permitiéndoles acceder a las operaciones de acceso y actualización propias de los ficheros, muy superiores al simple acceso por índice de serie.

 

                                                             _________

A.I.2.3 Tamaño

 

El tamaño de una estructura será al menos igual que la suma de los tamaños de sus miembros, esto es así porque al compilador se le permite completar una estructura para conseguir un alineamiento de palabra (8 bytes) o de párrafo (16 bytes).

 

En la plataforma iSeries el tamaño realmente coincide con la suma, pero en MsC++ normalmente el sizeof de una estructura es múltiplo de 8, aunque se pueden encontrar casos particulares de interés.

 

Por ejemplo, consideremos las siguientes estructuras anidadas que componen una estructura de persistencia de datos para un interfaz de valoración binomial de opciones:

 

// Ds del fichero virtual de persistencia de datos

 

struct Psrrcd_dat

{

 struct sDsK sDk;     // Clave principal

 struct sDsK01 sDk01; // Clave secundaria

 struct sDsD sDd;     // Datos

} Psrrcd_reg;

 

 

// Subestructura de claves de ordenación principal

 

struct sDsK

{

 int iNsec; // Nº de secuencia principal

};

 

 

// Subestructura de claves de caché virtual. Parámetros de petición de proceso

 

struct sDsK01

{

 char cOpc[2];  // Tipo. Sobre futuro o sobre contado

 long lFval;    // Fecha valor

 long lFvct;    // Fecha vencimiento

 double dPrecS; // Precio subyacente

 double dPrecE; // Precio entregable

 double dTasa;  // Tipo de interés libre de riesgo

 double dUp;    // Tasa incremento binomial

 double dDown;  // Tasa decremento binomial

 long lNumP;    // Nº de pasos del árbol binomial a construir

 long lSF1;     // Fecha de 1er.cupón

 double dSD1;   // Importe 1er.cupón

 long lSF2;     // Fecha 2º cupón

 double dSD2;   // Importe 2º cupón

 long lSF3;     // Fecha 3er.cupón

 double dSD3;   // Importe 3er.cupón

 long lSF4;     // Fecha 4ºcupón

 double dSD4;   // Importe 4ºcupon

};

 

 

// Subestructura de datos de caché virtual. Datos de respuesta de proceso

 

struct sDsD

{

 double dP;    // Probabilidad de paso nodal

 long lNumN;   // Nº de nodos construidos

 double dPreC; // Prima call

 double dDelC; // Delta call

 double dPreP; // Prima put

 double dDelP; // Delta put

 double dGamC; // Gamma call

 double dGamP; // Gamma put

 double dTheC; // Theta call

 double dTheP; // Theta put

};

 

 

 

Este diseño de estructuras permite soportar una base de datos virtual con una vía de acceso principal en sDk y otra en sDk01, pero resulta que en éste ejemplo el compilador de MsC++ devuelve

 

sizeof(Psrrcd_reg.sDk) = 4

 

 ¡sin embargo los datos de Psrrcd_reg.sDk01 comienzan en la posición 9!

 

 

 

Si ayudamos al compilador cambiando las estructuras a

 

 

// Ds del fichero virtual de persistencia de datos

 

struct Psrrcd_dat

{

 struct sDsK sDk;

 struct sDsK01 sDk01;

 struct sDsD sDd;

} Psrrcd_reg;

 

 

// Subestructura de claves de ordenación principal

 

struct sDsK

{

 int iNsec;

 char EOR;

};

 

 

// Subestructura de claves de caché virtual

 

struct sDsK01

{

 char cOpc[2];

 long lFval;

 long lFvct;

 double dPrecS;

 double dPrecE;

 double dTasa;

 double dUp;

 double dDown;

 long lNumP;

 long lSF1;

 double dSD1;

 long lSF2;

 double dSD2;

 long lSF3;

 double dSD3;

 long lSF4;

 double dSD4;

 char EOR;

};

 

 

// Subestructura de datos de caché virtual

 

struct sDsD

{

 double dP;

 long lNumN;

 double dPreC;

 double dDelC;

 double dPreP;

 double dDelP;

 double dGamC;

 double dGamP;

 double dTheC;

 double dTheP;

 char EOR;

};

 

 

donde "char EOR" es una marca de fin de estructura de valor constante '*'.

 

 

Con esta redefinición, sizeof devuelve

 

sizeof(Psrrcd_reg.sDk)   = 8

sizeof(Psrrcd_reg.sDk01) = 128

sizeof(Psrrcd_reg.sDd)   = 88

sizeof(Psrrcd_reg)       = 224

 

que ya son todos múltiplos del tamaño de palabra y en donde realmente coincide el comienzo real de datos con el predicho por sizeof.

 

 

Estas precauciones sobre el tamaño son importantes al trabajar con lógicos de ficheros virtuales en MsC++ a fin de garantizar la respuesta correcta del producto y deben tenerse en cuenta si se desea mantener la portabilidad de los desarrollos entre iSeries y MsC++.

 

Con vistas a esta portabilidad es por lo que prefiero la definición de variables tampón auxiliares a la alternativa de usar directivas de compilación propietarias del lenguaje C++ para desactivar la opción de optimización que hace tomar tamaños múltiplo de palabra tal como __attribute__ ((__packed__))

 

                                                             _________

 

A.I.2.4 Orden. La función SRRCAL_lLexi

 

Al implementar el interfaz de pruebas observé una aparente anomalía de funcionamiento al utilizar claves int o long en lugar de char que no había sufrido en el iSeries con los mismos ejemplos.

 

Investigando en detalle, observé que los números enteros tienen una forma peculiar de archivarse en MsC++.

 

Consideremos los 4 bytes de tamaño de un long, representemos con [ ] un byte no ocupado y con [*] uno ocupado.

 

Resulta entonces que los números de 0 a 255 se archivan como [*][ ][ ][ ], pero un número superior se archiva como [ ][*][ ][ ] y luego [*][*][ ][ ] y luego [ ][ ][*][ ] continuando hasta llegar a [ ][ ][ ][*] y prosiguiendo finalmente a [*][*][*][*]. Esto es:

 

 

                       [*][ ][ ][ ]

                       [ ][*][ ][ ]

                       [*][*][ ][ ]

                       [ ][ ][*][ ]

                 . . .

                       [ ][ ][ ][*]

                 . . .

                       [*][*][*][*]

 

      

 

Para que el orden natural reflejara el peso lexicográfico, debería tenerse una situación como [ ][ ][ ][*]

y luego [ ][ ][*][ ] y luego [ ][ ][*][*] y luego [ ][*][ ][ ] continuando hasta llegar a [*][ ][ ][ ] y prosiguiendo finalmente a [*][*][*][*] de forma que los números mayores tuvieran mayor peso. Esto es:

 

                       [ ][ ][ ][*]

                       [ ][ ][*][ ]

                       [ ][ ][*][*]

                       [ ][*][ ][ ]

                 . . .

                       [*][ ][ ][ ]

                 . . .

                       [*][*][*][*]

 

 

Técnicamente, esta situación es resultado del modelo de almacenamiento. Cuando el modelo de almacenamiento sigue el estilo little endian, el byte menos significativo está en la dirección de memoria más baja y el más significativo en la posición más alta. Por el contrario, en el estilo

big endian el orden es el inverso. Este último modelo, que para nuestros propósitos resulta más práctico, es el que sigue IBM.

 

Por ello desarrollé la función SRRCAL_lLexi que realiza ésta tarea y permite garantizar que el orden natural se archiva en el orden de peso lexicográfico permitiendo utilizar números enteros en las vías de acceso sin dificultades.

 

Por esta razón se encontrarán en prácticamente todos los ejemplos que se presentan instrucciones como las siguientes, normalmente en un contexto de bucles primero de grabación y posteriormente de lectura:

 

                                                             _________

 

(Supuesto que estemos situados en un bucle inicial de grabación)

 

. . .

 

 ++k;

 DSM.DSK.k = k;                   // Carga un valor de clave en una estructura

 

 SRRCAL_lLexi(&DSM.DSK.k);        // Pasa valor a peso lexicográfico

 

 lresul = SRRCW_WRITE("M", &DSM); // Graba la estructura en el fichero virtual "M"

 

. . .

 

 

 

(Situados en un bucle posterior de lectura)

 

. . .

 

 ++i;

 er = SRRCW_READ("M", i, &DSM); // Lee una estructura desde el registro i del fichero virtual "M"

 

 SRRCAL_lLexi(&DSM.DSK.k);      // Aplica lLexi para decodificar de peso a valor

 

. . .

 

 

 

(Utilizando una definición de estructura de soporte siguiente:

 

 

 // Estructura de soporte del fichero virtual "M" del ejemplo

 

 static struct sDSM

 {

   struct sDsK DSK; // Clave

   struct sDsD DSD; // Datos

 } DSM;

 

 

 // Ds claves

 

 struct sDsK { long k; };

 

 

 // Ds datos asociados

 

 struct sDsD { long i; }; )

 

 

 

Hay algunos casos en que lLexi no se necesita, son aquellos en los que no se precisa recuperar los datos de una forma secuencial ordenada, en los que basta con tenerlos cacheados en memoria y obtenerlos de forma esporádica; en otros términos, aquellos casos en que no se necesite utilizar READ, READE o READPE, siendo suficiente un simple CHAIN.

 

También están aquellos casos en que sí se precise recorrer todo un rango, pero sin importar el orden concreto de extracción.

 

 

En iSeries, por su modelo de almacenamiento no se precisa esta conversión, por lo que estas instrucciones o bien no se incluyen o bien se invoca un SRRCAL_lLexi nulo para facilitar el mantenimiento compatible.

 

 

                                                             _________

 

 

La función SRRCAL_lLexi es muy sencilla, funciona como strrev, transponiendo los bytes componentes del número recibido pero sin parar en la marca de terminación /00 si la encontrase. Es reversible por construcción, esto es, al pasarse dos veces seguidas devuelve el número original.

 

 

Su código se expone a continuación. Junto con el resto de fuentes se incluye en las carpetas de proyecto.

 

//-----------------------------------------------------------------------------------

// lLexi Lexicográfica para long's en MsC++

//

// Reordena un long para almacenarlo lexicográficamente y poder utilizarlo como clave

// o subclave de un fichero virtual

//

// Cuando se le pasa dos veces seguidas, devuelve el nº a su valor original

//

// Parámetros: (Entrada/Salida) *l Numero que se convierte ó restaura

//

//-----------------------------------------------------------------------------------

void SRRCAL_lLexi(long *l)

{

 register short int i = 0; // Contador de for

 static char cli[] = " ";  // Work de proceso char para retorno de l inverso

 char *cl = (char *) l;    // Work de proceso char para l

 

 

 // Intercambio [La función directa, strrev, que hace lo mismo para una cadena, no se puede emplear

 // porque se detiene prematuramente al encontrarse /00's y considerarlos fin de cadena]

 

 for (i=0;i<sizeof(long);++i) memcpy(cli + i, cl + sizeof(long) - (i+1), 1);

 

 

 // Salida resultado

 

 memcpy(l, cli, sizeof(long));

 

 return;

}

                                                             _________