Mejor forma de procesar una cadena de formato [Cantidad de memoria]

Iniciado por AlbertoBSD, 16 Septiembre 2016, 00:12 AM

0 Miembros y 1 Visitante están viendo este tema.

AlbertoBSD

Buenas!!

Estoy haciendo un pequeño servidor WEB en C, algo basico y como ejercicio, ya que este algo pulido dejare el link al codigo por aqui.


El detalle que me encuentro es que quiero crear una función de argumentos variables asi como el printf donde le ahorre al usuario "final" ( :rolleyes: :rolleyes:) el reservar memoria para el buffer de respuesta al cliente. para ello solo creare una función llamada "server_print" donde el usuario colocara su formato de cadena y las variables que el quiera. Y que la función haga el trabajo duro...

session es un contenedor de variables auxiliares y ese de momento, se puede obviar...

void server_print(struct peticion *session,char *format,...) {
char *buffer = NULL;
int X;
va_list args;
va_start(args, format);
buffer =malloc(X);
vsnprintf(buffer,X,format, args);

//realizar operaciones sobre session...


va_end(args);
}


Lo que me interesa saber es el cuando debe de valer X para reservar esa cantidad de memoria exacta.. el detalles esta en que puedo ahórrame el "pensar" y asignar MUCHA memoria por cada argumento del programa, pero el detalle esta en que:

¿Que pasa si no alcanza?

Quiero tener un programa eficiente que no malgaste memoria y sea rápido...

Se me ocurre buscar en la cadena de formato por cada % que se encuentre y por ejemplo si es un Entero reservar solo 12 bytes mas, si es cadena sacar el strlen del argumento en cuestion, si es %l dejar sobre 30 bytes mas y asi...

¿Como procederían ustedes?

Saludos!
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

MAFUS

Si tienes que guardarlo todo (no puedes procesar iterativamente sobre cada argumento) puedes crear una estructura así:
struct argumento {
    int tipo;
    void *valor;
};


Donde tipo es un entero que indicará de qué tipo fundamental es el argumento, podrías generar una lista con un enum para más comodidad.
En valor, mediante malloc, reservas tanta memoria como sea necesaria, según tipo y tamaño (en caso de cadena).

Al procesar tu cadena cuentas cuántos % hay en tu cadena y no sean %%, funcionamiento típico de printf, y mediante malloc generas un array dinámico de tantas estructuras argumento.
Creado el array iteras sobre los argumentos y vas rellenando el array.

A la hora de leerlo solo debes ir fijándote sobre que tipo es el elemento del array y hacer el cast correspondiente a su tipo a la hora de recuperar el valor.

AlbertoBSD

Muchas gracias MAFUS segui mas o menos tu consejo y quedo asi el codigo:

void server_print(struct peticion *session,char *format,...) {
int len = 0,count = 0,i,X;
char *buffer = NULL;
struct m_args *arreglo = NULL;
va_list args;
va_start(args, format);
len = strlen(format);
X = len;
i = 0;
while(i < len) {
if(format[i] == '%') {
i++;
printf("Evaluando %c\n",format[i]);
switch(format[i]) {
case 's':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+= strlen((char*)arreglo[count].valor);
count++;
break;
case 'i':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=11;
count++;
break;
case 'u':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=11;
count++;
break;
case 'd':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=11;
count++;
break;
case 'f':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=11;
count++;
break;
case 'x':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=11;
count++;
break;
case 'l':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=20;
count++;
break;
case 'c':
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=1;
count++;
break;
case '%':
X+=1;
break;
default:
arreglo = realloc(arreglo,sizeof(struct m_args)*(count+1));
arreglo[count].valor = va_arg(args, void *);
X+=20;
count++;
break;
}
}
i++;
}
free(arreglo);
va_end(args);
buffer = malloc(X+1);
va_start(args, format);
vsnprintf(buffer,X,format, args);
va_end(args);
buffer[X] = 0;
len = strlen(buffer);
printf("Longitud reservada %i vs Real %i\n",X,len);
X = session->reply_length + len;
session->reply = realloc(session->reply,X+1);
memcpy(session->reply+ session->reply_length,buffer,len);
session->reply[X] = 0;
session->reply_length = X;
free(buffer);
}


Resulta que podria llamar varias veces a va_start(args, format); y va_end...

Y asi solo itere sobre los argumentos viendo su tipo (Segun el formato de Cadena) reserve la memoria mas o menos necesitada por el tipo y volvi a llamar a va_start para posteriormente hacer un vsnprintf

vsnprintf(buffer,X,format, args);

Aun asi me faltan los formatos mas especializados pero eso con tiempo lo voy hacer, puse un default de +20 bytes por parametro "desconocido"



Saldos!
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW

ivancea96

Utiliza los datos que te da la propia función. La función retorna la cantidad de caracteres que necesita.

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>


void temp(char* format, ...){
va_list list;

va_start(list, format);
int n = vsnprintf(0, 0, format, list);
va_end(list);

char* t = malloc(n+1);

va_start(list, format);
vsnprintf(t, n+1, format, list);
va_end(list);

printf("%s", t);
fflush(stdout);
}

int main(){
temp("%i%i%i", 123,456,789);
}

AlbertoBSD

Eso me pasa por no leer la documentacion de la funcion  ;D  ;-) ;-) ;-)

Ya quedo mas optimizada mi funcion, muchas gracias  :xD
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW