no message

This was SVN commit r167.
This commit is contained in:
janwas 2004-03-03 15:16:20 +00:00
parent 805328d2bd
commit a8d448ad19
11 changed files with 267 additions and 162 deletions

View File

@ -27,6 +27,7 @@ extern "C" {
// useful for choosing a video mode. not called by detect().
// if we fail, don't change the outputs (assumed initialized to defaults)
extern void get_cur_resolution(int& xres, int& yres);

View File

@ -728,8 +728,7 @@ ssize_t file_io(File* const f, const size_t raw_ofs, size_t raw_size, void** con
{
if(raw_ofs >= f->size)
return ERR_EOF;
size_t bytes_left = f->size - raw_ofs; // > 0
size_t max_size = MIN(bytes_left, raw_size);
raw_size = MIN(f->size - raw_ofs, raw_size);
}
// writing: make sure buffer is valid
else

View File

@ -138,19 +138,19 @@ static int Font_reload(Font* f, const char* fn)
Handle err = vfs_load(fn, tmp_file, file_size);
if(err <= 0)
return (int)err;
void* p = mem_alloc(file_size + 1);
if(!p)
void* file = mem_alloc(file_size + 1);
if(!file)
return ERR_NO_MEM;
memcpy(p, tmp_file, file_size);
((char*)p)[file_size] = 0; // 0-terminate for sscanf
memcpy(file, tmp_file, file_size);
((char*)file)[file_size] = 0; // 0-terminate for sscanf
int pos; // current position in the file
const char* file = (const char*)p;
const char* p = (const char*)file;
// read header
char tex_filename[PATH_MAX];
int x_stride, y_stride; // glyph spacing in texture
if(sscanf(file, "%s\n%d %d\n%n", tex_filename, &x_stride, &y_stride, &pos) != 3)
if(sscanf(p, "%s\n%d %d\n%n", tex_filename, &x_stride, &y_stride, &pos) != 3)
{
debug_out("Font_reload: \"%s\": header is invalid", fn);
return -1;
@ -160,15 +160,15 @@ static int Font_reload(Font* f, const char* fn)
int adv[128];
for(int i = 32; i < 128; i++)
{
file += pos;
if(sscanf(file, "%d %n", &adv[i], &pos) != 1)
p += pos;
if(sscanf(p, "%d %n", &adv[i], &pos) != 1)
{
debug_out("Font_reload: \"%s\": glyph width array is invalid", fn);
return -1;
}
}
mem_free(p);
mem_free(file);
// load glyph texture
const Handle ht = tex_load(tex_filename);

View File

@ -1,4 +1,4 @@
// handle-based resource manager
// handle manager
//
// Copyright (c) 2003 Jan Wassenberg
//
@ -33,28 +33,50 @@
// handle
//
// TODO: explain handle scheme, how it solves problems
// handles are an indirection layer between client code and resources.
// they allow an important check not possible with a direct pointer:
// guaranteeing the handle references a given resource /instance/.
//
// problem: code C1 allocates a resource, and receives a pointer p to its
// control block. C1 passes p on to C2, and later frees it.
// now other code allocates a resource, and happens to reuse the free slot
// pointed to by p (also possible if simply allocating from the heap).
// when C2 accesses p, the pointer is valid, but we cannot tell that
// it is referring to a resource that had already been freed. big trouble.
//
// solution: each allocation receives a unique tag (a global counter that
// is large enough to never overflow). Handles include this tag, as well
// as a reference (array index) to the control block, which isn't directly
// accessible. when dereferencing the handle, we check if the handle's tag
// matches the copy stored in the control block. this protects against stale
// handle reuse, double-free, and accidentally referencing other resources.
//
// type: each handle has an associated type. these must be checked to prevent
// using textures as sounds, for example. with the manual vtbl scheme,
// this type is actually a pointer to the resource object's vtbl, and is
// set up via H_TYPE_DEFINE. see header for rationale. this means that
// types are private to the module that declared the handle; knowledge
// of the type ensures the caller actually declared, and owns the resource.
// 0 = invalid handle value
// < 0 is an error code (we assume < 0 <==> MSB is set -
// true for 1s and 2s complement and sign-magnitude systems)
//
// tag = 1-based; index = 0-based
//
// shift value = # bits between LSB and field LSB.
// may be larger than the field type - only shift Handle vars!
// fields:
// - allows checking if the resource has been freed
// determines maximum unambiguous resource allocs
// (shift value = # bits between LSB and field LSB.
// may be larger than the field type - only shift Handle vars!)
// - tag (1-based) ensures the handle references a certain resource instance.
// (field width determines maximum unambiguous resource allocs)
#define TAG_BITS 32
const uint TAG_SHIFT = 0;
const Handle TAG_MASK = (((Handle)1) << TAG_BITS) - 1;
// - index into data array
// determines maximum (currently open) handles
const u32 TAG_MASK = 0xffffffff; // safer than (1 << 32) - 1
// - index (0-based) points to control block in our array.
// (field width determines maximum currently open handles)
#define IDX_BITS 16
const uint IDX_SHIFT = 32;
const u32 IDX_MASK = (((Handle)1) << IDX_BITS) - 1;
const i32 IDX_MASK = (1l << IDX_BITS) - 1;
cassert(IDX_BITS + TAG_BITS <= sizeof(Handle)*CHAR_BIT);
@ -156,17 +178,11 @@ static HDATA* h_data(const i32 idx)
}
int h_data_tag = 0;
// get HDATA for the given handle. verifies the handle
// isn't invalid or an error code, and checks the tag field.
// used by the few functions callable for any handle type, e.g. h_filename.
static HDATA* h_data_any_type(const Handle h)
{
h_data_tag++;
#ifdef PARANOIA
check_heap();
#endif
@ -222,7 +238,7 @@ static void cleanup(void)
if(hd)
{
// somewhat messy, but this only happens on cleanup.
// better, i think, than an additional h_free(i32 idx) version.
// better than an additional h_free(i32 idx) version though.
Handle h = handle(i, hd->tag);
h_free(h, hd->type);
}
@ -239,15 +255,10 @@ static void cleanup(void)
static int alloc_idx(i32* pidx, HDATA** phd)
// idx and hd are undefined if we fail.
// called by h_alloc only.
static int alloc_idx(i32& idx, HDATA*& hd)
{
assert(pidx && phd && "alloc_idx: invalid param");
*pidx = 0;
*phd = 0;
i32 idx;
HDATA* hd;
// we already know the first free entry
if(first_free != -1)
{
@ -297,8 +308,6 @@ have_idx:;
if(idx > last_in_use)
last_in_use = idx;
*pidx = idx;
*phd = hd;
return 0;
}
@ -319,9 +328,14 @@ int h_free(Handle& h, H_Type type)
if(!hd)
return ERR_INVALID_HANDLE;
// not the last reference
if(--hd->refs)
return 0;
// have valid refcount (don't decrement if alread 0)
if(hd->refs > 0)
{
hd->refs--;
// not the last reference
if(hd->refs > 0)
return 0;
}
// TODO: keep this handle open (cache)
@ -345,7 +359,9 @@ int h_free(Handle& h, H_Type type)
// any further params are passed to type's init routine
Handle h_alloc(H_Type type, const char* fn, uint flags, ...)
{
// ONCE(atexit(cleanup))
ONCE(atexit(cleanup))
Handle err;
i32 idx;
HDATA* hd;
@ -400,13 +416,15 @@ Handle h_alloc(H_Type type, const char* fn, uint flags, ...)
*/
}
if(alloc_idx(&idx, &hd) < 0)
return 0;
err = alloc_idx(idx, hd);
if(err < 0)
return err;
static u32 tag;
if(++tag >= TAG_MASK)
{
assert(!"h_alloc: tag overflow - may not notice stale handle reuse (increase TAG_BITS)");
assert(0 && "h_alloc: tag overflow - allocations are no longer unique."\
"may not notice stale handle reuse. increase TAG_BITS.");
tag = 1;
}
@ -427,11 +445,11 @@ Handle h_alloc(H_Type type, const char* fn, uint flags, ...)
if(vtbl->reload)
{
int err = vtbl->reload(hd->user, fn);
err = vtbl->reload(hd->user, fn);
if(err < 0)
{
h_free(h, type);
return 0;
return err;
}
}

View File

@ -1,4 +1,4 @@
// handle based caching resource manager
// handle manager
//
// Copyright (c) 2003 Jan Wassenberg
//
@ -16,8 +16,8 @@
// Jan.Wassenberg@stud.uni-karlsruhe.de
// http://www.stud.uni-karlsruhe.de/~urkt/
#ifndef __RES_H__
#define __RES_H__
#ifndef H_MGR_H__
#define H_MGR_H__
#ifdef __cplusplus
extern "C" {
@ -71,14 +71,14 @@ but- has to handle variable params, a bit ugly
// - class (difficult to compare type, handle manager needs to know of all users)
//
// checked in h_alloc:
// - user_size must fit in what res.cpp is offering (currently 44 bytes)
// - user_size must fit in what the handle manager provides
// - name must not be 0
//
// init: user data is initially zeroed
// dtor: user data is zeroed automatically afterwards
// dtor: user data is zeroed afterwards
// reload: if this resource type is opened by another resource's reload,
// our reload routine MUST check if already opened! This is relevant when
// a file is invalidated: if e.g. a sound object opens a file, the handle
// a file is reloaded: if e.g. a sound object opens a file, the handle
// manager calls the reload routines for the 2 handles in unspecified order.
// ensuring the order would require a tag field that can't overflow -
// not really guaranteed with 32-bit handles. it'd also be more work
@ -96,17 +96,24 @@ struct H_VTbl
typedef H_VTbl* H_Type;
#define H_TYPE_DEFINE(t)\
static void t##_init(t*, va_list);\
static int t##_reload(t*, const char*);\
static void t##_dtor(t*);\
static H_VTbl V_##t = {\
(void(*)(void*, va_list))t##_init,\
(int(*)(void*, const char*))t##_reload,\
(void(*)(void*))t##_dtor,\
sizeof(t),\
#t\
};\
static H_Type H_##t = &V_##t;
/* 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 =\
{\
(void(*)(void*, va_list))t##_init,\
(int(*)(void*, const char*))t##_reload,\
(void(*)(void*))t##_dtor,\
sizeof(t), /* control block size */\
#t /* name */\
};\
static H_Type H_##t = &V_##t;
// 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);
@ -185,4 +192,4 @@ extern int res_cur_scope;
#endif
#endif // #ifndef __RES_H__
#endif // #ifndef H_MGR_H__

View File

@ -24,7 +24,6 @@ extern void* mem_alloc(size_t size, uint align = 1, uint flags = 0, Handle* ph =
#define mem_free(p) mem_free_p((void*&)p)
extern int mem_free_p(void*& p);
// faster than mem_free(void*) - no scan of open handles for the pointer
extern int mem_free_h(Handle& hm);
// create a H_MEM handle of type MEM_USER,

View File

@ -577,7 +577,7 @@ Handle vfs_load(const char* fn, void*& p, size_t& size)
size = vf->size;
{ // VC6 goto fix
size_t nread = vfs_io(hf, 0, size, p);
ssize_t nread = vfs_io(hf, 0, size, p);
if(nread > 0)
hm = mem_assign(p, size);
}

View File

@ -82,7 +82,7 @@ ssize_t zip_inflate(uintptr_t ctx, void* in, size_t in_size)
size_t avail_out = stream->avail_out;
assert(avail_out <= prev_avail_out);
// make sure output buffer size didn't magically increase
size_t nread = prev_avail_out - avail_out;
ssize_t nread = (ssize_t)(prev_avail_out - avail_out);
if(!nread)
return (err < 0)? err : 0;
// try to pass along the ZLib error code, but make sure

View File

@ -30,7 +30,7 @@
//////////////////////////////////////////////////////////////////////////////
//
//
// AioHandles
//
//////////////////////////////////////////////////////////////////////////////
@ -54,8 +54,6 @@ private:
uint size;
};
static AioHandles* aio_hs;
AioHandles::~AioHandles()
{
@ -76,6 +74,14 @@ AioHandles::~AioHandles()
}
bool is_valid_file_handle(HANDLE h)
{
SetLastError(0);
bool valid = (GetFileSize(h, 0) != INVALID_FILE_SIZE);
assert(valid);
return valid;
}
// get async capable handle to file <fd>
HANDLE AioHandles::get(int fd)
{
@ -85,6 +91,11 @@ HANDLE AioHandles::get(int fd)
if((unsigned)fd < size)
h = hs[fd];
else
assert(0);
if(!is_valid_file_handle(h))
return INVALID_HANDLE_VALUE;
win_unlock(WAIO_CS);
@ -110,10 +121,20 @@ int AioHandles::set(int fd, HANDLE h)
size = size2;
}
if(hs[fd] != INVALID_HANDLE_VALUE)
if(h == INVALID_HANDLE_VALUE)
;
else
{
assert("set_aio_h: handle already set!");
goto fail;
if(hs[fd] != INVALID_HANDLE_VALUE)
{
assert("AioHandles::set: handle already set!");
goto fail;
}
if(!is_valid_file_handle(h))
{
assert("AioHandles::set: setting invalid handle");
goto fail;
}
}
hs[fd] = h;
@ -123,79 +144,14 @@ int AioHandles::set(int fd, HANDLE h)
fail:
win_unlock(WAIO_CS);
assert(0 && "AioHandles::set failed");
return -1;
}
int aio_assign_handle(uintptr_t handle)
{
//
// CRT stores osfhandle. if we pass an invalid handle (say, 0),
// we get an exception when closing the handle if debugging.
// events can be created relatively quickly (~1800 clocks = 1µs),
// and are also freed with CloseHandle, so just pass that.
HANDLE h = CreateEvent(0,0,0,0);
if(h == INVALID_HANDLE_VALUE)
return -1;
int fd = _open_osfhandle((intptr_t)h, 0);
if(fd < 0)
return fd;
return aio_hs->set(fd, (HANDLE)handle);
}
static void init();
// open fn in async mode; associate with fd (retrieve via aio_h(fd))
int aio_open(const char* fn, int mode, int fd)
{
WIN_ONCE(init()); // TODO: need to do this elsewhere in case other routines called first?
// interpret mode
DWORD access = GENERIC_READ; // assume O_RDONLY
DWORD share = 0;
if(mode & O_WRONLY)
access = GENERIC_WRITE;
else if(mode & O_RDWR)
access = GENERIC_READ|GENERIC_WRITE;
else
share = FILE_SHARE_READ;
DWORD create = OPEN_EXISTING;
if(mode & O_CREAT)
create = (mode & O_EXCL)? CREATE_NEW : CREATE_ALWAYS;
DWORD flags = FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN;
// open file
HANDLE h = CreateFile(fn, access, share, 0, create, flags, 0);
if(h == INVALID_HANDLE_VALUE)
return -1;
if(aio_hs->set(fd, h) < 0)
{
CloseHandle(h);
return -1;
}
return 0;
}
int aio_close(int fd)
{
HANDLE h = aio_hs->get(fd);
if(h == INVALID_HANDLE_VALUE) // out of bounds or already closed
return -1;
CloseHandle(h);
aio_hs->set(fd, INVALID_HANDLE_VALUE);
return 0;
}
//////////////////////////////////////////////////////////////////////////////
//
//
// Req
//
//////////////////////////////////////////////////////////////////////////////
@ -206,7 +162,7 @@ struct Req
aiocb* cb;
OVERLAPPED ovl;
// hEvent signals when transfer complete
// hEvent signals when transfer complete
// read into a separate align buffer if necessary
// (note: unaligned writes aren't supported. see aio_rw)
@ -269,6 +225,7 @@ Req* Reqs::find(const aiocb* cb)
if(r->cb == cb)
return r;
assert(0 && "Reqs::find failed");
return 0;
}
@ -292,17 +249,27 @@ Req* Reqs::alloc(const aiocb* cb)
return r;
}
//////////////////////////////////////////////////////////////////////////////
//
// init / cleanup
//
//////////////////////////////////////////////////////////////////////////////
static AioHandles* aio_hs;
static Reqs* reqs;
// Win32 functions require sector aligned transfers.
// max of all drives' size is checked in init().
static size_t sector_size = 4096; // minimum: one page
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
static Reqs* reqs;
static void cleanup(void)
{
delete aio_hs;
delete reqs;
}
// caller ensures this is not re-entered!
@ -311,6 +278,8 @@ static void init()
reqs = new Reqs;
aio_hs = new AioHandles;
atexit(cleanup);
// Win32 requires transfers to be sector aligned.
// find maximum of all drive's sector sizes, then use that.
// (it's good to know this up-front, and checking every open() is slow).
@ -339,6 +308,92 @@ static void init()
}
int aio_assign_handle(uintptr_t handle)
{
//
// CRT stores osfhandle. if we pass an invalid handle (say, 0),
// we get an exception when closing the handle if debugging.
// events can be created relatively quickly (~1800 clocks = 1µs),
// and are also freed with CloseHandle, so just pass that.
HANDLE h = CreateEvent(0,0,0,0);
if(h == INVALID_HANDLE_VALUE)
{
assert(0 && "aio_assign_handle failed");
return -1;
}
int fd = _open_osfhandle((intptr_t)h, 0);
if(fd < 0)
{
assert(0 && "aio_assign_handle failed");
return fd;
}
return aio_hs->set(fd, (HANDLE)handle);
}
// open fn in async mode; associate with fd (retrieve via aio_h(fd))
int aio_open(const char* fn, int mode, int fd)
{
WIN_ONCE(init()); // TODO: need to do this elsewhere in case other routines called first?
// interpret mode
DWORD access = GENERIC_READ; // assume O_RDONLY
DWORD share = 0;
if(mode & O_WRONLY)
access = GENERIC_WRITE;
else if(mode & O_RDWR)
access = GENERIC_READ|GENERIC_WRITE;
else
share = FILE_SHARE_READ;
DWORD create = OPEN_EXISTING;
if(mode & O_CREAT)
create = (mode & O_EXCL)? CREATE_NEW : CREATE_ALWAYS;
DWORD flags = FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN;
// open file
HANDLE h = CreateFile(fn, access, share, 0, create, flags, 0);
if(h == INVALID_HANDLE_VALUE)
{
assert(0 && "aio_open failed");
return -1;
}
if(aio_hs->set(fd, h) < 0)
{
assert(0 && "aio_open failed");
CloseHandle(h);
return -1;
}
return 0;
}
int aio_close(int fd)
{
HANDLE h = aio_hs->get(fd);
if(h == INVALID_HANDLE_VALUE) // out of bounds or already closed
{
assert(0 && "aio_close failed");
return -1;
}
SetLastError(0);
if(!CloseHandle(h))
assert(0);
aio_hs->set(fd, INVALID_HANDLE_VALUE);
return 0;
}
// called by aio_read, aio_write, and lio_listio
// cb->aio_lio_opcode specifies desired operation
//
@ -347,9 +402,15 @@ static void init()
static int aio_rw(struct aiocb* cb)
{
if(!cb)
{
assert(0);
return -EINVAL;
}
if(cb->aio_lio_opcode == LIO_NOP)
{
assert(0);
return 0;
}
HANDLE h = aio_hs->get(cb->aio_fildes);
if(h == INVALID_HANDLE_VALUE)
@ -360,7 +421,10 @@ static int aio_rw(struct aiocb* cb)
Req* r = reqs->alloc(cb);
if(!r)
{
assert(0);
return -1;
}
size_t ofs = 0;
size_t size = cb->aio_nbytes;
@ -406,6 +470,8 @@ static int aio_rw(struct aiocb* cb)
}
}
r->ovl.Internal = r->ovl.InternalHigh = 0;
#if _MSC_VER >= 1300
r->ovl.Pointer = (void*)ofs;
#else
@ -414,6 +480,8 @@ static int aio_rw(struct aiocb* cb)
assert(cb->aio_buf != 0);
SetLastError(0);
DWORD size32 = (DWORD)(size & 0xffffffff);
ResetEvent(r->ovl.hEvent);
BOOL ok = (cb->aio_lio_opcode == LIO_READ)?
@ -443,11 +511,20 @@ int lio_listio(int mode, struct aiocb* const cbs[], int n, struct sigevent* se)
{
UNUSED(se)
int err = 0;
for(int i = 0; i < n; i++)
aio_rw(cbs[i]); // aio_rw checks for 0 param
{
int ret = aio_rw(cbs[i]); // aio_rw checks for 0 param
if(ret < 0)
err = ret;
}
if(err < 0)
return err;
if(mode == LIO_WAIT)
aio_suspend(cbs, n, 0);
return aio_suspend(cbs, n, 0);
return 0;
}

View File

@ -30,13 +30,20 @@
#endif
// useful for choosing a video mode. not called by detect().
// if we fail, don't change the outputs (assumed initialized to defaults)
void get_cur_resolution(int& xres, int& yres)
{
static DEVMODE dm;
DEVMODEA dm;
memset(&dm, 0, sizeof(dm));
dm.dmSize = sizeof(dm);
EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm);
xres = dm.dmPelsWidth;
yres = dm.dmPelsHeight;
// dm.dmDriverExtra already set to 0 by memset
if(EnumDisplaySettingsA(0, ENUM_CURRENT_SETTINGS, &dm))
{
xres = dm.dmPelsWidth;
yres = dm.dmPelsHeight;
}
}

View File

@ -20,15 +20,12 @@
#include <Xlib.h>
// useful for choosing a video mode. not called by detect().
// if we fail, don't change the outputs (assumed initialized to defaults)
void get_cur_resolution(int& xres, int& yres)
{
Display* disp = XOpenDisplay(NULL);
Display* disp = XOpenDisplay(0);
if(!disp)
{
xres = 1024;
yres = 768;
return;
}
int screen = XDefaultScreen(disp);
xres = XDisplayWidth (disp, screen);