1
1
forked from 0ad/0ad

dyn_array: efficient expandable array that doesn't waste mem or relocate itself. TODO: self test and dox

file: expose block size constant; always round user transfers up
tex: transform() now receives what to change, not what to achieve.
tex*: encoding works with DynArray+VFS backend
GameSetup: free g_BuildingPlacer to avoid mem leak.
fixed some warnings+bugs.

This was SVN commit r2679.
This commit is contained in:
janwas 2005-09-06 22:44:48 +00:00
parent 9dc8a71b15
commit 1a7d67a97f
18 changed files with 476 additions and 189 deletions

213
source/lib/dyn_array.cpp Normal file
View File

@ -0,0 +1,213 @@
#include "precompiled.h"
#include "posix.h"
#include "dyn_array.h"
static const size_t page_size = sysconf(_SC_PAGE_SIZE);
static bool is_page_multiple(uintptr_t x)
{
return (x % page_size) == 0;
}
static size_t round_up_to_page(size_t size)
{
return round_up(size, page_size);
}
static int validate_da(DynArray* da)
{
if(!da)
return ERR_INVALID_PARAM;
u8* const base = da->base;
const size_t max_size_pa = da->max_size_pa;
const size_t cur_size = da->cur_size;
const size_t pos = da->pos;
const int prot = da->prot;
if(debug_is_pointer_bogus(base))
return -1;
if(!is_page_multiple((uintptr_t)base))
return -2;
if(!is_page_multiple(max_size_pa))
return -3;
if(cur_size > max_size_pa)
return -4;
if(pos > cur_size || pos > max_size_pa)
return -5;
if(prot & ~(PROT_READ|PROT_WRITE|PROT_EXEC))
return -6;
return 0;
}
#define CHECK_DA(da) CHECK_ERR(validate_da(da))
//-----------------------------------------------------------------------------
// very thin wrapper on top of sys/mman.h that makes the intent more obvious
// (its commit/decommit semantics are difficult to tell apart).
static const int mmap_flags = MAP_PRIVATE|MAP_ANONYMOUS;
static int mem_reserve(size_t size, u8** pp)
{
void* ret = mmap(0, size, PROT_NONE, mmap_flags|MAP_NORESERVE, -1, 0);
if(ret == MAP_FAILED)
return ERR_NO_MEM;
*pp = (u8*)ret;
return 0;
}
static int mem_release(u8* p, size_t size)
{
return munmap(p, size);
}
static int mem_commit(u8* p, size_t size, int prot)
{
if(prot == PROT_NONE)
{
debug_warn("mem_commit: prot=PROT_NONE isn't allowed (misinterpreted by mmap)");
return ERR_INVALID_PARAM;
}
void* ret = mmap(p, size, prot, mmap_flags|MAP_FIXED, -1, 0);
return (ret == MAP_FAILED)? -1 : 0;
}
static int mem_decommit(u8* p, size_t size)
{
void* ret = mmap(p, size, PROT_NONE, mmap_flags|MAP_NORESERVE|MAP_FIXED, -1, 0);
return (ret == MAP_FAILED)? -1 : 0;
}
static int mem_protect(u8* p, size_t size, int prot)
{
return mprotect(p, size, prot);
}
//-----------------------------------------------------------------------------
int da_alloc(DynArray* da, size_t max_size)
{
const size_t max_size_pa = round_up_to_page(max_size);
u8* p;
CHECK_ERR(mem_reserve(max_size_pa, &p));
da->base = p;
da->max_size_pa = max_size_pa;
da->cur_size = 0;
da->pos = 0;
da->prot = PROT_READ|PROT_WRITE;
CHECK_DA(da);
return 0;
}
int da_free(DynArray* da)
{
CHECK_DA(da);
// latch pointer; wipe out the DynArray for safety
// (must be done here because mem_release may fail)
u8* p = da->base;
size_t size = da->max_size_pa;
memset(da, 0, sizeof(*da));
CHECK_ERR(mem_release(p, size));
return 0;
}
int da_set_size(DynArray* da, size_t new_size)
{
CHECK_DA(da);
// determine how much to add/remove
const size_t cur_size_pa = round_up_to_page(da->cur_size);
const size_t new_size_pa = round_up_to_page(new_size);
if(new_size_pa > da->max_size_pa)
CHECK_ERR(ERR_INVALID_PARAM);
const ssize_t size_delta_pa = (ssize_t)new_size_pa - (ssize_t)cur_size_pa;
u8* end = da->base + cur_size_pa;
// expanding
if(size_delta_pa > 0)
CHECK_ERR(mem_commit(end, size_delta_pa, da->prot));
// shrinking
else if(size_delta_pa < 0)
CHECK_ERR(mem_decommit(end+size_delta_pa, size_delta_pa));
// else: no change in page count, e.g. if going from size=1 to 2
// (we don't want mem_* to have to handle size=0)
da->cur_size = new_size;
CHECK_DA(da);
return 0;
}
int da_set_prot(DynArray* da, int prot)
{
CHECK_DA(da);
da->prot = prot;
CHECK_ERR(mem_protect(da->base, da->cur_size, prot));
CHECK_DA(da);
return 0;
}
int da_read(DynArray* da, void* data, size_t size)
{
void* src = da->base + da->pos;
// make sure we have enough data to read
da->pos += size;
if(da->pos > da->cur_size)
return -1;
memcpy(data, src, size);
return 0;
}
int da_append(DynArray* da, const void* data, size_t size)
{
RETURN_ERR(da_set_size(da, da->pos+size));
memcpy(da->base+da->pos, data, size);
da->pos += size;
return 0;
}
//-----------------------------------------------------------------------------
// built-in self test
//-----------------------------------------------------------------------------
#if SELF_TEST_ENABLED
namespace test {
static void test_api()
{
}
static void test_expand()
{
}
static void self_test()
{
test_api();
test_expand();
}
RUN_SELF_TEST;
} // namespace test
#endif // #if SELF_TEST_ENABLED

25
source/lib/dyn_array.h Normal file
View File

