2010-03-25 5 views
5

Estoy escribiendo un plugin fusible en C. Estoy hacer el seguimiento de las estructuras de datos en el sistema de archivos a través de estructuras como:¿Las matrices están almacenadas como un puntero o en su totalidad dentro de C struct? (Era: Cómo volcar estructuras en el disco cuando algunos campos son punteros?)

typedef struct { 
    block_number_t inode; 
    filename_t filename; //char[SOME_SIZE] 
    some_other_field_t other_field; 
} fs_directory_table_item_t; 

Obviamente, tengo que leer (escribir) estas estructuras desde (hasta) el disco en algún momento. Podría tratar la estructura como una secuencia de bytes y hacer algo como esto:

read(disk_fd, directory_table_item, sizeof(fs_directory_table_item_t)); 

... excepto que no puede funcionar como filename es en realidad un puntero a la matriz char.

realmente me gustaría evitar tener que escribir código como:

read(disk_df, *directory_table_item.inode,  sizeof(block_number_t)); 
read(disk_df, directory_table_item.filename, sizeof(filename_t)); 
read(disk_df, *directory_table_item.other_field, sizeof(some_other_field_t)); 

... para cada estructura en el código, ya que tendría que replicar código y cambios en no menos de tres diferentes lugares (definición, lectura, escritura).

¿Alguna idea de DRYer pero que aún se puede mantener?

+0

por curiosidad, ¿qué FUSE? – falstro

+0

oh, y es 'filename' un puntero, o como su comentario (y uso del operador sizeof) implica, una matriz? (en este último caso, no tendría problema ..) – falstro

+0

Este FUSE: http://fuse.sourceforge.net/ Y sí, 'filename' es una matriz de caracteres de longitud fija. (Al menos, es para el futuro previsible). – badp

Respuesta

5

La memoria de la cadena formará parte de su estructura, aunque el tipo de matriz es promovido a un puntero en muchos casos, el tipo almacenado en la estructura es la matriz, no el puntero.

typedef struct { 
    block_number_t inode; 
    filename_t filename; //char[SOME_SIZE] 
    some_other_field_t other_field; 
} fs_directory_table_item_t; 

lo tanto su estado de lectura:

read(disk_fd, directory_table_item, sizeof(fs_directory_table_item_t)); 

va a funcionar y llevar a los datos.

Al leer y escribir bloques de memoria, debe tener en cuenta relleno. El relleno es adicional, los campos vacíos añadidos por el compilador al alinean los datos en los límites relevantes; p.ej. un valor de 32 bytes a menudo debería comenzar en un límite de 4 bytes en la memoria para permitir que el procesador lo lea de manera eficiente. Esto normalmente no es algo de lo que preocuparse, pero cuando persiste la estructura en el disco, puede presentar problemas si recompila el código con otra configuración. A menudo hay algún tipo de directivas #pragma que deshabilita el relleno, creo que se llama #pragma pack en MS Visual C++.

+0

El problema de relleno también puede aparecer si simplemente cambia su compilador, también. – Baltasarq

+0

Gracias para aclarar mi confusión :) – badp

+0

En realidad, no deberíamos usar "leer()" como este en primer lugar. Read() usa E/S sin búfer. Puede ser muy ineficiente cuando se lo llama con frecuencia en pequeños fragmentos de datos. fread() es la llamada correcta. – user172818

2

Una forma de hacer esto es crear tablas estáticas de datos que describan sus estructuras para que un simple motor de lectura/escritura pueda trabajar con ellas.

Necesita definir una estructura que pueda representar todo lo que necesita saber para leer o escribir un solo campo de una sola estructura.

typedef struct { 
    char * name; 
    size_t offset; 
    size_t size; 
    int format_as; 
    void* format_struct; // if format_as & IS_STRUCT, this is the structure type 
    } field_info_t 

enum { 
    AS_CHAR =1, 
    AS_SHORT, 
    AS_LONG, 
    // add other types here 
    AS_MASK = 0xFF, 

    // these flags can be OR'd with type to refine the behavior 
    IS_POINTER = 0x100, 
    IS_STRUCT = 0x200, 
    }; 

A continuación, cree tablas de estos que describan todas sus estructuras de datos.

#define FIELD_OFF(type, field) ((size_t)(LONG_PTR)&(((type *)0)->field)) 
#define FIELD_SIZE(type, field) (sizeof(((type *)0)->field)) 

static const field_info_t g_fs_directory_table_item_table[] = { 
    { "inode", 
     FIELD_OFF(fs_directory_table_item_t, inode), 
     FIELD_SIZE(fs_directory_table_item_t, inode), 
     AS_LONG, 
     NULL 
    }, 

    { "filename", 
     FIELD_OFF(fs_directory_table_item_t, filename), 
     sizeof(filename_t), 
     AS_CHAR | IS_POINTER, 
     NULL 
    }, 

    { "other_field", 
     FIELD_OFF(fs_directory_table_item_t, other_field), 
     FIELD_SIZE(fs_directory_table_item_t, other_field), 
     AS_STRUCT, 
     &some_other_field_table, 
    }, 
}; 

Y a continuación, leer y escribir en los motores que tienen un puntero a una estructura, y un puntero a la tabla que describe la estructura y leer/escribir los diversos campos.

void ReadStructure(FILE * fh, void * pStruct, field_info_t * pFields, int num_fields) 
{ 
    // this is just a rough sketch of the code. 
    for (int ii = 0; ii < num_fields; ++ii) 
    { 
     int * field_size = pFields[ii].size; 
     char * pfield = (char*)pStruct + pFields[ii].offset; 
     if (pFields[ii].format_as & AS_POINTER) 
      pfield = *(char**)pfield; 

     switch (pFields[ii].format_as & AS_MASK) 
     { 
      case AS_CHAR: 
      .... 
     }   
    } 
} 
void WriteStructure(FILE * fh, void * pStruct, field_info_t * pFields, int num_fields); 

Todavía se acaban de tener que mantener una matriz field_info_t para cada una de las estructuras de datos, pero una vez que lo tienes, se puede leer, escribir, validar y bastante-imprimir los datos con un conjunto de funciones bastante simples .

Cuestiones relacionadas