/*
 *	Software virtual memory system
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */

/* Page header */

struct vpage {
	VPAGE	*next;		/* Next page with same hash value */
	VFILE	*vfile;		/* Owner vfile */
	off_t	addr;		/* Address of this page */
	int	count;		/* Reference count */
	int	dirty;		/* Set if page changed */
	char	*data;		/* The data in the page */
};

/* File structure */

struct vfile {
	LINK(VFILE)	link;	/* Doubly linked list of vfiles */
	off_t	size;		/* Number of bytes in physical file */
	off_t	alloc;		/* Number of bytes allocated to file */
	int	fd;		/* Physical file */
	int	writeable;	/* Set if we can write */
	char	*name;		/* File name.  0 if unnamed */
	int	flags;		/* Set if this is only a temporary file */

	/* For array I/O */
	char	*vpage1;	/* Page address */
	off_t	addr;		/* File address of above page */

	/* For stream I/O */
	char	*bufp;		/* Buffer pointer */
	char	*vpage;		/* Buffer pointer points in here */
	ptrdiff_t	left;		/* Space left in bufp */
	ptrdiff_t	lv;		/* Amount of append space at end of buffer */
};
/* Additions:
 *
 * Should we remove size checking from rc()?  Would make it faster...
 *
 * Should be able to open more than one stream on a file so that vseek
 * doesn't have to get called so much when more than one user is involved
 *
 * Also should have dupopen call to make more streams for a file
 *
 * Make vputs faster
 *
 * Should have a version which will use memory mapped files, if they exist
 * in the os.
 *
 * Would be nice if we could transparently open non-file streams and pipes.
 * Should there be an buffering option for that?  So we can seek on pipes to
 * get previously read data?
 */

extern char *vbase;		/* Data first entry in vheader refers to */
extern VPAGE **vheaders;	/* Array of headers */

/* VFILE *vtmp(V);
 *
 * Open a temporary virtual file.  File goes away when closed.  No actual
 * file is generated if everything fits in memory.
 */
VFILE *vtmp(void);

#ifdef junk
/* VFILE *vopen(char *name);
 *
 * Open a file for reading and if possible, writing.  If the file could not
 * be opened, NULL is returned.
 */
VFILE *vopen();
#endif

/* off_t vsize(VFILE *);
 *
 * Return size of file
 */

#define vsize(vfile) \
	( \
	  (vfile)->left<(vfile)->lv ? \
	    (vfile)->alloc+(vfile)->lv-(vfile)->left \
	  : \
	    (vfile)->alloc \
	)

/* void vclose(VFILE *vfile);
 *
 * Close a file.
 */
void vclose(VFILE *vfile);

#ifdef junk
/* void vlimit(long amount);
 *
 * Set limit (in bytes) on amount of memory the virtual file system may
 * use.  This limit can be exceeded if all existing vpages are being referenced
 * and a new vpage is requested.
 *
 * When vlimit is called, the limit is immediately enforced by eliminating
 * non-referenced vpages.
 */

void vlimit();
#endif

/* void vflsh(void); 
 *
 * Write all changed pages to the disk
 */

void vflsh(void);

/* void vflshf(VFILE *vfile);
 *
 * Write changed pages for a specific file to the disk
 */

void vflshf(VFILE *vfile);

/* char *vlock(VFILE *vfile,off_t addr);
 *
 * Translate virtual address to physical address.  'addr' does not have
 * to be on any particular alignment, but if you wish to access more than
 * a single byte, you have to be aware of where page boundaries are (virtual
 * address multiples of PGSIZE).
 *
 * The page containing the data is locked in memory (so that it won't be
 * freed or used for something else) until 'vunlock' is used.
 *
 * Warning:  If you allocate more than one page and use (change) them out of
 * order, vflsh will screw up if writing past the end of a file is illegal
 * in the host filesystem.
 *
 * Also:  This function does not allocate space to the file.  Use valloc()
 * for that.  You can vlock() pages past the allocated size of the file, but
 * be careful when you do this (you normally shouldn't- the only time you
 * ever might want to is to implement your own version of valloc()).
 */

char *vlock(VFILE *vfile, off_t addr);

/* VPAGE *vheader(char *);
 * Return address of page header for given page
 */

#define vheader(p) (vheaders[(physical((char *)(p))-physical(vbase))>>LPGSIZE])

/* void vchanged(char *);
 *
 * Indicate that a vpage was changed so that it will be written back to the
 * file.  Any physical address which falls within the page may be given.
 */

#define vchanged(vpage) ( vheader(vpage)->dirty=1 )

/* void vunlock(char *);
 * Unreference a vpage (call one vunlock for every vlock)
 * Any physical address which falls within the page may be given.
 */

#define vunlock(vpage)  ( --vheader(vpage)->count )

/* void vupcount(char *);
 * Indicate that another reference is being made to a vpage
 */

#define vupcount(vpage) ( ++vheader(vpage)->count )

/* long valloc(VFILE *vfile,long size);
 *
 * Allocate space at end of file
 *
 * Returns file address of beginning of allocated space
 */

