Need a GURU! I'm stuck....

C

Chris LaVelle

I don't know the easiest way to explain this so, I'm going to give an
example of how it is today....and what I'm trying to accomplish.

in the header...
typedef struct XXX_ELEMENT_tag
{
UINT8 num;
UINT8 param;
UINT16 fre1;
INT16 a1;
} ;
typedef struct XXX_ELEMENT_tag ELEMENT_T;

#define MAX_ELEMENTS 8
typedef struct XYZ_S_tag {
UINT8 num_elements;
UINT8 flag;
ELEMENT_T element[MAX_E]; /*** This is the spot ***/
} XYZ_S_tag;

And of course the S_tag structure is buried one more structure down. Say
something like:

typedef struct T_TABLE_tag
{
XYZ_S_tag run;
XYZ_S_tag walk;
.... /* alot of these */
XYZ_S_tag crawl;
} TABLE_T;
typedef struct T_TABLE_tag T_table;

And in a C file a global is initialized like this...
T_table t_table =
{
{ /* run */
2, /* Num Elements */
APPLY_WINDOW, /* Flag */
{
/* N p f a */
/* 0 */ { 2, 0, 480, -240 },
/* 1 */ { 0, 0, 0, 500 },
/* 2 */ { 0, 0, 0, 0 },
/* 3 */ { 0, 0, 0, 0 },
/* 4 */ { 0, 0, 0, 0 },
/* 5 */ { 0, 0, 0, 0 },
/* 6 */ { 0, 0, 0, 0 },
/* 7 */ { 0, 0, 0, 0 },
/* 8 */ { 0, 0, 0, 0 },
}
},
{ /* walk */
/* repeat many many times */

}
}

Ok, let's just say element is bigger than my example above and there are
about a 1000 of these, only half of which have data. I've inherited
this code and this table is occupying 300k of memory and memory's tight.
I want to delete all the filler rows and declare element in a way that
the compiler will auto size what it needs for each instance. I have the
number of elements. I would have thought something along the lines of
*element instead of element[MAX_E]. But I get warnings about the
unexpected brace for element. Is there a way to make the compiler(gcc)
auto size the space for me? In the above example I would be deleting
rows 2-8.

Any and all help is appreciated?

Thanks
cj
 
C

CJ LaVelle

when I did the *element, tons of warnings and then errors out saying to
many items for element. I also tried element[] and it didn't like that
at all. The header file errors during compile with incomplete
declaration error. Sorry I don't have it in front of me...

Again, any thoughts or ideas would be appreciated.
 
R

Richard Heathfield

Chris LaVelle said:
I don't know the easiest way to explain this so, I'm going to give an
example of how it is today....and what I'm trying to accomplish.

[Chris LV is trying to get himself a ragged array, but he wants to
initialise it using ordinary array initialisation syntax.]

Alas, no, you can't do this in the way you would like to do it.

You *can*, however, get the behaviour you desire. The easiest way to do it
is probably via a runtime file containing the data, one item per line. If
this can contain an item count at the start of each line, so much the
better. If it can contain a line count at the very start of the file,
that's better still. (But if either or both of those hints-to-the-program
aren't possible, there's a workaround called realloc.)

Define your variable-length data as... well, I was about to suggest that
you use ELEMENT_T *element, but I can't do that with a clear conscience
because the ELEMENT_T identifier is reserved for the implementation! But
whatever type name you end up using, element needs to be a pointer to that
type.

Having decided how many items you want, you do this in your read-loop:

/* we need n items on this line */
t_table.run.element = malloc(n * sizeof *t_table.run.element);
if(t_table.run.element != NULL)
{
read n lots of num, param, fre1, and a1 from the file and store
them in t_table.run.element[0] through t_table.run.element[n-1]
}


Disadvantage of this technique: the successful running of your program now
requires the existence of this external file.

Advantages: it gives you the memory saving you require; and it allows you
to change the initialisation values without recompiling the code (okay,
whether this is an advantage is a matter of opinion).
 
P

Peter Nilsson

Chris LaVelle said:
...
typedef struct XXX_ELEMENT_tag
{
   UINT8   num;
   UINT8   param;
   UINT16  fre1;
   INT16   a1;} ;

typedef struct XXX_ELEMENT_tag ELEMENT_T;

