forked from 0ad/0ad
document DynArray and add self-test. fix 2 tiny little bugs.
This was SVN commit r3083.
This commit is contained in:
parent
d3441f3f48
commit
e527c66fda
@ -1,3 +1,20 @@
|
|||||||
|
// suballocators
|
||||||
|
// Copyright (c) 2005 Jan Wassenberg
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// Contact info:
|
||||||
|
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||||
|
// http://www.stud.uni-karlsruhe.de/~urkt/
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
#include "posix.h"
|
#include "posix.h"
|
||||||
|
|
||||||
@ -5,7 +22,7 @@
|
|||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// expandable array
|
// dynamic (expandable) array
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static const size_t page_size = sysconf(_SC_PAGE_SIZE);
|
static const size_t page_size = sysconf(_SC_PAGE_SIZE);
|
||||||
@ -100,6 +117,10 @@ static int mem_protect(u8* p, size_t size, int prot)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// API
|
// API
|
||||||
|
|
||||||
|
// ready the DynArray object for use. preallocates max_size bytes
|
||||||
|
// (rounded up to the next page size multiple) of address space for the
|
||||||
|
// array; it can never grow beyond this.
|
||||||
|
// no virtual memory is actually committed until calls to da_set_size.
|
||||||
int da_alloc(DynArray* da, size_t max_size)
|
int da_alloc(DynArray* da, size_t max_size)
|
||||||
{
|
{
|
||||||
const size_t max_size_pa = round_up_to_page(max_size);
|
const size_t max_size_pa = round_up_to_page(max_size);
|
||||||
@ -110,46 +131,56 @@ int da_alloc(DynArray* da, size_t max_size)
|
|||||||
da->base = p;
|
da->base = p;
|
||||||
da->max_size_pa = max_size_pa;
|
da->max_size_pa = max_size_pa;
|
||||||
da->cur_size = 0;
|
da->cur_size = 0;
|
||||||
da->pos = 0;
|
|
||||||
da->prot = PROT_READ|PROT_WRITE;
|
da->prot = PROT_READ|PROT_WRITE;
|
||||||
|
da->pos = 0;
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// "wrap" (i.e. store information about) the given buffer in a
|
||||||
|
// DynArray object, preparing it for use with da_read or da_append.
|
||||||
|
// da_free should be called when the DynArray is no longer needed,
|
||||||
|
// even though it doesn't free this memory (but does zero the DynArray).
|
||||||
int da_wrap_fixed(DynArray* da, u8* p, size_t size)
|
int da_wrap_fixed(DynArray* da, u8* p, size_t size)
|
||||||
{
|
{
|
||||||
da->base = p;
|
da->base = p;
|
||||||
da->max_size_pa = round_up_to_page(size);
|
da->max_size_pa = round_up_to_page(size);
|
||||||
da->cur_size = size;
|
da->cur_size = size;
|
||||||
da->pos = 0;
|
|
||||||
da->prot = PROT_READ|PROT_WRITE|DA_NOT_OUR_MEM;
|
da->prot = PROT_READ|PROT_WRITE|DA_NOT_OUR_MEM;
|
||||||
|
da->pos = 0;
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// free all memory (address space + physical) that constitutes the
|
||||||
|
// given array. use-after-free is impossible because the memory is
|
||||||
|
// marked not-present via MMU. also zeroes the contents of <da>.
|
||||||
int da_free(DynArray* da)
|
int da_free(DynArray* da)
|
||||||
{
|
{
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
|
|
||||||
if(da->prot & DA_NOT_OUR_MEM)
|
u8* p = da->base;
|
||||||
{
|
size_t size = da->max_size_pa;
|
||||||
debug_warn("da is marked DA_NOT_OUR_MEM, must not be altered");
|
bool was_wrapped = (da->prot & DA_NOT_OUR_MEM) != 0;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// latch pointer; wipe out the DynArray for safety
|
// wipe out the DynArray for safety
|
||||||
// (must be done here because mem_release may fail)
|
// (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));
|
memset(da, 0, sizeof(*da));
|
||||||
|
|
||||||
CHECK_ERR(mem_release(p, size));
|
// skip mem_release if <da> was allocated via da_wrap_fixed
|
||||||
|
// (i.e. it doesn't actually own any memory). don't complain;
|
||||||
|
// da_free is supposed to be called even in the above case.
|
||||||
|
if(!was_wrapped)
|
||||||
|
CHECK_ERR(mem_release(p, size));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// expand or shrink the array: changes the amount of currently committed
|
||||||
|
// (i.e. usable) memory pages. pages are added/removed until
|
||||||
|
// new_size (rounded up to the next page size multiple) is met.
|
||||||
int da_set_size(DynArray* da, size_t new_size)
|
int da_set_size(DynArray* da, size_t new_size)
|
||||||
{
|
{
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
@ -164,7 +195,7 @@ int da_set_size(DynArray* da, size_t new_size)
|
|||||||
const size_t cur_size_pa = round_up_to_page(da->cur_size);
|
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);
|
const size_t new_size_pa = round_up_to_page(new_size);
|
||||||
if(new_size_pa > da->max_size_pa)
|
if(new_size_pa > da->max_size_pa)
|
||||||
CHECK_ERR(ERR_INVALID_PARAM);
|
CHECK_ERR(ERR_LIMIT);
|
||||||
const ssize_t size_delta_pa = (ssize_t)new_size_pa - (ssize_t)cur_size_pa;
|
const ssize_t size_delta_pa = (ssize_t)new_size_pa - (ssize_t)cur_size_pa;
|
||||||
|
|
||||||
u8* end = da->base + cur_size_pa;
|
u8* end = da->base + cur_size_pa;
|
||||||
@ -183,6 +214,10 @@ int da_set_size(DynArray* da, size_t new_size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// change access rights of the array memory; used to implement
|
||||||
|
// write-protection. affects the currently committed pages as well as
|
||||||
|
// all subsequently added pages.
|
||||||
|
// prot can be a combination of the PROT_* values used with mprotect.
|
||||||
int da_set_prot(DynArray* da, int prot)
|
int da_set_prot(DynArray* da, int prot)
|
||||||
{
|
{
|
||||||
CHECK_DA(da);
|
CHECK_DA(da);
|
||||||
@ -203,20 +238,22 @@ int da_set_prot(DynArray* da, int prot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// "read" from array, i.e. copy into the given buffer.
|
||||||
|
// starts at offset DynArray.pos and advances this.
|
||||||
int da_read(DynArray* da, void* data, size_t size)
|
int da_read(DynArray* da, void* data, size_t size)
|
||||||
{
|
{
|
||||||
void* src = da->base + da->pos;
|
|
||||||
|
|
||||||
// make sure we have enough data to read
|
// make sure we have enough data to read
|
||||||
da->pos += size;
|
if(da->pos+size > da->cur_size)
|
||||||
if(da->pos > da->cur_size)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
memcpy2(data, src, size);
|
memcpy2(data, da->base+da->pos, size);
|
||||||
|
da->pos += size;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// "write" to array, i.e. copy from the given buffer.
|
||||||
|
// starts at offset DynArray.pos and advances this.
|
||||||
int da_append(DynArray* da, const void* data, size_t size)
|
int da_append(DynArray* da, const void* data, size_t size)
|
||||||
{
|
{
|
||||||
RETURN_ERR(da_set_size(da, da->pos+size));
|
RETURN_ERR(da_set_size(da, da->pos+size));
|
||||||
@ -230,16 +267,11 @@ int da_append(DynArray* da, const void* data, size_t size)
|
|||||||
// pool allocator
|
// pool allocator
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// design goals: O(1) alloc and free; doesn't preallocate the entire pool;
|
// design parameters:
|
||||||
// returns sequential addresses.
|
// - O(1) alloc and free;
|
||||||
//
|
// - fixed-size blocks;
|
||||||
// (note: this allocator returns fixed-size blocks, the size of which is
|
// - doesn't preallocate the entire pool;
|
||||||
// specified at pool_create time. this makes O(1) time possible.)
|
// - returns sequential addresses.
|
||||||
|
|
||||||
// parameters:
|
|
||||||
// - fixed-size allocations
|
|
||||||
// - can free/reuse allocations
|
|
||||||
|
|
||||||
|
|
||||||
// "freelist" is a pointer to the first unused element (0 if there are none);
|
// "freelist" is a pointer to the first unused element (0 if there are none);
|
||||||
// its memory holds a pointer to the next free one in list.
|
// its memory holds a pointer to the next free one in list.
|
||||||
@ -349,16 +381,17 @@ void pool_free(Pool* p, void* el)
|
|||||||
// bucket allocator
|
// bucket allocator
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// parameters:
|
// design goals:
|
||||||
// - variable-size allocations
|
// - variable-size allocations;
|
||||||
// - can only free all allocations at once
|
// - no reuse of allocations, can only free all at once;
|
||||||
// - no init necessary
|
// - no init necessary;
|
||||||
// - no fixed limit
|
// - no fixed limit.
|
||||||
|
|
||||||
// must be constant and power-of-2 to allow fast modulo.
|
// must be constant and power-of-2 to allow fast modulo.
|
||||||
const size_t BUCKET_SIZE = 4*KiB;
|
const size_t BUCKET_SIZE = 4*KiB;
|
||||||
|
|
||||||
|
// allocate <size> bytes of memory from the given Bucket object.
|
||||||
|
// <b> must initially be zeroed (e.g. by defining it as static data).
|
||||||
void* bucket_alloc(Bucket* b, size_t size)
|
void* bucket_alloc(Bucket* b, size_t size)
|
||||||
{
|
{
|
||||||
// would overflow a bucket
|
// would overflow a bucket
|
||||||
@ -392,6 +425,7 @@ void* bucket_alloc(Bucket* b, size_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// free all allocations that ensued from the given Bucket.
|
||||||
void bucket_free_all(Bucket* b)
|
void bucket_free_all(Bucket* b)
|
||||||
{
|
{
|
||||||
while(b->bucket)
|
while(b->bucket)
|
||||||
@ -472,8 +506,24 @@ void matrix_free(void** matrix)
|
|||||||
#if SELF_TEST_ENABLED
|
#if SELF_TEST_ENABLED
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
static void test_api()
|
static void test_da()
|
||||||
{
|
{
|
||||||
|
DynArray da;
|
||||||
|
|
||||||
|
// basic test of functionality (not really meaningful)
|
||||||
|
TEST(da_alloc(&da, 1000) == 0);
|
||||||
|
TEST(da_set_size(&da, 1000) == 0);
|
||||||
|
TEST(da_set_prot(&da, PROT_NONE) == 0);
|
||||||
|
TEST(da_free(&da) == 0);
|
||||||
|
|
||||||
|
// test wrapping existing mem blocks for use with da_read
|
||||||
|
const u8 data[4] = { 0x12, 0x34, 0x56, 0x78 };
|
||||||
|
TEST(da_wrap_fixed(&da, data, sizeof(data)) == 0);
|
||||||
|
u8 buf[4];
|
||||||
|
TEST(da_read(&da, buf, 4) == 0); // success
|
||||||
|
TEST(read_le32(buf) == 0x78563412); // read correct value
|
||||||
|
TEST(da_read(&da, buf, 1) < 0); // no more data left
|
||||||
|
TEST(da_free(&da) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_expand()
|
static void test_expand()
|
||||||
@ -494,7 +544,7 @@ static void test_matrix()
|
|||||||
|
|
||||||
static void self_test()
|
static void self_test()
|
||||||
{
|
{
|
||||||
test_api();
|
test_da();
|
||||||
test_expand();
|
test_expand();
|
||||||
test_matrix();
|
test_matrix();
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,80 @@
|
|||||||
|
// suballocators
|
||||||
|
// Copyright (c) 2005 Jan Wassenberg
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License as
|
||||||
|
// published by the Free Software Foundation; either version 2 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful, but
|
||||||
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// General Public License for more details.
|
||||||
|
//
|
||||||
|
// Contact info:
|
||||||
|
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||||
|
// http://www.stud.uni-karlsruhe.de/~urkt/
|
||||||
|
|
||||||
#ifndef ALLOCATORS_H__
|
#ifndef ALLOCATORS_H__
|
||||||
#define ALLOCATORS_H__
|
#define ALLOCATORS_H__
|
||||||
|
|
||||||
#include "lib/types.h"
|
#include "lib/types.h"
|
||||||
#include "lib/posix.h" // PROT_* constants for da_set_prot
|
#include "lib/posix.h" // PROT_* constants for da_set_prot
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// dynamic (expandable) array
|
||||||
|
//
|
||||||
|
|
||||||
|
// provides a memory range that can be expanded but doesn't waste
|
||||||
|
// physical memory or relocate itself. building block for other allocators.
|
||||||
|
|
||||||
struct DynArray
|
struct DynArray
|
||||||
{
|
{
|
||||||
u8* base;
|
u8* base;
|
||||||
size_t max_size_pa; // reserved
|
size_t max_size_pa; // reserved
|
||||||
size_t cur_size; // committed
|
size_t cur_size; // committed
|
||||||
size_t pos;
|
|
||||||
int prot; // applied to newly committed pages
|
int prot; // applied to newly committed pages
|
||||||
|
|
||||||
|
size_t pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ready the DynArray object for use. preallocates max_size bytes
|
||||||
|
// (rounded up to the next page size multiple) of address space for the
|
||||||
|
// array; it can never grow beyond this.
|
||||||
|
// no virtual memory is actually committed until calls to da_set_size.
|
||||||
extern int da_alloc(DynArray* da, size_t max_size);
|
extern int da_alloc(DynArray* da, size_t max_size);
|
||||||
|
|
||||||
|
// free all memory (address space + physical) that constitutes the
|
||||||
|
// given array. use-after-free is impossible because the memory is
|
||||||
|
// marked not-present via MMU. also zeroes the contents of <da>.
|
||||||
extern int da_free(DynArray* da);
|
extern int da_free(DynArray* da);
|
||||||
|
|
||||||
|
// expand or shrink the array: changes the amount of currently committed
|
||||||
|
// (i.e. usable) memory pages. pages are added/removed until
|
||||||
|
// new_size (rounded up to the next page size multiple) is met.
|
||||||
extern int da_set_size(DynArray* da, size_t new_size);
|
extern int da_set_size(DynArray* da, size_t new_size);
|
||||||
|
|
||||||
|
// change access rights of the array memory; used to implement
|
||||||
|
// write-protection. affects the currently committed pages as well as
|
||||||
|
// all subsequently added pages.
|
||||||
|
// prot can be a combination of the PROT_* values used with mprotect.
|
||||||
extern int da_set_prot(DynArray* da, int prot);
|
extern int da_set_prot(DynArray* da, int prot);
|
||||||
|
|
||||||
|
// "wrap" (i.e. store information about) the given buffer in a
|
||||||
|
// DynArray object, preparing it for use with da_read or da_append.
|
||||||
|
// da_free should be called when the DynArray is no longer needed,
|
||||||
|
// even though it doesn't free this memory (but does zero the DynArray).
|
||||||
extern int da_wrap_fixed(DynArray* da, u8* p, size_t size);
|
extern int da_wrap_fixed(DynArray* da, u8* p, size_t size);
|
||||||
|
|
||||||
|
// "read" from array, i.e. copy into the given buffer.
|
||||||
|
// starts at offset DynArray.pos and advances this.
|
||||||
extern int da_read(DynArray* da, void* data_dst, size_t size);
|
extern int da_read(DynArray* da, void* data_dst, size_t size);
|
||||||
|
|
||||||
|
// "write" to array, i.e. copy from the given buffer.
|
||||||
|
// starts at offset DynArray.pos and advances this.
|
||||||
extern int da_append(DynArray* da, const void* data_src, size_t size);
|
extern int da_append(DynArray* da, const void* data_src, size_t size);
|
||||||
|
|
||||||
|
|
||||||
@ -34,11 +83,11 @@ extern int da_append(DynArray* da, const void* data_src, size_t size);
|
|||||||
// pool allocator
|
// pool allocator
|
||||||
//
|
//
|
||||||
|
|
||||||
// design goals: O(1) alloc and free; doesn't preallocate the entire pool;
|
// design parameters:
|
||||||
// returns sequential addresses.
|
// - O(1) alloc and free;
|
||||||
//
|
// - fixed-size blocks;
|
||||||
// (note: this allocator returns fixed-size blocks, the size of which is
|
// - doesn't preallocate the entire pool;
|
||||||
// specified at pool_create time. this makes O(1) time possible.)
|
// - returns sequential addresses.
|
||||||
|
|
||||||
// opaque! do not read/write any fields!
|
// opaque! do not read/write any fields!
|
||||||
struct Pool
|
struct Pool
|
||||||
@ -82,6 +131,13 @@ extern void pool_free(Pool* p, void* el);
|
|||||||
// bucket allocator
|
// bucket allocator
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// design goals:
|
||||||
|
// - variable-size allocations;
|
||||||
|
// - no reuse of allocations, can only free all at once;
|
||||||
|
// - no init necessary;
|
||||||
|
// - no fixed limit.
|
||||||
|
|
||||||
|
// opaque! do not read/write any fields!
|
||||||
struct Bucket
|
struct Bucket
|
||||||
{
|
{
|
||||||
// currently open bucket. must be initialized to 0.
|
// currently open bucket. must be initialized to 0.
|
||||||
@ -97,8 +153,11 @@ struct Bucket
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// allocate <size> bytes of memory from the given Bucket object.
|
||||||
|
// <b> must initially be zeroed (e.g. by defining it as static data).
|
||||||
extern void* bucket_alloc(Bucket* b, size_t size);
|
extern void* bucket_alloc(Bucket* b, size_t size);
|
||||||
|
|
||||||
|
// free all allocations that ensued from the given Bucket.
|
||||||
extern void bucket_free_all(Bucket* b);
|
extern void bucket_free_all(Bucket* b);
|
||||||
|
|
||||||
|
|
||||||
@ -125,8 +184,29 @@ extern void** matrix_alloc(uint cols, uint rows, size_t el_size);
|
|||||||
extern void matrix_free(void** matrix);
|
extern void matrix_free(void** matrix);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// overrun protection
|
||||||
|
//
|
||||||
|
|
||||||
|
// this class wraps an arbitrary object in DynArray memory and can detect
|
||||||
|
// inadvertent writes to it. this is useful for tracking down memory overruns.
|
||||||
|
//
|
||||||
|
// the basic idea is to require users to request access to the object and
|
||||||
|
// notify us when done; memory access permission is temporarily granted.
|
||||||
|
// (similar in principle to Software Transaction Memory).
|
||||||
|
//
|
||||||
|
// since this is quite slow, the protection is disabled unless
|
||||||
|
// CONFIG_OVERRUN_PROTECTION == 1; this avoids having to remove the
|
||||||
|
// wrapper code in release builds and re-write when looking for overruns.
|
||||||
|
//
|
||||||
|
// example usage:
|
||||||
|
// OverrunProtector<your_class> your_class_wrapper;
|
||||||
|
// ..
|
||||||
|
// your_class* yc = your_class_wrapper.get();
|
||||||
|
// if(!yc) abort(); // not enough memory to allocate a your_class instance
|
||||||
|
// // access/write to <yc>
|
||||||
|
// your_class_wrapper.lock(); // disallow further access
|
||||||
|
// ..
|
||||||
template<class T> class OverrunProtector
|
template<class T> class OverrunProtector
|
||||||
{
|
{
|
||||||
DynArray da;
|
DynArray da;
|
||||||
|
@ -427,6 +427,8 @@ static int tex_load_impl(void* file_, size_t file_size, Tex* t)
|
|||||||
|
|
||||||
RETURN_ERR(c->decode(&da, t));
|
RETURN_ERR(c->decode(&da, t));
|
||||||
|
|
||||||
|
(void)da_free(&da); // for completeness only; just zeros <da>
|
||||||
|
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if(!t->w || !t->h || t->bpp > 32)
|
if(!t->w || !t->h || t->bpp > 32)
|
||||||
return ERR_TEX_FMT_INVALID;
|
return ERR_TEX_FMT_INVALID;
|
||||||
|
Loading…
Reference in New Issue
Block a user