off_t my_valloc(VFILE *vfile, off_t size);

#ifdef junk
/******************************************************************************
 * The following functions implement stream I/O on top of the above software   *
 * virtual memory system                                                      *
 ******************************************************************************/

/* void vseek(VFILE *vfile,long addr);
 *
 * Seek to a file address.  Allocates space to the file if you seek past the
 * end.
 */
void vseek();

/* int vrgetc(VFILE *);
 * int vgetc(VFILE *);
 *
 * Get next character / Get previous character functions.
 * They return NO_MORE_DATA for end of file / beginning of file.
 */

int _vgetc();
int _vrgetc();

#define vrgetc(v) \
        ( (v)->left!=PGSIZE ? ( ++(v)->left, (int)(unsigned char)*(--(v)->bufp) ) : _vrgetc(v) )

#define vgetc(v) \
	( (v)->left>(v)->lv ? ( --(v)->left, (int)(unsigned char)*((v)->bufp++) ) : _vgetc(v) )

/* int vputc(VFILE *,I);
 *
 * Put character.  Returns character which is written.
 */

int _vputc();

#define vputc(v,c) \
	( \
	  (v)->left ? \
	   ( \
	   --(v)->left, \
	   vchanged((v)->vpage), \
 	   (int)(unsigned char)(*((v)->bufp++)=(c)) \
	   ) \
	  : \
	   _vputc((v),(c)) \
	)

/* long vtell(VFILE *);
 *
 * Return current file position
 */

#define vtell(v) \
	( \
	 (v)->vpage ? \
	  ( vheader((v)->vpage)->addr+(v)->bufp-(v)->vpage ) \
	 : \
	  0L \
	)

/* long vgetl(VFILE *);
 *
 * Get long.  No alignment requirements.  Returns -1 if goes past end of file.
 */

long vgetl();

/* short vgetw(VFILE *);
 *
 * Get short.  No alignment requirements.  Returns -1 if goes past end of file.
 */

short vgetw();

/* long vputl(VFILE *,long);
 *
 * Put long.  No alignment requirements.
 * Returns value written.
 */

long vputl();

/* short vputw(VFILE *,short);
 *
 * Put long.  No alignment requirements.
 * Returns value written.
 */

short vputw();

/* char *vgets(VFILE *v,char *s);
 *
 * Read up to next '\n' or end of file into a variable length string.  If 's'
 * is 0, a new string is created.  The \n is not copied into the string.
 *
 * Eliminates the variable length string and returns NULL if
 * vgets is called on the end of the file.
 *
 * This requires that you use the 'vs.h' / 'vs.c' library.
 */

char *vgets();

/* void vputs(VFILE *v,char *s);
 *
 * Write zero terminated string. \n is not appended */

void vputs();

/* void vread(VFILE *,char *,int size);
 *
 * Read bytes from a virtual file into a local data block
 */

void vread();

/* void vwrite(VFILE *,char *,int size);
 *
 * Write bytes from a local data block into a virtual file
 */

void vwrite();

/*************************************************************************** 
 * The following functions implement array I/O on top of the above virtual *
 * memory system (cheap memory mapped files)                               *
 ***************************************************************************/

/* int rc(VFILE *vfile,long addr);
 *
 * Read character.  Returns NO_MORE_DATA if past end of file.
 */

int _rc();

#define rc(v,a) \
	( \
	  (a)>=vsize(v) ? NO_MORE_DATA : \
	    ( \
	      (v)->addr==((a)&~(PGSIZE-1)) ? \
	       (v)->vpage1[(a)&(PGSIZE-1)] \
	      : \
	       _rc((v),(a)) \
	    ) \
	)

/* int wc(VFILE *vfile,long addr,char c);
 *
 * Write character.  Return character written.  This automatically allocates
 * space to the file.
 */

int _wc();

#define wc(v,a,c) \
	( \
	  (v)->addr==((a)&~(PGSIZE-1)) ? \
	   ( \
	   vheader((v)->vpage1)->dirty=1, \
	   ((a)+1>vsize(v) && my_valloc(v,(a)+1-vsize(v))), \
	   (v)->vpage1[(a)&(PGSIZE-1)]=(c) \
	   ) \
	  : \
	   _wc((v),(a),(c)) \
	)

/* long rl(VFILE *vfile,long addr);
 * Read big-endian long.  No alignment requirements.  Returns -1 if goes past
 * end of file.
 */

long rl();

/* long wl(VFILE *vfile,long addr,long c);
 * Write big-endian long.  No alignment requirements.  Automatically expands
 * file if necessary.
 */

long wl();

/* short rw(VFILE *vfile,long addr);
 * Read big-endian short.  No alignment requirements.  Returns -1 if goes past
 * end of file.
 */

short rw();

/* short ww(VFILE *vfile,long addr,short c);
 * Write big-endian short.  No alignment requirements.  Automatically expands
 * file if necessary.
 */

short ww();

#endif /* junk */
