blargg said:
Chris said:
blargg said:
:
Here is the code all wrapped up in a header to make it easier to use:
http://pastebin.com/f37a23918
[...]
http://groups.google.com/group/comp.lang.c/browse_frm/thread/97a1c978a0f20195 [...]
why not write this as the following?
#define RALLOC_ALIGN_UP(mp_ptr, mp_align) \
((void*)((char*)(mp_ptr)+(-(ralloc_uintptr_type)(mp_ptr))%(mp_align)))
[...] I guess I thought the modulo operator was too
expensive or something retarded like that.
Modulo by a power of two on an unsigned value should generate a mask
operation on any halfway decent compiler.
Humm. I really need to disassemble some programs to see what's going on with
most of the compilers I use. Modulo is slow when compared to a simple and
operation.
Modulo isn't necessary anyway;
it just simplifies the expression. Note that this won't align properly for
a non-power-of-two; for that, you'd need something like
((void*)((char*)(mp_ptr)+\
((mp_align_)-(ralloc_uintptr_type)(mp_ptr)%(mp_align))%(mp_align)
))
so that you get an offset of 0 if it's already aligned, or a value up to
mp_align-1 if it's not.
Good point. Perhaps I should allow support for alignments for values that
are not powers of two. Humm... Need to think.
Interesting. You could always just subtract it from zero; should generate
the same code.
:^)
Hmmm, instead of your custom RALLOC_UINTPTR_TYPE type, you could just use
uintptr_t from C99's <stdint.h>. Your use of size_t as a fallback for C89
is a good idea.
I was under the impression that `uintptr_t' was optional in C99 (e.g., only
exists in an XSI conformant system). I guess I could just check to see if
the compilation environment is C99; include <stdint.h> and finally check if
the `UINTPTR_MAX' macro is defined. Probably something along the lines of:
___________________________________________________________
#if (__STDC_VERSION__ >= 199901L)
# include <stdint.h>
# if defined (UINTPTR_MAX)
# undef RALLOC_UINTPTR_TYPE
# define RALLOC_UINTPTR_TYPE uintptr_t
# endif
#endif
#if ! defined (RALLOC_UINTPTR_TYPE)
# define RALLOC_UINTPTR_TYPE size_t
#endif
___________________________________________________________
Your assertion could cause problems, as you're casting an arbitrary
integer to a void*:
#define RALLOC_ALIGN_UP(mp_ptr, mp_align) \
((void*)((unsigned char*)(mp_ptr)+ \
(-(ralloc_uintptr_type)(mp_ptr))%(mp_align)))
#define RALLOC_ALIGN_ASSERT(mp_ptr, mp_align) \
(((void*)(mp_ptr)) == RALLOC_ALIGN_UP(mp_ptr, mp_align))
[...]
static void*
rallocex(
struct region* const self,
size_t size,
size_t align
) {
[...]
assert(align == 1 || RALLOC_ALIGN_ASSERT(align, 2));
Here you're casting align to a void* within the assertion.
I should just define the `RALLOC_ALIGN_ASSERT()' function macro to something
like:
#define RALLOC_ALIGN_ASSERT(mp_ptr, mp_align) ( \
! (((ralloc_uintptr_type)(mp_ptr)) % (mp_align)) \
)
Another issue, inside rallocex(), you align the buffer pointer BEFORE
verifying there's enough space to even align it:
align_buffer = RALLOC_ALIGN_UP(raw_buffer, align);
size += align_buffer - raw_buffer;
if (offset + size > self->size)
return NULL;
Even just incrementing a pointer outside the array/one past the end it
points into is undefined behavior.
Oh shi%. I thought that it if was okay to increment a pointer to one past
the end, it should be okay to increment some more... So, the following
program could theoretically reboot the machine or launch the nukes:
___________________________________________________________
int main(void) {
char x[1];
char* boom = x;
++boom;
++boom;
++boom;
return 0;
}
___________________________________________________________
It seems a lot of machinery for really simple functionality. I tried
coding up a minimal function to do mostly the same thing:
void* ralloc( size_t size, size_t align, char** pos, char* end )
{
void* result = NULL;
size_t pad = (align - ((uintptr_t)*pos % align)) % align;
size_t padded_size = size + pad;
if ( end - *pos >= padded_size )
{
result = *pos + pad;
*pos += padded_size;
}
return result;
}
/* Example usage with static buffer */
static char buf [100];
static char* pos = buf;
void init_buf( void )
{
pos = buf;
}
void* alloc_from_buf( size_t size, size_t align )
{
return ralloc( size, align, &pos, buf + sizeof buf );
}
Well, at first glance it should work. I would just have to augment it with
the nifty `RALLOC_ALIGN_OF()' function macro in order to automatically
determine alignments for any type.
The core ralloc() function first finds how much padding is necessary to
align *pos, calculates an adjusted size that includes padding, then
verifies that enough space remains in the buffer. If so, it returns the
aligned beginning and adjusts the buffer pointer to just past the end.
User code then allocates the buffer and keeps a pointer to the next
position in it.
Sounds like a plan.
Thank you. I will try to amend the code and re-post it sometime today or
tomorrow.