#define MAX_ELEMENTS 8
typedef struct XYZ_S_tag {
    UINT8     num_elements;
    UINT8     flag;
    ELEMENT_T element[MAX_E];    /*** This is the spot ***/

} XYZ_S_tag;

And of course the S_tag structure is buried one more
structure down. Say something like:

typedef struct T_TABLE_tag
{
   XYZ_S_tag run;
   XYZ_S_tag walk;
   ....  /* alot of these */
   XYZ_S_tag crawl;} TABLE_T;

typedef struct T_TABLE_tag T_table;

And in a C file a global is initialized like this...
Ok, let's just say element is bigger than my example
above and there are about a 1000 of these, only half
of which have data.  I've inherited this code and this
table is occupying 300k of memory and memory's tight.
I want to delete all the filler rows and declare
element in a way that the compiler will auto size what
it needs for each instance. I have the number of
elements.  I would have thought something along the
lines of *element instead of element[MAX_E].

Yes, but that does come at a compromise in that the
pointer takes up space in addition to the array it
points to.
 But I get warnings about the unexpected brace for
element.

Sounds like a syntactical thing. Take a look at...

#include <stdio.h>

#define countof(x) ( (size_t) (sizeof(x)/sizeof*(x)) )

struct thang
{
const char *name;
size_t count;
const char **element;
};

const char *dwarfs[] =
{
"Bashful",
"Doc",
"Dopey",
"Grumpy",
"Happy",
"Sleepy",
"Sneezy"
};

const char *solids[] =
{
"Tetrahedron",
"Octahedron",
"Cube",
"Icosahedron",
"Dodecahedron"
};

struct thang thangs[] =
{
{ "Seven Dwarfs", countof(dwarfs), dwarfs },
{ "Perfect Solids", countof(solids), solids }
};

int main(void)
{
size_t i, j;

for (i = 0; i < countof(thangs); i++)
{
if (i) puts("");
printf("%s:\n", thangs.name);
for (j = 0; j < thangs.count; j++)
printf(" %s\n", thangs.element[j]);
}

return 0;
}
 
C

CJ LaVelle

Firmware, so really no place to stick a file. It has to stay in the
image for now and continue to be a global for madness reasons.

CJ
Richard said:
Chris LaVelle said:

I don't know the easiest way to explain this so, I'm going to give an
example of how it is today....and what I'm trying to accomplish.


[Chris LV is trying to get himself a ragged array, but he wants to
initialise it using ordinary array initialisation syntax.]

Alas, no, you can't do this in the way you would like to do it.

You *can*, however, get the behaviour you desire. The easiest way to do it
is probably via a runtime file containing the data, one item per line. If
this can contain an item count at the start of each line, so much the
better. If it can contain a line count at the very start of the file,
that's better still. (But if either or both of those hints-to-the-program
aren't possible, there's a workaround called realloc.)

Define your variable-length data as... well, I was about to suggest that
you use ELEMENT_T *element, but I can't do that with a clear conscience
because the ELEMENT_T identifier is reserved for the implementation! But
whatever type name you end up using, element needs to be a pointer to that
type.

Having decided how many items you want, you do this in your read-loop:

/* we need n items on this line */
t_table.run.element = malloc(n * sizeof *t_table.run.element);
if(t_table.run.element != NULL)
{
read n lots of num, param, fre1, and a1 from the file and store
them in t_table.run.element[0] through t_table.run.element[n-1]
}


Disadvantage of this technique: the successful running of your program now
requires the existence of this external file.

Advantages: it gives you the memory saving you require; and it allows you
to change the initialisation values without recompiling the code (okay,
whether this is an advantage is a matter of opinion).
 
C

CJ LaVelle

Firmware, so really no place to stick a file. It has to stay in the
image for now and continue to be a global for madness reasons.

But thanks..

CJ
Richard said:
Chris LaVelle said:

I don't know the easiest way to explain this so, I'm going to give an
example of how it is today....and what I'm trying to accomplish.


[Chris LV is trying to get himself a ragged array, but he wants to
initialise it using ordinary array initialisation syntax.]

Alas, no, you can't do this in the way you would like to do it.

