Re: [AD] file slices

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


On Tue, Jul 19, 2011 at 7:09 PM, Peter Wang <novalazy@xxxxxxxxxx> wrote:
> That still sounds complicated.  I prefer your previous suggestion
> to remove any buffering from file slices.  Then file slices are
> analogous to sub-bitmaps, and memfiles are analogous to memory bitmaps.
>
Attached is an implementation of unbuffered file slices. To run the
included test, you need to make test.tga that contains texture.tga
twice:

cat texture.tga texture.tga > test.tga

When using slices both can be loaded successfully, but without them
the second try fails. (I think the TGA loader doesn't seek to the end
of file when done.)

It would introduce one function:

ALLEGRO_FILE *al_fopen_slice(ALLEGRO_FILE *fp, size_t initial_size,
const char *mode);

It opens the slice at the current location of the file with an initial
size as specified by the user. Modes can be any combination of
[r]eadable, [w]ritable, and [e]xpandable.

If the file is not expandable, then writes and reads will be limited
to that initial size.

If the file is expandable, then the size will increase as data is read
or written. In such case, the initial_size would affect what SEEK_END
initially does.

There is no "until EOF" size, as the programmer can just do that
himself by passing al_fsize(fp) - al_ftell(fp) as initial size.

When closing the file, the parent is always moved to the end of the
slice's current size.

The following limitations exist:

* the underlying file must be random access (if reading)
* the underlying file must be open in regular write mode (if writing)
as the slice assumes that writes happen in place at the current
position.
* the parent cannot be used while the slice is open
* only one slice can be open per parent at a time, but slicing a slice
should theoretically work.

And of course there are some other edge cases that are left undefined,
like specifying a size that is larger than the parent, etc. But the
programmer should be well aware of what he is doing, so I don't think
that matters.

--
Matthew Leverton
#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct SLICE_DATA SLICE_DATA;

enum {
	SLICE_READ = 1,
	SLICE_WRITE = 2,
	SLICE_EXPANDABLE = 4
};

struct SLICE_DATA
{
	ALLEGRO_FILE *fp; /* parent file handle */
	size_t anchor;    /* beginning position relative to parent */
	size_t pos;       /* position relative to anchor */
	size_t size;      /* sice of slice relative to anchor */
	int mode;
};

static void slice_fclose(ALLEGRO_FILE *f)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	
	/* seek to end of slice */
	al_fseek(slice->fp, slice->anchor + slice->size, ALLEGRO_SEEK_SET);
	
	al_free(slice);
}

static size_t slice_fread(ALLEGRO_FILE *f, void *ptr, size_t size)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	
	if (!(slice->mode & SLICE_READ)) {
		/* no read permissions */
		return 0;
	}
	
	if (!(slice->mode & SLICE_EXPANDABLE) && slice->pos + size > slice->size) {
		/* don't read past the buffer size if not expandable */
		size = slice->size - slice->pos;
	}
	
	if (!size) {
		return 0;
	}
	else {
		/* unbuffered, read directly from parent file */
		size_t b = al_fread(slice->fp, ptr, size);
		slice->pos += b;
	
		if (slice->pos > slice->size)
			slice->size = slice->pos;
		
		return b;
	}
}

static size_t slice_fwrite(ALLEGRO_FILE *f, const void *ptr, size_t size)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	
	if (!(slice->mode & SLICE_WRITE)) {
		/* no write permissions */
		return 0;
	}
	
	if (!(slice->mode & SLICE_EXPANDABLE) && slice->pos + size > slice->size) {
		/* don't write past the buffer size if not expandable */
		size = slice->size - slice->pos;
	}
	
	if (!size) {
		return 0;
	}
	else {
		/* unbuffered, write directly to parent file */
		size_t b = al_fwrite(slice->fp, ptr, size);
		slice->pos += b;
	
		if (slice->pos > slice->size)
			slice->size = slice->pos;
		
		return b;
	}
}

static bool slice_fflush(ALLEGRO_FILE *f)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	
	return al_fflush(slice->fp);
}

static int64_t slice_ftell(ALLEGRO_FILE *f)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	return slice->pos;
}

static bool slice_fseek(ALLEGRO_FILE *f, int64_t offset, int whence)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	
	if (whence == ALLEGRO_SEEK_SET) {
		offset = slice->anchor + offset;
	}
	else if (whence == ALLEGRO_SEEK_CUR) {
		offset = slice->anchor + slice->pos + offset;
	}
	else if (whence == ALLEGRO_SEEK_END) {
		offset = slice->anchor + slice->size + offset;
	}
	else {
		return false;
	}
	
	if ((size_t) offset < slice->anchor) {
		offset = slice->anchor;
	}
	else if ((size_t) offset > slice->anchor + slice->size) {
		offset = slice->anchor + slice->size;
	}
	
	if (al_fseek(slice->fp, offset, ALLEGRO_SEEK_SET)) {
		slice->pos = offset - slice->anchor;
		return true;
	}
	
	return false;
}

static bool slice_feof(ALLEGRO_FILE *f)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	return slice->pos >= slice->size;
}

static bool slice_ferror(ALLEGRO_FILE *f)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	return al_ferror(slice->fp);
}

static void slice_fclearerr(ALLEGRO_FILE *f)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	al_fclearerr(slice->fp);
}

static off_t slice_fsize(ALLEGRO_FILE *f)
{
	SLICE_DATA *slice = al_get_file_userdata(f);
	return slice->size;
}

static ALLEGRO_FILE_INTERFACE fi =
{
	NULL,
	slice_fclose,
	slice_fread,
	slice_fwrite,
	slice_fflush,
	slice_ftell,
	slice_fseek,
	slice_feof,
	slice_ferror,
	slice_fclearerr,
	NULL,
	slice_fsize
};

ALLEGRO_FILE *al_fopen_slice(ALLEGRO_FILE *fp, size_t initial_size, const char *mode)
{
	SLICE_DATA *userdata = al_malloc(sizeof(*userdata));
	
	if (!userdata) {
		return NULL;
	}
	
	memset(userdata, 0, sizeof(*userdata));
	
	if (strstr(mode, "r") || strstr(mode, "R")) {
		userdata->mode |= SLICE_READ;
	}
	
	if (strstr(mode, "w") || strstr(mode, "W")) {
		userdata->mode |= SLICE_WRITE;
	}
	
	if (strstr(mode, "e") || strstr(mode, "E")) {
		userdata->mode |= SLICE_EXPANDABLE;
	}
	
	userdata->fp = fp;
	userdata->anchor = al_ftell(fp);
	userdata->size = initial_size;
	
	return al_create_file_handle(&fi, userdata);
}

int main(void)
{
	ALLEGRO_FILE *fp, *slice;
	ALLEGRO_BITMAP *bmp;
	
	al_init();
	al_init_image_addon();
	
	al_create_display(640, 480);
	
	/* load two tga bitmaps stored back-to-back */
	fp = al_fopen("test.tga", "r");
	
	slice = al_fopen_slice(fp, 6383, "r");
	bmp = al_load_bitmap_f(slice, ".tga");
	al_fclose(slice);
	
	if (bmp)
		al_draw_bitmap(bmp, 0, 0, 0);
	
	slice = al_fopen_slice(fp, 6383, "r");
	bmp = al_load_bitmap_f(slice, ".tga");
	al_fclose(slice);
	
	if (bmp)
		al_draw_bitmap(bmp, 320, 0, 0);
	al_fclose(fp);
	
	al_flip_display();
	
	al_rest(1);
	
	return 0;
}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/