@ -0,0 +1,25 @@
#include "lib/types.h"
struct DynArray
{
u8* base;
size_t max_size_pa; // reserved
size_t cur_size; // committed
size_t pos;
int prot; // applied to newly committed pages
};
extern int da_alloc(DynArray* da, size_t max_size);
extern int da_free(DynArray* da);
extern int da_set_size(DynArray* da, size_t new_size);
extern int da_set_prot(DynArray* da, int prot);
extern int da_read(DynArray* da, void* data_dst, size_t size);
extern int da_append(DynArray* da, const void* data_src, size_t size);

View File

@ -351,7 +351,6 @@ int dir_open(const char* P_path, DirIterator* d_)
CHECK_ERR(err);
}
// (see pp declaration)
return pp_set_dir(&d->pp, n_path);
}
@ -1109,12 +1108,8 @@ debug_printf("file_io fd=%d size=%d ofs=%d\n", f->fd, data_size, data_ofs);
const size_t ofs_misalign = data_ofs % BLOCK_SIZE;
const size_t lead_padding = do_align? ofs_misalign : 0;
// for convenience; used below.
if(do_align)
{
actual_ofs -= (off_t)ofs_misalign;
actual_size = round_up(ofs_misalign + data_size, BLOCK_SIZE);
}
actual_ofs -= (off_t)lead_padding;
actual_size = round_up(lead_padding + data_size, BLOCK_SIZE);
// skip aio code, use lowio

View File