You *can*, however, get the behaviour you desire. The easiest way to do it
is probably via a runtime file containing the data, one item per line. If
this can contain an item count at the start of each line, so much the
better. If it can contain a line count at the very start of the file,
that's better still. (But if either or both of those hints-to-the-program
aren't possible, there's a workaround called realloc.)

Define your variable-length data as... well, I was about to suggest that
you use ELEMENT_T *element, but I can't do that with a clear conscience
because the ELEMENT_T identifier is reserved for the implementation! But
whatever type name you end up using, element needs to be a pointer to that
type.

Having decided how many items you want, you do this in your read-loop:

/* we need n items on this line */
t_table.run.element = malloc(n * sizeof *t_table.run.element);
if(t_table.run.element != NULL)
{
read n lots of num, param, fre1, and a1 from the file and store
them in t_table.run.element[0] through t_table.run.element[n-1]
}


Disadvantage of this technique: the successful running of your program now
requires the existence of this external file.

Advantages: it gives you the memory saving you require; and it allows you
to change the initialisation values without recompiling the code (okay,
whether this is an advantage is a matter of opinion).
 
G

Gene

I don't know the easiest way to explain this so, I'm going to give an
example of how it is today....and what I'm trying to accomplish.

in the header...
typedef struct XXX_ELEMENT_tag
{
   UINT8   num;
   UINT8   param;
   UINT16  fre1;
   INT16   a1;} ;

typedef struct XXX_ELEMENT_tag ELEMENT_T;

#define MAX_ELEMENTS 8
typedef struct XYZ_S_tag {
    UINT8     num_elements;
    UINT8     flag;
    ELEMENT_T element[MAX_E];    /*** This is the spot ***/

} XYZ_S_tag;

And of course the S_tag structure is buried one more structure down. Say
something like:

typedef struct T_TABLE_tag
{
   XYZ_S_tag run;
   XYZ_S_tag walk;
   ....  /* alot of these */
   XYZ_S_tag crawl;} TABLE_T;

typedef struct T_TABLE_tag T_table;

And in a C file a global is initialized like this...
T_table t_table =
{
        { /* run      */
                2, /* Num Elements */
                APPLY_WINDOW, /* Flag */
                {
                        /*        N  p   f     a */
                        /* 0 */ { 2, 0, 480, -240 },
                        /* 1 */ { 0, 0,   0, 500 },
                        /* 2 */ { 0, 0,   0, 0 },
                        /* 3 */ { 0, 0,   0, 0 },
                        /* 4 */ { 0, 0,   0, 0 },
                        /* 5 */ { 0, 0,   0, 0 },
                        /* 6 */ { 0, 0,   0, 0 },
                        /* 7 */ { 0, 0,   0, 0 },
                        /* 8 */ { 0, 0,   0, 0 },
                }
        },
        { /* walk  */
              /* repeat many many times */

        }

}

Ok, let's just say element is bigger than my example above and there are
about a 1000 of these, only half of which have data.  I've inherited
this code and this table is occupying 300k of memory and memory's tight.
I want to delete all the filler rows and declare element in a way that
the compiler will auto size what it needs for each instance.  I have the
number of elements.  I would have thought something along the lines of
*element instead of element[MAX_E].  But I get warnings about the
unexpected brace for element.  Is there a way to make the compiler(gcc)
auto size the space for me?  In the above example I would be deleting
rows 2-8.

Any and all help is appreciated?

Thanks
cj

You have the right idea (one anyway). Replace the mostly ignored
array with a pointer to a correctly sized array.

It sounds like you were trying to intialize a pointer with an
aggregate. Won't work in C. You have to name the array independently
for this to work. You can easily write a perl script to do this
translation. No other code will need changing.

typedef struct XYZ_S_tag {
UINT8 num_elements;
UINT8 flag;
ELEMENT_T *element; /*** Changed to pointer ***/
} XYZ_S_tag;

typedef struct T_TABLE_tag
{
XYZ_S_tag run;
XYZ_S_tag walk;
.... /* alot of these */
XYZ_S_tag crawl;
} TABLE_T;

typedef struct T_TABLE_tag T_table;

ELEMENT_T run_elements[] = {
/* 0 */ { 2, 0, 480, -240 },
/* 1 */ { 0, 0, 0, 500 } };

