xmalloc.c - my xmalloc

V

vippstar

I have posted a previous version of this code some weeks ago which
unfortunately got no replies.
I am reposting this and I hope to see comments from c.l.c people.
You can use this code as you want, credits are not required.
Hopefully this is portable to any system and doesn't invoke undefined
behavior anywhere.
I also don't see how my code would not work with system specific
concepts such as processes and threads.
(However, receiving a signal while one of the following functions does
something will most likely break things)

This'll be the last time I am posting this (so I won't bother you
again :)

Hopefully I'll get some opinions.

-- xmalloc.c --

/*
vippstar 2008 - comp.lang.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _xmalloc_chunk_size 128

/* global struct of pointers allocated
all members initialized to 0/NULL because
it is global */

struct {
void ** ptr;
size_t sz; /* pointers initialized */
size_t total; /* total pointers allocated */
} _xmalloc_s;

/* user functions */

/* inits an xmalloc session, registers xmalloc_end if
it's the first time it's called, calling it while
there is an xmalloc session won't do anything */
int xmalloc_init(void);

/* ends an xmalloc session, freeing all resources,
can be called even if there was no xmalloc_init() before */
void xmalloc_end(void);

/* allocates memory like malloc(). if the allocation fails,
xmalloc looks at the second argument to see what to do
if the second argument is true, xmalloc will return NULL
if false, xmalloc will exit(EXIT_FAILURE); */
void * xmalloc(size_t, int);

/* frees a pointer previously allocated with xmalloc.
passing NULL or a pointer not allocated with xmalloc
won't do anything */
void xfree(void *);

/* frees all pointers allocated with xmalloc() in the currect
xmalloc session */
void xfreeall(void);

/* implementation specific functions */
int xmalloc_resize(void);
void xmalloc_clear(void);
void xmalloc_remove(size_t);

#ifdef DEBUG
/* debug function */
void xmalloc_debug(void);

void xmalloc_debug(void) {

printf(
"ptr -> %p\n"
"sz -> %zu\n"
"total -> %zu\n\n",
(void *)_xmalloc_s.ptr, _xmalloc_s.sz,
_xmalloc_s.total
);

}
#endif

void xmalloc_clear(void) {

_xmalloc_s.ptr = NULL;
_xmalloc_s.sz = 0;
_xmalloc_s.total = 0;
}

int xmalloc_init(void) {

static int first_time = 0;

if(_xmalloc_s.ptr)
return 0; /* xmalloc already initialized */

_xmalloc_s.ptr = malloc(_xmalloc_chunk_size * sizeof
*_xmalloc_s.ptr);
if(_xmalloc_s.ptr == NULL)
return -1;

if(first_time == 0) {
if(atexit(xmalloc_end) == -1) {
free(_xmalloc_s.ptr);
return -1;
}
first_time = 1;
}

_xmalloc_s.total = _xmalloc_chunk_size;

return 0;
}

int xmalloc_resize(void) {

void *tmp;

if(_xmalloc_s.ptr == NULL)
return -1;

tmp = realloc(_xmalloc_s.ptr, (_xmalloc_s.total +
_xmalloc_chunk_size) *
sizeof *_xmalloc_s.ptr);
if(tmp == NULL)
return -1;

_xmalloc_s.ptr = tmp;
_xmalloc_s.total += _xmalloc_chunk_size;

return 0;
}

void xmalloc_end(void) {

size_t n;

xfreeall();
free(_xmalloc_s.ptr);

xmalloc_clear();

}

/* opt = 0 we exit, opt = 1 we return NULL */
void * xmalloc(size_t size, int opt) {

if(_xmalloc_s.sz == _xmalloc_s.total) {
/* we need to resize */
if(xmalloc_resize() == -1) {
if(opt == 0)
exit(EXIT_FAILURE);
return NULL;
}
}

_xmalloc_s.ptr[_xmalloc_s.sz] = malloc(size);
if(_xmalloc_s.ptr[_xmalloc_s.sz] == NULL) {
if(opt == 0)
exit(EXIT_FAILURE);
return NULL;
}

_xmalloc_s.sz++;

return _xmalloc_s.ptr[_xmalloc_s.sz - 1];
}

void xfree(void * ptr) {

size_t n;

if(ptr != NULL) {

for(n = 0; n < _xmalloc_s.sz; n++)
if(ptr == _xmalloc_s.ptr[n]) {
xmalloc_remove(n);
free(ptr);
}
}

}