@ -94,9 +94,12 @@ extern int file_rel_chdir(const char* argv0, const char* rel_path);
// rationale: some private storage apart from opendir's DIR* is required
// to support stat(). we prefer having the caller reserve room (on the stack)
// rather than allocating dynamically (less efficient or more complicated).
//
// this is an opaque struct to avoid exposing our internals and insulate
// user code against changes; we verify at compile-time that the
// public/private definitions match.
// note: cannot just typedef to DirIterator_ because other modules
// instantiate this.
struct DirIterator
{
char opaque[512];
@ -245,6 +248,10 @@ extern int file_discard_io(FileIo* io);
// synchronous IO
//
// user-specified offsets and transfer lengths must be multiples of this!
// (simplifies file_io)
const size_t FILE_BLOCK_SIZE = 64*KiB;
// return value:
// < 0: failed; abort transfer.
// >= 0: bytes output; continue.

View File

@ -73,22 +73,21 @@ static int validate_format(uint bpp, uint flags)
// but is much easier to maintain than providing all<->all conversion paths.
//
// somewhat optimized (loops are hoisted, cache associativity accounted for)
static int plain_transform(Tex* t, uint new_format)
static int plain_transform(Tex* t, uint transforms)
{
// extract texture info
const int pending_transforms = t->flags ^ new_format;
const uint w = t->w, h = t->h, bpp = t->bpp, flags = t->flags;
u8* const img = tex_get_data(t);
// sanity checks (not errors, we just can't handle these cases)
// .. unknown transform
if(pending_transforms & ~(TEX_BGR|TEX_ORIENTATION))
if(transforms & ~(TEX_BGR|TEX_ORIENTATION))
return TEX_CODEC_CANNOT_HANDLE;
// .. img is not in "plain" format
if(validate_format(bpp, flags) != 0)
return TEX_CODEC_CANNOT_HANDLE;
// .. nothing to do
if(!pending_transforms)
if(!transforms)
return 0;
// setup row source/destination pointers (simplifies outer loop)
@ -99,7 +98,7 @@ static int plain_transform(Tex* t, uint new_format)
// avoid y*pitch multiply in row loop; instead, add row_ofs.
void* clone_img = 0;
// flipping rows (0,1,2 -> 2,1,0)
if(pending_transforms & TEX_ORIENTATION)
if(transforms & TEX_ORIENTATION)
{
// L1 cache is typically A2 => swapping in-place with a line buffer
// leads to thrashing. we'll assume the whole texture*2 fits in cache,
@ -117,7 +116,7 @@ static int plain_transform(Tex* t, uint new_format)
}
// no BGR convert necessary
if(!(pending_transforms & TEX_BGR))
if(!(transforms & TEX_BGR))
{
for(uint y = 0; y < h; y++)
{
@ -206,18 +205,22 @@ int tex_codec_register(const TexCodecVTbl* c)
// note: we don't allocate the data param ourselves because this function is
// needed for encoding, too (where data is already present).
int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
int file_orientation, RowArray& rows)
uint src_flags, uint dst_orientation, RowArray& rows)
{
debug_assert((file_orientation & ~TEX_ORIENTATION) == 0);
// convenience for caller: allow passing all flags.
const uint src_orientation = src_flags & TEX_ORIENTATION;
if(dst_orientation == 0)
dst_orientation = global_orientation;
rows = (RowArray)malloc(h * sizeof(RowPtr));
if(!rows)
return ERR_NO_MEM;
// determine start position and direction
const bool flip = (file_orientation ^ global_orientation) != 0;
RowPtr pos = flip? data+pitch*(h-1) : data;
ssize_t add = flip? -(ssize_t)pitch : (ssize_t)pitch;
const bool flip = (src_orientation ^ dst_orientation) != 0;
RowPtr pos = flip? data+pitch*(h-1) : data;
const ssize_t add = flip? -(ssize_t)pitch : (ssize_t)pitch;
const RowPtr end = flip? data-pitch : data+pitch*h;
for(size_t i = 0; i < h; i++)
{
@ -225,15 +228,26 @@ int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
pos += add;
}
debug_assert(pos == end);
return 0;
}
int tex_codec_set_orientation(Tex* t, int file_orientation)
int tex_codec_set_orientation(Tex* t, uint file_orientation)
{
int pending_transforms = file_orientation ^ global_orientation;
int new_flags = t->flags ^ pending_transforms;
return plain_transform(t, new_flags);
uint transforms = file_orientation ^ global_orientation;
return plain_transform(t, transforms);
}
int tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da)
{
RETURN_ERR(tex_transform(t, transforms));
void* img_data = tex_get_data(t); const size_t img_size = tex_img_size(t);
RETURN_ERR(da_append(da, hdr, hdr_size));
RETURN_ERR(da_append(da, img_data, img_size));
return 0;
}
@ -277,6 +291,9 @@ int tex_load_mem(Handle hm, const char* fn, Tex* t)
// find codec that understands the data, and decode
for(int i = 0; i < MAX_CODECS; i++)
{
// MAX_CODECS isn't a tight bound and we have hit a 0 entry
if(!codecs[i])
continue;
const char* err_msg = 0;
int err = codecs[i]->decode(p, size, t, &err_msg);
if(err == TEX_CODEC_CANNOT_HANDLE)
@ -321,14 +338,21 @@ u8* tex_get_data(const Tex* t)
return p + t->ofs;
}
size_t tex_img_size(const Tex* t)
{
return t->w * t->h * t->bpp/8;
}
int tex_transform(Tex* t, uint new_flags)
int tex_transform(Tex* t, uint transforms)
{
// find codec that understands the data, and transform
for(int i = 0; i < MAX_CODECS; i++)
{
const char* err_msg = 0;
int err = codecs[i]->transform(t, new_flags);
// MAX_CODECS isn't a tight bound and we have hit a 0 entry
if(!codecs[i])
continue;
int err = codecs[i]->transform(t, transforms);
if(err == TEX_CODEC_CANNOT_HANDLE)
continue;
if(err == 0)
@ -338,12 +362,14 @@ int tex_transform(Tex* t, uint new_flags)
}
// last chance
return plain_transform(t, new_flags);
return plain_transform(t, transforms);
}
int tex_write(const char* fn, uint w, uint h, uint bpp, uint flags, void* in_img)
{
int err = -1;
if(validate_format(bpp, flags) != 0)
return ERR_TEX_FMT_INVALID;
@ -362,24 +388,37 @@ int tex_write(const char* fn, uint w, uint h, uint bpp, uint flags, void* in_img
w, h, bpp, flags
};
u8* out; size_t out_size;
DynArray da;
const size_t max_out_size = (w*h*4 + 256*KiB)*2;
CHECK_ERR(da_alloc(&da, max_out_size));
for(int i = 0; i < MAX_CODECS; i++)
{
// MAX_CODECS isn't a tight bound and we have hit a 0 entry
if(!codecs[i])
continue;
const char* err_msg = 0;
int err = codecs[i]->encode(ext, &t, &out, &out_size, &err_msg);
err = codecs[i]->encode(ext, &t, &da, &err_msg);
if(err == TEX_CODEC_CANNOT_HANDLE)
continue;
if(err == 0)
goto have_codec;
debug_printf("tex_write(%s): %s: %s", codecs[i]->name, fn, err_msg);
CHECK_ERR(err);
if(err < 0)
{
debug_printf("tex_write(%s): %s: %s", codecs[i]->name, fn, err_msg);
WARN_ERR(err);
goto ret;
}
goto have_codec; // success
}
// no codec found
return ERR_UNKNOWN_FORMAT;
have_codec:
WARN_ERR(vfs_store(fn, out, out_size));
free(out);
return 0;
size_t rounded_size = round_up(da.cur_size, FILE_BLOCK_SIZE);
CHECK_ERR(da_set_size(&da, rounded_size));
WARN_ERR(vfs_store(fn, da.base, da.pos));
err = 0;
ret:
WARN_ERR(da_free(&da));
return err;
}

View File

@ -72,7 +72,9 @@ extern int tex_load_mem(Handle hm, const char* fn, Tex* t);
extern int tex_free(Tex* t);
extern u8* tex_get_data(const Tex* t);
extern int tex_transform(Tex* t, uint new_flags);
extern size_t tex_img_size(const Tex* t);
extern int tex_transform(Tex* t, uint transforms);
extern int tex_write(const char* fn, uint w, uint h, uint bpp, uint flags, void* img);

View File

@ -34,7 +34,7 @@ struct BmpHeader
#define BI_RGB 0 // biCompression
static int bmp_transform(Tex* t, int new_flags)
static int bmp_transform(Tex* UNUSED(t), uint UNUSED(transforms))
{
return TEX_CODEC_CANNOT_HANDLE;
}
@ -67,9 +67,7 @@ fail:
const int orientation = (h_ < 0)? TEX_TOP_DOWN : TEX_BOTTOM_UP;
const long h = abs(h_);
const size_t pitch = w * bpp/8;
const size_t img_size = h * pitch;
u8* const img = file + ofs;
const size_t img_size = tex_img_size(t);
int flags = TEX_BGR;
if(bpp == 32)
@ -101,21 +99,19 @@ fail:
}
static int bmp_encode(const char* ext, Tex* t, u8** out, size_t* out_size, const char** perr_msg)
static int bmp_encode(const char* ext, Tex* t, DynArray* da, const char** UNUSED(perr_msg))
{
if(stricmp(ext, "bmp"))
return TEX_CODEC_CANNOT_HANDLE;
size_t img_size = t->w * t->h * t->bpp/8;
const size_t hdr_size = sizeof(BmpHeader); // needed for BITMAPFILEHEADER
const size_t img_size = tex_img_size(t);
const size_t file_size = hdr_size + img_size;
const long h = (t->flags & TEX_TOP_DOWN)? -(long)t->h : t->h;
int transforms = t->flags;
transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit.
transforms ^= TEX_BGR; // BMP is native BGR.
tex_transform(t, transforms);
const BmpHeader hdr =
{
@ -135,8 +131,7 @@ size_t img_size = t->w * t->h * t->bpp/8;
(u32)img_size, // biSizeImage
0, 0, 0, 0 // unused (bi?PelsPerMeter, biClr*)
};
return 0;
return tex_codec_write(t, transforms, &hdr, hdr_size, da);
}
TEX_CODEC_REGISTER(bmp);

View File

@ -2,6 +2,7 @@
#define TEX_CODEC_H__
#include "tex.h"
#include "dyn_array.h"
// rationale: no C++ to allow us to store const char* name in vtbl.
@ -17,9 +18,9 @@ struct TexCodecVTbl
// rationale: some codecs cannot calculate the output size beforehand
// (e.g. PNG output via libpng); we therefore require each one to
// allocate memory itself and return the pointer.
int (*encode)(const char* ext, Tex* t, u8** out, size_t* out_size, const char** perr_msg);
int (*encode)(const char* ext, Tex* t, DynArray* da, const char** perr_msg);
int (*transform)(Tex* t, int new_flags);
int (*transform)(Tex* t, uint transforms);
const char* name;
};
@ -52,9 +53,11 @@ extern int tex_codec_register(const TexCodecVTbl* c);
typedef const u8* RowPtr;
typedef RowPtr* RowArray;
extern int tex_codec_alloc_rows(const u8* data, size_t h, size_t pitch,
int file_orientation, RowArray& rows);
uint src_flags, uint dst_orientation, RowArray& rows);
extern int tex_codec_set_orientation(Tex* t, int file_orientation);
extern int tex_codec_set_orientation(Tex* t, uint file_orientation);
extern int tex_codec_write(Tex* t, uint transforms, const void* hdr, size_t hdr_size, DynArray* da);
struct MemSource

