Pregunta

Actualmente estoy rediseñando una aplicación y encontré un problema al serializar algunos datos.

Digamos que tengo una variedad de tamaño mxn

double **data;

que quiero serializar en un

char *dataSerialized

usando delimitadores simples (uno para filas, otro para elementos).

La deserialización es bastante sencilla: cuenta los delimitadores y asigna el tamaño de los datos que se almacenarán.Sin embargo, ¿qué pasa con la función de serialización, digamos?

serialize_matrix(double **data, int m, int n, char **dataSerialized);

¿Cuál sería la mejor estrategia para determinar el tamaño que necesita la matriz de caracteres y asignarle la memoria adecuada?

¿Quizás usar alguna representación exponencial de ancho fijo de dobles en una cadena?¿Es posible simplemente convertir todos los bytes de doble en caracteres y tener una matriz de caracteres alineada con tamaño de (doble)?¿Cómo mantendría intacta la precisión de los números?

NOTA:

Necesito los datos en una matriz de caracteres, no en binario, no en un archivo.

Los datos serializados se enviarán a través de la red utilizando ZeroMQ entre un servidor C y un cliente Java.¿Sería posible, dadas las dimensiones de la matriz y el tamaño de (doble), que siempre pueda reconstruirse con precisión entre esos dos?

¿Fue útil?

Solución

Java tiene un soporte bastante bueno para leer bytes sin procesar y convertirlos en lo que quieras. Puede elegir un formato de cable simple y luego serializarlo en C y anular la serialización en Java.

Aquí hay un ejemplo de un formato extremadamente simple, con código para anular la serialización y serialización.

He escrito un programa de prueba un poco más grande que puedo volcar en algún lugar si lo desea;crea una matriz de datos aleatoria en C, serializa, escribe la cadena serializada codificada en base64 en stdout.El programa java mucho más pequeño luego lee, decodifica y deserializa esto.

Código C para serializar:

/* 
I'm using this format:
32 bit signed int                   32 bit signed int                   See below
[number of elements in outer array] [number of elements in inner array] [elements]

[elements] is buildt like
[element(0,0)][element(0,1)]...[element(0,y)][element(1,0)]...

each element is sendt like a 64 bit iee754 "double". If your C compiler/architecture is doing something different with its "double"'s, look forward to hours of fun :)

I'm using a couple non-standard functions for byte-swapping here, originally from a BSD, but present in glibc>=2.9.
*/

/* Calculate the bytes required to store a message of x*y doubles */
size_t calculate_size(size_t x, size_t y)
{
    /* The two dimensions in the array  - each in 32 bits - (2 * 4)*/
    size_t sz = 8;  
    /* a 64 bit IEE754 is by definition 8 bytes long :) */
    sz += ((x * y) * 8);    
    /* and a NUL */
    sz++;
    return sz;
}

/* Helpers */
static char* write_int32(int32_t, char*);
static char* write_double(double, char*);
/* Actual conversion. That wasn't so hard, was it? */
void convert_data(double** src, size_t x, size_t y, char* dst)
{

    dst = write_int32((int32_t) x, dst);    
    dst = write_int32((int32_t) y, dst);    

    for(int i = 0; i < x; i++) {
        for(int j = 0; j < y; j++) {
            dst = write_double(src[i][j], dst);
        }
    }
    *dst = '\0';
}


static char* write_int32(int32_t num,  char* c)
{
    char* byte; 
    int i = sizeof(int32_t); 
    /* Convert to network byte order */
    num = htobe32(num);
    byte = (char*) (&num);
    while(i--) {
        *c++ = *byte++;
    }
    return c;
}

static char* write_double(double d, char* c)
{
    /* Here I'm assuming your C programs use IEE754 'double' precision natively.
    If you don't, you should be able to convert into this format. A helper library most likely already exists for your platform.
    Note that IEE754 endianess isn't defined, but in practice, normal platforms use the same byte order as they do for integers.
*/
    char* byte; 
    int i = sizeof(uint64_t);
    uint64_t num = *((uint64_t*)&d);
    /* convert to network byte order */
    num = htobe64(num);
    byte = (char*) (&num);
    while(i--) {
        *c++ = *byte++; 
    }
    return c;
}

Código Java para anular la serialización:

/* The raw char array from c is now read into the byte[] `bytes` in java */
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes));

int dim_x; int dim_y;
double[][] data;

try {   
    dim_x = stream.readInt();
    dim_y = stream.readInt();
    data = new double[dim_x][dim_y];
    for(int i = 0; i < dim_x; ++i) {
        for(int j = 0; j < dim_y; ++j) {
            data[i][j] = stream.readDouble();
        }
    }

    System.out.println("Client:");
    System.out.println("Dimensions: "+dim_x+" x "+dim_y);
    System.out.println("Data:");
    for(int i = 0; i < dim_x; ++i) {
        for(int j = 0; j < dim_y; ++j) {
            System.out.print(" "+data[i][j]);
        }
        System.out.println();
    }


} catch(IOException e) {
    System.err.println("Error reading input");
    System.err.println(e.getMessage());
    System.exit(1);
}

Otros consejos

Si está escribiendo un archivo binario, debería pensar en una buena manera de serializar los datos binarios reales (64 bits) de su double.Esto podría ir desde escribir directamente el contenido del doble en el archivo (teniendo en cuenta el endianismo) hasta algunos esquemas de serialización de normalización más elaborados (p. ej.con una representación bien definida de NaN).Eso realmente depende de ti.Si espera estar básicamente entre arquitecturas homogéneas, probablemente sea suficiente un volcado de memoria directo.

Si desea escribir en un archivo de texto y busca una representación ASCII, le desaconsejo encarecidamente una representación numérica decimal.En su lugar, podría convertir los datos sin procesar de 64 bits a ASCII usando base64 o algo así.

Realmente quieres mantener toda la precisión que tienes en tu double!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top