void xfreeall(void) {

size_t n;

for(n = 0; n < _xmalloc_s.sz; n++)
free(_xmalloc_s.ptr[n]);

_xmalloc_s.sz = 0;
}

void xmalloc_remove(size_t n) {

if(n != _xmalloc_s.sz - 1)
memmove(&_xmalloc_s.ptr[n], &_xmalloc_s.ptr[n+1],
(_xmalloc_s.sz - n - 1) * sizeof *_xmalloc_s.ptr);

_xmalloc_s.sz--;
}
 
M

Michael Mair

I have posted a previous version of this code some weeks ago which
unfortunately got no replies.

You probably mean
<[email protected]>

Please provide code without tabs; I took the freedom to replace
every tab by two spaces.
I am reposting this and I hope to see comments from c.l.c people.
You can use this code as you want, credits are not required.
Hopefully this is portable to any system and doesn't invoke undefined
behavior anywhere.
I also don't see how my code would not work with system specific
concepts such as processes and threads.
(However, receiving a signal while one of the following functions does
something will most likely break things)

This'll be the last time I am posting this (so I won't bother you
again :)

Hopefully I'll get some opinions.

-- xmalloc.c --

/*
vippstar 2008 - comp.lang.c
*/

Start with explaining the idea -- either here or at the beginning
of the header file.
"Convenience layer for malloc()/free() allowing summary free() from
about anywhere; registers a cleanup handler via atexit().
Can handle null pointers returned by malloc() via exit(EXIT_FAILURE)"
or as flowery as you like it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _xmalloc_chunk_size 128

/* global struct of pointers allocated
all members initialized to 0/NULL because
it is global */

Comments pointing out the obvious do not improve the code
quality. If you feel that automagical initialisation of
static storage duration variables needs explaining, then do
not rely on the automagic and provide an initialiser.
struct {
void ** ptr;

What is ptr good for?
size_t sz; /* pointers initialized */

You probably mean "number of"
size_t total; /* total pointers allocated */

The same.
What is the relationship between total and sz?
This is the stuff for comments.
Note that this is my first impression; I read your code as it is
and will not "read further code to find out what sz and total are".
} _xmalloc_s;

All file scope identifiers with leading underscore except the
identifier "_" invade the implementation namespace.
You already may wander in the shade of the valley of "interesting"
behaviour.
Note that giving _xmalloc_s internal linkage does not help this
problem but at least you are not exposing your housekeeping
identifiers to the user.
/* user functions */

The following belongs in a header file.
/* inits an xmalloc session, registers xmalloc_end if
it's the first time it's called, calling it while
there is an xmalloc session won't do anything */
int xmalloc_init(void);

/* ends an xmalloc session, freeing all resources,
can be called even if there was no xmalloc_init() before */
void xmalloc_end(void);

How often can I call xmalloc_init()/xmalloc_end()?
/* allocates memory like malloc(). if the allocation fails,
xmalloc looks at the second argument to see what to do
if the second argument is true, xmalloc will return NULL
if false, xmalloc will exit(EXIT_FAILURE); */
void * xmalloc(size_t, int);

/* frees a pointer previously allocated with xmalloc.
passing NULL or a pointer not allocated with xmalloc
won't do anything */
void xfree(void *);

/* frees all pointers allocated with xmalloc() in the currect
xmalloc session */
void xfreeall(void);

Note that this may be the most dangerous function of them all...
Suppose you have main() calling xmalloc_init() and xmalloc_end(),
respectively, and calling a function foo() making use of xmalloc().
Half a year later, you find that foo() makes a nice utility within
a larger context and end up with bar():
void bar (int SomeParam)
{
....
if (C1) {
SpecialP = xmalloc(...);
baz(SomeParam, SpecialP);
....
}
if (C2) {
....
Something = foo(SomeParam);
....
} else {
Something = qux();
}
if (C1) {
quux(Something, SpecialP);
}
xfree(SpecialP);
}
Somehow, your special handling sometimes does not work on
(C1 && C2) == 1.
Reason: Some wit (guess who) used xfreeall() in his or her "frame
function" foo()...

I'd rather not expose that to the user.
/* implementation specific functions */

"implementation specific" is misleading in comp.lang.c...
If they are internal functions for use in your xmalloc implementation
only, then give them internal linkage.
int xmalloc_resize(void);

What does this thing resize? And why does it return int?
And what does this int mean?
void xmalloc_clear(void);
void xmalloc_remove(size_t);

Parameter names are cheap today -- is this parameter an amount,
an index, just reserved for arbitrary fun? Is it related to
something in your housekeeping struct?
#ifdef DEBUG