View File

@ -191,11 +191,11 @@ static int dds_decompress(Tex* t)
return 0;
}
static int dds_transform(Tex* t, int new_flags)
static int dds_transform(Tex* t, uint transforms)
{
const int dxt = t->flags & TEX_DXT, new_dxt = new_flags & TEX_DXT;
const int is_dxt = t->flags & TEX_DXT, transform_dxt = transforms & TEX_DXT;
// requesting decompression
if(dxt && !new_dxt)
if(is_dxt && transform_dxt)
return dds_decompress(t);
// both are DXT (unsupported; there are no flags we can change while
// compressed) or requesting compression (not implemented) or
@ -308,9 +308,12 @@ fail:
}
static int dds_encode(const char* UNUSED(ext), Tex* UNUSED(t), u8** UNUSED(out), size_t* UNUSED(out_size), const char** UNUSED(perr_msg))
static int dds_encode(const char* UNUSED(ext), Tex* UNUSED(t), DynArray* UNUSED(da), const char** UNUSED(perr_msg))
{
return ERR_NOT_IMPLEMENTED;
// note: do not return ERR_NOT_IMPLEMENTED et al. because that would
// break tex_write (which assumes either this, 0 or errors are returned).
return TEX_CODEC_CANNOT_HANDLE;
}
TEX_CODEC_REGISTER(dds);

View File

