linux fmemopen, bsd funopen
Publicado; dezembro 12, 2008 Arquivado em: C, mac os, programação, sistema | Tags: ansi c, bsd, fmemopen, funopen, linux, mac os x 1 comentárioEnquanto desenvolvia um sistema, me deparei com um problema entre plataformas, relacionado ao uso e manipulação de arquivos temporários em memória.
Ao invés de gravar um arquivo temporário no disco e ir populando com o conteúdo, meu caso exigia que esse arquivo fosse populado em memória para uso posterior, e a API do Linux (via libc) fornece a função fmemopen(3).
Porém, meu ambiente de desenvolvimento é no Mac OS X, e devido ao tipo do software, era inevitátel tanto desenvolver como testar tudo nessa mesma máquina, para então posteriormente colocá-lo em produção em um Linux. O problema é que o OS X herdou a API do BSD, que ao invés de fmemopen, fornece apenas o funopen(3).
Por esse motivo, acabei escrevendo uma versão bem simples do fmemopen do Linux para usar no OS X, usando funopen internamente. O único detalhe, e provavelmente a diferença entre a versão original do fmemopen pra minha, é que a minha simplesmente ignora o modo de acesso ao arquivo (“r”, “w”, etc). O fato é que agora posso continuar desenvolvendo tudo no OS X e depois compilar no Linux obtendo o mesmo resultado.
fmem.h:
/* * fmem.h : fmemopen() on top of BSD's funopen() * 20081017 AF */ #ifndef _FMEM_H #define _FMEM_H #ifndef linux #include <stdio.h> extern FILE *fmemopen(void *buf, size_t size, const char *mode); #else #define _GNU_SOURCE #endif #endif /* fmem.h */
fmem.c:
/* * fmem.c : fmemopen() on top of BSD's funopen() * 20081017 AF */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #ifndef linux struct fmem { size_t pos; size_t size; char *buffer; }; typedef struct fmem fmem_t; static int readfn(void *handler, char *buf, int size) { int count = 0; fmem_t *mem = handler; size_t available = mem->size - mem->pos; if(size > available) size = available; for(count=0; count < size; mem->pos++, count++) buf[count] = mem->buffer[mem->pos]; return count; } static int writefn(void *handler, const char *buf, int size) { int count = 0; fmem_t *mem = handler; size_t available = mem->size - mem->pos; if(size > available) size = available; for(count=0; count < size; mem->pos++, count++) mem->buffer[mem->pos] = buf[count]; return count; // ? count : size; } static fpos_t seekfn(void *handler, fpos_t offset, int whence) { size_t pos; fmem_t *mem = handler; switch(whence) { case SEEK_SET: pos = offset; break; case SEEK_CUR: pos = mem->pos + offset; break; case SEEK_END: pos = mem->size + offset; break; default: return -1; } if(pos < 0 || pos > mem->size) return -1; mem->pos = pos; return (fpos_t) pos; } static int closefn(void *handler) { free(handler); return 0; } /* simple, but portable version of fmemopen for OS X / BSD */ FILE *fmemopen(void *buf, size_t size, const char *mode) { fmem_t *mem = (fmem_t *) malloc(sizeof(fmem_t)); memset(mem, 0, sizeof(fmem_t)); mem->size = size, mem->buffer = buf; return funopen(mem, readfn, writefn, seekfn, closefn); } #endif
E finalmente, um programa simples para testar o uso da função, que compila e produz o exato mesmo resultado tanto no Linux como no OS X – não testei em outros BSDs, mas suponho que deva funcionar também.
test.c:
/* * test.c : memory fp test * 20081212 AF */ #include "fmem.h" #include <stdio.h> #include <string.h> int main() { FILE *fp; char buff[128], temp[128]; memset(buff, 0, sizeof buff); fp = fmemopen(buff, sizeof buff, "r+"); fprintf(fp, "hello world"); fseek(fp, 0, SEEK_SET); memset(temp, 0, sizeof buff); fgets(temp, sizeof temp, fp); fclose(fp); fprintf(stdout, "read: [%s]\n", temp); return 0; }
Para compilar, basta o usar o seguinte Makefile:
CC=gcc
RM=rm -f
NAME=test
.SUFFIXES = .o
OBJECTS = test.o fmem.o
.c.o:
$(CC) -Wall -c -o $@ $<
all: $(OBJECTS)
$(CC) -Wall -o $(NAME) $(OBJECTS)
clean:
$(RM) $(NAME) $(OBJECTS)[/sourcecode]
ae!
muito interessante, não trabalho com C (ainda), mas curto muito.
com certeza irei acompanhar o seu blog, já vai pro blogroll.
abraços. :)