Note: Rather use XMALLOC_DEBUG or similar -- it might be
double plus unnice if your xmalloc dump came with other debug
output.
/* debug function */
void xmalloc_debug(void);

void xmalloc_debug(void) {

printf(
"ptr -> %p\n"
"sz -> %zu\n"
"total -> %zu\n\n",
(void *)_xmalloc_s.ptr, _xmalloc_s.sz,
_xmalloc_s.total
);

}

"z" is not available for C90 and some semi-C99 implementations
like the gcc on Cygwin just may have to rely on a library that
does not have it. Casting to unsigned long and using %lu may
be necessary.

If you want this to hold up for real use, then provide a
xmalloc_debug_init()/end() functionality opening a file or
setting the debug FILE* variable to stdout in your xmalloc_init()/
end() functions and consider calling xmalloc_debug() on every
xmalloc()/xfree() operation (via conditional compilation).
Your typical XMALLOC_DEBUG_PRINT() macro may be even more
helpful.
#endif

void xmalloc_clear(void) {

_xmalloc_s.ptr = NULL;
_xmalloc_s.sz = 0;
_xmalloc_s.total = 0;
}

int xmalloc_init(void) {

static int first_time = 0;

if(_xmalloc_s.ptr)
return 0; /* xmalloc already initialized */

_xmalloc_s.ptr = malloc(_xmalloc_chunk_size * sizeof
*_xmalloc_s.ptr);
if(_xmalloc_s.ptr == NULL)
return -1;

if(first_time == 0) {
if(atexit(xmalloc_end) == -1) {
free(_xmalloc_s.ptr);
return -1;
}
first_time = 1;
}

_xmalloc_s.total = _xmalloc_chunk_size;

return 0;
}

int xmalloc_resize(void) {

void *tmp;

if(_xmalloc_s.ptr == NULL)
return -1;

tmp = realloc(_xmalloc_s.ptr, (_xmalloc_s.total +
_xmalloc_chunk_size) *
sizeof *_xmalloc_s.ptr);
if(tmp == NULL)
return -1;

_xmalloc_s.ptr = tmp;
_xmalloc_s.total += _xmalloc_chunk_size;

return 0;
}

void xmalloc_end(void) {

size_t n;

What do you need that for?
xfreeall();
free(_xmalloc_s.ptr);

xmalloc_clear();

}

/* opt = 0 we exit, opt = 1 we return NULL */
void * xmalloc(size_t size, int opt) {

if(_xmalloc_s.sz == _xmalloc_s.total) {
/* we need to resize */
if(xmalloc_resize() == -1) {
if(opt == 0)
exit(EXIT_FAILURE);
return NULL;
}
}

_xmalloc_s.ptr[_xmalloc_s.sz] = malloc(size);
if(_xmalloc_s.ptr[_xmalloc_s.sz] == NULL) {
if(opt == 0)
exit(EXIT_FAILURE);
return NULL;
}

_xmalloc_s.sz++;

return _xmalloc_s.ptr[_xmalloc_s.sz - 1];
}

void xfree(void * ptr) {

size_t n;

if(ptr != NULL) {

for(n = 0; n < _xmalloc_s.sz; n++)
if(ptr == _xmalloc_s.ptr[n]) {
xmalloc_remove(n);
free(ptr);
}
}

}

void xfreeall(void) {

size_t n;

for(n = 0; n < _xmalloc_s.sz; n++)
free(_xmalloc_s.ptr[n]);

_xmalloc_s.sz = 0;
}

void xmalloc_remove(size_t n) {
"n"?


if(n != _xmalloc_s.sz - 1)
memmove(&_xmalloc_s.ptr[n], &_xmalloc_s.ptr[n+1],
(_xmalloc_s.sz - n - 1) * sizeof *_xmalloc_s.ptr);

_xmalloc_s.sz--;
}

Note that your internal functions are asymmetric; I would
have expected xmalloc_insert(size_t Index, void *pNewStorage).
The xmalloc_resize() function ought to be called xmalloc_expand()
and there might be need for an xmalloc_compress().

Your xmalloc_remove() alternatively could just set the respective
array element to NULL and you could remember, say, up to junk_size
free()d element numbers and move the memory when necessary.
This strongly depends on the user's allocation behaviour; in the end,
you may have to change to a more sophisticated strategy / data
structures.

I did not test your code.


Cheers
Michael
 
V

vippstar

You probably mean
<[email protected]>