@ -197,30 +197,37 @@ GLOBAL(void) src_prepare(j_decompress_ptr cinfo, void* p, size_t size)
typedef struct {
struct jpeg_destination_mgr pub; /* public fields */
JOCTET* buf;
// jpeg-6b interface needs a memory buffer
DynArray* da;
} DstMgr;
typedef DstMgr* DstPtr;
#define OUTPUT_BUF_SIZE 16*KiB /* choose an efficiently writeable size */
#define OUTPUT_BUF_SIZE 64*KiB /* choose an efficiently writeable size */
// note: can't call dst_empty_output_buffer from dst_init or vice versa
// because only the former must advance da->pos.
static void make_room_in_buffer(j_compress_ptr cinfo)
{
DstPtr dst = (DstPtr)cinfo->dest;
DynArray* da = dst->da;
void* start = da->base + da->cur_size;
if(da_set_size(da, da->cur_size+OUTPUT_BUF_SIZE) != 0)
ERREXIT(cinfo, JERR_FILE_WRITE);
dst->pub.next_output_byte = (JOCTET*)start;
dst->pub.free_in_buffer = OUTPUT_BUF_SIZE;
}
/*
* Initialize destination --- called by jpeg_start_compress
* before any data is actually written.
*/
METHODDEF(void) dst_init(j_compress_ptr cinfo)
{
DstPtr dst = (DstPtr)cinfo->dest;
/* Allocate the output buffer --- it will be released when done with image */
dst->buf = (JOCTET*)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_IMAGE,
OUTPUT_BUF_SIZE * sizeof(JOCTET));
dst->pub.next_output_byte = dst->buf;
dst->pub.free_in_buffer = OUTPUT_BUF_SIZE;
make_room_in_buffer(cinfo);
}
@ -232,34 +239,20 @@ METHODDEF(void) dst_init(j_compress_ptr cinfo)
* reset the pointer & count to the start of the buffer, and return TRUE
* indicating that the buffer has been dumped.
*
* In applications that need to be able to suspend compression due to output
* overrun, a FALSE return indicates that the buffer cannot be emptied now.
* In this situation, the compressor will return to its caller (possibly with
* an indication that it has not accepted all the supplied scanlines). The
* application should resume compression after it has made more room in the
* output buffer. Note that there are substantial restrictions on the use of
* suspension --- see the documentation.
*
* When suspending, the compressor will back up to a convenient restart point
* (typically the start of the current MCU). next_output_byte & free_in_buffer
* indicate where the restart point will be if the current call returns FALSE.
* Data beyond this point will be regenerated after resumption, so do not
* write it out when emptying the buffer externally.
* <snip comments on suspended IO>
*/
METHODDEF(boolean) dst_empty_output_buffer(j_compress_ptr cinfo)
{
DstPtr dst = (DstPtr)cinfo->dest;
DynArray* da = dst->da;
// writing out OUTPUT_BUF_SIZE-dst->pub.free_in_buffer bytes
// sounds reasonable, but makes for broken output.
// if(vfs_io(dst->hf, OUTPUT_BUF_SIZE, (void**)&dst->buf) != OUTPUT_BUF_SIZE)
// ERREXIT(cinfo, JERR_FILE_WRITE);
da->pos += OUTPUT_BUF_SIZE;
dst->pub.next_output_byte = dst->buf;
dst->pub.free_in_buffer = OUTPUT_BUF_SIZE;
make_room_in_buffer(cinfo);
return TRUE;
return TRUE; // not suspended
}
@ -271,17 +264,13 @@ METHODDEF(boolean) dst_empty_output_buffer(j_compress_ptr cinfo)
* application must deal with any cleanup that should happen even
* for error exit.
*/
METHODDEF(void) dst_term(j_compress_ptr cinfo)
{
DstPtr dst = (DstPtr)cinfo->dest;
DynArray* da = dst->da;
// make sure any data left in the buffer is written out
const size_t bytes_in_buf = OUTPUT_BUF_SIZE - dst->pub.free_in_buffer;
// if(vfs_io(dst->hf, bytes_in_buf, (void**)&dst->buf) != (ssize_t)bytes_in_buf)
// ERREXIT(cinfo, JERR_FILE_WRITE);
// flush file, if necessary.
// account for nbytes left in buffer
da->pos += OUTPUT_BUF_SIZE - dst->pub.free_in_buffer;
}
@ -291,7 +280,7 @@ METHODDEF(void) dst_term(j_compress_ptr cinfo)
* for closing it after finishing compression.
*/
GLOBAL(void) dst_prepare(j_compress_ptr cinfo)
GLOBAL(void) dst_prepare(j_compress_ptr cinfo, DynArray* da)
{
/* The destination object is made permanent so that multiple JPEG images
* can be written to the same file without re-executing jpeg_stdio_dest.
@ -308,6 +297,7 @@ GLOBAL(void) dst_prepare(j_compress_ptr cinfo)
dst->pub.init_destination = dst_init;
dst->pub.empty_output_buffer = dst_empty_output_buffer;
dst->pub.term_destination = dst_term;
dst->da = da;
}
@ -396,7 +386,7 @@ JpgErrorMgr::JpgErrorMgr(j_common_ptr cinfo)
//-----------------------------------------------------------------------------
static int jpg_transform(Tex* t, int new_flags)
static int jpg_transform(Tex* UNUSED(t), uint UNUSED(transforms))
{
return TEX_CODEC_CANNOT_HANDLE;
}
@ -457,7 +447,7 @@ static int jpg_decode_impl(Tex* t, u8* file, size_t file_size,
return ERR_NO_MEM;
// read rows
RETURN_ERR(tex_codec_alloc_rows(img, h, pitch, TEX_TOP_DOWN, rows));
RETURN_ERR(tex_codec_alloc_rows(img, h, pitch, TEX_TOP_DOWN, 0, rows));
// could use cinfo->output_scanline to keep track of progress,
// but we need to count lines_left anyway (paranoia).
JSAMPARRAY row = (JSAMPARRAY)rows;
@ -495,9 +485,9 @@ static int jpg_decode_impl(Tex* t, u8* file, size_t file_size,
static int jpg_encode_impl(Tex* t,
jpeg_compress_struct* cinfo,
RowArray& rows, const char** perr_msg)
RowArray& rows, DynArray* da, const char** perr_msg)
{
dst_prepare(cinfo);
dst_prepare(cinfo, da);
// describe image format
// required:
@ -514,12 +504,12 @@ static int jpg_encode_impl(Tex* t,
jpeg_start_compress(cinfo, TRUE);
// if BGR, convert to RGB.
const int bgr_transform = t->flags & TEX_BGR; // JPG is native RGB.
WARN_ERR(tex_transform(t, bgr_transform));
const int transform_bgr = t->flags & TEX_BGR; // JPG is native RGB.
WARN_ERR(tex_transform(t, transform_bgr));
const size_t pitch = t->w * t->bpp / 8;
u8* data = tex_get_data(t);
RETURN_ERR(tex_codec_alloc_rows(data, t->h, pitch, TEX_TOP_DOWN, rows));
RETURN_ERR(tex_codec_alloc_rows(data, t->h, pitch, t->flags, TEX_TOP_DOWN, rows));
// could use cinfo->output_scanline to keep track of progress,
@ -551,6 +541,7 @@ static int jpg_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg)
if(file[0] != 0xff || file[1] == 0xd8)
return TEX_CODEC_CANNOT_HANDLE;
int err = -1;
// freed when ret is reached:
// .. contains the JPEG decompression parameters and pointers to
// working space (allocated as needed by the JPEG library).
@ -572,7 +563,7 @@ fail:
jpeg_create_decompress(&cinfo);
int err = jpg_decode_impl(t, file, file_size, &cinfo, img_hm, rows, perr_msg);
err = jpg_decode_impl(t, file, file_size, &cinfo, img_hm, rows, perr_msg);
if(err < 0)
goto fail;
@ -584,11 +575,12 @@ ret:
// limitation: palette images aren't supported
static int jpg_encode(const char* ext, Tex* t, u8** out, size_t* out_size, const char** perr_msg)
static int jpg_encode(const char* ext, Tex* t, DynArray* da, const char** perr_msg)
{
if(stricmp(ext, "jpg") && stricmp(ext, "jpeg"))
return TEX_CODEC_CANNOT_HANDLE;
int err = -1;
// freed when ret is reached:
// .. contains the JPEG compression parameters and pointers to
// working space (allocated as needed by the JPEG library).
@ -596,9 +588,6 @@ static int jpg_encode(const char* ext, Tex* t, u8** out, size_t* out_size, const
// .. array of pointers to scanlines (see rationale above)
RowArray rows = 0;
// freed when fail is reached:
// (mem buffer)
JpgErrorMgr jerr((j_common_ptr)&cinfo);
if(setjmp(jerr.call_site))
{
@ -609,14 +598,13 @@ fail:
jpeg_create_compress(&cinfo);
int err = jpg_encode_impl(t, &cinfo, rows, perr_msg);
err = jpg_encode_impl(t, &cinfo, rows, da, perr_msg);
if(err < 0)
goto fail;
ret:
jpeg_destroy_compress(&cinfo); // releases a "good deal" of memory
free(rows);
// (free mem buffer)
return err;
}

View File

@ -42,30 +42,22 @@
// pass data from PNG file in memory to libpng
static void io_read(png_struct* png_ptr, u8* data, png_size_t length)
{
MemSource* ms = (MemSource*)png_ptr->io_ptr;
void* src = (u8*)(ms->p + ms->pos);
// make sure there's enough new data remaining in the buffer
ms->pos += length;
if(ms->pos > ms->size)
png_error(png_ptr, "png_read: not enough data to satisfy request!");
memcpy(data, src, length);
DynArray* da = (DynArray*)png_ptr->io_ptr;
if(da_read(da, data, length) != 0)
png_error(png_ptr, "io_read failed");
}
// write libpng output to PNG file
static void io_write(png_struct* png_ptr, u8* data, png_size_t length)
{
void* p = (void*)data;
Handle hf = *(Handle*)png_ptr->io_ptr;
if(vfs_io(hf, length, &p) != (ssize_t)length)
png_error(png_ptr, "png_write: !");
DynArray* da = (DynArray*)png_ptr->io_ptr;
if(da_append(da, data, length) != 0)
png_error(png_ptr, "io_write failed");
}
static void io_flush(png_structp)
static void io_flush(png_structp UNUSED(png_ptr))
{
}
@ -73,7 +65,7 @@ static void io_flush(png_structp)
//-----------------------------------------------------------------------------
static int png_transform(Tex* t, int new_flags)
static int png_transform(Tex* UNUSED(t), uint UNUSED(transforms))
{
return TEX_CODEC_CANNOT_HANDLE;
}
@ -88,8 +80,13 @@ static int png_decode_impl(Tex* t, u8* file, size_t file_size,
png_structp png_ptr, png_infop info_ptr,
Handle& img_hm, RowArray& rows, const char** perr_msg)
{
MemSource ms = { file, file_size, 0 };
png_set_read_fn(png_ptr, &ms, io_read);
DynArray da;
da.base = file;
da.max_size_pa = round_up(file_size, 4096);
da.cur_size = file_size;
da.pos = 0;
da.prot = PROT_READ|PROT_WRITE;
png_set_read_fn(png_ptr, &da, io_read);
// read header and determine format
png_read_info(png_ptr, info_ptr);
@ -97,7 +94,7 @@ static int png_decode_impl(Tex* t, u8* file, size_t file_size,
int bit_depth, colour_type;
png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &colour_type, 0, 0, 0);
const size_t pitch = png_get_rowbytes(png_ptr, info_ptr);
const u32 bpp = (u32)(pitch / w * 8);
const u32 bpp = (u32)(pitch/w * 8);
int flags = 0;
if(bpp == 32)
@ -122,13 +119,13 @@ static int png_decode_impl(Tex* t, u8* file, size_t file_size,
if(!img)
return ERR_NO_MEM;
CHECK_ERR(tex_codec_alloc_rows(img, h, pitch, TEX_TOP_DOWN, rows));
RETURN_ERR(tex_codec_alloc_rows(img, h, pitch, TEX_TOP_DOWN, 0, rows));
png_read_image(png_ptr, (png_bytepp)rows);
png_read_end(png_ptr, info_ptr);
// success; make sure all data was consumed.
debug_assert(ms.p == file && ms.size == file_size && ms.pos == ms.size);
debug_assert(da.base == file && da.cur_size == file_size && da.pos == da.cur_size);
// store image info
// .. transparently switch handles - free the old (compressed)
@ -149,13 +146,10 @@ static int png_decode_impl(Tex* t, u8* file, size_t file_size,
// "dtor / setjmp interaction" warning.
static int png_encode_impl(Tex* t,
png_structp png_ptr, png_infop info_ptr,
RowArray& rows, const char** perr_msg)
RowArray& rows, DynArray* da, const char** perr_msg)
{
UNUSED2(perr_msg); // we don't produce any error messages ATM.
const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
// PNG is native RGB.
const png_uint_32 w = t->w, h = t->h;
const size_t pitch = w * t->bpp / 8;
@ -175,16 +169,16 @@ static int png_encode_impl(Tex* t,
colour_type = PNG_COLOR_TYPE_RGB;
break;
}
/*
hf = vfs_open(fn, FILE_WRITE|FILE_NO_AIO);
CHECK_ERR(hf);
png_set_write_fn(png_ptr, &hf, io_write, io_flush);
*/
png_set_write_fn(png_ptr, da, io_write, io_flush);
png_set_IHDR(png_ptr, info_ptr, w, h, 8, colour_type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
u8* data = tex_get_data(t);
CHECK_ERR(tex_codec_alloc_rows(data, h, pitch, TEX_TOP_DOWN, rows));
RETURN_ERR(tex_codec_alloc_rows(data, h, pitch, t->flags, TEX_TOP_DOWN, rows));
// PNG is native RGB.
const int png_transforms = (t->flags & TEX_BGR)? PNG_TRANSFORM_BGR : PNG_TRANSFORM_IDENTITY;
png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
png_write_png(png_ptr, info_ptr, png_transforms, 0);
@ -202,6 +196,7 @@ static int png_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg)
if(*(u32*)file != FOURCC('\x89','P','N','G'))
return TEX_CODEC_CANNOT_HANDLE;
int err = -1;
// freed when ret is reached:
png_structp png_ptr = 0;
png_infop info_ptr = 0;
@ -221,13 +216,13 @@ static int png_decode(u8* file, size_t file_size, Tex* t, const char** perr_msg)
// setup error handling
if(setjmp(png_jmpbuf(png_ptr)))
{
// libpng longjmp-ed here after an error, or code below failed.
// libpng longjmps here after an error
fail:
mem_free_h(img_hm);
goto ret;
}
int err = png_decode_impl(t, file, file_size, png_ptr, info_ptr, img_hm, rows, perr_msg);
err = png_decode_impl(t, file, file_size, png_ptr, info_ptr, img_hm, rows, perr_msg);
if(err < 0)
goto fail;
@ -240,19 +235,17 @@ ret:
// limitation: palette images aren't supported
static int png_encode(const char* ext, Tex* t, u8** out, size_t* UNUSED(out_size), const char** perr_msg)
static int png_encode(const char* ext, Tex* t, DynArray* da, const char** perr_msg)
{
if(stricmp(ext, "png"))
return TEX_CODEC_CANNOT_HANDLE;
int err = -1;
// freed when ret is reached:
png_structp png_ptr = 0;
png_infop info_ptr = 0;
RowArray rows = 0;
// free when fail is reached:
// (mem buffer)
// allocate PNG structures; use default stderr and longjmp error handlers
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if(!png_ptr)
@ -264,21 +257,20 @@ static int png_encode(const char* ext, Tex* t, u8** out, size_t* UNUSED(out_size
// setup error handling
if(setjmp(png_jmpbuf(png_ptr)))
{
// libpng longjmps here after an error
fail:
// libjpg longjmp-ed here after an error, or code below failed.
// currently no cleanup to be done.
goto ret;
}
int err = png_encode_impl(t, png_ptr, info_ptr, rows, perr_msg);
err = png_encode_impl(t, png_ptr, info_ptr, rows, da, perr_msg);
if(err < 0)
goto fail;
// shared cleanup
ret:
png_destroy_write_struct(&png_ptr, &info_ptr);
free(rows);
return err;
}

View File

@ -41,7 +41,7 @@ TgaHeader;
#pragma pack(pop)
static int tga_transform(Tex* t, int new_flags)
static int tga_transform(Tex* UNUSED(t), uint UNUSED(transforms))
{
return TEX_CODEC_CANNOT_HANDLE;
}
@ -78,9 +78,7 @@ fail:
const u8 alpha_bits = desc & 0x0f;
const int orientation = (desc & TGA_TOP_DOWN)? TEX_TOP_DOWN : TEX_BOTTOM_UP;
u8* const img = file + hdr_size;
const size_t img_size = w * h * bpp/8;
const size_t img_size = tex_img_size(t);
int flags = 0;
if(alpha_bits != 0)
@ -118,7 +116,7 @@ fail:
}
static int tga_encode(const char* ext, Tex* t, u8** out, size_t* out_size, const char** perr_msg)
static int tga_encode(const char* ext, Tex* t, DynArray* da, const char** UNUSED(perr_msg))
{
if(stricmp(ext, "tga"))
return TEX_CODEC_CANNOT_HANDLE;
@ -130,11 +128,9 @@ static int tga_encode(const char* ext, Tex* t, u8** out, size_t* out_size, const
img_desc |= 8; // size of alpha channel
TgaImgType img_type = (t->flags & TEX_GREY)? TGA_GREY : TGA_TRUE_COLOUR;
// transform
int transforms = t->flags;
transforms &= ~TEX_ORIENTATION; // no flip needed - we can set top-down bit.
transforms ^= TEX_BGR; // TGA is native BGR.
tex_transform(t, transforms);
const TgaHeader hdr =
{
@ -148,8 +144,8 @@ static int tga_encode(const char* ext, Tex* t, u8** out, size_t* out_size, const
t->bpp,
img_desc
};
return 0;
const size_t hdr_size = sizeof(hdr);
return tex_codec_write(t, transforms, &hdr, hdr_size, da);
}
TEX_CODEC_REGISTER(tga);

View File

@ -242,12 +242,15 @@ int debug_is_pointer_bogus(const void* p)
return true;
#endif
// note: we don't check alignment because nothing can be assumed about a
// pointer to the middle of a string and we mustn't reject valid pointers.
// nor do we bother checking the address against known stack/heap areas
// because that doesn't cover everything (e.g. DLLs, VirtualAlloc, etc.).
// notes:
// - we don't check alignment because nothing can be assumed about a
// string pointer and we mustn't reject any actually valid pointers.
// - nor do we bother checking the address against known stack/heap areas
// because that doesn't cover everything (e.g. DLLs, VirtualAlloc).
// - cannot use IsBadReadPtr because it accesses the mem
// (false alarm for reserved address space).
return IsBadReadPtr(p, 1) != 0;
return false;
}

View File

@ -244,8 +244,8 @@ static int dll_list_add(const char* name)
// didn't fit; complain
sprintf(dll_list_pos, "..."); // (room was reserved above)
debug_warn("dll_list_add: not enough room");
dll_list_pos = 0; // poison pill, prevent further calls
debug_warn("dll_list_add: not enough room");
return ERR_BUF_SIZE;
}
@ -376,7 +376,7 @@ int win_get_gfx_info()
// ensures each OpenAL DLL is only listed once (even if present in several
// directories on our search path).
typedef std::set<std::string> DllSet;
typedef std::set<std::string> StringSet;
// if this directory entry is an OpenAL DLL, add it to our list.
// (matches "*oal.dll" and "*OpenAL*", as with OpenAL router's search)
@ -385,7 +385,7 @@ typedef std::set<std::string> DllSet;
// note: we need the full DLL path for dll_list_add but DirEnt only gives us
// the name. for efficiency, we append this in a PathPackage allocated by
// add_oal_dlls_in_dir.
static void add_if_oal_dll(const DirEnt* ent, PathPackage* pp, DllSet* dlls)
static void add_if_oal_dll(const DirEnt* ent, PathPackage* pp, StringSet* dlls)
{
const char* fn = ent->name;
@ -400,13 +400,13 @@ static void add_if_oal_dll(const DirEnt* ent, PathPackage* pp, DllSet* dlls)
if(!oal && !openal)
return;
// skip if already in DllSet (i.e. has already been dll_list_add-ed)
std::pair<DllSet::iterator, bool> ret = dlls->insert(fn);
// skip if already in StringSet (i.e. has already been dll_list_add-ed)
std::pair<StringSet::iterator, bool> ret = dlls->insert(fn);
if(!ret.second) // insert failed - element already there
return;
WARN_ERR_RETURN(pp_append_file(pp, fn));
dll_list_add(pp->path);
WARN_ERR_RETURN(pp_append_file(pp, fn)); // todo4
(void)dll_list_add(pp->path);
}
@ -416,7 +416,7 @@ static void add_if_oal_dll(const DirEnt* ent, PathPackage* pp, DllSet* dlls)
// same name in the system directory.
//
// <dir>: no trailing.
static void add_oal_dlls_in_dir(const char* dir, DllSet* dlls)
static void add_oal_dlls_in_dir(const char* dir, StringSet* dlls)
{
int err;
@ -424,7 +424,7 @@ static void add_oal_dlls_in_dir(const char* dir, DllSet* dlls)
(void)pp_set_dir(&pp, dir);
DirIterator d;
WARN_ERR_RETURN(dir_open(dir, &d));
WARN_ERR_RETURN(dir_open(dir, &d)); // TODO4
DirEnt ent;
for(;;) // instead of while to avoid warning
@ -432,11 +432,10 @@ static void add_oal_dlls_in_dir(const char* dir, DllSet* dlls)
err = dir_next_ent(&d, &ent);
if(err == ERR_DIR_END)
break;
debug_assert(err == 0);
add_if_oal_dll(&ent, &pp, dlls);
}
WARN_ERR(dir_close(&d));
(void)dir_close(&d);
}
@ -483,7 +482,7 @@ int win_get_snd_info()
// and store in snd_drv_ver string.
dll_list_init(snd_drv_ver, SND_DRV_VER_LEN);
dll_list_add(ds_drv_path);
std::set<std::string> dlls; // ensures uniqueness
StringSet dlls; // ensures uniqueness
add_oal_dlls_in_dir(win_exe_dir, &dlls);
add_oal_dlls_in_dir(win_sys_dir, &dlls);
return 0;

View File

@ -615,12 +615,17 @@ int mprotect(void* addr, size_t len, int prot)
const DWORD flNewProtect = win32_prot(prot);
DWORD flOldProtect; // required by VirtualProtect
BOOL ok = VirtualProtect(addr, len, flNewProtect, &flOldProtect);
return ok? 0 : -1;
if(!ok)
{
debug_warn("mprotect failed"); // todo4
return -1;
}
return 0;
}
// called when flags & MAP_ANONYMOUS
static int map_mem(void* start, size_t len, int prot, int flags, int fd, void** pp)
static int mmap_mem(void* start, size_t len, int prot, int flags, int fd, void** pp)
{
// sanity checks. we don't care about these but enforce them to
// ensure callers are compatible with mmap.
@ -631,9 +636,27 @@ static int map_mem(void* start, size_t len, int prot, int flags, int fd, void**
debug_assert(flags & MAP_PRIVATE);
// see explanation at MAP_NORESERVE definition.
DWORD flAllocationType = (flags & MAP_NORESERVE)? MEM_RESERVE : MEM_COMMIT;
bool want_commit = (prot != PROT_NONE && !(flags & MAP_NORESERVE));
if(!want_commit && start != 0 && flags & MAP_FIXED)
{
MEMORY_BASIC_INFORMATION mbi;
BOOL ok = VirtualQuery(start, &mbi, len);
debug_assert(ok); // todo4
DWORD state = mbi.State;
if(state == MEM_COMMIT)
{
ok = VirtualFree(start, len, MEM_DECOMMIT);
debug_assert(ok);
*pp = 0;
return 0;
}
}
DWORD op = want_commit? MEM_COMMIT : MEM_RESERVE;
DWORD flProtect = win32_prot(prot);
void* p = VirtualAlloc(start, len, flAllocationType, flProtect);
void* p = VirtualAlloc(start, len, op, flProtect);
if(!p)
return ERR_NO_MEM;
*pp = p;
@ -645,7 +668,7 @@ static int map_mem(void* start, size_t len, int prot, int flags, int fd, void**
// CreateFileMapping / MapViewOfFile. they only support read-only,
// read/write and copy-on-write, so we dumb it down to that and later
// set the correct (and more restrictive) permission via mprotect.
static int map_file_access(int prot, int flags, DWORD& flProtect, DWORD& dwAccess)
static int mmap_file_access(int prot, int flags, DWORD& flProtect, DWORD& dwAccess)
{
// assume read-only; other cases handled below.
flProtect = PAGE_READONLY;
@ -678,7 +701,7 @@ static int map_file_access(int prot, int flags, DWORD& flProtect, DWORD& dwAcces
}
static int map_file(void* start, size_t len, int prot, int flags,
static int mmap_file(void* start, size_t len, int prot, int flags,
int fd, off_t ofs, void** pp)
{
debug_assert(fd != -1); // handled by mmap_mem
@ -698,7 +721,7 @@ static int map_file(void* start, size_t len, int prot, int flags,
// MapViewOfFile. these are weaker than what PROT_* allows and
// are augmented below by subsequently mprotect-ing.
DWORD flProtect; DWORD dwAccess;
RETURN_ERR(map_file_access(prot, flags, flProtect, dwAccess));
RETURN_ERR(mmap_file_access(prot, flags, flProtect, dwAccess));
// enough foreplay; now actually map.
const HANDLE hMap = CreateFileMapping(hFile, 0, flProtect, 0, 0, (LPCSTR)0);
@ -718,7 +741,7 @@ static int map_file(void* start, size_t len, int prot, int flags,
return ERR_NO_MEM;
// slap on correct (more restrictive) permissions.
WARN_ERR(mprotect(p, len, prot));
(void)mprotect(p, len, prot);
WIN_RESTORE_LAST_ERROR;
*pp = p;
@ -731,9 +754,9 @@ void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t ofs)
void* p;
int err;
if(flags & MAP_ANONYMOUS)
err = map_mem(start, len, prot, flags, fd, &p);
err = mmap_mem(start, len, prot, flags, fd, &p);
else
err = map_file(start, len, prot, flags, fd, ofs, &p);
err = mmap_file(start, len, prot, flags, fd, ofs, &p);
if(err < 0)
{
WARN_ERR(err);
@ -756,7 +779,7 @@ int munmap(void* start, size_t UNUSED(len))
// both failed
if(!ok)
{
debug_warn("munmap failed");
debug_warn("munmap failed"); // todo4
return -1;
}
return 0;
@ -925,6 +948,8 @@ long sysconf(int name)
switch(name)
{
case _SC_PAGESIZE:
// note: don't add _SC_PAGE_SIZE - they are different names but
// have the same value.
return page_size;
case _SC_PHYS_PAGES:

View File

@ -625,8 +625,10 @@ void Shutdown()
delete &g_Mouseover;
delete &g_Selection;
delete &g_BuildingPlacer;
delete &g_Pathfinder;
// Managed by CWorld
// delete &g_EntityManager;

View File

@ -169,12 +169,12 @@ void WriteScreenshot(const char* extension)
// could fix via enumerating all files, but it's not worth it ATM.
char fn[VFS_MAX_PATH];
const char* file_format_string = "screenshots/screenshot%04d.%s";
// %04d -> always 4 digits, so sorting by filename works correctly.
const char* file_format_string = "screenshots/screenshot%04d.%s";
static int next_num = 1;
do
sprintf(fn, file_format_string, next_num++, extension);
sprintf(fn, file_format_string, next_num++, extension);
while(vfs_exists(fn));
const int w = g_xres, h = g_yres;

View File

@ -744,7 +744,7 @@ JSBool getGlobal( JSContext* cx, JSObject* globalObject, uint argc, jsval* argv,
// Activates the building placement cursor for placing a building.
// params: templateName - the name of the entity to place.
// returns: true if cursor was activated, false if cursor was already active.
JSBool startPlacing( JSContext* cx, JSObject* globalObject, uint argc, jsval* argv, jsval* rval )
JSBool startPlacing( JSContext* cx, JSObject* UNUSED(globalObject), uint argc, jsval* argv, jsval* rval )
{
CStrW name;
if(argc == 0) {