updated file mapping (was partially unimplemented)
This was SVN commit r371.
This commit is contained in:
parent
8b91bbc27a
commit
af3949ab7a
@ -188,12 +188,13 @@ struct DirEnt
|
||||
{
|
||||
const std::string name;
|
||||
const uint flags;
|
||||
const ssize_t size;
|
||||
const off_t size;
|
||||
|
||||
DirEnt(const char* const _name, const uint _flags, const ssize_t _size)
|
||||
DirEnt(const char* const _name, const uint _flags, const off_t _size)
|
||||
: name(_name), flags(_flags), size(_size) {}
|
||||
};
|
||||
|
||||
// pointer to DirEnt: faster sorting, but more allocs.
|
||||
typedef std::vector<const DirEnt*> DirEnts;
|
||||
typedef DirEnts::const_iterator DirEntIt;
|
||||
|
||||
@ -253,7 +254,7 @@ int file_enum(const char* const dir, const FileCB cb, const uintptr_t user)
|
||||
}
|
||||
|
||||
uint flags = 0;
|
||||
ssize_t size = s.st_size;
|
||||
off_t size = s.st_size;
|
||||
|
||||
// dir
|
||||
if(s.st_mode & S_IFDIR)
|
||||
@ -399,7 +400,7 @@ int file_open(const char* p_fn, uint flags, File* f)
|
||||
|
||||
{
|
||||
// don't stat if opening for writing - the file may not exist yet
|
||||
size_t size = 0;
|
||||
off_t size = 0;
|
||||
|
||||
int mode = O_RDONLY;
|
||||
if(flags & FILE_WRITE)
|
||||
@ -466,7 +467,7 @@ struct ll_cb
|
||||
};
|
||||
|
||||
|
||||
int ll_start_io(File* f, size_t ofs, size_t size, void* p, ll_cb* lcb)
|
||||
int ll_start_io(File* f, off_t ofs, size_t size, void* p, ll_cb* lcb)
|
||||
{
|
||||
CHECK_FILE(f)
|
||||
|
||||
@ -481,12 +482,14 @@ int ll_start_io(File* f, size_t ofs, size_t size, void* p, ll_cb* lcb)
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t bytes_left = f->size - ofs; // > 0
|
||||
off_t bytes_left = f->size - ofs; // > 0
|
||||
int op = (f->flags & FILE_WRITE)? LIO_WRITE : LIO_READ;
|
||||
|
||||
// don't read beyond EOF
|
||||
if(size > bytes_left) // avoid min() - it wants int
|
||||
size = bytes_left;
|
||||
// cut off at EOF.
|
||||
// avoid min() due to type conversion warnings.
|
||||
if((off_t)size > bytes_left)
|
||||
size = (size_t)bytes_left;
|
||||
// guaranteed to fit, since size was > bytes_left
|
||||
|
||||
aiocb* cb = &lcb->cb;
|
||||
|
||||
@ -495,7 +498,7 @@ int ll_start_io(File* f, size_t ofs, size_t size, void* p, ll_cb* lcb)
|
||||
cb->aio_buf = p;
|
||||
cb->aio_fildes = f->fd;
|
||||
cb->aio_offset = (off_t)ofs;
|
||||
cb->aio_nbytes = size;
|
||||
cb->aio_nbytes = (size_t)size;
|
||||
return lio_listio(LIO_NOWAIT, &cb, 1, (struct sigevent*)0);
|
||||
// this just issues the I/O - doesn't wait until complete.
|
||||
}
|
||||
@ -536,7 +539,7 @@ static Cache<void*> c;
|
||||
|
||||
// create an id for use with the Cache that uniquely identifies
|
||||
// the block from the file <fn_hash> containing <ofs>.
|
||||
static u64 block_make_id(const u32 fn_hash, const size_t ofs)
|
||||
static u64 block_make_id(const u32 fn_hash, const off_t ofs)
|
||||
{
|
||||
// id format: filename hash | block number
|
||||
// 63 32 31 0
|
||||
@ -684,7 +687,7 @@ struct IO
|
||||
// so we don't allocate a new cb every file_start_io.
|
||||
|
||||
void* user_p;
|
||||
size_t user_ofs;
|
||||
off_t user_ofs;
|
||||
size_t user_size;
|
||||
|
||||
int cached : 1;
|
||||
@ -700,8 +703,8 @@ H_TYPE_DEFINE(IO)
|
||||
|
||||
static void IO_init(IO* io, va_list args)
|
||||
{
|
||||
size_t size = round_up(sizeof(struct ll_cb), 16);
|
||||
io->cb = (ll_cb*)mem_alloc(size, 16, MEM_ZERO);
|
||||
const size_t cb_size = round_up(sizeof(struct ll_cb), 16);
|
||||
io->cb = (ll_cb*)mem_alloc(cb_size, 16, MEM_ZERO);
|
||||
}
|
||||
|
||||
static void IO_dtor(IO* io)
|
||||
@ -867,7 +870,7 @@ static Handle io_find(u64 block_id)
|
||||
// pads the request up to BLOCK_SIZE, and stores the original parameters in IO.
|
||||
// transfers of more than 1 block (including padding) are allowed, but do not
|
||||
// go through the cache. don't see any case where that's necessary, though.
|
||||
Handle file_start_io(File* f, size_t user_ofs, size_t user_size, void* user_p)
|
||||
Handle file_start_io(File* f, off_t user_ofs, size_t user_size, void* user_p)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -884,12 +887,13 @@ Handle file_start_io(File* f, size_t user_ofs, size_t user_size, void* user_p)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t bytes_left = f->size - user_ofs; // > 0
|
||||
const off_t bytes_left = f->size - user_ofs; // > 0
|
||||
int op = (f->flags & FILE_WRITE)? LIO_WRITE : LIO_READ;
|
||||
|
||||
// don't read beyond EOF
|
||||
if(user_size > bytes_left) // avoid min() - it wants int
|
||||
user_size = bytes_left;
|
||||
if((off_t)user_size > bytes_left) // avoid min() - it wants int
|
||||
user_size = (size_t)bytes_left;
|
||||
// guaranteed to fit in user_size, since user_size > bytes_left
|
||||
|
||||
|
||||
u64 block_id = block_make_id(f->fn_hash, user_ofs);
|
||||
@ -919,9 +923,9 @@ debug_out("file_start_io hio=%I64x ofs=%d size=%d\n", hio, user_ofs, user_size);
|
||||
// a zip archive may contain one last file in the block.
|
||||
// if not, no loss - the buffer will be LRU, and reused.
|
||||
|
||||
size_t ofs = user_ofs;
|
||||
off_t ofs = user_ofs;
|
||||
size_t padding = ofs % BLOCK_SIZE;
|
||||
ofs -= padding;
|
||||
ofs -= (off_t)padding;
|
||||
size_t size = round_up(padding + user_size, BLOCK_SIZE);
|
||||
|
||||
|
||||
@ -1060,7 +1064,7 @@ int file_discard_io(Handle& hio)
|
||||
//
|
||||
// return (positive) number of raw bytes transferred if successful;
|
||||
// otherwise, an error code.
|
||||
ssize_t file_io(File* const f, const size_t raw_ofs, size_t raw_size, void** const p,
|
||||
ssize_t file_io(File* const f, const off_t raw_ofs, size_t raw_size, void** const p,
|
||||
const FILE_IO_CB cb, const uintptr_t ctx) // optional
|
||||
{
|
||||
#ifdef PARANOIA
|
||||
@ -1078,9 +1082,14 @@ debug_out("file_io fd=%d size=%d ofs=%d\n", f->fd, raw_size, raw_ofs);
|
||||
// reading: make sure we don't go beyond EOF
|
||||
if(!is_write)
|
||||
{
|
||||
if(raw_ofs >= f->size)
|
||||
// cut off at EOF.
|
||||
// avoid min() due to type conversion warnings.
|
||||
off_t bytes_left = f->size - raw_ofs;
|
||||
if(bytes_left < 0)
|
||||
return ERR_EOF;
|
||||
raw_size = MIN(f->size - raw_ofs, raw_size);
|
||||
if((off_t)raw_size > bytes_left)
|
||||
raw_size = (size_t)bytes_left;
|
||||
// guaranteed to fit, since size was > bytes_left
|
||||
}
|
||||
// writing: make sure buffer is valid
|
||||
else
|
||||
@ -1098,7 +1107,7 @@ debug_out("file_io fd=%d size=%d ofs=%d\n", f->fd, raw_size, raw_ofs);
|
||||
// actual transfer start offset
|
||||
// not aligned! aio takes care of initial unalignment;
|
||||
// next read will be aligned, because we read up to the next block.
|
||||
const size_t start_ofs = raw_ofs;
|
||||
const off_t start_ofs = raw_ofs;
|
||||
|
||||
|
||||
void* buf = 0; // I/O source or sink; assume temp buffer
|
||||
@ -1187,7 +1196,7 @@ debug_out("file_io fd=%d size=%d ofs=%d\n", f->fd, raw_size, raw_ofs);
|
||||
{
|
||||
// calculate issue_size:
|
||||
// at most, transfer up to the next block boundary.
|
||||
size_t issue_ofs = start_ofs + issue_cnt;
|
||||
off_t issue_ofs = (off_t)(start_ofs + issue_cnt);
|
||||
const size_t left_in_block = BLOCK_SIZE - (issue_ofs % BLOCK_SIZE);
|
||||
const size_t total_left = raw_size - issue_cnt;
|
||||
size_t issue_size = MIN(left_in_block, total_left);
|
||||
|
@ -34,7 +34,7 @@ struct File
|
||||
// it is accessed by VFS and must be the same for both (union).
|
||||
// dirty, but necessary because VFile is pushing the HDATA size limit.
|
||||
int flags;
|
||||
size_t size;
|
||||
off_t size;
|
||||
|
||||
u32 fn_hash;
|
||||
|
||||
@ -91,7 +91,7 @@ extern int file_close(File* f);
|
||||
extern int file_map(File* f, void*& p, size_t& size);
|
||||
extern int file_unmap(File* f);
|
||||
|
||||
extern Handle file_start_io(File* f, size_t ofs, size_t size, void* buf);
|
||||
extern Handle file_start_io(File* f, off_t ofs, size_t size, void* buf);
|
||||
extern int file_wait_io(const Handle hio, void*& p, size_t& size);
|
||||
extern int file_discard_io(Handle& hio);
|
||||
|
||||
@ -103,7 +103,7 @@ extern int file_discard_io(Handle& hio);
|
||||
// > 0: bytes output (not used ATM; useful for statistics) - continue.
|
||||
typedef ssize_t(*FILE_IO_CB)(uintptr_t ctx, void* p, size_t size);
|
||||
|
||||
extern ssize_t file_io(File* f, size_t ofs, size_t size, void** p,
|
||||
extern ssize_t file_io(File* f, off_t ofs, size_t size, void** p,
|
||||
FILE_IO_CB cb = 0, uintptr_t ctx = 0);
|
||||
|
||||
|
||||
|
@ -94,31 +94,37 @@ struct H_VTbl
|
||||
|
||||
typedef H_VTbl* H_Type;
|
||||
|
||||
#define H_TYPE_DEFINE(t)\
|
||||
#define H_TYPE_DEFINE(type)\
|
||||
/* forward decls */\
|
||||
static void t##_init(t*, va_list);\
|
||||
static int t##_reload(t*, const char*);\
|
||||
static void t##_dtor(t*);\
|
||||
static H_VTbl V_##t =\
|
||||
static void type##_init(type*, va_list);\
|
||||
static int type##_reload(type*, const char*);\
|
||||
static void type##_dtor(type*);\
|
||||
static H_VTbl V_##type =\
|
||||
{\
|
||||
(void(*)(void*, va_list))t##_init,\
|
||||
(int(*)(void*, const char*))t##_reload,\
|
||||
(void(*)(void*))t##_dtor,\
|
||||
sizeof(t), /* control block size */\
|
||||
#t /* name */\
|
||||
(void(*)(void*, va_list))type##_init,\
|
||||
(int(*)(void*, const char*))type##_reload,\
|
||||
(void(*)(void*))type##_dtor,\
|
||||
sizeof(type), /* control block size */\
|
||||
#type /* name */\
|
||||
};\
|
||||
static H_Type H_##t = &V_##t;
|
||||
static H_Type H_##type = &V_##type;
|
||||
|
||||
// note: we cast to void* pointers so the functions can be declared to
|
||||
// take the control block pointers, instead of requiring a cast in each.
|
||||
// the forward decls ensure the function signatures are correct.
|
||||
|
||||
|
||||
// <type>* <var> = H_USER_DATA(<h_var>, <type>)
|
||||
#define H_USER_DATA(h, type) (type*)h_user_data(h, H_##type);
|
||||
// convenience macro for h_user_data:
|
||||
// casts its return value to the control block type.
|
||||
// use if H_DEREF's returning a negative error code isn't acceptable.
|
||||
#define H_USER_DATA(h, type) (type*)h_user_data(h, H_##type)
|
||||
|
||||
// even more convenient wrapper for h_user_data:
|
||||
// declares a pointer (<var>), assigns it H_USER_DATA, and has
|
||||
// the user's function return a negative error code on failure.
|
||||
#define H_DEREF(h, type, var)\
|
||||
type* const var = (type*)h_user_data(h, H_##type);\
|
||||
/* don't use STMT - var decl must be visible to "caller" */\
|
||||
type* const var = H_USER_DATA(h, type);\
|
||||
if(!var)\
|
||||
return ERR_INVALID_HANDLE;
|
||||
|
||||
@ -173,7 +179,9 @@ extern int h_free(Handle& h, H_Type type);
|
||||
// currently O(n).
|
||||
extern Handle h_find(H_Type type, uintptr_t key);
|
||||
|
||||
// return a pointer to handle data, or 0 on error
|
||||
// returns a void* pointer to the control block of the resource <h>,
|
||||
// or 0 on error (i.e. h is invalid or of the wrong type).
|
||||
// prefer using H_DEREF or H_USER_DATA.
|
||||
extern void* h_user_data(Handle h, H_Type type);
|
||||
|
||||
extern const char* h_filename(Handle h);
|
||||
|
@ -955,7 +955,7 @@ H_TYPE_DEFINE(VFile)
|
||||
// use the functions below to insulate against change a bit.
|
||||
|
||||
|
||||
static size_t& vf_size(VFile* vf)
|
||||
static off_t& vf_size(VFile* vf)
|
||||
{
|
||||
assert(offsetof(struct File, size) == offsetof(struct ZFile, ucsize));
|
||||
return vf->f.size;
|
||||
@ -1061,7 +1061,7 @@ debug_out("vfs_close %I64x\n", h);
|
||||
}
|
||||
|
||||
|
||||
ssize_t vfs_io(Handle hf, size_t ofs, size_t size, void*& p)
|
||||
ssize_t vfs_io(Handle hf, off_t ofs, size_t size, void*& p)
|
||||
{
|
||||
#ifdef PARANOIA
|
||||
debug_out("vfs_io ofs=%d size=%d\n", ofs, size);
|
||||
@ -1146,20 +1146,24 @@ int vfs_store(const char* fn, void* p, size_t size)
|
||||
|
||||
|
||||
|
||||
Handle vfs_map(const char* fn, uint flags, void*& p, size_t& size)
|
||||
int vfs_map(const Handle hf, uint flags, void*& p, size_t& size)
|
||||
{
|
||||
Handle hf = vfs_open(fn, flags);
|
||||
H_DEREF(hf, VFile, vf);
|
||||
CHECK_ERR(file_map(&vf->f, p, size));
|
||||
MEM_DTOR dtor = 0;
|
||||
uintptr_t ctx = 0;
|
||||
return mem_assign(p, size, 0, dtor, ctx);
|
||||
|
||||
if(vf_flags(vf) & VF_ZIP)
|
||||
CHECK_ERR(zip_map(&vf->zf, p, size));
|
||||
else
|
||||
CHECK_ERR(file_map(&vf->f, p, size));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vfs_unmap(Handle& hm)
|
||||
int vfs_unmap(Handle hf)
|
||||
{
|
||||
return -1;
|
||||
// return h_free(hm, H_MMap);
|
||||
H_DEREF(hf, VFile, vf);
|
||||
if(vf_flags(vf) & VF_ZIP)
|
||||
CHECK_ERR(zip_unmap(&vf->zf));
|
||||
else
|
||||
CHECK_ERR(file_unmap(&vf->f));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,8 @@ extern Handle vfs_load(const char* fn, void*& p, size_t& size);
|
||||
extern Handle vfs_open(const char* fn, uint flags = 0);
|
||||
extern int vfs_close(Handle& h);
|
||||
|
||||
extern Handle vfs_map(Handle hf, uint flags, void*& p, size_t& size);
|
||||
extern int vfs_map(Handle hf, uint flags, void*& p, size_t& size);
|
||||
extern int vfs_unmap(Handle hf);
|
||||
|
||||
|
||||
struct vfsDirEnt
|
||||
@ -70,11 +71,11 @@ extern int vfs_rebuild();
|
||||
// async read interface
|
||||
//
|
||||
|
||||
extern Handle vfs_start_read(const Handle hf, size_t ofs, size_t& advance, void* buf);
|
||||
extern Handle vfs_start_read(const Handle hf, off_t ofs, size_t& advance, void* buf);
|
||||
extern int vfs_wait_read(Handle hr, void*& p, size_t& size);
|
||||
extern int vfs_discard_read(Handle& hr);
|
||||
|
||||
extern ssize_t vfs_io(Handle hf, size_t ofs, size_t size, void*& p);
|
||||
extern ssize_t vfs_io(Handle hf, off_t ofs, size_t size, void*& p);
|
||||
|
||||
|
||||
// keep in sync with File flags!
|
||||
|
@ -46,9 +46,9 @@
|
||||
// convenience container for location / size of file in archive.
|
||||
struct ZFileLoc
|
||||
{
|
||||
size_t ofs;
|
||||
size_t csize; // = 0 if not compressed
|
||||
size_t ucsize;
|
||||
off_t ofs;
|
||||
off_t csize; // = 0 if not compressed
|
||||
off_t ucsize;
|
||||
|
||||
// why csize?
|
||||
// file I/O may be N-buffered, so it's good to know when the raw data
|
||||
@ -108,7 +108,7 @@ static int zip_find_ecdr(const void* const file, const size_t size, const u8*& e
|
||||
if(*(u32*)ecdr == *(u32*)&ecdr_id)
|
||||
goto found_ecdr;
|
||||
|
||||
// check next 4 bytes (non aligned!!)
|
||||
// check next 4 bytes (unaligned!!)
|
||||
ecdr++;
|
||||
bytes_left--;
|
||||
}
|
||||
@ -127,7 +127,7 @@ found_ecdr:
|
||||
|
||||
// make sure the LFH fields match those passed (from the CDFH).
|
||||
// only used in PARANOIA builds - costs time when opening archives.
|
||||
static int zip_verify_lfh(const void* const file, const size_t lfh_ofs, const size_t file_ofs)
|
||||
static int zip_verify_lfh(const void* const file, const off_t lfh_ofs, const off_t file_ofs)
|
||||
{
|
||||
const char lfh_id[] = "PK\3\4"; // signature
|
||||
const size_t LFH_SIZE = 30;
|
||||
@ -145,7 +145,7 @@ static int zip_verify_lfh(const void* const file, const size_t lfh_ofs, const si
|
||||
const u16 lfh_fn_len = read_le16(lfh+26);
|
||||
const u16 lfh_e_len = read_le16(lfh+28);
|
||||
|
||||
const size_t lfh_file_ofs = lfh_ofs + LFH_SIZE + lfh_fn_len + lfh_e_len;
|
||||
const off_t lfh_file_ofs = lfh_ofs + LFH_SIZE + lfh_fn_len + lfh_e_len;
|
||||
|
||||
if(file_ofs != lfh_file_ofs)
|
||||
{
|
||||
@ -196,9 +196,9 @@ static int zip_read_cdfh(const u8*& cdfh, const char*& fn, size_t& fn_len, ZFile
|
||||
fn = fn_;
|
||||
fn_len = fn_len_;
|
||||
|
||||
loc->ofs = lfh_ofs + LFH_SIZE + fn_len_ + e_len;
|
||||
loc->csize = csize_;
|
||||
loc->ucsize = ucsize_;
|
||||
loc->ofs = (off_t)(lfh_ofs + LFH_SIZE + fn_len_ + e_len);
|
||||
loc->csize = (off_t)csize_;
|
||||
loc->ucsize = (off_t)ucsize_;
|
||||
|
||||
// performance issue: want to avoid seeking between LFHs and central dir.
|
||||
// would be safer to calculate file offset from the LFH, since its
|
||||
@ -856,7 +856,7 @@ int zip_stat(Handle ha, const char* fn, struct stat* s)
|
||||
lookup_get_file_info(li, idx, fn2, &loc);
|
||||
// can't fail - returned valid index above
|
||||
|
||||
s->st_size = (off_t)loc.ucsize;
|
||||
s->st_size = loc.ucsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -874,7 +874,7 @@ static inline bool is_compressed(ZFile* zf)
|
||||
|
||||
// note: we go to a bit of trouble to make sure the buffer we allocated
|
||||
// (if p == 0) is freed when the read fails.
|
||||
ssize_t zip_read(ZFile* zf, size_t raw_ofs, size_t size, void*& p)
|
||||
ssize_t zip_read(ZFile* zf, off_t raw_ofs, size_t size, void*& p)
|
||||
{
|
||||
CHECK_ZFILE(zf)
|
||||
|
||||
@ -885,7 +885,7 @@ ssize_t zip_read(ZFile* zf, size_t raw_ofs, size_t size, void*& p)
|
||||
if(!za)
|
||||
return ERR_INVALID_HANDLE;
|
||||
|
||||
const size_t ofs = zf->ofs + raw_ofs;
|
||||
const off_t ofs = zf->ofs + raw_ofs;
|
||||
|
||||
// not compressed - just pass it on to file_io
|
||||
// (avoid the Zip inflate start/finish stuff below)
|
||||
@ -946,7 +946,7 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
int zip_map(ZFile* zf, void*& p, size_t& size)
|
||||
int zip_map(ZFile* const zf, void*& p, size_t& size)
|
||||
{
|
||||
CHECK_ZFILE(zf)
|
||||
|
||||
@ -958,5 +958,17 @@ int zip_map(ZFile* zf, void*& p, size_t& size)
|
||||
}
|
||||
|
||||
H_DEREF(zf->ha, ZArchive, za)
|
||||
// increase refs
|
||||
|
||||
return file_map(&za->f, p, size);
|
||||
}
|
||||
|
||||
|
||||
int zip_unmap(ZFile* const zf)
|
||||
{
|
||||
CHECK_ZFILE(zf)
|
||||
H_DEREF(zf->ha, ZArchive, za)
|
||||
// decrement refs
|
||||
// unmap archive if 0
|
||||
return 0;
|
||||
}
|
||||
|
@ -77,9 +77,9 @@ struct ZFile
|
||||
size_t ucsize;
|
||||
// size of logical file
|
||||
|
||||
size_t ofs;
|
||||
size_t csize;
|
||||
size_t last_raw_ofs;
|
||||
off_t ofs;
|
||||
off_t csize;
|
||||
off_t last_raw_ofs;
|
||||
|
||||
Handle ha;
|
||||
uintptr_t read_ctx;
|
||||
@ -96,30 +96,10 @@ extern int zip_close(ZFile* zf);
|
||||
|
||||
|
||||
extern int zip_map(ZFile* zf, void*& p, size_t& size);
|
||||
extern int zip_unmap(ZFile* zf);
|
||||
|
||||
extern ssize_t zip_read(ZFile* zf, size_t ofs, size_t size, void*& p);
|
||||
|
||||
|
||||
//--------
|
||||
|
||||
// read from file <hz>, starting at offset <ofs> in the compressed data
|
||||
// (typically only used if the file is known to be stored).
|
||||
// p == 0: allocate, read into, and return the output buffer
|
||||
// p != 0: read into given output buffer, return handle to it
|
||||
// if file is compressed, size must be >= uncompressed file size
|
||||
// size: no input value, unless specifying an output buffer (p != 0)
|
||||
// out:
|
||||
|
||||
|
||||
|
||||
|
||||
//extern void* zip_mmap(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
#endif // #ifndef __ZIP_H__
|
||||
|
Loading…
Reference in New Issue
Block a user