Please provide code without tabs; I took the freedom to replace
every tab by two spaces.
I'll keep that in mind
Start with explaining the idea -- either here or at the beginning
of the header file.
"Convenience layer for malloc()/free() allowing summary free() from
about anywhere; registers a cleanup handler via atexit().
Can handle null pointers returned by malloc() via exit(EXIT_FAILURE)"
or as flowery as you like it.




Comments pointing out the obvious do not improve the code
quality. If you feel that automagical initialisation of
static storage duration variables needs explaining, then do
not rely on the automagic and provide an initialiser.


What is ptr good for?


You probably mean "number of"


The same.
What is the relationship between total and sz?
This is the stuff for comments.
Note that this is my first impression; I read your code as it is
and will not "read further code to find out what sz and total are".


All file scope identifiers with leading underscore except the
identifier "_" invade the implementation namespace.
It does? As far as I know only identifiers starting with an underscore
followed by an uppercase character or another underscore are reserved.
You are right about the commenting part, as for the names, I'm not
very good at that. :(
The following belongs in a header file.
I chose to post it to comp.lang.c as one file to simplify things, but
yes these declarations should be in a header file.
How often can I call xmalloc_init()/xmalloc_end()?
Is that a question or do you mean that someone might wonder that?
As often as you want.
Note that this may be the most dangerous function of them all...
Suppose you have main() calling xmalloc_init() and xmalloc_end(),
respectively, and calling a function foo() making use of xmalloc().
Half a year later, you find that foo() makes a nice utility within
a larger context and end up with bar():
void bar (int SomeParam)
{
....
if (C1) {
SpecialP = xmalloc(...);
baz(SomeParam, SpecialP);
....
}
if (C2) {
....
Something = foo(SomeParam);
....
} else {
Something = qux();
}
if (C1) {
quux(Something, SpecialP);
}
xfree(SpecialP);
}
Somehow, your special handling sometimes does not work on
(C1 && C2) == 1.
Reason: Some wit (guess who) used xfreeall() in his or her "frame
function" foo()...

I'd rather not expose that to the user.
Hm.. true, that would be a very dangerous function.
Perhaps I should document that it should be used only if followed
immidiatelly by exit() or _Exit() or a return from main().
Then again, it could still break if the user has registered a function
like yours with atexit().
"implementation specific" is misleading in comp.lang.c...
If they are internal functions for use in your xmalloc implementation
only, then give them internal linkage.


What does this thing resize? And why does it return int?
And what does this int mean?


Parameter names are cheap today -- is this parameter an amount,
an index, just reserved for arbitrary fun? Is it related to
something in your housekeeping struct?




Note: Rather use XMALLOC_DEBUG or similar -- it might be
double plus unnice if your xmalloc dump came with other debug
output.





"z" is not available for C90 and some semi-C99 implementations
like the gcc on Cygwin just may have to rely on a library that
does not have it. Casting to unsigned long and using %lu may
be necessary.
I wrote this with C99 in mind. You are correct, and I will change this
along with some #ifdef's.
If you want this to hold up for real use, then provide a
xmalloc_debug_init()/end() functionality opening a file or
setting the debug FILE* variable to stdout in your xmalloc_init()/
end() functions and consider calling xmalloc_debug() on every
xmalloc()/xfree() operation (via conditional compilation).
Your typical XMALLOC_DEBUG_PRINT() macro may be even more
helpful.

Thanks, nice ideas.
What do you need that for?
I don't know, apparently the function does not need it ;-)
void xmalloc_remove(size_t n) {
"n"?



if(n != _xmalloc_s.sz - 1)
memmove(&_xmalloc_s.ptr[n], &_xmalloc_s.ptr[n+1],
(_xmalloc_s.sz - n - 1) * sizeof *_xmalloc_s.ptr);
_xmalloc_s.sz--;
}

Note that your internal functions are asymmetric; I would
have expected xmalloc_insert(size_t Index, void *pNewStorage).
The xmalloc_resize() function ought to be called xmalloc_expand()
and there might be need for an xmalloc_compress().

Your xmalloc_remove() alternatively could just set the respective
array element to NULL and you could remember, say, up to junk_size
free()d element numbers and move the memory when necessary.
I *do* have problems with object/function naming.
By the time I posted this I realised that my code lacks xrealloc()/
xcalloc(), I might add these too.
This strongly depends on the user's allocation behaviour; in the end,
you may have to change to a more sophisticated strategy / data
structures.
Indeed I have noticed that too, but it was written as a 'quick hack'
that does some of the stuff discussed in a previous discussion here.
I don't suggest to a reader to use this code, as there are far more
sophisticated solutions "out there".
One could get ideas by looking at the implementation of malloc() and
friends on his system.
I did not test your code.
Well that is understandable, everyone has his own code to test, but
still, I was hoping for commenters not testers.
Thanks again.
 
M

Michael Mair

It does? As far as I know only identifiers starting with an underscore
followed by an uppercase character or another underscore are reserved.
You are right about the commenting part, as for the names, I'm not
very good at that. :(

The above is not entirely correct. The full version can be found, e.g.,
in N1124, 7.1.3(Reserved Identifiers), #1:
,---
— All identifiers that begin with an underscore and either an uppercase
letter or another underscore are always reserved for any use.
— All identifiers that begin with an underscore are always reserved for
use as identifiers with file scope in both the ordinary and tag name
spaces.
`---
So, of course, you could use _7xmalloc_s but I would not recommend it...
I chose to post it to comp.lang.c as one file to simplify things, but
yes these declarations should be in a header file.


Is that a question or do you mean that someone might wonder that?
As often as you want.

The latter; it is "implicitly clear" that this should work as often
as I want but a sentence stating that explicitly is the thing I
would look for if reading only the header.
Calling xmalloc_end() in some "inner function" has the same damage
potential as xfreeall(). Without an explicit explanation, I do not
know if I dare calling xmalloc_init() more than once.

Hm.. true, that would be a very dangerous function.
Perhaps I should document that it should be used only if followed
immidiatelly by exit() or _Exit() or a return from main().
Then again, it could still break if the user has registered a function
like yours with atexit().

I would just not give this to the user as it is. Provided with
a comment explaining why it is dangerous (just as *end() is), it
may be alright.
You can suggest that xfreeall() should be called by the same
function that initializes xmalloc:
xmalloc_init();
GetInput();
TransformToIR();
TransformToXmlFiles();
xfreeall(); /* Make room for XSLT */
PerformXslt();
Cleanupt();
xmalloc_end();
Of course, if you could register xmalloc sessions, then you could
clean up the unused stuff after each step while preserving the stuff
needed for later steps. If you think about the problems involved,
then the simplest possible scheme (just giving xmalloc_init(),
xmalloc(), and xfree() to the user) seems to be the safest course.

<snip>

Cheers
Michael
 
C

CBFalconer

.... snip ...

I chose to post it to comp.lang.c as one file to simplify things,
but yes these declarations should be in a header file.

I haven't read it, but probably they should not be in a header.
The only things that go in a header file are the definitions,
constants, prototypes etc. that you want to export so that other
programs can access your file. Headers are a misnomer - they are
not the place to put headings, but they are the place to export
linkages.
 
C

CBFalconer

Michael said:
Keith said:
The above is not entirely correct. The full version can be found,
e.g., in N1124, 7.1.3(Reserved Identifiers), #1:

[...]

You might want to upgrade to N1256, which incorporates TC3 as well
as TC1 and TC2.

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

Thank you :)

Actually, the whole sequence is:

N869 Last draft before std issuance
C99 std The thing itself
N1124 plus TC1 and TC2
N1256 plus TC3

and the last with a decent text version is N869. This is handy for
searching with grep and text utilities, etc. You can get a version
adjusted for ease of quoting and searching, bzip2 compressed, at:

<http://cbfalconer.home.att.net/download/n869_txt.bz2>
 
L

lawrence.jones

CBFalconer said:
Actually, the whole sequence is:

N869 Last draft before std issuance
C99 std The thing itself
N1124 plus TC1 and TC2
N1256 plus TC3

You left out:

N949 plus TC1

-Larry Jones

That gives me a FABULOUS idea. -- Calvin
 
M

Michael Mair

CBFalconer said:
... snip ...


I haven't read it, but probably they should not be in a header.
The only things that go in a header file are the definitions,
constants, prototypes etc. that you want to export so that other
programs can access your file. Headers are a misnomer - they are
not the place to put headings, but they are the place to export
linkages.

In the snipped context, I referred to function prototypes which
probably still are also declarations :)

Cheers
Michael
 
V

vippstar

... snip ...



I haven't read it, but probably they should not be in a header.
The only things that go in a header file are the definitions,
A function declaration is
int foo(int);
A function definition is
int foo(int) { return 0; }
The former, I have seen on most header files; the latter is quite
exceptional.
constants, prototypes etc. that you want to export so that other
programs can access your file. Headers are a misnomer - they are
not the place to put headings, but they are the place to export
linkages.
The rest I agree.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top