T_table t_table =
{
{ /* run */
2, /* Num Elements */
APPLY_WINDOW, /* Flag */
run_elements
},
{ /* walk */
/* repeat many many times */

}
}

Another approach is to declare a different record type for each
possible number of elements. If your max is really only 8, this is
reasonable. Preprocessor abuse will help:

#define DECLARE_XYZ(Size) \
typedef struct XYZ_S_ ## Size ## _tag { \
UINT8 num_elements; \
UINT8 flag; \
ELEMENT_T element[Size]; \
} XYZ_S_ ## Size ## _tag

DECLARE_XYZ(1); DECLARE_XYZ(2); DECLARE_XYZ(3);DECLARE_XYZ(4);
DECLARE_XYZ(5); DECLARE_XYZ(6); DECLARE_XYZ(7);DECLARE_XYZ(8);

Now,

typedef struct T_TABLE_tag
{
XYZ_S_2_tag run;
XYZ_S_4_tag walk;
.... /* alot of these */
XYZ_S_1_tag crawl;
} TABLE_T;

T_table t_table =
{
{ /* run */
2, /* Num Elements */
APPLY_WINDOW, /* Flag */
{
/* N p f a */
/* 0 */ { 2, 0, 480, -240 },
/* 1 */ { 0, 0, 0, 500 },
}
},
{ /* walk */
4,
FLAG,
{
/* N p f a */
/* 0 */ { 2, 0, 480, -240 },
/* 1 */ { 0, 0, 0, 500 },
/* 2 */ { 2, 0, 480, -240 },
/* 3 */ { 0, 0, 0, 500 },
}
/* repeat many many times */
}
}

Again a little script will fix you right up...
 
P

Peter Nilsson

Richard Heathfield said:
...I was about to suggest that you use ELEMENT_T *element,
but I can't do that with a clear conscience because the
ELEMENT_T identifier is reserved for the implementation!

Strictly speaking, it's reserved for use as a macro if
<errno.h> is included. But since someone might one day
want to include that header, that's normally enough
reason to steer clear of EXXXX identifiers.
 
D

dj3vande

[Please don't top post; responses should go under the relevant quoted
text, and non-relevant quoted text should be removed.]
Richard Heathfield wrote:
[Chris LV is trying to get himself a ragged array, but he wants to
initialise it using ordinary array initialisation syntax.]

Alas, no, you can't do this in the way you would like to do it.

You *can*, however, get the behaviour you desire. The easiest way to do it
is probably via a runtime file containing the data, one item per line. If
this can contain an item count at the start of each line, so much the
better. If it can contain a line count at the very start of the file,
that's better still. (But if either or both of those hints-to-the-program
aren't possible, there's a workaround called realloc.)

[And mallocate memory and read the data from a file at run time,
allocating only the amount of memory that's actually needed]


Firmware, so really no place to stick a file. It has to stay in the
image for now and continue to be a global for madness reasons.

You can write code that is set up as a ragged array that's entirely in
the image, and end up with something not entirely unlike this:
(We've lost your original data structure definitions, so I'll use
minimal ones for the purposes of illustration)
--------
struct element
{
/*Stuff that goes in each element*/
};

struct row
{
/*Number of elements in this row*/
int nelems;
struct element *elems;
};

/*The actual data, compacted into a single one-dimensional array*/
struct element *all_elems[]=
{
/*The next seventeen elements are for row 0*/
{ /*initializer for first element in row 0*/ },
/*...sixteen more initializers for row 0...*/

/*The next three elements are for row 1*/
{ /*initializer for first element in row 1*/ },
{ /*initializer for second element in row 1*/ },
{ /*initializer for third element in row 1*/ },

/*The next 42 elements are for row 2*/
{ /*initializer for first element in row 2*/ },
/*...41 more initializers for row 2...*/

/*Initializers for the rest of the rows*/
};

/*We access the data through this array of struct row*/
struct row *rows[]=
{
/*Row 0: 17 elements*/
{17, &all_elems[0]},
/*Row 1: 3 elements*/
{3, &all_elems[17]},
/*Row 2: 42 elements*/
{42, &all_elems[20]},

/*...and for the rest of the rows...*/
};
--------
This will obviously be incredibly tedious and fragile to maintain by
hand; every time something gets added in the middle, every row after
that has to have its initializers updated, and forgetting something or
getting an addition wrong will give you a bug that's likely to be hard
to trace.

It should, however, not be too hard to write a program that reads a
file in the format that Richard H described and writes this code for
you - computers are, after all, extremely good at things that are
tedious and purely mechanical to do by hand. Then all you have to do
is run the code generator as part of your build process, and you get a
nice compact (and statically initialized, which may be important for a
firmware application) internal representation in your image AND an
easy-to-edit input format for your development.


dave
 
A

Ark Khasin

[Please don't top post; responses should go under the relevant quoted
text, and non-relevant quoted text should be removed.]
Richard Heathfield wrote:
[Chris LV is trying to get himself a ragged array, but he wants to
initialise it using ordinary array initialisation syntax.]

Alas, no, you can't do this in the way you would like to do it.

You *can*, however, get the behaviour you desire. The easiest way to do it
is probably via a runtime file containing the data, one item per line. If
this can contain an item count at the start of each line, so much the
better. If it can contain a line count at the very start of the file,
that's better still. (But if either or both of those hints-to-the-program
aren't possible, there's a workaround called realloc.)

