1
0
forked from 0ad/0ad

more dox!

also found problem with partial reads from zip; not important since all
files are currently loaded in one go, but will attack tomorrow.

This was SVN commit r1065.
This commit is contained in:
janwas 2004-08-27 00:33:20 +00:00
parent d607aad4db
commit 1aae7dcead
6 changed files with 244 additions and 165 deletions

View File

@ -335,8 +335,11 @@ int file_enum(const char* const dir, const FileCB cb, const uintptr_t user)
}
// get file status. output param is zeroed on error.
int file_stat(const char* const path, struct stat* const s)
{
memset(s, 0, sizeof(struct stat));
char n_path[PATH_MAX+1];
CHECK_ERR(convert_path(n_path, path));
@ -799,24 +802,6 @@ skip_issue:
return issue_size;
}
// transfer modes:
// *p != 0: *p is the source/destination address for the transfer.
// (FILE_MEM_READONLY?)
// *p == 0: allocate a buffer, read into it, and return it in *p.
// when no longer needed, it must be freed via file_free_buf.
// p == 0: read raw_size bytes from file, starting at offset raw_ofs,
// into temp buffers; each block read is passed to cb, which is
// expected to write actual_size bytes total to its output buffer
// (for which it is responsible).
// useful for reading compressed data.
//
// return (positive) number of raw bytes transferred if successful;
// otherwise, an error code.
// the underlying aio implementation likes buffer and offset to be
// sector-aligned; if not, the transfer goes through an align buffer,
// and requires an extra memcpy.
@ -833,8 +818,17 @@ skip_issue:
// transfer <size> bytes, starting at <ofs>, to/from the given file.
// (read or write access was chosen at file-open time).
//
// if non-NULL, <cb> is called for each block transferred, passing <ctx>.
// it returns how much data was actually transferred, or a negative error
// code (in which case we abort the transfer and return that value).
// the callback mechanism is useful for user progress notification or
// processing data while waiting for the next I/O to complete
// (quasi-parallel, without the complexity of threads).
//
// return number of bytes transferred (see above), or a negative error code.
ssize_t file_io(File* const f, const off_t data_ofs, size_t data_size, void* const data_buf,
const FileIOCB cb, const uintptr_t ctx) // optional
{

View File

@ -91,16 +91,61 @@ typedef int(*FileCB)(const char* const name, const ssize_t size, const uintptr_t
// call <cb>, passing <user> and the entries's name (not path!)
extern int file_enum(const char* dir, FileCB cb, uintptr_t user);
// get file status. output param is zeroed on error.
extern int file_stat(const char* path, struct stat*);
extern int file_open(const char* fn, uint flags, File* f);
extern int file_close(File* f);
//
// asynchronous IO
//
typedef void* FileIO;
extern int file_start_io(File* f, off_t ofs, size_t size, void* buf, FileIO* io);
// indicates if the given IO has completed.
// return value: 0 if pending, 1 if complete, < 0 on error.
extern int file_io_complete(FileIO io);
extern int file_wait_io(FileIO io, void*& p, size_t& size);
extern int file_discard_io(FileIO io);
//
// synchronous IO
//
// return value:
// < 0: failed; abort transfer.
// >= 0: bytes output; continue.
typedef ssize_t(*FileIOCB)(uintptr_t ctx, void* p, size_t size);
// transfer <size> bytes, starting at <ofs>, to/from the given file.
// (read or write access was chosen at file-open time).
//
// if non-NULL, <cb> is called for each block transferred, passing <ctx>.
// it returns how much data was actually transferred, or a negative error
// code (in which case we abort the transfer and return that value).
// the callback mechanism is useful for user progress notification or
// processing data while waiting for the next I/O to complete
// (quasi-parallel, without the complexity of threads).
//
// return number of bytes transferred (see above), or a negative error code.
extern ssize_t file_io(File* f, off_t ofs, size_t size, void* buf, FileIOCB cb = 0, uintptr_t ctx = 0);
//
// memory mapping
//
// useful for files that are too large to be loaded into memory,
// or if only (non-sequential) portions of a file are needed at a time.
// map the entire file <f> into memory. if already currently mapped,
// return the previous mapping (reference-counted).
// output parameters are zeroed on failure.
@ -122,31 +167,4 @@ extern int file_map(File* f, void*& p, size_t& size);
// may be removed when no longer needed.
extern int file_unmap(File* f);
//
// async IO
//
typedef void* FileIO;
extern int file_start_io(File* f, off_t ofs, size_t size, void* buf, FileIO* io);
// indicates if the given IO has completed.
// return value: 0 if pending, 1 if complete, < 0 on error.
extern int file_io_complete(FileIO io);
extern int file_wait_io(FileIO io, void*& p, size_t& size);
extern int file_discard_io(FileIO io);
// return value:
// < 0: failed; abort transfer.
// >= 0: bytes output; continue.
typedef ssize_t(*FileIOCB)(uintptr_t ctx, void* p, size_t size);
extern ssize_t file_io(File* f, off_t ofs, size_t size, void* buf, FileIOCB cb = 0, uintptr_t ctx = 0);
#endif // #ifndef FILE_H

View File

@ -877,7 +877,7 @@ int vfs_mount(const char* const v_mount_point, const char* const p_dir, const ui
// rebuild the VFS, i.e. re-mount everything. open files are not affected.
// necessary after loose files or directories change, so that the VFS
// "notices" the changes and updates file locations. res calls this after
// FAM reports changes; can also be called from the console after a
// dir_watch reports changes; can also be called from the console after a
// rebuild command. there is no provision for updating single VFS dirs -
// it's not worth the trouble.
int vfs_rebuild()
@ -1145,8 +1145,7 @@ bool vfs_exists(const char* v_fn)
}
// return information about the specified file as in stat(2),
// most notably size. stat buffer is undefined on error.
// get file status (currently only size). output param is zeroed on error.
int vfs_stat(const char* v_fn, struct stat* s)
{
const Loc* loc;
@ -1328,10 +1327,26 @@ debug_out("vfs_close %I64x\n", h);
}
// try to transfer the next <size> bytes to/from the given file.
// transfer the next <size> bytes to/from the given file.
// (read or write access was chosen at file-open time).
// return bytes of actual data transferred, or a negative error code.
// TODO: buffer types
//
// if non-NULL, <cb> is called for each block transferred, passing <ctx>.
// it returns how much data was actually transferred, or a negative error
// code (in which case we abort the transfer and return that value).
// the callback mechanism is useful for user progress notification or
// processing data while waiting for the next I/O to complete
// (quasi-parallel, without the complexity of threads).
//
// p (value-return) indicates the buffer mode:
// - *p == 0: read into buffer we allocate; set *p.
// caller should mem_free it when no longer needed.
// - *p != 0: read into or write into the buffer *p.
// - p == 0: only read into temp buffers. useful if the callback
// is responsible for processing/copying the transferred blocks.
// since only temp buffers can be added to the cache,
// this is the preferred read method.
//
// return number of bytes transferred (see above), or a negative error code.
ssize_t vfs_io(const Handle hf, const size_t size, void** p, FileIOCB cb, uintptr_t ctx)
{
#ifdef PARANOIA
@ -1378,6 +1393,8 @@ void dump()
// load the entire file <fn> into memory; return a handle to the memory
// and the buffer address/size. output parameters are zeroed on failure.
// in addition to the regular file cache, the entire buffer is kept in memory
// if flags & FILE_CACHE.
Handle vfs_load(const char* const v_fn, void*& p, size_t& size, uint flags /* default 0 */)
{
ONCE(atexit(dump));
@ -1479,52 +1496,6 @@ int vfs_store(const char* const v_fn, void* p, const size_t size, uint flags /*
}
///////////////////////////////////////////////////////////////////////////////
//
// memory mapping
//
///////////////////////////////////////////////////////////////////////////////
// map the entire file <hf> into memory. if already currently mapped,
// return the previous mapping (reference-counted).
// output parameters are zeroed on failure.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
int vfs_map(const Handle hf, const uint flags, void*& p, size_t& size)
{
UNUSED(flags);
p = 0;
size = 0;
// need to zero these here in case H_DEREF fails
H_DEREF(hf, VFile, vf);
if(vf_flags(vf) & VF_ZIP)
return zip_map(&vf->zf, p, size);
else
return file_map(&vf->f, p, size);
}
// decrement the reference count for the mapping belonging to file <f>.
// fail if there are no references; remove the mapping if the count reaches 0.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
int vfs_unmap(const Handle hf)
{
H_DEREF(hf, VFile, vf);
if(vf_flags(vf) & VF_ZIP)
return zip_unmap(&vf->zf);
else
return file_unmap(&vf->f);
}
///////////////////////////////////////////////////////////////////////////////
//
// asynchronous I/O
@ -1602,3 +1573,49 @@ inline int vfs_discard_io(Handle& hio)
{
return h_free(hio, H_IO);
}
///////////////////////////////////////////////////////////////////////////////
//
// memory mapping
//
///////////////////////////////////////////////////////////////////////////////
// map the entire (uncompressed!) file <hf> into memory. if currently
// already mapped, return the previous mapping (reference-counted).
// output parameters are zeroed on failure.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
int vfs_map(const Handle hf, const uint flags, void*& p, size_t& size)
{
UNUSED(flags);
p = 0;
size = 0;
// need to zero these here in case H_DEREF fails
H_DEREF(hf, VFile, vf);
if(vf_flags(vf) & VF_ZIP)
return zip_map(&vf->zf, p, size);
else
return file_map(&vf->f, p, size);
}
// decrement the reference count for the mapping belonging to file <f>.
// fail if there are no references; remove the mapping if the count reaches 0.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
int vfs_unmap(const Handle hf)
{
H_DEREF(hf, VFile, vf);
if(vf_flags(vf) & VF_ZIP)
return zip_unmap(&vf->zf);
else
return file_unmap(&vf->f);
}

View File

@ -34,11 +34,13 @@
// large fixed-size user buffers should be. length includes trailing '\0'.
#define VFS_MAX_PATH 256
// VFS paths are of the form:
// "[dir/{subdir/}]file" or "[dir/{subdir/}]dir[/]".
// in English: '/' as path separator; trailing '/' allowed for dir names;
// no leading '/', since "" is the root dir.
// mount either a single archive or a directory into the VFS at
// <vfs_mount_point>, which is created if it does not yet exist.
// new files override the previous VFS contents if pri(ority) is not lower.
@ -50,7 +52,7 @@ extern int vfs_mount(const char* vfs_mount_point, const char* name, uint pri);
// rebuild the VFS, i.e. re-mount everything. open files are not affected.
// necessary after loose files or directories change, so that the VFS
// "notices" the changes and updates file locations. res calls this after
// FAM reports changes; can also be called from the console after a
// dir_watch reports changes; can also be called from the console after a
// rebuild command. there is no provision for updating single VFS dirs -
// it's not worth the trouble.
extern int vfs_rebuild();
@ -106,8 +108,7 @@ extern int vfs_realpath(const char* fn, char* realpath);
// useful because a "file not found" warning is not raised, unlike vfs_stat.
extern bool vfs_exists(const char* fn);
// return information about the specified file as in stat(2),
// most notably size. stat buffer is undefined on error.
// get file status (currently only size). output param is zeroed on error.
extern int vfs_stat(const char* fn, struct stat*);
// open the file for synchronous or asynchronous IO. write access is
@ -119,32 +120,13 @@ extern Handle vfs_open(const char* fn, uint flags = 0);
extern int vfs_close(Handle& h);
//
// memory mapping
//
// map the entire file <hf> into memory. if already currently mapped,
// return the previous mapping (reference-counted).
// output parameters are zeroed on failure.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
extern int vfs_map(Handle hf, uint flags, void*& p, size_t& size);
// decrement the reference count for the mapping belonging to file <f>.
// fail if there are no references; remove the mapping if the count reaches 0.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
extern int vfs_unmap(Handle hf);
//
// asynchronous I/O
//
// low-level file routines - no caching or alignment.
//
// begin transferring <size> bytes, starting at <ofs>. get result
// with vfs_wait_read; when no longer needed, free via vfs_discard_io.
extern Handle vfs_start_io(Handle hf, off_t ofs, size_t size, void* buf);
@ -161,17 +143,67 @@ extern int vfs_discard_io(Handle& hio);
// synchronous I/O
//
// try to transfer the next <size> bytes to/from the given file.
// transfer the next <size> bytes to/from the given file.
// (read or write access was chosen at file-open time).
// return bytes of actual data transferred, or a negative error code.
// TODO: buffer types
//
// if non-NULL, <cb> is called for each block transferred, passing <ctx>.
// it returns how much data was actually transferred, or a negative error
// code (in which case we abort the transfer and return that value).
// the callback mechanism is useful for user progress notification or
// processing data while waiting for the next I/O to complete
// (quasi-parallel, without the complexity of threads).
//
// p (value-return) indicates the buffer mode:
// - *p == 0: read into buffer we allocate; set *p.
// caller should mem_free it when no longer needed.
// - *p != 0: read into or write into the buffer *p.
// - p == 0: only read into temp buffers. useful if the callback
// is responsible for processing/copying the transferred blocks.
// since only temp buffers can be added to the cache,
// this is the preferred read method.
//
// return number of bytes transferred (see above), or a negative error code.
extern ssize_t vfs_io(Handle hf, size_t size, void** p, FileIOCB cb = 0, uintptr_t ctx = 0);
// convenience functions that replace vfs_open / vfs_io / vfs_close:
// load the entire file <fn> into memory; return a memory handle to the
// buffer and its address/size. output parameters are zeroed on failure.
// in addition to the regular file cache, the entire buffer is kept in memory
// if flags & FILE_CACHE.
extern Handle vfs_load(const char* fn, void*& p, size_t& size, uint flags = 0);
extern int vfs_store(const char* fn, void* p, size_t size, uint flags = 0);
//
// memory mapping
//
// useful for files that are too large to be loaded into memory,
// or if only (non-sequential) portions of a file are needed at a time.
//
// this is of course only possible for uncompressed files - compressed files
// would have to be inflated sequentially, which defeats the point of mapping.
// map the entire (uncompressed!) file <hf> into memory. if currently
// already mapped, return the previous mapping (reference-counted).
// output parameters are zeroed on failure.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
extern int vfs_map(Handle hf, uint flags, void*& p, size_t& size);
// decrement the reference count for the mapping belonging to file <f>.
// fail if there are no references; remove the mapping if the count reaches 0.
//
// the mapping will be removed (if still open) when its file is closed.
// however, map/unmap calls should still be paired so that the mapping
// may be removed when no longer needed.
extern int vfs_unmap(Handle hf);
#endif // #ifndef __VFS_H__

View File

@ -915,8 +915,7 @@ static inline bool zfile_compressed(ZFile* zf)
// get file status (currently only size).
// return < 0 on error (output param zeroed).
// get file status (currently only size). output param is zeroed on error.
int zip_stat(Handle ha, const char* fn, struct stat* s)
{
// zero output param in case we fail below.
@ -1064,48 +1063,54 @@ static ssize_t io_cb(uintptr_t ctx, void* buf, size_t size)
{
IOCBParams* p = (IOCBParams*)ctx;
ssize_t ret = inf_inflate(p->inf_ctx, p->compressed, buf, size);
ssize_t ucsize = inf_inflate(p->inf_ctx, p->compressed, buf, size);
if(p->user_cb)
return p->user_cb(p->user_ctx, buf, size);
{
ssize_t user_ret = p->user_cb(p->user_ctx, buf, size);
// only pass on error codes - we need to return number of actual
// bytes inflated to file_io in the normal case.
if(user_ret < 0)
return user_ret;
}
return ret;
return ucsize;
}
#include "timer.h"
ssize_t zip_read(ZFile* zf, off_t raw_ofs, size_t size, void* p, FileIOCB cb, uintptr_t ctx)
// read from the (possibly compressed) file <zf> as if it were a normal file.
// starting at the beginning of the logical (decompressed) file,
// skip <ofs> bytes of data; read the next <size> bytes into <buf>.
//
// if non-NULL, <cb> is called for each block read, passing <ctx>.
// if it returns a negative error code,
// the read is aborted and that value is returned.
// the callback mechanism is useful for user progress notification or
// processing data while waiting for the next I/O to complete
// (quasi-parallel, without the complexity of threads).
//
// return bytes read, or a negative error code.
ssize_t zip_read(ZFile* zf, off_t ofs, size_t size, void* p, FileIOCB cb, uintptr_t ctx)
{
CHECK_ZFILE(zf);
const bool compressed = zfile_compressed(zf);
ssize_t raw_bytes_read;
ZArchive* za = H_USER_DATA(zf->ha, ZArchive);
if(!za)
return ERR_INVALID_HANDLE;
const off_t ofs = zf->ofs + raw_ofs;
ofs += zf->ofs;
// not compressed - just pass it on to file_io
// (avoid the Zip inflate start/finish stuff below)
// if(!compressed)
// return file_io(&za->f, ofs, size, p);
// return file_io(&za->f, ofs, csize, p);
// no need to set last_raw_ofs - only checked if compressed.
// compressed
// make sure we continue where we left off
// (compressed data must be read in one stream / sequence)
//
// problem: partial reads
if(raw_ofs != zf->last_raw_ofs)
{
debug_warn("zip_read: compressed read offset is non-continuous");
return -1;
}
CHECK_ERR(inf_set_dest(zf->inf_ctx, p, size));
/*
@ -1129,24 +1134,19 @@ ssize_t zip_read(ZFile* zf, off_t raw_ofs, size_t size, void* p, FileIOCB cb, ui
const IOCBParams params = { zf->inf_ctx, compressed, cb, ctx };
// read blocks from the archive's file starting at ofs and pass them to
// inf_inflate, until all compressed data has been read, or it indicates
// the desired output amount has been reached.
size_t raw_size = zf->csize;
// we had set csize to 0 to indicate the file isn't compressed.
// see zfile_compressed implementation.
if(!compressed)
raw_size = zf->ucsize;
// HACK: shouldn't read the whole thing into mem
size_t csize = zf->csize;
if(!csize)
csize = zf->ucsize; // HACK on HACK: csize = 0 if file not compressed
raw_bytes_read = file_io(&za->f, ofs, raw_size, (void**)0, io_cb, (uintptr_t)&params);
ssize_t uc_transferred = file_io(&za->f, ofs, csize, (void**)0, io_cb, (uintptr_t)&params);
zf->last_raw_ofs = raw_ofs + (off_t)raw_bytes_read;
zf->last_read_ofs += (off_t)csize;
CHECK_ERR(inf_finish(zf->inf_ctx));
return raw_bytes_read;
return uc_transferred;
}

View File

@ -60,16 +60,15 @@ struct ZFile
size_t ucsize;
// size of logical file
off_t ofs;
off_t ofs; // in archive
off_t csize;
off_t last_raw_ofs;
off_t last_read_ofs; // in compressed file
Handle ha;
uintptr_t inf_ctx;
};
// get file status (currently only size).
// return < 0 on error (output param zeroed).
// get file status (currently only size). output param is zeroed on error.
extern int zip_stat(Handle ha, const char* fn, struct stat* s);
// open file, and fill *zf with information about it.
@ -84,6 +83,7 @@ extern int zip_close(ZFile* zf);
// asynchronous read
//
//
// currently only supported for compressed files to keep things simple.
// see rationale in source.
@ -107,14 +107,32 @@ extern int zip_discard_io(FileIO io);
// synchronous read
//
// read from file <zf>, starting at offset <ofs> in the compressed data.
extern ssize_t zip_read(ZFile* zf, off_t ofs, size_t size, void* p, FileIOCB cb = 0, uintptr_t ctx = 0);
// read from the (possibly compressed) file <zf> as if it were a normal file.
// starting at the beginning of the logical (decompressed) file,
// skip <ofs> bytes of data; read the next <size> bytes into <buf>.
//
// if non-NULL, <cb> is called for each block read, passing <ctx>.
// if it returns a negative error code,
// the read is aborted and that value is returned.
// the callback mechanism is useful for user progress notification or
// processing data while waiting for the next I/O to complete
// (quasi-parallel, without the complexity of threads).
//
// return bytes read, or a negative error code.
extern ssize_t zip_read(ZFile* zf, off_t ofs, size_t size, void* buf, FileIOCB cb = 0, uintptr_t ctx = 0);
//
// memory mapping
//
// useful for files that are too large to be loaded into memory,
// or if only (non-sequential) portions of a file are needed at a time.
//
// this is of course only possible for uncompressed files - compressed files
// would have to be inflated sequentially, which defeats the point of mapping.
// map the entire file <zf> into memory. mapping compressed files
// isn't allowed, since the compression algorithm is unspecified.
// output parameters are zeroed on failure.