[Tip] Simple serializador para C

Simple Net Serializer

Simple Net Serializer

El otro día necesitaba serializar unos mensajes para una aplicación cliente/servidor que estaba desarrollando.

En un primer momento intenté buscar alguna librería que me diera lo que estaba buscando. Probé con GLib pero no tiene nada para serializar mensajes (se pueden serializar objectos con GObject).

Decidí hacer una implementación sencilla de lo que tenia mente.

Así fue como desarrolle el siguiente código:

// PUSH/POP Simple net serializer

#define PUSH_BEGIN(buff,l)       { int i = 0; int lm = (l); char* b = (char*) (buff);
#define PUSH_BYTE(b)                 assert(i+1 <= lm); b[i++] = (b);
#define PUSH_SHORT(s)                assert(i+sizeof(short) <= lm); { short* ps = (short*) &b[i]; *ps = htons((s)); i += sizeof(short); }
#define PUSH_INT(s)                  assert(i+sizeof(int) <= lm); { int* ps = (int*) &b[i]; *ps = htonl((s)); i += sizeof(int);}
#define PUSH_SZ(s)                  { int l = strlen(s); assert(i+l+sizeof(int) <= lm); PUSH_INT(l); memcpy(&b[i],s,l); i += l; }
#define PUSH_END(l)              (l) = i; }

#define POP_BEGIN(buff,l)        { int i = 0; int lm = (l); char* b = (char*) (buff);
#define POP_BYTE(b)              assert(i < lm); (b) = b[i]; i++;
#define POP_SHORT(s)             assert(i+sizeof(short) <= lm); (s) = ntohs(  *((short*) &b[i])     ); i += sizeof(short);
#define POP_INT(s)               assert(i+sizeof(int) <= lm); (s) = ntohl(  *((int*) &b[i])     ); i += sizeof(int); printf("val: %i\n",(s));
#define POP_SZ(s)                { int l = 0; POP_INT(l);  assert(i+l <= lm); (s) = (char*) malloc(l+1); memcpy(s,&b[i],l); s[l] = '\0'; i += l; }
#define POP_END()             }

A poco que examinéis el código comprobareis que se trata en realidad de una macro.

Esta implementación cumple mis requisitos:

  • Eficiente. Las macros se resuelven en el código y es equivalente a las funciones inline.
  • Sencillo. La nomenclatura de las macros auto-describe su propósito, PUSH_x para agregar y POP_x para extraer.
  • Poca sobrecarga. La mayoría de los serializadores incluyen el tipo de dato del dato agregado. En este caso el tipo está bien determinado e identifica unívocamente el tipo de mensaje.
  • Depuración. Gracias a los assert() puede comprobar que el tamaño de los bufferes para el envio/recepción de los mensajes es correcto durante la fase de pruebas.
  • Portable. El uso de ntoh/hton posibilita la independencia de arquitectura big/little endian.

Así por ejemplo si queremos mandar el siguiente mensaje: “bote de champú”,3,”suave”, éste seria el código resultante:


#define MAXBUFFER 1024

char buff[1024];
int len;

PUSH_BEGIN(buff,MAXBUFFER)
PUSH_SZ("bote de champu")
PUSH_INT(3)
PUSH_SZ("suave")
len= PUSH_END()

Seguro que no es la mejor implementación y es probable que tenga algun bug. Si ves algo que no te gusta o bugs, te animo a que lo modifiques y comentes. Libero el código bajo licencia BSD.

Translate to:English
MenefanteMenéame TwitterTwitter

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: