Re: [AD] implementing seek

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


On Wed, Apr 7, 2010 at 5:31 PM, Peter Wang <novalazy@xxxxxxxxxx> wrote:
> I suppose there are three choices:
>
> 1. leave the position whereever it ends up
> 2. return to the start of the slice
> 3. skip past the slice
>
> Only (3) can be implemented when the stream is not backwards-seekable,
> and it seems to me like the most useful behaviour anyway.
>
I agree.

Attached is a simple implementation of it as a user program for
demonstration purposes.

Should a splice be able to work concurrently with a random-access
parent? Or can two splices be open at the same time? In order to do
so, it would have to implicitly seek before and after every
read/write.

An aside: Can you query a file interface to determine what it
supports? (Random access, write past end of file, etc)

My proposal is that some form of this is added to the core I/O
routines (not an addon). And subsequently, anybody who wants to treat
a sub part of a file as a single entity to a loader / saver function
must explicitly set up a splice.

~

The output of example loading a text file with the letters a-z:

First character in real file: a
splice is opened with length 3
Read 3 characters, but tried 10000: bcd
EOF? yes
EOF after fseek(0)? no
Next character: b
splice is closed
Next character in real file is: e
Splicing length 10
Splicing the splice with length 2
Read 2 characters, but tried 10000: fg
Inner splice is closed
Next character in first splice is: h
Outer splice is closed
Next character in real file is: p

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

typedef struct ALLEGRO_FILE_SPLICE ALLEGRO_FILE_SPLICE;
struct ALLEGRO_FILE_SPLICE {
   ALLEGRO_FILE file;   /* must be first */
   
   ALLEGRO_FILE *parent;

   bool eof;
   int64_t origin, size, pos;
};

static void splice_fclose(ALLEGRO_FILE *fp)
{
   ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
	 al_fseek(s->parent, s->origin + s->size, ALLEGRO_SEEK_SET);
   free(fp);
}

static size_t splice_fread(ALLEGRO_FILE *fp, void *ptr, size_t size)
{
	 ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
	 size_t n  = 0;
	 
	 if (!s->eof) { 
	 	  if (size + s->pos > s->size) {
	 	     size = s->size - s->pos;
	 	  }
	 
	 	  n = al_fread(s->parent, ptr, size);
	 	  s->pos += n;
	 	  if (s->pos >= s->size) {
	 	     s->eof = true;
	 	  }
   }
	 
   return n;
}

static size_t splice_fwrite(ALLEGRO_FILE *fp, const void *ptr, size_t size)
{
	 ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
	 size_t n = 0;
	 
	 if (!s->eof) { 
	 	  if (size + s->pos > s->size) {
	 	     size = s->size - s->pos;
	 	  }
	 
	 	  n = al_fwrite(s->parent, ptr, size);
	 	  s->pos += n;
	 	  if (s->pos >= s->size) {
	 	     s->eof = true;
	 	  }
   }
   
   return n;
}

static bool splice_fflush(ALLEGRO_FILE *fp)
{
	 ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;

   return al_fflush(s->parent);
}

static int64_t splice_ftell(ALLEGRO_FILE *fp)
{
	 ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
   return s->pos;
}

static bool splice_fseek(ALLEGRO_FILE *fp, int64_t offset,
   int whence)
{
	 ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
	 int64_t ppos = -1;
	 
	 if (whence == ALLEGRO_SEEK_SET) {
      ppos = s->origin + offset;
	 }
	 else if (whence == ALLEGRO_SEEK_CUR) {
      ppos = s->origin + s->pos + offset;
	 }
	 else if (whence == ALLEGRO_SEEK_END) {
	    ppos = s->origin + s->size + offset;
	 }
	 
	 if (ppos < s->origin || ppos > s->origin + s->size) {
	    return false;
	 }
	 
	 if (!al_fseek(s->parent, ppos, ALLEGRO_SEEK_SET)) {
	    return false;
	 }
	 
	 s->pos = ppos - s->origin;
	 s->eof = (s->pos >= s->size);
	 
   return true;
}

static bool splice_feof(ALLEGRO_FILE *fp)
{
	 ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
   return s->eof;
}

static bool splice_ferror(ALLEGRO_FILE *fp)
{
   ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
   return al_ferror(s->parent);
}

static int splice_fungetc(ALLEGRO_FILE *fp, int c)
{
	 ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
	 /* TODO ? */
   return c;
}

static off_t splice_fsize(ALLEGRO_FILE *fp)
{
   ALLEGRO_FILE_SPLICE *s = (ALLEGRO_FILE_SPLICE *)fp;
   return s->size;
}

static struct ALLEGRO_FILE_INTERFACE splice_vtable = {
   NULL,    /* open */
   splice_fclose,
   splice_fread,
   splice_fwrite,
   splice_fflush,
   splice_ftell,
   splice_fseek,
   splice_feof,
   splice_ferror,
   splice_fungetc,
   splice_fsize
};

ALLEGRO_FILE *al_fopen_splice(ALLEGRO_FILE *fh, size_t size)
{
   ALLEGRO_FILE_SPLICE *s = malloc(sizeof(ALLEGRO_FILE_SPLICE));
	
   memset(s, 0, sizeof(*s));	
	
   s->file.vtable = &splice_vtable;
   s->parent = fh;   
      
   s->pos = 0;
   s->origin = al_ftell(fh);   
   s->size = size;
			
	
	 return (ALLEGRO_FILE *)s;
}

int main()
{
	ALLEGRO_FILE *f1, *f2, *f3;
	char buffer[100];
	size_t n;
	al_init();
	
	f1 = al_fopen("foo.txt", "rb");  // abcdefghijklmnopqrstuvwxyz
	printf("First character in real file: %c\n", al_fgetc(f1));
	
	printf("splice is opened with length 3\n");
	f2 = al_fopen_splice(f1, 3);
	n = al_fread(f2, buffer, 10000);
	buffer[n] = 0;
	printf("Read %d characters, but tried 10000: %s\n", n, buffer);
	printf("EOF? %s\n", al_feof(f2) ? "yes" : "no");
	al_fseek(f2, 0, SEEK_SET);
  printf("EOF after fseek(0)? %s\n", al_feof(f2) ? "yes" : "no");
  printf("Next character: %c\n", al_fgetc(f1));
	al_fclose(f2);
	printf("splice is closed\n");
		
	printf("Next character in real file is: %c\n", al_fgetc(f1));
	
	f2 = al_fopen_splice(f1, 10);
	f3 = al_fopen_splice(f2, 2);	
	
	printf("Splicing length 10\n");
	printf("Splicing the splice with length 2\n");
	n = al_fread(f3, buffer, 10000);
	buffer[n] = 0;
	printf("Read %d characters, but tried 10000: %s\n", n, buffer);
	
	al_fclose(f3);
	printf("Inner splice is closed\n");
	
	printf("Next character in first splice is: %c\n", al_fgetc(f1));
	
	al_fclose(f2);
	
	printf("Outer splice is closed\n");
	
	printf("Next character in real file is: %c\n", al_fgetc(f1));
	
	al_fclose(f1);
	
	return 0;
}


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