[And mallocate memory and read the data from a file at run time,
allocating only the amount of memory that's actually needed]


Firmware, so really no place to stick a file. It has to stay in the
image for now and continue to be a global for madness reasons.

You can write code that is set up as a ragged array that's entirely in
the image, and end up with something not entirely unlike this:
(We've lost your original data structure definitions, so I'll use
minimal ones for the purposes of illustration)
--------
struct element
{
/*Stuff that goes in each element*/
};

struct row
{
/*Number of elements in this row*/
int nelems;
struct element *elems;
};

/*The actual data, compacted into a single one-dimensional array*/
struct element *all_elems[]=
{
/*The next seventeen elements are for row 0*/
{ /*initializer for first element in row 0*/ },
/*...sixteen more initializers for row 0...*/

/*The next three elements are for row 1*/
{ /*initializer for first element in row 1*/ },
{ /*initializer for second element in row 1*/ },
{ /*initializer for third element in row 1*/ },

/*The next 42 elements are for row 2*/
{ /*initializer for first element in row 2*/ },
/*...41 more initializers for row 2...*/

/*Initializers for the rest of the rows*/
};

/*We access the data through this array of struct row*/
struct row *rows[]=
{
/*Row 0: 17 elements*/
{17, &all_elems[0]},
/*Row 1: 3 elements*/
{3, &all_elems[17]},
/*Row 2: 42 elements*/
{42, &all_elems[20]},

/*...and for the rest of the rows...*/
};
--------
This will obviously be incredibly tedious and fragile to maintain by
hand; every time something gets added in the middle, every row after
that has to have its initializers updated, and forgetting something or
getting an addition wrong will give you a bug that's likely to be hard
to trace.

It should, however, not be too hard to write a program that reads a
file in the format that Richard H described and writes this code for
you - computers are, after all, extremely good at things that are
tedious and purely mechanical to do by hand. Then all you have to do
is run the code generator as part of your build process, and you get a
nice compact (and statically initialized, which may be important for a
firmware application) internal representation in your image AND an
easy-to-edit input format for your development.
[(Especially in embedded) the use of const helps.]

A somewhat more maintainable way is
#define NELEM(array) sizeof(array)/array[0])
#define ARRSZ(array) {NELEM(array), array}
const struct element row0[] =
{
/* a bunch of elements */
};
...........
const struct element rowN[] =
{
/* a bunch of elements */
};
const struct row *const rows[] =
{
ARRSZ(row0),
..............
ARRSZ(rowN),
};
If the order is important, one can use designated initializers if supported.
Still, it would be more than nice to generate rows[] automatically, for
which an external tool is unfortunately needed.

While at it:

A more parsimonious way is to use an array of indexes, like so:
const struct element all_elems[] =
{
//begin row 0
{ /* first in row 0 */ },
.......
{ /* last in row 0 */ },
..........
//begin row N
{ /* first in row N */ },
.......
{ /* last in row N */ },
};
const int_or_smaller_perhaps_unsigned index[] =
{
index_of_first_in_row_0,
..........
index_of_first_in_row_N,
NELEM(all_elems), //number of elements
};
The number of elements in row x is index[x+1]-index[x].

To populate index[], an external tool is needed indeed. This tool can be
e.g. a Microsoft compiler with __COUNTER__ extension to run in the
preprocessor mode, if you promise to use the same number of lines for
each entry (but it's not robust)

An external preprocessor would do the job nicely, with the index array
generated automatically. E.g. in Unimal:
#MP Row = 0
#MP index = 0
const struct element all_elems[] =
{
//begin Row 0
{ /* first in Row 0 */ },
#MP index = index+1 ;count number of elements in the row
.......
{ /* last in Row 0 */ },
#MP index = index+1
#MP ;End of row:
#MP index%dRow = index;
#MP Row = Row+1 ;count rows
..........
//begin Row N
{ /* first in Row N */ },
#MP index = index+1 ;count number of elements in the row
.......
{ /* last in Row N */ },
#MP index = index+1 ;count number of elements in the row
#MP ;End of row:
#MP index%dRow = index;
#MP Row = Row+1 ;count rows
};
const int_or_smaller_perhaps_unsigned index[] =
{
0,
#MP For x = 0, Row-1
#mp%dindex%dx,
#MP Endfor
NELEM(all_elems),
};
[Of course, all repetitive ugliness should be stashed away in macros.]
 
T

Thad Smith

Chris said:
I don't know the easiest way to explain this so, I'm going to give an
example of how it is today....and what I'm trying to accomplish.

in the header...
typedef struct XXX_ELEMENT_tag
{
UINT8 num;
UINT8 param;
UINT16 fre1;
INT16 a1;
} ;
typedef struct XXX_ELEMENT_tag ELEMENT_T;

#define MAX_ELEMENTS 8
typedef struct XYZ_S_tag {
UINT8 num_elements;
UINT8 flag;
ELEMENT_T element[MAX_E]; /*** This is the spot ***/
} XYZ_S_tag;

And of course the S_tag structure is buried one more structure down. Say
something like:

typedef struct T_TABLE_tag
{
XYZ_S_tag run;
XYZ_S_tag walk;
.... /* alot of these */
XYZ_S_tag crawl;
} TABLE_T;
typedef struct T_TABLE_tag T_table;

And in a C file a global is initialized like this...
T_table t_table =
{
{ /* run */
2, /* Num Elements */
APPLY_WINDOW, /* Flag */
{
/* N p f a */
/* 0 */ { 2, 0, 480, -240 },
/* 1 */ { 0, 0, 0, 500 },
/* 2 */ { 0, 0, 0, 0 },
/* 3 */ { 0, 0, 0, 0 },
/* 4 */ { 0, 0, 0, 0 },
/* 5 */ { 0, 0, 0, 0 },
/* 6 */ { 0, 0, 0, 0 },
/* 7 */ { 0, 0, 0, 0 },
/* 8 */ { 0, 0, 0, 0 },
}
},
{ /* walk */
/* repeat many many times */

}
}

Ok, let's just say element is bigger than my example above and there are
about a 1000 of these, only half of which have data. I've inherited
this code and this table is occupying 300k of memory and memory's tight.
I want to delete all the filler rows and declare element in a way that
the compiler will auto size what it needs for each instance.


As others have said, it can't be done the way you suggest. My
recommendation is to write a special purpose tool to do what you want.
Change your declaration to be

struct XYZ_S_tag {
UINT8 num_elements;
UINT8 flag;
ELEMENT_T *element; /*** This is changed ***/
} XYZ_S_tag;

The output of the preprocessor would be something like:

const ELEMENT_T allet = {
/* run N p f a */
/* 0 */ { 2, 0, 480, -240 },
/* 1 */ { 0, 0, 0, 500 },
/* walk N p f a */
/* 0 */ { 3, 0, 200, -240 },
/* 1 */ { 0, 5, 5, 500 },
/* 2 */ { 0, 5, 5, 500 },
...
};

const T_table t_table =
{
{ /* run */
2, /* Num Elements */
APPLY_WINDOW, /* Flag */
allet+0 /* starting row */
},
{ /* walk */
2, /* Num Elements */
APPLY_WINDOW, /* Flag */
allet+2 /* starting row */
},
...
};

It should be fairly easy to write a program to generate the table you need.
You can keep the original data declaration as the source for the table and
rerun the tool whenever there is a change.
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top