TinyURL widget - shorten your URL's for free!

Enter a long URL to make tiny:

Sunday, December 18, 2016

How to mention someone in Blogger Blogspot posting.

To mention someone in a post or comment:
  1. Type +[person's name] or @[person's name]. (You can also type their email address instead of their name.)
  2. As you type, an autocomplete list of people will appear.
  3. Select the person you want to mention.
My addendum:

You can also copy a twitter handle from Twitter.com and that includes the hyperlink to Twitter in your post.

Friday, December 16, 2016

OpenMPI working nameserver publish / lookup example

This blog post entails a simple client / server example using OpenMPI, the opmi-server, and simple commands to publish a named server, lookup the server using a client, then connect and transceive data between the server and client.

If you need a refresher on OpenMPI first then this is a good start


The Gist is located here. 


I got this working on:

 uname -a
Linux hellion 3.19.0-77-generic #85-Ubuntu SMP Fri Dec 2 03:43:54 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

mpicc --version
gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
This post assumes you've got the OpenMPI installed completely and working properly.


This builds on pseudo-examples left here and mainly here: http://www.mcs.anl.gov/research/projects/mpi/mpi-standard/mpi-report-2.0/node106.htm#Node108

You compile the client and server:

The client is here

https://gist.github.com/DaemonDave/21fea476847d94326ec6c9664c15fb87#file-name-client-c 


/*!
*
* OpenMPI nameserver example
*
* client that looks up name from nameserver then connects to that server
*
* */
#include "mpi.h"
#include "stdio.h"
#include "string.h"
#define MAX_DATA 50
/*!
\brief How to execute mpi name server
mpirun -np 1 ompi-server --no-daemonize -r + &
*
* Success looks like this :
*
"server available at 3653042176.0;tcp://192.168.10.191:52434+3653042177.0;tcp://192.168.10.191:48880:300"
*/
int main( int argc, char **argv )
{
MPI_Comm server;
double buf[MAX_DATA];
char port_name[MPI_MAX_PORT_NAME];
int tag = 0;
int done = 0;
int n;
double * p;
buf[0] = 25.5;
buf[1] = 26.5;
buf[2] = 27.5;
n = 2;
MPI_Init( &argc, &argv );
//strcpy(port_name, argv[1] );/* assume server's name is cmd-line arg */
fprintf(stderr, "looking up server ... \n");
// second command line parameter is the name of server to lookup
MPI_Lookup_name( "ocean", MPI_INFO_NULL, port_name);
// connect to name and place data into server
MPI_Comm_connect( port_name, MPI_INFO_NULL, 0, MPI_COMM_WORLD, &server );
// aim p at first double entry
p = buf;
while (!done)
{
tag = 2; /* Action to perform */
MPI_Send( (void *) p, 1, MPI_DOUBLE, 0, tag, server );
/* etc */
n--;
p++;
if ( n < 0) done = 1;
}
MPI_Send( buf, 0, MPI_DOUBLE, 0, 1, server );
MPI_Comm_disconnect( &server );
MPI_Finalize();
return 0;
}
/* client side lookup request
MPI_Lookup_name("ocean", MPI_INFO_NULL, port_name);
MPI_Comm_connect( port_name, MPI_INFO_NULL, 0, MPI_COMM_SELF,
&intercomm);
*/
view raw name-client.c hosted with ❤ by GitHub
/*!
*
* OpenMPI nameserver example
*
* server that publishes name to nameserver then awaits client connections
*
* */
#include "mpi.h"
#include "stdio.h"
#include "string.h"
#define MAX_DATA 50
/*!
\brief How to execute mpi name server
mpirun -np 1 ompi-server --no-daemonize -r + &
*
* Success looks like this :
*
"server available at 3653042176.0;tcp://192.168.10.191:52434+3653042177.0;tcp://192.168.10.191:48880:300"
*/
int main( int argc, char **argv )
{
MPI_Comm client;
MPI_Status status;
char port_name[MPI_MAX_PORT_NAME];
double buf[MAX_DATA];
int size, again;
MPI_Info info;
double * p;
MPI_Init( &argc, &argv );
MPI_Comm_size(MPI_COMM_WORLD, &size);
if (size != 1) error( "Server too big");
MPI_Open_port(MPI_INFO_NULL, port_name);
printf("server available at %s\n",port_name);
MPI_Info_create(&info);
MPI_Info_set(info, "ompi_global_scope", "true");
// publish to name server with global scope info
MPI_Publish_name("ocean", info, port_name);
while (1)
{
// accept clients
MPI_Comm_accept( port_name, MPI_INFO_NULL, 0, MPI_COMM_WORLD, &client );
again = 1;
while (again)
{
MPI_Recv( buf, MAX_DATA, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG, client, &status );
switch (status.MPI_TAG)
{
case 0:
MPI_Unpublish_name("ocean", MPI_INFO_NULL, port_name);
MPI_Comm_free( &client );
MPI_Close_port(port_name);
MPI_Finalize();
return 0;
case 1:
MPI_Comm_disconnect( &client );
again = 0;
break;
case 2: /* do something */
p = (double *) buf;
fprintf(stderr, " we got a client's data: %f\n", *p);
break;
default:
/* Unexpected message type */
// radical original version aborted everything...
;//MPI_Abort( MPI_COMM_WORLD, 1 );
}
}
}
}
/*
MPI_Open_port(MPI_INFO_NULL, port_name);
MPI_Publish_name("ocean", MPI_INFO_NULL, port_name);
MPI_Comm_accept(port_name, MPI_INFO_NULL, 0, MPI_COMM_SELF, &intercomm);
// do something with intercomm
MPI_Unpublish_name("ocean", MPI_INFO_NULL, port_name);
*/
view raw name-server.c hosted with ❤ by GitHub


 mpicc -o client name-client.c
 The server is here

https://gist.github.com/DaemonDave/21fea476847d94326ec6c9664c15fb87#file-name-server-c

mpicc -o server  name-server.c


You run the ompi-server first, to establish the name server. But to do this you run mpirun with ompi-server and not just run it.

*!
 \brief How to execute mpi name server
 mpirun -np 1  ompi-server --no-daemonize -r + &
 *
 * Success looks like this :
 *
    "server available at 3653042176.0;tcp://192.168.10.191:52434+3653042177.0;tcp://192.168.10.191:48880:300"

*/


Once the ompi-server is running, you can refer to it by file, but you can also refer to it by it's pid if it's local. To get things running with the minimum of overlapping errors, I start with simple local setups.

You find the pid of the ompi-server:

ps -ef | grep ompi     

1634  1458  0 Dec15 ?        00:03:41 compiz  dave     

16050  1954  0 10:26 pts/18   00:00:00 mpirun -n 1 ompi-server --no-daemonize -r + dave    

16051 16050  0 10:26 pts/18   00:00:00 ompi-server --no-daemonize -r + dave

You run the server like this:
mpirun -np 1  --ompi-server pid:16050  ./server
It looks successful like this:
server available at 3500802048.0;tcp://192.168.10.191:50129+3500802049.0;tcp://192.168.10.191:53693:300

Now you mpirun the client and it looks like this:
  mpirun -np 1 --ompi-server pid:14341 ./client
looking up  server ...
The server  responds like this:

 we got a client's data: 25.500000
 we got a client's data: 26.500000
 we got a client's data: 27.500000
^Cmpirun: killing job...

You can mpirun the client and server from a config file as well instead of informing of the ompi-server location by PID like this:

 mpirun -np 1 --ompi-server file:./nameserver.cfg ./server
server available at 2886402048.0;tcp://192.168.10.191:51344+2886402049.0;tcp://192.168.10.191:54857:300



Wednesday, October 5, 2016

Trouble with 2 Functions in C [on hold]

Sorry people on Stackexchange tend to be lonely angry introverts looking for needy people to show how smart they are, so they bicker more with format, rather than just answering your question:

I think you are asking how do you "call on" or access the function variables you sent inside the function.

You don't post "Type" so let's use the example of "Student".

Since you pass a pointer to an array of Students called "class"  you would access the Students directly - you are working on the original not a copy.

     class->weightavg

would get you the double value inside class ASSUMING your pointer was to one and only one student in class[0] which is the same as class-> or pointer to the first array element.

If you have more than one which is what I believe your length variable is for, then it would be

    class[length].weightavg

for the double value.

or if you had a bunch of students then make an int i, iterate it, and


    class[i].weightavg

so long as you check up to length but not AT length (C arrays start from 0 not 1) so length should be outside the array.

your char * strings are a special case, look up strcpy() like functions or use printf("%s", class[i].name ); to show that value.

BTW: I addressed the kind of unhelpful anti-social behavior that goes on in stack exchange here where I poked the lonely introverts and they responded exactly the way I predicted.

Wednesday, September 21, 2016

Using Linux on chrome book

Once you install linux to get back there from reboot:

Once logged in, Ctrl-Alt-T brings up chrome shell crosh

Type: shell

Then type: sudo startxfce4 to start X server session. 

Friday, August 5, 2016

Stripping personal information from URL / URI


http://www.reuters.com/article/us-usa-economy-trade-idUSKCN10G1C6?feedType=RSS&feedName=businessNews&utm_source=Twitter&utm_medium=Social&utm_campaign=Feed%3A+reuters%2FbusinessNews+%28Business+News%29


For those that don't want to broadcast information to HTTP servers about where they came from, what news feed worked, etc. to a company, then you can strip it when you see a URL Universal Resource Locator / URI Universal Resource Indicator. 

In HTML, the ? symbol represents variables. Or in WWW language:

The HTTP query string is specified by the values following the question mark(?). 

So everything after the ? are variables sent to the HTML / HTTP server to process along with the request, which is the reuters article above, can be stripped from the URL request (your browser asks "Can you find me this URI?" and the server sends back files).

http://www.reuters.com/article/us-usa-economy-trade-idUSKCN10G1C6?feedType=RSS&feedName=businessNews&utm_source=Twitter&utm_medium=Social&utm_campaign=Feed%3A+reuters%2FbusinessNews+%28Business+News%29
 
 Strip all characters past the ? and it still works:
 
http://www.reuters.com/article/us-usa-economy-trade-idUSKCN10G1C6

Friday, July 29, 2016

Why I hate a majority of stackoverflow participants

If you want to see an example of why lonely introverted Tech workers can't be trusted to work effectively with others.



http://stackoverflow.com/questions/38593588/reading-a-value-with-a-timer-in-c/38595314#38595314

Wherein I try to help the person asking a question. And rather than helping me help that person, I get assaulted by a moron lacking any empathy spouting format rules in the mode of rude. Notice Inspectable never once did any actual work!

This is the kind of thing that makes people avoid these  help sites, people feel stupid asking questions or answering them, and then they get some rude moron making the situation worse through bad manners and unsympathetic actions.

I post on many stack exchange sites, go to the English or Buddhist stacks and you will find polite people trying to exchange and learn, and most of all consider the ideas on display. They do not act the same as IT workers that troll stack overflow. I know there are a few good ones.  I have worked with people that helped me. But they are in the minority, the angry ones that do no work seem to take the game rules to extreme for personal reasons outside helping people.

I wonder if stack overflow will survive the decade.

So this is a major reason why I post my own blog solutions, because some little shit can't stroll along and squash what I wrote without even trying the solutions.

Tuesday, July 26, 2016

Septic Pump Maintenance


A few days ago, we had a powerful hailstorm, the worst since 2006/2005 when the entire house was pummeled by hail the size of softballs.

This time the hail was the size of fastballs





It just so happened that we had requested a septic pump truck to empty our septic system two days before the storm.  The guy came and emptied it, and then when I was paying for it he said the water level had been really high, that the pump might not been working. 

So I will relay all the information I have learned as I recount how I fixed my septic system.  It was hard won information from trial and error, from fixing it in -40C when tampon strings burned out the pump motor,  what I changed after fixing it 4 times in 13 years and how to make it easier and faster to fix.


I live in a cold climate, it can get as low as -55C with wind chill.  The first recommended fix, if your system was installed by an amateur, is to make a fortified manhole instead of the flimsy cheap stuff a "professional" would install.  The system I inherited had a regular electrical socket mounted through a wood plank. The septic tank opening was surrounded by plywood planks.  Some professional.  I built a 2-tall concrete garden brick building around the top, I filled it with sand and redid the electrical to a proper IP54 box for exposed sockets.






I made my system with 2 covers, an inner atop the man hole and the outer above the concrete wall.  


This is the outer cover.


 This is the custom inner cover.


It has happened that the electrical connections outside the manhole were wet and froze over.  The last time, I can tell from fixing it with bare fingers at -40C, you want your pump to stop is in the dead of winter. What you can use, which is a real source of heat, is the ambient heat from microbial action (bugs eating poo) to warm up around the plugs. You position the electrical connections inside the wellhead above the water spill zone. So long as you make sure they are properly protected from water build up, they will only get moist but stay warm. I use a single outdoor cord to connect to the socket to the electrical block in the warmer air. I tape the electrical to the top of the outlet pipe connected to the septic pump.



My septic system uses the outlet pipe as both the outport and check valve. The guy was too cheap to install a proper valve, but the up, over, and down pipe design makes an effective check valve when the pump stops. The trick is it needs a hole drilled into the pipe to prevent a vapour lock situation.  The first time I fixed it, I got it all done, and then turned it on and nothing happened. I sat there aghast at how all parts were in and nothing moved. Upon some searching, the answer appeared.  One 1/32 inch hole on the upward pipe and it stabilizes the pressure.


The original connection was a pipe fitting male to female. He glued a connection on the right angle pipe and it becomes quite a pain.  I can assure you if you remove the septic pump then try and replace it and don't set it on the pedestal properly, if the ends are slightly dirty (there is effluent all over this system in parts you don't expect) or frozen because you had the pieces on the ground in the cold, that you will spend more time than you need to trying to screw the ends together. You don't want to snap the ends or rip the pipe out either while trying to work them together.  I replaced the female connector with a rubber sleeve and sealing clamps called a coupler in this case 2inch to 2inch. All I need is to loosen one clamp screw and it peels right out and I can reseat it just as easily.

First step in determining what is broken, assemble replacement parts.  Since I was pulling out the septic pump anyway, and I have to remove the electrical connections, I will replace parts as I go. If you weren't aware most mechanical floats have a 2 year warranty so if they are questionable you may as well replace them.  I got a new cable, electrical tape, and zap straps.



I bought a $47 replacement 15 foot 90 degree mechanical switch to for the turn on switch.  When the water gets to the top of the range you want, you want the switch to finally turn on.  Then when it reaches the bottom, you want it to turn off. The 450467 has a long cord in case I need to hook it right to the top socket. I buy the longer ones then if I need to connect to the outlet with the pump I can do it. Excess cord can just be wrapped in the top of the hole near the  electrical block.



Since the pump might not be turning on, this is the suspect switch not the lower one at the bottom that prevents the motor from running dry.  There are 2 float switches in the electrical block. I will replace this one first run and see if that fixes it.

The second step is to unhook the septic pump, remove the pump and pipe, remove the electrical block, remove the pump plug, and plug the pump straight into the electrical socket. If the pump won't turn on then, it's shot.



With this new system, it takes 2 minutes to disconnect and pull out. One other benefit of the concrete wall is that you can lean on the rim and not fear shattering the plexiglass manhole - that would allow sand or dirt to get in.


Here is the septic system with the pump removed. I don't use gloves because I want to make sure I have a firm grip on the pipe/cable while working it around out of the hole. Expect you will get dirty and smell like shit. The rewards for a job well done is a shower.



Here is the septic pump, at bottom and the outlet pipe all the way at the top of image. Right beside the motor is the low end mechanical switch that turns off to stop motor burnout. Halfway up is another coupling I will explain. At the top is the electrical block.

If you cut the outlet pipe too short, you need to replace it. If you cut the outlet pipe too long you need to pull it all apart, recut, and replace it. By adding a coupler above the septic pump, you can make a quick adjustment, you can fit a 2 inch outlet pipe onto a 3 inch pipe on the septic pump as this one in the image above is. This also allows you to twist the direction of the pump versus the outlet pipe. It all becomes easier with an extra $5 part.


I use stereo wire doubled up as the hauling cable tied to the pump ring instead of using the pipe to lift it out.  This saves a little time because you can haul heavier and you don't risk damaging the pipe installing / reinstalling.

I pulled apart the electrical block, I unwrapped electrical tape, reinstalled the septic pump and plugged the pump by itself back into the electrical socket. It worked and I left the pump on to empty out the water.  Then I removed the septic pump so I could replace the mechanical switch. 


Here is the original float switch and the new one laid on top. If you want it to work the same the first thing you connect is the new float switch tied with zap straps at the same point with the same length under the line. This allows the switch to turn on for the right setting of your tank.  Once that is done you can snip the old switch and pull the dead cable out of the run. 



Once that float is installed, you connect all the plugs into an electrical block and then wrap them. You soft connect it to the top of the outlet pipe with electrical tape rather than zap straps and then wrap the extra cord lengths in a bundle. Last time I used gun tape this time I used electrical tape.  I replace the old electrical cord and taped it as well to the pipe.


If you've done everything properly, took your time and made sure your setting was right, then it's reinstall, connect, and plug it in. This fix took 1 hour 12 minutes, compared to the worst of many hours over many days in dribs and drabs.












Saturday, July 9, 2016

HOE TO: Air Spade to excavate under concrete using compressor

This post covers how to use a compressor and a pneumatic excavation tool, made in this case from copper pipe, a ball valve, and fitting, to dig under a concrete slab without a lot of effort. For small hole, a 1/2 inch 3 foot copper pipe works fine.  I use my small compressor with about 50 PSI, mainly because I am not in a hurry and can wait for it to store up air.

This hole under a 3 foot concrete slab took about 3.5 hours. The intended pipe was a 1/4 inch sprinkler pipe so I didn't need anything more than common stuff you would have at home: a screwdriver, ear defenders,  electrical cable, and about 20 feet of 200PSI compressor line.




I learned about air spades when working at General Dynamics and they were a potential system for mine clearance as they spread pressure around an excavating land mine; this means they could expose landmines with lesser risk of initiating fuses. In those applications the air pressure could be much greater than what I would recommend using on home made versions.

In this case, the goal is to use air pressure to expose and remove just enough dirt to run a pipe through.



I made this air spade out of copper because you need a semi-hard metal that can take some force from prodding and withstand enough pressurization. I solder the shit out of the connections, they are full welds and can survive prodding into the ground and scratching on concrete.

A reducer nozzle soldered to one end, a 1/2 inch ball valve soldered to the other and a compressor fitting is all you need.



I use 50 PSI and crimp the reducer into a venturi. You want an opening that can't swallow a rock so it runs without bursting the pipe, and can be used to prod and loosen rocks and soil.  While you aren't digging with a shovel, you are actively stabbing the rocks and soil to jar loose rocks.  The air pushes loose fill out the hole.


Copper won't take steel-type forcing, not without exploding or bending so you will apply steady firm force and move the tip around. Since the parts for this amount to $30 or so, I would recommend replacing one after a few uses rather than risk splayed copper shards in your hand.


  You could go without a valve but this way you can build up pressure.

This is the hole location, running a 1/4 inch pipe under sidewalk to the irrigation pipe in the garden. We, I, shovelled out 2 tons of gravel and we are going to lay sod in the dead space under the Mugo pines. Last step with our hot dry climate is irrigation!


First dig out small holes and remove enough vegetation you can be comfortable working on both sides. The holes start small and you are going to work from both sides but in case there is a large rock don't dig out too much- you might be moving. Air spades can't break rocks - at least not small ones. You want to dig shallow holes from both ends. As your spade gets longer into the hole, you want to extend the holes on both sides so you keep the spade under the concrete and not digging a larger hole.




The steeper the angle, the longer the hole and in the future the softer steeper hole will pour moisture into it which you don't want for frost heave reasons. You want the hole hugging the concrete.  N.B. I blow out my lines in the fall so they aren't going to freeze.


Once the hole is prepped, you can begin.  Of course you are working from both sides so you only have to go about half way from either side. This air spade could dig a whole sidewalk from one side but it would take longer.


I start loosening with a screwdriver and while you are working excavate the loose soil from the hole mouth.



Important, the more dirt you clear from the hole mouth, the more air will push dirt right out of the hole for you. If you don't clear while you work, you will just dig more yourself. Let the spade work. I did leave the spade on and went for a pee break and it did some work blowing out loose dirt but you really need to actively grind to work faster. Stab, blow, and remove loose dirt are the repeated actions.

Here's a small video from the excavation end.



Notice how the angle of attack is lower as you dig out the entrance. 

I use gloves even though it's low pressure because the venturi might rip skin back and I normally have skin tears on my hands. I've seen the video of compressors ripping skin back to the elbows, not worth the risk. 


 I use a prodding motion and alternate working both sides.

 Once you have gotten the spade fully in, you will see dirt shooting out the other side.




The gravel spoil on this photo came from the middle.



Last task is to feed the pipe through the hole. I tape the pipe to the air spade and shove it through.


Line is through in 3.5 hours in this case with dry soil that consisted of clay and sediment.


Thanks for reading my how to/ hoe to guide to air excavating.

Variable argument C functions with va_arg

Most tutorials online and those left standing for C give simplistic examples for the variable argument functions in <stdarg.h> for handling variable argument passing of various types.  The examples show you a simple data type like an integer and so on.

In this example, you can pass larger data types like structs into a variadic function just as easily. This means one can reproduce a simpler version of encapsulation/ polymorphism as in OOP languages. To present it first; here is a simple example that passes a struct, and then a struct inside a struct.

#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
// how to make a variadic va_arg that can handle structs passed as variable parameters / variables to a va_arg function
typedef struct Vec3df
{
float x, y, z;
} vec3df_t;
float sum(int num,...)
{
va_list valist;
float totalsum = 0.0;
int i;
float sumx = 0.0;
float sumy = 0.0;
float sumz = 0.0;
// dynamic data area
vec3df_t * data;
// initialize valist for num number of arguments
va_start(valist, num);
// create a dynamic pointer array using the num variable pre-assigned
data = ( vec3df_t *) malloc ( num * sizeof( vec3df_t) );
// aim pointers all the arguments assigned to valist
for (i = 0; i < num; i++)
{
// aim individual values
data[i] = va_arg(valist, vec3df_t );
}
// data can be access by subelem
for (i = 0; i < num; i++)
{
sumx += data[i].x;
sumy += data[i].y;
sumz += data[i].z;
}
totalsum = sumx + sumy + sumz;
// clean memory reserved for valist
va_end(valist);
return totalsum;
}
// this is a struct within a struct
// demonstrates a deeper point access for
typedef struct larger
{
vec3df_t pose;
int azimuth;
} larger_t;
// accessing inner members of passed variadic struct
float innersum(int num,...)
{
va_list valist;
float totalsum = 0.0;
int i;
float sumx = 0.0;
float sumy = 0.0;
float sumz = 0.0;
// dynamic data area
larger_t * data;
// initialize valist for num number of arguments
va_start(valist, num);
// create a dynamic pointer array using the num variable pre-assigned
data = ( larger_t *) malloc ( num * sizeof( larger_t) );
// aim pointers all the arguments assigned to valist
for (i = 0; i < num; i++)
{
// aim individual values
data[i] = va_arg(valist, larger_t );
}
// data can be access by subelem
for (i = 0; i < num; i++)
{
sumx += data[i].pose.x;
sumy += data[i].pose.y;
sumz += data[i].pose.z;
}
totalsum = sumx + sumy + sumz;
// clean memory reserved for valist
va_end(valist);
return totalsum;
}
int main()
{
// heap allocated structs
vec3df_t a;
vec3df_t b;
vec3df_t c;
larger_t q;
larger_t r;
larger_t s;
a.x = 20;
a.y = 30;
a.z = 40;
b.x = -30;
b.y = -60;
b.z = -99.5;
c.x = 0.03;
c.y = 0.0004;
c.z = -0.023;
q.pose.x = 25;
q.pose.y = 25;
q.pose.z = 25;
r.pose.x = 125;
r.pose.y = 125;
r.pose.z = 125;
s.pose.x = 225;
s.pose.y = 225;
s.pose.z = 225;
q.azimuth = 180.1;
r.azimuth = 180.1;
s.azimuth = 180.1;
printf("Sum of a + b + c = %f\n", sum(3, a,b,c));
printf("Sum of a + b = %f\n", sum(2, a,b));
printf("Sum of a = %f\n", sum(1, a));
printf("InnerSum of a + b + c = %f\n", innersum(3, q,r,s));
printf("InnerSum of a + b = %f\n", innersum(2, q,r));
printf("InnerSum of a = %f\n", innersum(1, q));
return 0;
}
view raw va_arg.c hosted with ❤ by GitHub


This passes a position struct containing x,y,z as coordinates, and the the same struct is passed to another function that accesses the inner position struct.   As a more complicated example, this suffices.

Now, when on considers pointer to functions and unions can be part of a struct, then you have all the encapsulation needed to contain an object struct that can be passed as argument. With unions, one can make a polymorphic version of a struct that can pass many types within the same container struct.

Tuesday, May 10, 2016

A real n-ary tree exemplar with GNodes from glib-2.XXX

This post presents a realistic GNodes unbalanced n-ary tree exemplar in C using Gnome glib-2.XXX.You can find more information on the API here.

I wanted to design a client/server node structuring system based on distance metric from a common point, so it made sense to explore  unbalanced trees. I wasn't familiar with them nor GLib at the outset. This work took about 2 days of fidgeting.

Online examples are bare presentations of the basic function calls. Here is a completed application that does something. The code you need is here ntree-distance.c.

This one presents an n-tree made from 3D Euclidean distances to and from an arbitrary point (city) and structures them on bounded distance neighbourhood balls. Essentially, the closest cities are siblings, those beyond a minimum metric are children, and those beyond a second metric are grandchildren. One could extend the paradigm as far as one likes.

The data for these cities is randomly generated, cities are then fed into a for loop that sorts them.  At the end of the code it uses the GLib g_tree_traverse and executes a simple printf function to display the data at each node.

In order to make children and grandchildren nodes at the start with no data entered, one can make notional child and grandchild nodes etc. to whatever depth is required. So in the code are a base, a middle, and a far (and a farthest I didn't include) GNode that are used to make a 4-depth tree structure before the data is available.

One important thing to note about GNodes is that there is a deep copy but no remove function. There is no direct way to remove a single child without destroying the rest under since the links are all pointers not a more complicated form of structure.  There is an unlink so one can construct a different tree from items but that may not be helpful.  Of course this means I have extra tree nodes at every level but I considered it as beneficial for my purpose because if in the future I removed other siblings I can maintain the tree skeleton as it were.

You will note in printouts the notional nodes appear as 0.0000 in the list for tree traversed so they are easy to pick out. As we append nodes at each level the notional tree elements are also 0 child so there is another way to ignore them.

Included in the gist github is a Makefile and the original notional GNode file from IBM that wasn't very helpful... like IBM...


I have included a copy of the old gnode.c code for examination, a glibtest.c from MIT I found if you need to sanity check your GLIB, a Makefile that will compile the ntree-distance.c and the glibtest.c  if you need them but the main file is ntree-distance.c



/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#undef GLIB_COMPILATION
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "glib.h"
#include "gstdio.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef G_OS_WIN32
#include <io.h> /* For read(), write() etc */
#endif
#define GLIB_TEST_STRING "el dorado "
#define GLIB_TEST_STRING_5 "el do"
/* --- variables --- */
static gint test_nums[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
static gint more_nums[10] = { 8, 9, 7, 0, 3, 2, 5, 1, 4, 6};
/* --- functions --- */
static gint
my_list_compare_one (gconstpointer a, gconstpointer b)
{
gint one = *((const gint*)a);
gint two = *((const gint*)b);
return one-two;
}
static gint
my_list_compare_two (gconstpointer a, gconstpointer b)
{
gint one = *((const gint*)a);
gint two = *((const gint*)b);
return two-one;
}
/* static void
my_list_print (gpointer a, gpointer b)
{
gint three = *((gint*)a);
g_print("%d", three);
}; */
static void
glist_test (void)
{
GList *list = NULL;
guint i;
for (i = 0; i < 10; i++)
list = g_list_append (list, &test_nums[i]);
list = g_list_reverse (list);
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != (9 - i))
g_error ("Regular insert failed");
}
for (i = 0; i < 10; i++)
if (g_list_position (list, g_list_nth (list, i)) != i)
g_error ("g_list_position does not seem to be the inverse of g_list_nth\n");
g_list_free (list);
list = NULL;
for (i = 0; i < 10; i++)
list = g_list_insert_sorted (list, &more_nums[i], my_list_compare_one);
/*
g_print("\n");
g_list_foreach (list, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != i)
g_error ("Sorted insert failed");
}
g_list_free (list);
list = NULL;
for (i = 0; i < 10; i++)
list = g_list_insert_sorted (list, &more_nums[i], my_list_compare_two);
/*
g_print("\n");
g_list_foreach (list, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != (9 - i))
g_error ("Sorted insert failed");
}
g_list_free (list);
list = NULL;
for (i = 0; i < 10; i++)
list = g_list_prepend (list, &more_nums[i]);
list = g_list_sort (list, my_list_compare_two);
/*
g_print("\n");
g_list_foreach (list, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != (9 - i))
g_error ("Merge sort failed");
}
g_list_free (list);
}
static void
gslist_test (void)
{
GSList *slist = NULL;
guint i;
for (i = 0; i < 10; i++)
slist = g_slist_append (slist, &test_nums[i]);
slist = g_slist_reverse (slist);
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != (9 - i))
g_error ("failed");
}
g_slist_free (slist);
slist = NULL;
for (i = 0; i < 10; i++)
slist = g_slist_insert_sorted (slist, &more_nums[i], my_list_compare_one);
/*
g_print("\n");
g_slist_foreach (slist, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != i)
g_error ("Sorted insert failed");
}
g_slist_free (slist);
slist = NULL;
for (i = 0; i < 10; i++)
slist = g_slist_insert_sorted (slist, &more_nums[i], my_list_compare_two);
/*
g_print("\n");
g_slist_foreach (slist, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != (9 - i))
g_error("Sorted insert failed");
}
g_slist_free(slist);
slist = NULL;
for (i = 0; i < 10; i++)
slist = g_slist_prepend (slist, &more_nums[i]);
slist = g_slist_sort (slist, my_list_compare_two);
/*
g_print("\n");
g_slist_foreach (slist, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != (9 - i))
g_error("Sorted insert failed");
}
g_slist_free(slist);
}
static gboolean
node_build_string (GNode *node,
gpointer data)
{
gchar **p = data;
gchar *string;
gchar c[2] = "_";
c[0] = ((gchar) ((long) (node->data)));
string = g_strconcat (*p ? *p : "", c, NULL);
g_free (*p);
*p = string;
return FALSE;
}
static void
gnode_test (void)
{
#define C2P(c) ((gpointer) ((long) (c)))
#define P2C(p) ((gchar) ((long) (p)))
GNode *root;
GNode *node;
GNode *node_B;
GNode *node_F;
GNode *node_G;
GNode *node_J;
guint i;
gchar *tstring, *cstring;
root = g_node_new (C2P ('A'));
g_assert (g_node_depth (root) == 1 && g_node_max_height (root) == 1);
node_B = g_node_new (C2P ('B'));
g_node_append (root, node_B);
g_assert (root->children == node_B);
g_node_append_data (node_B, C2P ('E'));
g_node_prepend_data (node_B, C2P ('C'));
g_node_insert (node_B, 1, g_node_new (C2P ('D')));
node_F = g_node_new (C2P ('F'));
g_node_append (root, node_F);
g_assert (root->children->next == node_F);
node_G = g_node_new (C2P ('G'));
g_node_append (node_F, node_G);
node_J = g_node_new (C2P ('J'));
g_node_prepend (node_G, node_J);
g_node_insert (node_G, 42, g_node_new (C2P ('K')));
g_node_insert_data (node_G, 0, C2P ('H'));
g_node_insert (node_G, 1, g_node_new (C2P ('I')));
g_assert (g_node_depth (root) == 1);
g_assert (g_node_max_height (root) == 4);
g_assert (g_node_depth (node_G->children->next) == 4);
g_assert (g_node_n_nodes (root, G_TRAVERSE_LEAFS) == 7);
g_assert (g_node_n_nodes (root, G_TRAVERSE_NON_LEAFS) == 4);
g_assert (g_node_n_nodes (root, G_TRAVERSE_ALL) == 11);
g_assert (g_node_max_height (node_F) == 3);
g_assert (g_node_n_children (node_G) == 4);
g_assert (g_node_find_child (root, G_TRAVERSE_ALL, C2P ('F')) == node_F);
g_assert (g_node_find (root, G_LEVEL_ORDER, G_TRAVERSE_NON_LEAFS, C2P ('I')) == NULL);
g_assert (g_node_find (root, G_IN_ORDER, G_TRAVERSE_LEAFS, C2P ('J')) == node_J);
for (i = 0; i < g_node_n_children (node_B); i++)
{
node = g_node_nth_child (node_B, i);
g_assert (P2C (node->data) == ('C' + i));
}
for (i = 0; i < g_node_n_children (node_G); i++)
g_assert (g_node_child_position (node_G, g_node_nth_child (node_G, i)) == i);
/* we have built: A
* / \
* B F
* / | \ \
* C D E G
* / /\ \
* H I J K
*
* for in-order traversal, 'G' is considered to be the "left"
* child of 'F', which will cause 'F' to be the last node visited.
*/
tstring = NULL;
g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABCDEFGHIJK");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_POST_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "CDEBHIJKGFA");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "CBDEAHGIJKF");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABFCDEGHIJK");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_LEAFS, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "CDEHIJK");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_NON_LEAFS, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABFG");
g_free (tstring); tstring = NULL;
g_node_reverse_children (node_B);
g_node_reverse_children (node_G);
g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABFEDCGKJIH");
g_free (tstring); tstring = NULL;
cstring = NULL;
node = g_node_copy (root);
g_assert (g_node_n_nodes (root, G_TRAVERSE_ALL) == g_node_n_nodes (node, G_TRAVERSE_ALL));
g_assert (g_node_max_height (root) == g_node_max_height (node));
g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &cstring);
g_assert_cmpstr (tstring, ==, cstring);
g_free (tstring); tstring = NULL;
g_free (cstring); cstring = NULL;
g_node_destroy (node);
g_node_destroy (root);
/* allocation tests */
root = g_node_new (NULL);
node = root;
for (i = 0; i < 2048; i++)
{
g_node_append (node, g_node_new (NULL));
if ((i%5) == 4)
node = node->children->next;
}
g_assert (g_node_max_height (root) > 100);
g_assert (g_node_n_nodes (root, G_TRAVERSE_ALL) == 1 + 2048);
g_node_destroy (root);
#undef C2P
#undef P2C
}
static gint
my_compare (gconstpointer a,
gconstpointer b)
{
const char *cha = a;
const char *chb = b;
return *cha - *chb;
}
static gint
my_traverse (gpointer key,
gpointer value,
gpointer data)
{
char *ch = key;
g_print ("%c ", *ch);
return FALSE;
}
static void
binary_tree_test (void)
{
GTree *tree;
char chars[62];
guint i, j;
tree = g_tree_new (my_compare);
i = 0;
for (j = 0; j < 10; j++, i++)
{
chars[i] = '0' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
}
for (j = 0; j < 26; j++, i++)
{
chars[i] = 'A' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
}
for (j = 0; j < 26; j++, i++)
{
chars[i] = 'a' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
}
g_assert_cmpint (g_tree_nnodes (tree), ==, 10 + 26 + 26);
g_assert_cmpint (g_tree_height (tree), ==, 6);
if (g_test_verbose())
{
g_print ("tree: ");
g_tree_foreach (tree, my_traverse, NULL);
g_print ("\n");
}
for (i = 0; i < 10; i++)
g_tree_remove (tree, &chars[i]);
g_assert_cmpint (g_tree_nnodes (tree), ==, 26 + 26);
g_assert_cmpint (g_tree_height (tree), ==, 6);
if (g_test_verbose())
{
g_print ("tree: ");
g_tree_foreach (tree, my_traverse, NULL);
g_print ("\n");
}
}
static gboolean
my_hash_callback_remove (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
if ((*d) % 2)
return TRUE;
return FALSE;
}
static void
my_hash_callback_remove_test (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
if ((*d) % 2)
g_print ("bad!\n");
}
static void
my_hash_callback (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
*d = 1;
}
static guint
my_hash (gconstpointer key)
{
return (guint) *((const gint*) key);
}
static gboolean
my_hash_equal (gconstpointer a,
gconstpointer b)
{
return *((const gint*) a) == *((const gint*) b);
}
static gboolean
find_first_that(gpointer key,
gpointer value,
gpointer user_data)
{
gint *v = value;
gint *test = user_data;
return (*v == *test);
}
static void
test_g_mkdir_with_parents_1 (const gchar *base)
{
char *p0 = g_build_filename (base, "fum", NULL);
char *p1 = g_build_filename (p0, "tem", NULL);
char *p2 = g_build_filename (p1, "zap", NULL);
FILE *f;
g_remove (p2);
g_remove (p1);
g_remove (p0);
if (g_file_test (p0, G_FILE_TEST_EXISTS))
g_error ("failed, %s exists, cannot test g_mkdir_with_parents\n", p0);
if (g_file_test (p1, G_FILE_TEST_EXISTS))
g_error ("failed, %s exists, cannot test g_mkdir_with_parents\n", p1);
if (g_file_test (p2, G_FILE_TEST_EXISTS))
g_error ("failed, %s exists, cannot test g_mkdir_with_parents\n", p2);
if (g_mkdir_with_parents (p2, 0777) == -1)
g_error ("failed, g_mkdir_with_parents(%s) failed: %s\n", p2, g_strerror (errno));
if (!g_file_test (p2, G_FILE_TEST_IS_DIR))
g_error ("failed, g_mkdir_with_parents(%s) succeeded, but %s is not a directory\n", p2, p2);
if (!g_file_test (p1, G_FILE_TEST_IS_DIR))
g_error ("failed, g_mkdir_with_parents(%s) succeeded, but %s is not a directory\n", p2, p1);
if (!g_file_test (p0, G_FILE_TEST_IS_DIR))
g_error ("failed, g_mkdir_with_parents(%s) succeeded, but %s is not a directory\n", p2, p0);
g_rmdir (p2);
if (g_file_test (p2, G_FILE_TEST_EXISTS))
g_error ("failed, did g_rmdir(%s), but %s is still there\n", p2, p2);
g_rmdir (p1);
if (g_file_test (p1, G_FILE_TEST_EXISTS))
g_error ("failed, did g_rmdir(%s), but %s is still there\n", p1, p1);
f = g_fopen (p1, "w");
if (f == NULL)
g_error ("failed, couldn't create file %s\n", p1);
fclose (f);
if (g_mkdir_with_parents (p1, 0666) == 0)
g_error ("failed, g_mkdir_with_parents(%s) succeeded, even if %s is a file\n", p1, p1);
if (g_mkdir_with_parents (p2, 0666) == 0)
g_error("failed, g_mkdir_with_parents(%s) succeeded, even if %s is a file\n", p2, p1);
g_remove (p2);
g_remove (p1);
g_remove (p0);
}
static void
test_g_mkdir_with_parents (void)
{
gchar *cwd;
if (g_test_verbose())
g_print ("checking g_mkdir_with_parents() in subdir ./hum/");
test_g_mkdir_with_parents_1 ("hum");
g_remove ("hum");
if (g_test_verbose())
g_print ("checking g_mkdir_with_parents() in subdir ./hii///haa/hee/");
test_g_mkdir_with_parents_1 ("hii///haa/hee");
g_remove ("hii/haa/hee");
g_remove ("hii/haa");
g_remove ("hii");
cwd = g_get_current_dir ();
if (g_test_verbose())
g_print ("checking g_mkdir_with_parents() in cwd: %s", cwd);
test_g_mkdir_with_parents_1 (cwd);
g_free (cwd);
}
static void
test_g_parse_debug_string (void)
{
GDebugKey keys[3] = {
{ "foo", 1 },
{ "bar", 2 },
{ "baz", 4 }
};
guint n_keys = 3;
guint result;
result = g_parse_debug_string ("bar:foo:blubb", keys, n_keys);
g_assert (result == 3);
result = g_parse_debug_string (":baz::_E@~!_::", keys, n_keys);
g_assert (result == 4);
result = g_parse_debug_string ("", keys, n_keys);
g_assert (result == 0);
result = g_parse_debug_string (" : ", keys, n_keys);
g_assert (result == 0);
}
static void
log_warning_error_tests (void)
{
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
g_message ("this is a g_message test.");
g_message ("non-printable UTF-8: \"\xc3\xa4\xda\x85\"");
g_message ("unsafe chars: \"\x10\x11\x12\n\t\x7f\x81\x82\x83\"");
exit (0);
}
g_test_trap_assert_passed();
g_test_trap_assert_stderr ("*is a g_message test*");
g_test_trap_assert_stderr ("*non-printable UTF-8*");
g_test_trap_assert_stderr ("*unsafe chars*");
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
g_warning ("harmless warning with parameters: %d %s %#x", 42, "Boo", 12345);
exit (0);
}
g_test_trap_assert_failed(); /* we have fatal-warnings enabled */
g_test_trap_assert_stderr ("*harmless warning*");
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
g_print (NULL);
exit (0);
}
g_test_trap_assert_failed(); /* we have fatal-warnings enabled */
g_test_trap_assert_stderr ("*g_print*assertion*failed*");
g_test_trap_assert_stderr ("*NULL*");
}
static void
timer_tests (void)
{
GTimer *timer, *timer2;
gdouble elapsed;
/* basic testing */
timer = g_timer_new ();
g_timer_start (timer);
elapsed = g_timer_elapsed (timer, NULL);
g_timer_stop (timer);
g_assert_cmpfloat (elapsed, <=, g_timer_elapsed (timer, NULL));
g_timer_destroy (timer);
if (g_test_slow())
{
if (g_test_verbose())
g_print ("checking timers...\n");
timer = g_timer_new ();
if (g_test_verbose())
g_print (" spinning for 3 seconds...\n");
g_timer_start (timer);
while (g_timer_elapsed (timer, NULL) < 3)
;
g_timer_stop (timer);
g_timer_destroy (timer);
if (g_test_verbose())
g_print ("ok\n");
}
if (g_test_slow())
{
gulong elapsed_usecs;
if (g_test_verbose())
g_print ("checking g_timer_continue...\n");
timer2 = g_timer_new ();
if (g_test_verbose())
g_print ("\trun for 1 second...\n");
timer = g_timer_new();
g_usleep (G_USEC_PER_SEC); /* run timer for 1 second */
g_timer_stop (timer);
if (g_test_verbose())
g_print ("\tstop for 1 second...\n");
g_usleep (G_USEC_PER_SEC); /* wait for 1 second */
if (g_test_verbose())
g_print ("\trun for 2 seconds...\n");
g_timer_continue (timer);
g_usleep (2 * G_USEC_PER_SEC); /* run timer for 2 seconds */
g_timer_stop(timer);
if (g_test_verbose())
g_print ("\tstop for 1.5 seconds...\n");
g_usleep ((3 * G_USEC_PER_SEC) / 2); /* wait for 1.5 seconds */
if (g_test_verbose())
g_print ("\trun for 0.2 seconds...\n");
g_timer_continue (timer);
g_usleep (G_USEC_PER_SEC / 5); /* run timer for 0.2 seconds */
g_timer_stop (timer);
if (g_test_verbose())
g_print ("\tstop for 4 seconds...\n");
g_usleep (4 * G_USEC_PER_SEC); /* wait for 4 seconds */
if (g_test_verbose())
g_print ("\trun for 5.8 seconds...\n");
g_timer_continue (timer);
g_usleep ((29 * G_USEC_PER_SEC) / 5); /* run timer for 5.8 seconds */
g_timer_stop(timer);
elapsed = g_timer_elapsed (timer, &elapsed_usecs);
if (g_test_verbose())
g_print ("\t=> timer = %.6f = %d.%06ld (should be: 9.000000) (%.6f off)\n", elapsed, (int) elapsed, elapsed_usecs, ABS (elapsed - 9.));
g_assert_cmpfloat (elapsed, >, 8.8);
g_assert_cmpfloat (elapsed, <, 9.2);
if (g_test_verbose())
g_print ("g_timer_continue ... ok\n\n");
g_timer_stop (timer2);
elapsed = g_timer_elapsed (timer2, &elapsed_usecs);
if (g_test_verbose())
g_print ("\t=> timer2 = %.6f = %d.%06ld (should be: %.6f) (%.6f off)\n\n", elapsed, (int) elapsed, elapsed_usecs, 9.+6.5, ABS (elapsed - (9.+6.5)));
g_assert_cmpfloat (elapsed, >, 8.8 + 6.5);
g_assert_cmpfloat (elapsed, <, 9.2 + 6.5);
if (g_test_verbose())
g_print ("timer2 ... ok\n\n");
g_timer_destroy (timer);
g_timer_destroy (timer2);
}
}
static void
type_sizes (void)
{
guint16 gu16t1 = 0x44afU, gu16t2 = 0xaf44U;
guint32 gu32t1 = 0x02a7f109U, gu32t2 = 0x09f1a702U;
guint64 gu64t1 = G_GINT64_CONSTANT(0x1d636b02300a7aa7U),
gu64t2 = G_GINT64_CONSTANT(0xa77a0a30026b631dU);
/* type sizes */
g_assert_cmpint (sizeof (gint8), ==, 1);
g_assert_cmpint (sizeof (gint16), ==, 2);
g_assert_cmpint (sizeof (gint32), ==, 4);
g_assert_cmpint (sizeof (gint64), ==, 8);
/* endian macros */
if (g_test_verbose())
g_print ("checking endian macros (host is %s)...\n",
G_BYTE_ORDER == G_BIG_ENDIAN ? "big endian" : "little endian");
g_assert (GUINT16_SWAP_LE_BE (gu16t1) == gu16t2);
g_assert (GUINT32_SWAP_LE_BE (gu32t1) == gu32t2);
g_assert (GUINT64_SWAP_LE_BE (gu64t1) == gu64t2);
}
static void
test_info (void)
{
const gchar *un, *rn, *hn;
const gchar *tmpdir, *homedir, *userdatadir, *uconfdir, *ucachedir;
const gchar *uddesktop, *udddocs, *uddpubshare;
gchar **sv, *cwd, *sdatadirs, *sconfdirs, *langnames;
if (g_test_verbose())
g_print ("TestGLib v%u.%u.%u (i:%u b:%u)\n",
glib_major_version,
glib_minor_version,
glib_micro_version,
glib_interface_age,
glib_binary_age);
cwd = g_get_current_dir ();
un = g_get_user_name();
rn = g_get_real_name();
hn = g_get_host_name();
if (g_test_verbose())
{
g_print ("cwd: %s\n", cwd);
g_print ("user: %s\n", un);
g_print ("real: %s\n", rn);
g_print ("host: %s\n", hn);
}
g_free (cwd);
tmpdir = g_get_tmp_dir();
g_assert (tmpdir != NULL);
homedir = g_get_home_dir ();
g_assert (homedir != NULL);
userdatadir = g_get_user_data_dir ();
g_assert (userdatadir != NULL);
uconfdir = g_get_user_config_dir ();
g_assert (uconfdir != NULL);
ucachedir = g_get_user_cache_dir ();
g_assert (ucachedir != NULL);
uddesktop = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
g_assert (uddesktop != NULL);
udddocs = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
uddpubshare = g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE);
sv = (gchar **) g_get_system_data_dirs ();
sdatadirs = g_strjoinv (G_SEARCHPATH_SEPARATOR_S, sv);
sv = (gchar **) g_get_system_config_dirs ();
sconfdirs = g_strjoinv (G_SEARCHPATH_SEPARATOR_S, sv);
sv = (gchar **) g_get_language_names ();
langnames = g_strjoinv (":", sv);
if (g_test_verbose())
{
g_print ("tmp-dir: %s\n", tmpdir);
g_print ("home: %s\n", homedir);
g_print ("user_data: %s\n", userdatadir);
g_print ("user_config: %s\n", uconfdir);
g_print ("user_cache: %s\n", ucachedir);
g_print ("system_data: %s\n", sdatadirs);
g_print ("system_config: %s\n", sconfdirs);
g_print ("languages: %s\n", langnames);
g_print ("user_special[DESKTOP]: %s\n", uddesktop);
g_print ("user_special[DOCUMENTS]: %s\n", udddocs);
g_print ("user_special[PUBLIC_SHARE]: %s\n", uddpubshare);
}
g_free (sdatadirs);
g_free (sconfdirs);
g_free (langnames);
if (g_test_verbose())
{
#ifdef G_PLATFORM_WIN32
gchar *glib_dll;
#endif
const gchar *charset;
if (g_get_charset ((G_CONST_RETURN char**)&charset))
g_print ("current charset is UTF-8: %s\n", charset);
else
g_print ("current charset is not UTF-8: %s\n", charset);
#ifdef G_PLATFORM_WIN32
#ifdef G_OS_WIN32
/* Can't calculate GLib DLL name at runtime. */
glib_dll = "libglib-2.0-0.dll";
#endif
#ifdef G_WITH_CYGWIN
glib_dll = "cygglib-2.0-0.dll";
#endif
g_print ("current locale: %s\n", g_win32_getlocale ());
g_print ("GLib DLL name tested for: %s\n", glib_dll);
g_print ("GLib installation directory, from Registry entry for %s if available: %s\n",
GETTEXT_PACKAGE,
g_win32_get_package_installation_directory (GETTEXT_PACKAGE, NULL));
g_print ("Ditto, or from GLib DLL name: %s\n",
g_win32_get_package_installation_directory (GETTEXT_PACKAGE, glib_dll));
g_print ("Ditto, only from GLib DLL name: %s\n",
g_win32_get_package_installation_directory (NULL, glib_dll));
g_print ("locale subdirectory of GLib installation directory: %s\n",
g_win32_get_package_installation_subdirectory (NULL, glib_dll, "lib\\locale"));
g_print ("GTK+ 2.0 installation directory, if available: %s\n",
g_win32_get_package_installation_directory ("gtk20", NULL));
g_print ("found more.com as %s\n", g_find_program_in_path ("more.com"));
g_print ("found regedit as %s\n", g_find_program_in_path ("regedit"));
g_print ("a Win32 error message: %s\n", g_win32_error_message (2));
#endif
}
}
static void
test_paths (void)
{
struct {
gchar *filename;
gchar *dirname;
} dirname_checks[] = {
{ "/", "/" },
{ "////", "/" },
{ ".////", "." },
{ "../", ".." },
{ "..////", ".." },
{ "a/b", "a" },
{ "a/b/", "a/b" },
{ "c///", "c" },
#ifdef G_OS_WIN32
{ "\\", "\\" },
{ ".\\\\\\\\", "." },
{ "..\\", ".." },
{ "..\\\\\\\\", ".." },
{ "a\\b", "a" },
{ "a\\b/", "a\\b" },
{ "a/b\\", "a/b" },
{ "c\\\\/", "c" },
{ "//\\", "/" },
#endif
#ifdef G_WITH_CYGWIN
{ "//server/share///x", "//server/share" },
#endif
{ ".", "." },
{ "..", "." },
{ "", "." },
};
const guint n_dirname_checks = G_N_ELEMENTS (dirname_checks);
struct {
gchar *filename;
gchar *without_root;
} skip_root_checks[] = {
{ "/", "" },
{ "//", "" },
{ "/foo", "foo" },
{ "//foo", "foo" },
{ "a/b", NULL },
#ifdef G_OS_WIN32
{ "\\", "" },
{ "\\foo", "foo" },
{ "\\\\server\\foo", "" },
{ "\\\\server\\foo\\bar", "bar" },
{ "a\\b", NULL },
#endif
#ifdef G_WITH_CYGWIN
{ "//server/share///x", "//x" },
#endif
{ ".", NULL },
{ "", NULL },
};
const guint n_skip_root_checks = G_N_ELEMENTS (skip_root_checks);
gchar *string;
guint i;
if (g_test_verbose())
g_print ("checking g_path_get_basename()...");
string = g_path_get_basename (G_DIR_SEPARATOR_S "foo" G_DIR_SEPARATOR_S "dir" G_DIR_SEPARATOR_S);
g_assert (strcmp (string, "dir") == 0);
g_free (string);
string = g_path_get_basename (G_DIR_SEPARATOR_S "foo" G_DIR_SEPARATOR_S "file");
g_assert (strcmp (string, "file") == 0);
g_free (string);
if (g_test_verbose())
g_print ("ok\n");
#ifdef G_OS_WIN32
string = g_path_get_basename ("/foo/dir/");
g_assert (strcmp (string, "dir") == 0);
g_free (string);
string = g_path_get_basename ("/foo/file");
g_assert (strcmp (string, "file") == 0);
g_free (string);
#endif
if (g_test_verbose())
g_print ("checking g_path_get_dirname()...");
for (i = 0; i < n_dirname_checks; i++)
{
gchar *dirname = g_path_get_dirname (dirname_checks[i].filename);
if (strcmp (dirname, dirname_checks[i].dirname) != 0)
{
g_error ("\nfailed for \"%s\"==\"%s\" (returned: \"%s\")\n",
dirname_checks[i].filename,
dirname_checks[i].dirname,
dirname);
}
g_free (dirname);
}
if (g_test_verbose())
g_print ("ok\n");
if (g_test_verbose())
g_print ("checking g_path_skip_root()...");
for (i = 0; i < n_skip_root_checks; i++)
{
const gchar *skipped = g_path_skip_root (skip_root_checks[i].filename);
if ((skipped && !skip_root_checks[i].without_root) ||
(!skipped && skip_root_checks[i].without_root) ||
((skipped && skip_root_checks[i].without_root) &&
strcmp (skipped, skip_root_checks[i].without_root)))
{
g_error ("\nfailed for \"%s\"==\"%s\" (returned: \"%s\")\n",
skip_root_checks[i].filename,
(skip_root_checks[i].without_root ?
skip_root_checks[i].without_root : "<NULL>"),
(skipped ? skipped : "<NULL>"));
}
}
if (g_test_verbose())
g_print ("ok\n");
}
static void
test_file_functions (void)
{
const char hello[] = "Hello, World";
const int hellolen = sizeof (hello) - 1;
GError *error;
char template[32];
char *name_used, chars[62];
gint fd, n;
strcpy (template, "foobar");
fd = g_mkstemp (template);
if (g_test_verbose() && fd != -1)
g_print ("g_mkstemp works even if template doesn't end in XXXXXX\n");
close (fd);
strcpy (template, "fooXXXXXX");
fd = g_mkstemp (template);
if (fd == -1)
g_error ("g_mkstemp didn't work for template %s\n", template);
n = write (fd, hello, hellolen);
if (n == -1)
g_error ("write() failed: %s\n", g_strerror (errno));
else if (n != hellolen)
g_error ("write() should have written %d bytes, wrote %d\n", hellolen, n);
lseek (fd, 0, 0);
n = read (fd, chars, sizeof (chars));
if (n == -1)
g_error ("read() failed: %s\n", g_strerror (errno));
else if (n != hellolen)
g_error ("read() should have read %d bytes, got %d\n", hellolen, n);
chars[n] = 0;
if (strcmp (chars, hello) != 0)
g_error ("wrote '%s', but got '%s'\n", hello, chars);
close (fd);
remove (template);
error = NULL;
strcpy (template, "zap" G_DIR_SEPARATOR_S "barXXXXXX");
fd = g_file_open_tmp (template, &name_used, &error);
if (g_test_verbose())
{
if (fd != -1)
g_print ("g_file_open_tmp works even if template contains '%s'\n", G_DIR_SEPARATOR_S);
else
g_print ("g_file_open_tmp correctly returns error: %s\n", error->message);
}
close (fd);
g_clear_error (&error);
#ifdef G_OS_WIN32
strcpy (template, "zap/barXXXXXX");
fd = g_file_open_tmp (template, &name_used, &error);
if (g_test_verbose())
{
if (fd != -1)
g_print ("g_file_open_tmp works even if template contains '/'\n");
else
g_print ("g_file_open_tmp correctly returns error: %s\n", error->message);
}
close (fd);
g_clear_error (&error);
#endif
strcpy (template, "zapXXXXXX");
fd = g_file_open_tmp (template, &name_used, &error);
if (fd == -1)
g_error ("g_file_open_tmp didn't work for template '%s': %s\n", template, error->message);
else if (g_test_verbose())
g_print ("g_file_open_tmp for template '%s' used name '%s'\n", template, name_used);
close (fd);
g_clear_error (&error);
remove (name_used);
fd = g_file_open_tmp (NULL, &name_used, &error);
if (fd == -1)
g_error ("g_file_open_tmp didn't work for a NULL template: %s\n", error->message);
close (fd);
g_clear_error (&error);
remove (name_used);
}
static void
test_arrays (void)
{
GByteArray *gbarray;
GPtrArray *gparray;
GArray *garray;
guint i;
gparray = g_ptr_array_new ();
for (i = 0; i < 10000; i++)
g_ptr_array_add (gparray, GINT_TO_POINTER (i));
for (i = 0; i < 10000; i++)
if (g_ptr_array_index (gparray, i) != GINT_TO_POINTER (i))
g_error ("array fails: %p ( %p )\n", g_ptr_array_index (gparray, i), GINT_TO_POINTER (i));
g_ptr_array_free (gparray, TRUE);
gbarray = g_byte_array_new ();
for (i = 0; i < 10000; i++)
g_byte_array_append (gbarray, (guint8*) "abcd", 4);
for (i = 0; i < 10000; i++)
{
g_assert (gbarray->data[4*i] == 'a');
g_assert (gbarray->data[4*i+1] == 'b');
g_assert (gbarray->data[4*i+2] == 'c');
g_assert (gbarray->data[4*i+3] == 'd');
}
g_byte_array_free (gbarray, TRUE);
garray = g_array_new (FALSE, FALSE, sizeof (gint));
for (i = 0; i < 10000; i++)
g_array_append_val (garray, i);
for (i = 0; i < 10000; i++)
if (g_array_index (garray, gint, i) != i)
g_error ("failure: %d ( %d )\n", g_array_index (garray, gint, i), i);
g_array_free (garray, TRUE);
garray = g_array_new (FALSE, FALSE, sizeof (gint));
for (i = 0; i < 100; i++)
g_array_prepend_val (garray, i);
for (i = 0; i < 100; i++)
if (g_array_index (garray, gint, i) != (100 - i - 1))
g_error ("failure: %d ( %d )\n", g_array_index (garray, gint, i), 100 - i - 1);
g_array_free (garray, TRUE);
}
static void
hash_table_tests (void)
{
GHashTable *hash_table;
int array[10000];
gint *pvalue = NULL;
gint value = 120;
guint i;
hash_table = g_hash_table_new (my_hash, my_hash_equal);
for (i = 0; i < 10000; i++)
{
array[i] = i;
g_hash_table_insert (hash_table, &array[i], &array[i]);
}
pvalue = g_hash_table_find (hash_table, find_first_that, &value);
if (*pvalue != value)
g_error ("g_hash_table_find failed");
g_hash_table_foreach (hash_table, my_hash_callback, NULL);
for (i = 0; i < 10000; i++)
if (array[i] == 0)
g_error ("hashtable-test: wrong value: %d\n", i);
for (i = 0; i < 10000; i++)
g_hash_table_remove (hash_table, &array[i]);
for (i = 0; i < 10000; i++)
{
array[i] = i;
g_hash_table_insert (hash_table, &array[i], &array[i]);
}
if (g_hash_table_foreach_remove (hash_table, my_hash_callback_remove, NULL) != 5000 ||
g_hash_table_size (hash_table) != 5000)
g_error ("hashtable removal failed\n");
g_hash_table_foreach (hash_table, my_hash_callback_remove_test, NULL);
g_hash_table_destroy (hash_table);
}
static void
relation_test (void)
{
GRelation *relation = g_relation_new (2);
GTuples *tuples;
gint data [1024];
guint i;
g_relation_index (relation, 0, g_int_hash, g_int_equal);
g_relation_index (relation, 1, g_int_hash, g_int_equal);
for (i = 0; i < 1024; i += 1)
data[i] = i;
for (i = 1; i < 1023; i += 1)
{
g_relation_insert (relation, data + i, data + i + 1);
g_relation_insert (relation, data + i, data + i - 1);
}
for (i = 2; i < 1022; i += 1)
{
g_assert (! g_relation_exists (relation, data + i, data + i));
g_assert (! g_relation_exists (relation, data + i, data + i + 2));
g_assert (! g_relation_exists (relation, data + i, data + i - 2));
}
for (i = 1; i < 1023; i += 1)
{
g_assert (g_relation_exists (relation, data + i, data + i + 1));
g_assert (g_relation_exists (relation, data + i, data + i - 1));
}
for (i = 2; i < 1022; i += 1)
{
g_assert (g_relation_count (relation, data + i, 0) == 2);
g_assert (g_relation_count (relation, data + i, 1) == 2);
}
g_assert (g_relation_count (relation, data, 0) == 0);
g_assert (g_relation_count (relation, data + 42, 0) == 2);
g_assert (g_relation_count (relation, data + 43, 1) == 2);
g_assert (g_relation_count (relation, data + 41, 1) == 2);
g_relation_delete (relation, data + 42, 0);
g_assert (g_relation_count (relation, data + 42, 0) == 0);
g_assert (g_relation_count (relation, data + 43, 1) == 1);
g_assert (g_relation_count (relation, data + 41, 1) == 1);
tuples = g_relation_select (relation, data + 200, 0);
g_assert (tuples->len == 2);
#if 0
for (i = 0; i < tuples->len; i += 1)
{
printf ("%d %d\n",
*(gint*) g_tuples_index (tuples, i, 0),
*(gint*) g_tuples_index (tuples, i, 1));
}
#endif
g_assert (g_relation_exists (relation, data + 300, data + 301));
g_relation_delete (relation, data + 300, 0);
g_assert (!g_relation_exists (relation, data + 300, data + 301));
g_tuples_destroy (tuples);
g_relation_destroy (relation);
relation = NULL;
}
static void
gstring_tests (void)
{
GString *string1, *string2;
guint i;
if (g_test_verbose())
g_print ("test GString basics\n");
string1 = g_string_new ("hi pete!");
string2 = g_string_new ("");
g_assert (strcmp ("hi pete!", string1->str) == 0);
for (i = 0; i < 10000; i++)
g_string_append_c (string1, 'a'+(i%26));
#ifndef G_OS_WIN32
/* MSVC, mingw32 and LCC use the same run-time C library, which doesn't like
the %10000.10000f format... */
g_string_printf (string2, "%s|%0100d|%s|%s|%0*d|%*.*f|%10000.10000f",
"this pete guy sure is a wuss, like he's the number ",
1,
" wuss. everyone agrees.\n",
string1->str,
10, 666, 15, 15, 666.666666666, 666.666666666);
#else
g_string_printf (string2, "%s|%0100d|%s|%s|%0*d|%*.*f|%100.100f",
"this pete guy sure is a wuss, like he's the number ",
1,
" wuss. everyone agrees.\n",
string1->str,
10, 666, 15, 15, 666.666666666, 666.666666666);
#endif
if (g_test_verbose())
g_print ("string2 length = %lu...\n", (gulong)string2->len);
string2->str[70] = '\0';
if (g_test_verbose())
g_print ("first 70 chars:\n%s\n", string2->str);
string2->str[141] = '\0';
if (g_test_verbose())
g_print ("next 70 chars:\n%s\n", string2->str+71);
string2->str[212] = '\0';
if (g_test_verbose())
g_print ("and next 70:\n%s\n", string2->str+142);
if (g_test_verbose())
g_print ("last 70 chars:\n%s\n", string2->str+string2->len - 70);
g_string_free (string1, TRUE);
g_string_free (string2, TRUE);
/* append */
string1 = g_string_new ("firsthalf");
g_string_append (string1, "lasthalf");
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* append_len */
string1 = g_string_new ("firsthalf");
g_string_append_len (string1, "lasthalfjunkjunk", strlen ("lasthalf"));
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* prepend */
string1 = g_string_new ("lasthalf");
g_string_prepend (string1, "firsthalf");
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* prepend_len */
string1 = g_string_new ("lasthalf");
g_string_prepend_len (string1, "firsthalfjunkjunk", strlen ("firsthalf"));
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* insert */
string1 = g_string_new ("firstlast");
g_string_insert (string1, 5, "middle");
g_assert (strcmp (string1->str, "firstmiddlelast") == 0);
g_string_free (string1, TRUE);
/* insert with pos == end of the string */
string1 = g_string_new ("firstmiddle");
g_string_insert (string1, strlen ("firstmiddle"), "last");
g_assert (strcmp (string1->str, "firstmiddlelast") == 0);
g_string_free (string1, TRUE);
/* insert_len */
string1 = g_string_new ("firstlast");
g_string_insert_len (string1, 5, "middlejunkjunk", strlen ("middle"));
g_assert (strcmp (string1->str, "firstmiddlelast") == 0);
g_string_free (string1, TRUE);
/* insert_len with magic -1 pos for append */
string1 = g_string_new ("first");
g_string_insert_len (string1, -1, "lastjunkjunk", strlen ("last"));
g_assert (strcmp (string1->str, "firstlast") == 0);
g_string_free (string1, TRUE);
/* insert_len with magic -1 len for strlen-the-string */
string1 = g_string_new ("first");
g_string_insert_len (string1, 5, "last", -1);
g_assert (strcmp (string1->str, "firstlast") == 0);
g_string_free (string1, TRUE);
/* g_string_equal */
string1 = g_string_new ("test");
string2 = g_string_new ("te");
g_assert (! g_string_equal(string1, string2));
g_string_append (string2, "st");
g_assert (g_string_equal(string1, string2));
g_string_free (string1, TRUE);
g_string_free (string2, TRUE);
/* Check handling of embedded ASCII 0 (NUL) characters in GString. */
if (g_test_verbose())
g_print ("test embedded ASCII 0 (NUL) characters in GString\n");
string1 = g_string_new ("fiddle");
string2 = g_string_new ("fiddle");
g_assert (g_string_equal(string1, string2));
g_string_append_c(string1, '\0');
g_assert (! g_string_equal(string1, string2));
g_string_append_c(string2, '\0');
g_assert (g_string_equal(string1, string2));
g_string_append_c(string1, 'x');
g_string_append_c(string2, 'y');
g_assert (! g_string_equal(string1, string2));
g_assert (string1->len == 8);
g_string_append(string1, "yzzy");
g_assert (string1->len == 12);
g_assert ( memcmp(string1->str, "fiddle\0xyzzy", 13) == 0);
g_string_insert(string1, 1, "QED");
g_assert ( memcmp(string1->str, "fQEDiddle\0xyzzy", 16) == 0);
g_string_free (string1, TRUE);
g_string_free (string2, TRUE);
}
static void
various_string_tests (void)
{
GStringChunk *string_chunk;
GTimeVal ref_date, date;
gchar *tmp_string = NULL, *tmp_string_2, *string, *date_str;
guint i;
if (g_test_verbose())
g_print ("checking string chunks...");
string_chunk = g_string_chunk_new (1024);
for (i = 0; i < 100000; i ++)
{
tmp_string = g_string_chunk_insert (string_chunk, "hi pete");
if (strcmp ("hi pete", tmp_string) != 0)
g_error ("string chunks are broken.\n");
}
tmp_string_2 = g_string_chunk_insert_const (string_chunk, tmp_string);
g_assert (tmp_string_2 != tmp_string && strcmp (tmp_string_2, tmp_string) == 0);
tmp_string = g_string_chunk_insert_const (string_chunk, tmp_string);
g_assert (tmp_string_2 == tmp_string);
g_string_chunk_free (string_chunk);
if (g_test_verbose())
g_print ("test positional printf formats (not supported):");
string = g_strdup_printf ("%.*s%s", 5, "a", "b");
tmp_string = g_strdup_printf ("%2$*1$s", 5, "c");
if (g_test_verbose())
g_print ("%s%s\n", string, tmp_string);
g_free (tmp_string);
g_free (string);
#define REF_INVALID "Wed Dec 19 17:20:20 GMT 2007"
#define REF_SEC_UTC 320063760
#define REF_STR_UTC "1980-02-22T10:36:00Z"
#define REF_STR_CEST "1980-02-22T12:36:00+02:00"
#define REF_STR_EST "1980-02-22T05:36:00-05:00"
if (g_test_verbose())
g_print ("checking g_time_val_from_iso8601...\n");
ref_date.tv_sec = REF_SEC_UTC;
ref_date.tv_usec = 0;
g_assert (g_time_val_from_iso8601 (REF_INVALID, &date) == FALSE);
g_assert (g_time_val_from_iso8601 (REF_STR_UTC, &date) != FALSE);
if (g_test_verbose())
g_print ("\t=> UTC stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec);
g_assert (date.tv_sec == ref_date.tv_sec);
g_assert (g_time_val_from_iso8601 (REF_STR_CEST, &date) != FALSE);
if (g_test_verbose())
g_print ("\t=> CEST stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec);
g_assert (date.tv_sec == ref_date.tv_sec);
g_assert (g_time_val_from_iso8601 (REF_STR_EST, &date) != FALSE);
if (g_test_verbose())
g_print ("\t=> EST stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec);
g_assert (date.tv_sec == ref_date.tv_sec);
if (g_test_verbose())
g_print ("checking g_time_val_to_iso8601...\n");
ref_date.tv_sec = REF_SEC_UTC;
ref_date.tv_usec = 1;
date_str = g_time_val_to_iso8601 (&ref_date);
g_assert (date_str != NULL);
if (g_test_verbose())
g_print ("\t=> date string = %s (should be: %s)\n", date_str, REF_STR_UTC);
g_assert (strcmp (date_str, REF_STR_UTC) == 0);
g_free (date_str);
if (g_test_verbose())
g_print ("checking g_ascii_strcasecmp...");
g_assert (g_ascii_strcasecmp ("FroboZZ", "frobozz") == 0);
g_assert (g_ascii_strcasecmp ("frobozz", "frobozz") == 0);
g_assert (g_ascii_strcasecmp ("frobozz", "FROBOZZ") == 0);
g_assert (g_ascii_strcasecmp ("FROBOZZ", "froboz") > 0);
g_assert (g_ascii_strcasecmp ("", "") == 0);
g_assert (g_ascii_strcasecmp ("!#%&/()", "!#%&/()") == 0);
g_assert (g_ascii_strcasecmp ("a", "b") < 0);
g_assert (g_ascii_strcasecmp ("a", "B") < 0);
g_assert (g_ascii_strcasecmp ("A", "b") < 0);
g_assert (g_ascii_strcasecmp ("A", "B") < 0);
g_assert (g_ascii_strcasecmp ("b", "a") > 0);
g_assert (g_ascii_strcasecmp ("b", "A") > 0);
g_assert (g_ascii_strcasecmp ("B", "a") > 0);
g_assert (g_ascii_strcasecmp ("B", "A") > 0);
if (g_test_verbose())
g_print ("checking g_strdup...\n");
g_assert (g_strdup (NULL) == NULL);
string = g_strdup (GLIB_TEST_STRING);
g_assert (string != NULL);
g_assert (strcmp(string, GLIB_TEST_STRING) == 0);
g_free (string);
if (g_test_verbose())
g_print ("checking g_strconcat...\n");
string = g_strconcat (GLIB_TEST_STRING, NULL);
g_assert (string != NULL);
g_assert (strcmp (string, GLIB_TEST_STRING) == 0);
g_free (string);
string = g_strconcat (GLIB_TEST_STRING, GLIB_TEST_STRING,
GLIB_TEST_STRING, NULL);
g_assert (string != NULL);
g_assert (strcmp (string, GLIB_TEST_STRING GLIB_TEST_STRING
GLIB_TEST_STRING) == 0);
g_free (string);
if (g_test_verbose())
g_print ("checking g_strlcpy/g_strlcat...");
/* The following is a torture test for strlcpy/strlcat, with lots of
* checking; normal users wouldn't use them this way!
*/
string = g_malloc (6);
*(string + 5) = 'Z'; /* guard value, shouldn't change during test */
*string = 'q';
g_assert (g_strlcpy(string, "" , 5) == 0);
g_assert ( *string == '\0' );
*string = 'q';
g_assert (g_strlcpy(string, "abc" , 5) == 3);
g_assert ( *(string + 3) == '\0' );
g_assert (g_str_equal(string, "abc"));
g_assert (g_strlcpy(string, "abcd" , 5) == 4);
g_assert ( *(string + 4) == '\0' );
g_assert ( *(string + 5) == 'Z' );
g_assert (g_str_equal(string, "abcd"));
g_assert (g_strlcpy(string, "abcde" , 5) == 5);
g_assert ( *(string + 4) == '\0' );
g_assert ( *(string + 5) == 'Z' );
g_assert (g_str_equal(string, "abcd"));
g_assert (g_strlcpy(string, "abcdef" , 5) == 6);
g_assert ( *(string + 4) == '\0' );
g_assert ( *(string + 5) == 'Z' );
g_assert (g_str_equal(string, "abcd"));
*string = 'Y';
*(string + 1)= '\0';
g_assert (g_strlcpy(string, "Hello" , 0) == 5);
g_assert (*string == 'Y');
*string = '\0';
g_assert (g_strlcat(string, "123" , 5) == 3);
g_assert ( *(string + 3) == '\0' );
g_assert (g_str_equal(string, "123"));
g_assert (g_strlcat(string, "" , 5) == 3);
g_assert ( *(string + 3) == '\0' );
g_assert (g_str_equal(string, "123"));
g_assert (g_strlcat(string, "4", 5) == 4);
g_assert (g_str_equal(string, "1234"));
g_assert (g_strlcat(string, "5", 5) == 5);
g_assert ( *(string + 4) == '\0' );
g_assert (g_str_equal(string, "1234"));
g_assert ( *(string + 5) == 'Z' );
*string = 'Y';
*(string + 1)= '\0';
g_assert (g_strlcat(string, "123" , 0) == 3);
g_assert (*string == 'Y');
/* A few more tests, demonstrating more "normal" use */
g_assert (g_strlcpy(string, "hi", 5) == 2);
g_assert (g_str_equal(string, "hi"));
g_assert (g_strlcat(string, "t", 5) == 3);
g_assert (g_str_equal(string, "hit"));
g_free(string);
if (g_test_verbose())
g_print ("checking g_strdup_printf...\n");
string = g_strdup_printf ("%05d %-5s", 21, "test");
g_assert (string != NULL);
g_assert (strcmp(string, "00021 test ") == 0);
g_free (string);
/* g_debug (argv[0]); */
}
static void
test_mem_chunks (void)
{
GMemChunk *mem_chunk = g_mem_chunk_new ("test mem chunk", 50, 100, G_ALLOC_AND_FREE);
gchar *mem[10000];
guint i;
for (i = 0; i < 10000; i++)
{
guint j;
mem[i] = g_chunk_new (gchar, mem_chunk);
for (j = 0; j < 50; j++)
mem[i][j] = i * j;
}
for (i = 0; i < 10000; i++)
g_mem_chunk_free (mem_chunk, mem[i]);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/testglib/Infos", test_info);
g_test_add_func ("/testglib/Types Sizes", type_sizes);
g_test_add_func ("/testglib/GStrings", gstring_tests);
g_test_add_func ("/testglib/Various Strings", various_string_tests);
g_test_add_func ("/testglib/GList", glist_test);
g_test_add_func ("/testglib/GSList", gslist_test);
g_test_add_func ("/testglib/GNode", gnode_test);
g_test_add_func ("/testglib/GTree", binary_tree_test);
g_test_add_func ("/testglib/Arrays", test_arrays);
g_test_add_func ("/testglib/GHashTable", hash_table_tests);
g_test_add_func ("/testglib/Relation", relation_test);
g_test_add_func ("/testglib/File Paths", test_paths);
g_test_add_func ("/testglib/File Functions", test_file_functions);
g_test_add_func ("/testglib/Mkdir", test_g_mkdir_with_parents);
g_test_add_func ("/testglib/Parse Debug Strings", test_g_parse_debug_string);
g_test_add_func ("/testglib/GMemChunk (deprecated)", test_mem_chunks);
g_test_add_func ("/testglib/Warnings & Errors", log_warning_error_tests);
g_test_add_func ("/testglib/Timers (slow)", timer_tests);
return g_test_run();
}
view raw gnode.c hosted with ❤ by GitHub
CC = gcc
CFLAGS = -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/lib64/glib-2.0/include/ -I/usr/include/glib-2.0
LIBS = -lglib-2.0 -lc -lm
OBJ1 = ntree-distance.c
DEPS1 =
OBJ2 = testglib.c
DEPS2 =
ntree: $(OBJ1)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
test: $(OBJ2)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
clean:
rm -f *.o
rm -fr *~
rm -f ntree test
view raw Makefile hosted with ❤ by GitHub
//ex-gtree-6.c
#include <glib.h>
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
/*
* This simple version expands the city example to make a tree based on distances from each other
*
* Basic process;
*
* 1. Cities are created and stored to an array
* 2. Home city is declared
* 3. Cities are taken from the list and placed into a n-ary tree
* based on distance from home city
*
* 4. Once established, one can find all near nodes and
* define nodes based on distance versus level.
*
* Level tree position is based on bounded distance rings
* like
* [0,10]
* (10,100]
* (100, 1000]
* of arbitrary distances that define a ball of interest
*
*
* */
#define SMALLDISTANCE 200
#define MIDDISTANCE 600
#define NUM 30
// basic position
typedef struct point32
{
float x, y, z;
}point32_t;
// encapsulates distance from current node
typedef struct nodedist
{
point32_t coords;
float dist;
}nodedist_t;
typedef struct city
{
nodedist_t own;
unsigned int id;
char name[100];
}city_t;
// Create a databased element city
city_t * CreateCity( const char * cit, float ax, float ay, float az, int ID )
{
city_t * ret;
ret = ( city_t *) malloc ( sizeof(city_t ));
strcpy( ret->name, cit );
ret->own.coords.x = ax;
ret->own.coords.y = ay;
ret->own.coords.z = az;
ret->id = ID;
ret->own.dist = 0.0;
return ret;
}
int PrintCity( city_t * in)
{
printf("(%s)<%d>[%f,%f,%f] dist=%f \n", in->name, in->id, in->own.coords.x, in->own.coords.y, in->own.coords.z, in->own.dist);
return 0;
}
float EstimateCityDistance( nodedist_t a, nodedist_t b )
{
return ( sqrtf( (a.coords.x - b.coords.x)*(a.coords.x - b.coords.x) + (a.coords.y - b.coords.y)*(a.coords.y - b.coords.y) + (a.coords.z - b.coords.z)*(a.coords.z - b.coords.z)));
}
// compute distance from a city to b ans store in b
int ComputeDistance (city_t *a, city_t * b )
{
b->own.dist = EstimateCityDistance( a->own, b->own );
return 0;
}
// returns Euclidean distance from
float EstimateDistance( float ax, float ay, float az, float bx, float by, float bz )
{
return ( sqrtf( (ax - bx)*(ax - bx) + (ay - by)*(ay - by) + (az - bz)*(az - bz) ));
}
gboolean GNodeIterateFloatPrint(GNode* n, gpointer data)
{
float * convert = (float *) n->data;
printf("%f\n", *convert);
return FALSE;
}
gboolean GNodeIterateStringPrint(GNode* n, gpointer data)
{
printf("%s", (char*) n->data);
return FALSE;
}
int main(int argc, char** argv)
{
int number = NUM;
int i;
city_t ** list;
GNode ** tree_array;
int origin = 0;
int lower = SMALLDISTANCE;
int mid = MIDDISTANCE;
int byond = MIDDISTANCE+ SMALLDISTANCE;
// the home version
city_t *home;
GNode *middle = g_node_new((gpointer)(&lower));
GNode *far = g_node_new((gpointer)(&mid));
GNode *farthest = g_node_new((gpointer)(&byond));
tree_array = (GNode **) malloc( NUM* sizeof( GNode * ));
list = ( city_t ** ) malloc (number * sizeof(city_t *));
float f;
float g;
float h;
for (i = 0; i < number; i++)
{
// randomize x y z - these are empirically tested to produce
// distance metrics around 500
f = (rand()%500);
g = rand()%1000;
h = rand()%1000*rand()%2;
list[i] = CreateCity( "City\0", f , g , h , i );
}
for (i = 0; i < number; i++)
{
PrintCity( list[i] );
}
home = list[5];
for (i = 0; i < number; i++)
{
ComputeDistance (home , list[i] );
}
for (i = 0; i < number; i++)
{
PrintCity( list[i] );
}
// add node as home
float *z = &list[5]->own.dist;
GNode* base = g_node_new((gpointer)(&z));
printf("base node data %f %f \n", (float*)base->data, *z);
// appending places node below current therefore grows tree
// make notional middle child node
g_node_append(base, middle );
// make notional second layer child
g_node_append(middle, far);
// make notional far child.
g_node_append(far, farthest);
// NOTE Use append to add child to the current parent
// now make into a n-ary tree
for (i = 0; i < number; i++)
{
//printf("%f+", list[i]->own.dist);
if(i == 5)
{
;// do nothing for notional origin
}
else
{
// add cities by range of data
if ( list[i]->own.dist <= SMALLDISTANCE )
{
z = &list[i]->own.dist;
// shortest add to same level
tree_array[i] = g_node_new((gpointer)(z));
g_node_append(base, tree_array[i]);
printf ( "added sibling node %d with %f\n", i, *z );
}
else if ( list[i]->own.dist <= MIDDISTANCE )
{
// middle add to next level
z = &list[i]->own.dist;
tree_array[i] = g_node_new((gpointer)z);
g_node_append(middle, tree_array[i]);
printf ( "added child node %d with %f \n", i, *z );
}
else // beyond middle added to the above limit
{
if ( far != NULL)
{
z = &list[i]->own.dist;
tree_array[i] = g_node_new((gpointer)z);
g_node_append(far, tree_array[i]);
printf ( "added grandchild node %d with %f \n", i, *z );
}
}
}// i != 5
}
// now we remove the notional
// elements from the tree
// there is no remove function made, only a unlink which makes
// new trees with remaining below data nodes - disconnecting your work.
// making a remove and save old tree structure would be an extra
// to work on.
// A way around this without extra functionality is to
// ignore the exemplar child at each depth
// that allows for removal and insertion of actual nodes
// over time without a lot of rework
// traverse and find distances - printf
g_node_traverse(base, G_LEVEL_ORDER, G_TRAVERSE_MASK, -1, GNodeIterateFloatPrint, NULL);
int b = g_node_depth (base);
int a = g_node_n_nodes (base ,G_TRAVERSE_ALL);
int c = g_node_n_children (base);
int d = g_node_max_height (base);
printf ( "number of nodes in tree %d in depth %d with %d children of base to max height %d \n", a,b,c,d );
for (i = 0; i < number; i++)
{
;
}
g_node_destroy(base);
return 0;
}
//ex-gtree-6.c
#include <glib.h>
gboolean GNodeIteratePrint(GNode* n, gpointer data)
{
printf("%s ", n->data);
return FALSE;
}
int main(int argc, char** argv)
{
GNode* root = g_node_new("Atlanta");
g_node_append(root, g_node_new("Detroit"));
GNode* portland = g_node_prepend(root, g_node_new("Portland"));
printf(">Some cities to start with\n");
g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, GNodeIteratePrint, NULL);
printf("\n>Inserting Coos Bay before Portland\n");
g_node_insert_data_before(root, portland, "Coos Bay");
g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, GNodeIteratePrint, NULL);
printf("\n>Reversing the child nodes\n");
g_node_reverse_children(root);
g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, GNodeIteratePrint, NULL);
printf("\n>Root node is %s\n", g_node_get_root(portland)->data);
printf(">Portland node index is %d\n", g_node_child_index(root, "Portland"));
g_node_destroy(root);
return 0;
}
view raw ntree.c hosted with ❤ by GitHub
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
#undef GLIB_COMPILATION
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "glib.h"
#include "gstdio.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef G_OS_WIN32
#include <io.h> /* For read(), write() etc */
#endif
#define GLIB_TEST_STRING "el dorado "
#define GLIB_TEST_STRING_5 "el do"
/* --- variables --- */
static gint test_nums[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
static gint more_nums[10] = { 8, 9, 7, 0, 3, 2, 5, 1, 4, 6};
/* --- functions --- */
static gint
my_list_compare_one (gconstpointer a, gconstpointer b)
{
gint one = *((const gint*)a);
gint two = *((const gint*)b);
return one-two;
}
static gint
my_list_compare_two (gconstpointer a, gconstpointer b)
{
gint one = *((const gint*)a);
gint two = *((const gint*)b);
return two-one;
}
/* static void
my_list_print (gpointer a, gpointer b)
{
gint three = *((gint*)a);
g_print("%d", three);
}; */
static void
glist_test (void)
{
GList *list = NULL;
guint i;
for (i = 0; i < 10; i++)
list = g_list_append (list, &test_nums[i]);
list = g_list_reverse (list);
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != (9 - i))
g_error ("Regular insert failed");
}
for (i = 0; i < 10; i++)
if (g_list_position (list, g_list_nth (list, i)) != i)
g_error ("g_list_position does not seem to be the inverse of g_list_nth\n");
g_list_free (list);
list = NULL;
for (i = 0; i < 10; i++)
list = g_list_insert_sorted (list, &more_nums[i], my_list_compare_one);
/*
g_print("\n");
g_list_foreach (list, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != i)
g_error ("Sorted insert failed");
}
g_list_free (list);
list = NULL;
for (i = 0; i < 10; i++)
list = g_list_insert_sorted (list, &more_nums[i], my_list_compare_two);
/*
g_print("\n");
g_list_foreach (list, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != (9 - i))
g_error ("Sorted insert failed");
}
g_list_free (list);
list = NULL;
for (i = 0; i < 10; i++)
list = g_list_prepend (list, &more_nums[i]);
list = g_list_sort (list, my_list_compare_two);
/*
g_print("\n");
g_list_foreach (list, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GList *t = g_list_nth (list, i);
if (*((gint*) t->data) != (9 - i))
g_error ("Merge sort failed");
}
g_list_free (list);
}
static void
gslist_test (void)
{
GSList *slist = NULL;
guint i;
for (i = 0; i < 10; i++)
slist = g_slist_append (slist, &test_nums[i]);
slist = g_slist_reverse (slist);
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != (9 - i))
g_error ("failed");
}
g_slist_free (slist);
slist = NULL;
for (i = 0; i < 10; i++)
slist = g_slist_insert_sorted (slist, &more_nums[i], my_list_compare_one);
/*
g_print("\n");
g_slist_foreach (slist, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != i)
g_error ("Sorted insert failed");
}
g_slist_free (slist);
slist = NULL;
for (i = 0; i < 10; i++)
slist = g_slist_insert_sorted (slist, &more_nums[i], my_list_compare_two);
/*
g_print("\n");
g_slist_foreach (slist, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != (9 - i))
g_error("Sorted insert failed");
}
g_slist_free(slist);
slist = NULL;
for (i = 0; i < 10; i++)
slist = g_slist_prepend (slist, &more_nums[i]);
slist = g_slist_sort (slist, my_list_compare_two);
/*
g_print("\n");
g_slist_foreach (slist, my_list_print, NULL);
*/
for (i = 0; i < 10; i++)
{
GSList *st = g_slist_nth (slist, i);
if (*((gint*) st->data) != (9 - i))
g_error("Sorted insert failed");
}
g_slist_free(slist);
}
static gboolean
node_build_string (GNode *node,
gpointer data)
{
gchar **p = data;
gchar *string;
gchar c[2] = "_";
c[0] = ((gchar) ((long) (node->data)));
string = g_strconcat (*p ? *p : "", c, NULL);
g_free (*p);
*p = string;
return FALSE;
}
static void
gnode_test (void)
{
#define C2P(c) ((gpointer) ((long) (c)))
#define P2C(p) ((gchar) ((long) (p)))
GNode *root;
GNode *node;
GNode *node_B;
GNode *node_F;
GNode *node_G;
GNode *node_J;
guint i;
gchar *tstring, *cstring;
root = g_node_new (C2P ('A'));
g_assert (g_node_depth (root) == 1 && g_node_max_height (root) == 1);
node_B = g_node_new (C2P ('B'));
g_node_append (root, node_B);
g_assert (root->children == node_B);
g_node_append_data (node_B, C2P ('E'));
g_node_prepend_data (node_B, C2P ('C'));
g_node_insert (node_B, 1, g_node_new (C2P ('D')));
node_F = g_node_new (C2P ('F'));
g_node_append (root, node_F);
g_assert (root->children->next == node_F);
node_G = g_node_new (C2P ('G'));
g_node_append (node_F, node_G);
node_J = g_node_new (C2P ('J'));
g_node_prepend (node_G, node_J);
g_node_insert (node_G, 42, g_node_new (C2P ('K')));
g_node_insert_data (node_G, 0, C2P ('H'));
g_node_insert (node_G, 1, g_node_new (C2P ('I')));
g_assert (g_node_depth (root) == 1);
g_assert (g_node_max_height (root) == 4);
g_assert (g_node_depth (node_G->children->next) == 4);
g_assert (g_node_n_nodes (root, G_TRAVERSE_LEAFS) == 7);
g_assert (g_node_n_nodes (root, G_TRAVERSE_NON_LEAFS) == 4);
g_assert (g_node_n_nodes (root, G_TRAVERSE_ALL) == 11);
g_assert (g_node_max_height (node_F) == 3);
g_assert (g_node_n_children (node_G) == 4);
g_assert (g_node_find_child (root, G_TRAVERSE_ALL, C2P ('F')) == node_F);
g_assert (g_node_find (root, G_LEVEL_ORDER, G_TRAVERSE_NON_LEAFS, C2P ('I')) == NULL);
g_assert (g_node_find (root, G_IN_ORDER, G_TRAVERSE_LEAFS, C2P ('J')) == node_J);
for (i = 0; i < g_node_n_children (node_B); i++)
{
node = g_node_nth_child (node_B, i);
g_assert (P2C (node->data) == ('C' + i));
}
for (i = 0; i < g_node_n_children (node_G); i++)
g_assert (g_node_child_position (node_G, g_node_nth_child (node_G, i)) == i);
/* we have built: A
* / \
* B F
* / | \ \
* C D E G
* / /\ \
* H I J K
*
* for in-order traversal, 'G' is considered to be the "left"
* child of 'F', which will cause 'F' to be the last node visited.
*/
tstring = NULL;
g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABCDEFGHIJK");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_POST_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "CDEBHIJKGFA");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "CBDEAHGIJKF");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABFCDEGHIJK");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_LEAFS, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "CDEHIJK");
g_free (tstring); tstring = NULL;
g_node_traverse (root, G_PRE_ORDER, G_TRAVERSE_NON_LEAFS, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABFG");
g_free (tstring); tstring = NULL;
g_node_reverse_children (node_B);
g_node_reverse_children (node_G);
g_node_traverse (root, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_assert_cmpstr (tstring, ==, "ABFEDCGKJIH");
g_free (tstring); tstring = NULL;
cstring = NULL;
node = g_node_copy (root);
g_assert (g_node_n_nodes (root, G_TRAVERSE_ALL) == g_node_n_nodes (node, G_TRAVERSE_ALL));
g_assert (g_node_max_height (root) == g_node_max_height (node));
g_node_traverse (root, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &tstring);
g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_ALL, -1, node_build_string, &cstring);
g_assert_cmpstr (tstring, ==, cstring);
g_free (tstring); tstring = NULL;
g_free (cstring); cstring = NULL;
g_node_destroy (node);
g_node_destroy (root);
/* allocation tests */
root = g_node_new (NULL);
node = root;
for (i = 0; i < 2048; i++)
{
g_node_append (node, g_node_new (NULL));
if ((i%5) == 4)
node = node->children->next;
}
g_assert (g_node_max_height (root) > 100);
g_assert (g_node_n_nodes (root, G_TRAVERSE_ALL) == 1 + 2048);
g_node_destroy (root);
#undef C2P
#undef P2C
}
static gint
my_compare (gconstpointer a,
gconstpointer b)
{
const char *cha = a;
const char *chb = b;
return *cha - *chb;
}
static gint
my_traverse (gpointer key,
gpointer value,
gpointer data)
{
char *ch = key;
g_print ("%c ", *ch);
return FALSE;
}
static void
binary_tree_test (void)
{
GTree *tree;
char chars[62];
guint i, j;
tree = g_tree_new (my_compare);
i = 0;
for (j = 0; j < 10; j++, i++)
{
chars[i] = '0' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
}
for (j = 0; j < 26; j++, i++)
{
chars[i] = 'A' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
}
for (j = 0; j < 26; j++, i++)
{
chars[i] = 'a' + j;
g_tree_insert (tree, &chars[i], &chars[i]);
}
g_assert_cmpint (g_tree_nnodes (tree), ==, 10 + 26 + 26);
g_assert_cmpint (g_tree_height (tree), ==, 6);
if (g_test_verbose())
{
g_print ("tree: ");
g_tree_foreach (tree, my_traverse, NULL);
g_print ("\n");
}
for (i = 0; i < 10; i++)
g_tree_remove (tree, &chars[i]);
g_assert_cmpint (g_tree_nnodes (tree), ==, 26 + 26);
g_assert_cmpint (g_tree_height (tree), ==, 6);
if (g_test_verbose())
{
g_print ("tree: ");
g_tree_foreach (tree, my_traverse, NULL);
g_print ("\n");
}
}
static gboolean
my_hash_callback_remove (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
if ((*d) % 2)
return TRUE;
return FALSE;
}
static void
my_hash_callback_remove_test (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
if ((*d) % 2)
g_print ("bad!\n");
}
static void
my_hash_callback (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
*d = 1;
}
static guint
my_hash (gconstpointer key)
{
return (guint) *((const gint*) key);
}
static gboolean
my_hash_equal (gconstpointer a,
gconstpointer b)
{
return *((const gint*) a) == *((const gint*) b);
}
static gboolean
find_first_that(gpointer key,
gpointer value,
gpointer user_data)
{
gint *v = value;
gint *test = user_data;
return (*v == *test);
}
static void
test_g_mkdir_with_parents_1 (const gchar *base)
{
char *p0 = g_build_filename (base, "fum", NULL);
char *p1 = g_build_filename (p0, "tem", NULL);
char *p2 = g_build_filename (p1, "zap", NULL);
FILE *f;
g_remove (p2);
g_remove (p1);
g_remove (p0);
if (g_file_test (p0, G_FILE_TEST_EXISTS))
g_error ("failed, %s exists, cannot test g_mkdir_with_parents\n", p0);
if (g_file_test (p1, G_FILE_TEST_EXISTS))
g_error ("failed, %s exists, cannot test g_mkdir_with_parents\n", p1);
if (g_file_test (p2, G_FILE_TEST_EXISTS))
g_error ("failed, %s exists, cannot test g_mkdir_with_parents\n", p2);
if (g_mkdir_with_parents (p2, 0777) == -1)
g_error ("failed, g_mkdir_with_parents(%s) failed: %s\n", p2, g_strerror (errno));
if (!g_file_test (p2, G_FILE_TEST_IS_DIR))
g_error ("failed, g_mkdir_with_parents(%s) succeeded, but %s is not a directory\n", p2, p2);
if (!g_file_test (p1, G_FILE_TEST_IS_DIR))
g_error ("failed, g_mkdir_with_parents(%s) succeeded, but %s is not a directory\n", p2, p1);
if (!g_file_test (p0, G_FILE_TEST_IS_DIR))
g_error ("failed, g_mkdir_with_parents(%s) succeeded, but %s is not a directory\n", p2, p0);
g_rmdir (p2);
if (g_file_test (p2, G_FILE_TEST_EXISTS))
g_error ("failed, did g_rmdir(%s), but %s is still there\n", p2, p2);
g_rmdir (p1);
if (g_file_test (p1, G_FILE_TEST_EXISTS))
g_error ("failed, did g_rmdir(%s), but %s is still there\n", p1, p1);
f = g_fopen (p1, "w");
if (f == NULL)
g_error ("failed, couldn't create file %s\n", p1);
fclose (f);
if (g_mkdir_with_parents (p1, 0666) == 0)
g_error ("failed, g_mkdir_with_parents(%s) succeeded, even if %s is a file\n", p1, p1);
if (g_mkdir_with_parents (p2, 0666) == 0)
g_error("failed, g_mkdir_with_parents(%s) succeeded, even if %s is a file\n", p2, p1);
g_remove (p2);
g_remove (p1);
g_remove (p0);
}
static void
test_g_mkdir_with_parents (void)
{
gchar *cwd;
if (g_test_verbose())
g_print ("checking g_mkdir_with_parents() in subdir ./hum/");
test_g_mkdir_with_parents_1 ("hum");
g_remove ("hum");
if (g_test_verbose())
g_print ("checking g_mkdir_with_parents() in subdir ./hii///haa/hee/");
test_g_mkdir_with_parents_1 ("hii///haa/hee");
g_remove ("hii/haa/hee");
g_remove ("hii/haa");
g_remove ("hii");
cwd = g_get_current_dir ();
if (g_test_verbose())
g_print ("checking g_mkdir_with_parents() in cwd: %s", cwd);
test_g_mkdir_with_parents_1 (cwd);
g_free (cwd);
}
static void
test_g_parse_debug_string (void)
{
GDebugKey keys[3] = {
{ "foo", 1 },
{ "bar", 2 },
{ "baz", 4 }
};
guint n_keys = 3;
guint result;
result = g_parse_debug_string ("bar:foo:blubb", keys, n_keys);
g_assert (result == 3);
result = g_parse_debug_string (":baz::_E@~!_::", keys, n_keys);
g_assert (result == 4);
result = g_parse_debug_string ("", keys, n_keys);
g_assert (result == 0);
result = g_parse_debug_string (" : ", keys, n_keys);
g_assert (result == 0);
}
static void
log_warning_error_tests (void)
{
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
g_message ("this is a g_message test.");
g_message ("non-printable UTF-8: \"\xc3\xa4\xda\x85\"");
g_message ("unsafe chars: \"\x10\x11\x12\n\t\x7f\x81\x82\x83\"");
exit (0);
}
g_test_trap_assert_passed();
g_test_trap_assert_stderr ("*is a g_message test*");
g_test_trap_assert_stderr ("*non-printable UTF-8*");
g_test_trap_assert_stderr ("*unsafe chars*");
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
g_warning ("harmless warning with parameters: %d %s %#x", 42, "Boo", 12345);
exit (0);
}
g_test_trap_assert_failed(); /* we have fatal-warnings enabled */
g_test_trap_assert_stderr ("*harmless warning*");
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
{
g_print (NULL);
exit (0);
}
g_test_trap_assert_failed(); /* we have fatal-warnings enabled */
g_test_trap_assert_stderr ("*g_print*assertion*failed*");
g_test_trap_assert_stderr ("*NULL*");
}
static void
timer_tests (void)
{
GTimer *timer, *timer2;
gdouble elapsed;
/* basic testing */
timer = g_timer_new ();
g_timer_start (timer);
elapsed = g_timer_elapsed (timer, NULL);
g_timer_stop (timer);
g_assert_cmpfloat (elapsed, <=, g_timer_elapsed (timer, NULL));
g_timer_destroy (timer);
if (g_test_slow())
{
if (g_test_verbose())
g_print ("checking timers...\n");
timer = g_timer_new ();
if (g_test_verbose())
g_print (" spinning for 3 seconds...\n");
g_timer_start (timer);
while (g_timer_elapsed (timer, NULL) < 3)
;
g_timer_stop (timer);
g_timer_destroy (timer);
if (g_test_verbose())
g_print ("ok\n");
}
if (g_test_slow())
{
gulong elapsed_usecs;
if (g_test_verbose())
g_print ("checking g_timer_continue...\n");
timer2 = g_timer_new ();
if (g_test_verbose())
g_print ("\trun for 1 second...\n");
timer = g_timer_new();
g_usleep (G_USEC_PER_SEC); /* run timer for 1 second */
g_timer_stop (timer);
if (g_test_verbose())
g_print ("\tstop for 1 second...\n");
g_usleep (G_USEC_PER_SEC); /* wait for 1 second */
if (g_test_verbose())
g_print ("\trun for 2 seconds...\n");
g_timer_continue (timer);
g_usleep (2 * G_USEC_PER_SEC); /* run timer for 2 seconds */
g_timer_stop(timer);
if (g_test_verbose())
g_print ("\tstop for 1.5 seconds...\n");
g_usleep ((3 * G_USEC_PER_SEC) / 2); /* wait for 1.5 seconds */
if (g_test_verbose())
g_print ("\trun for 0.2 seconds...\n");
g_timer_continue (timer);
g_usleep (G_USEC_PER_SEC / 5); /* run timer for 0.2 seconds */
g_timer_stop (timer);
if (g_test_verbose())
g_print ("\tstop for 4 seconds...\n");
g_usleep (4 * G_USEC_PER_SEC); /* wait for 4 seconds */
if (g_test_verbose())
g_print ("\trun for 5.8 seconds...\n");
g_timer_continue (timer);
g_usleep ((29 * G_USEC_PER_SEC) / 5); /* run timer for 5.8 seconds */
g_timer_stop(timer);
elapsed = g_timer_elapsed (timer, &elapsed_usecs);
if (g_test_verbose())
g_print ("\t=> timer = %.6f = %d.%06ld (should be: 9.000000) (%.6f off)\n", elapsed, (int) elapsed, elapsed_usecs, ABS (elapsed - 9.));
g_assert_cmpfloat (elapsed, >, 8.8);
g_assert_cmpfloat (elapsed, <, 9.2);
if (g_test_verbose())
g_print ("g_timer_continue ... ok\n\n");
g_timer_stop (timer2);
elapsed = g_timer_elapsed (timer2, &elapsed_usecs);
if (g_test_verbose())
g_print ("\t=> timer2 = %.6f = %d.%06ld (should be: %.6f) (%.6f off)\n\n", elapsed, (int) elapsed, elapsed_usecs, 9.+6.5, ABS (elapsed - (9.+6.5)));
g_assert_cmpfloat (elapsed, >, 8.8 + 6.5);
g_assert_cmpfloat (elapsed, <, 9.2 + 6.5);
if (g_test_verbose())
g_print ("timer2 ... ok\n\n");
g_timer_destroy (timer);
g_timer_destroy (timer2);
}
}
static void
type_sizes (void)
{
guint16 gu16t1 = 0x44afU, gu16t2 = 0xaf44U;
guint32 gu32t1 = 0x02a7f109U, gu32t2 = 0x09f1a702U;
guint64 gu64t1 = G_GINT64_CONSTANT(0x1d636b02300a7aa7U),
gu64t2 = G_GINT64_CONSTANT(0xa77a0a30026b631dU);
/* type sizes */
g_assert_cmpint (sizeof (gint8), ==, 1);
g_assert_cmpint (sizeof (gint16), ==, 2);
g_assert_cmpint (sizeof (gint32), ==, 4);
g_assert_cmpint (sizeof (gint64), ==, 8);
/* endian macros */
if (g_test_verbose())
g_print ("checking endian macros (host is %s)...\n",
G_BYTE_ORDER == G_BIG_ENDIAN ? "big endian" : "little endian");
g_assert (GUINT16_SWAP_LE_BE (gu16t1) == gu16t2);
g_assert (GUINT32_SWAP_LE_BE (gu32t1) == gu32t2);
g_assert (GUINT64_SWAP_LE_BE (gu64t1) == gu64t2);
}
static void
test_info (void)
{
const gchar *un, *rn, *hn;
const gchar *tmpdir, *homedir, *userdatadir, *uconfdir, *ucachedir;
const gchar *uddesktop, *udddocs, *uddpubshare;
gchar **sv, *cwd, *sdatadirs, *sconfdirs, *langnames;
if (g_test_verbose())
g_print ("TestGLib v%u.%u.%u (i:%u b:%u)\n",
glib_major_version,
glib_minor_version,
glib_micro_version,
glib_interface_age,
glib_binary_age);
cwd = g_get_current_dir ();
un = g_get_user_name();
rn = g_get_real_name();
hn = g_get_host_name();
if (g_test_verbose())
{
g_print ("cwd: %s\n", cwd);
g_print ("user: %s\n", un);
g_print ("real: %s\n", rn);
g_print ("host: %s\n", hn);
}
g_free (cwd);
tmpdir = g_get_tmp_dir();
g_assert (tmpdir != NULL);
homedir = g_get_home_dir ();
g_assert (homedir != NULL);
userdatadir = g_get_user_data_dir ();
g_assert (userdatadir != NULL);
uconfdir = g_get_user_config_dir ();
g_assert (uconfdir != NULL);
ucachedir = g_get_user_cache_dir ();
g_assert (ucachedir != NULL);
uddesktop = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
g_assert (uddesktop != NULL);
udddocs = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
uddpubshare = g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE);
sv = (gchar **) g_get_system_data_dirs ();
sdatadirs = g_strjoinv (G_SEARCHPATH_SEPARATOR_S, sv);
sv = (gchar **) g_get_system_config_dirs ();
sconfdirs = g_strjoinv (G_SEARCHPATH_SEPARATOR_S, sv);
sv = (gchar **) g_get_language_names ();
langnames = g_strjoinv (":", sv);
if (g_test_verbose())
{
g_print ("tmp-dir: %s\n", tmpdir);
g_print ("home: %s\n", homedir);
g_print ("user_data: %s\n", userdatadir);
g_print ("user_config: %s\n", uconfdir);
g_print ("user_cache: %s\n", ucachedir);
g_print ("system_data: %s\n", sdatadirs);
g_print ("system_config: %s\n", sconfdirs);
g_print ("languages: %s\n", langnames);
g_print ("user_special[DESKTOP]: %s\n", uddesktop);
g_print ("user_special[DOCUMENTS]: %s\n", udddocs);
g_print ("user_special[PUBLIC_SHARE]: %s\n", uddpubshare);
}
g_free (sdatadirs);
g_free (sconfdirs);
g_free (langnames);
if (g_test_verbose())
{
#ifdef G_PLATFORM_WIN32
gchar *glib_dll;
#endif
const gchar *charset;
if (g_get_charset ((G_CONST_RETURN char**)&charset))
g_print ("current charset is UTF-8: %s\n", charset);
else
g_print ("current charset is not UTF-8: %s\n", charset);
#ifdef G_PLATFORM_WIN32
#ifdef G_OS_WIN32
/* Can't calculate GLib DLL name at runtime. */
glib_dll = "libglib-2.0-0.dll";
#endif
#ifdef G_WITH_CYGWIN
glib_dll = "cygglib-2.0-0.dll";
#endif
g_print ("current locale: %s\n", g_win32_getlocale ());
g_print ("GLib DLL name tested for: %s\n", glib_dll);
g_print ("GLib installation directory, from Registry entry for %s if available: %s\n",
GETTEXT_PACKAGE,
g_win32_get_package_installation_directory (GETTEXT_PACKAGE, NULL));
g_print ("Ditto, or from GLib DLL name: %s\n",
g_win32_get_package_installation_directory (GETTEXT_PACKAGE, glib_dll));
g_print ("Ditto, only from GLib DLL name: %s\n",
g_win32_get_package_installation_directory (NULL, glib_dll));
g_print ("locale subdirectory of GLib installation directory: %s\n",
g_win32_get_package_installation_subdirectory (NULL, glib_dll, "lib\\locale"));
g_print ("GTK+ 2.0 installation directory, if available: %s\n",
g_win32_get_package_installation_directory ("gtk20", NULL));
g_print ("found more.com as %s\n", g_find_program_in_path ("more.com"));
g_print ("found regedit as %s\n", g_find_program_in_path ("regedit"));
g_print ("a Win32 error message: %s\n", g_win32_error_message (2));
#endif
}
}
static void
test_paths (void)
{
struct {
gchar *filename;
gchar *dirname;
} dirname_checks[] = {
{ "/", "/" },
{ "////", "/" },
{ ".////", "." },
{ "../", ".." },
{ "..////", ".." },
{ "a/b", "a" },
{ "a/b/", "a/b" },
{ "c///", "c" },
#ifdef G_OS_WIN32
{ "\\", "\\" },
{ ".\\\\\\\\", "." },
{ "..\\", ".." },
{ "..\\\\\\\\", ".." },
{ "a\\b", "a" },
{ "a\\b/", "a\\b" },
{ "a/b\\", "a/b" },
{ "c\\\\/", "c" },
{ "//\\", "/" },
#endif
#ifdef G_WITH_CYGWIN
{ "//server/share///x", "//server/share" },
#endif
{ ".", "." },
{ "..", "." },
{ "", "." },
};
const guint n_dirname_checks = G_N_ELEMENTS (dirname_checks);
struct {
gchar *filename;
gchar *without_root;
} skip_root_checks[] = {
{ "/", "" },
{ "//", "" },
{ "/foo", "foo" },
{ "//foo", "foo" },
{ "a/b", NULL },
#ifdef G_OS_WIN32
{ "\\", "" },
{ "\\foo", "foo" },
{ "\\\\server\\foo", "" },
{ "\\\\server\\foo\\bar", "bar" },
{ "a\\b", NULL },
#endif
#ifdef G_WITH_CYGWIN
{ "//server/share///x", "//x" },
#endif
{ ".", NULL },
{ "", NULL },
};
const guint n_skip_root_checks = G_N_ELEMENTS (skip_root_checks);
gchar *string;
guint i;
if (g_test_verbose())
g_print ("checking g_path_get_basename()...");
string = g_path_get_basename (G_DIR_SEPARATOR_S "foo" G_DIR_SEPARATOR_S "dir" G_DIR_SEPARATOR_S);
g_assert (strcmp (string, "dir") == 0);
g_free (string);
string = g_path_get_basename (G_DIR_SEPARATOR_S "foo" G_DIR_SEPARATOR_S "file");
g_assert (strcmp (string, "file") == 0);
g_free (string);
if (g_test_verbose())
g_print ("ok\n");
#ifdef G_OS_WIN32
string = g_path_get_basename ("/foo/dir/");
g_assert (strcmp (string, "dir") == 0);
g_free (string);
string = g_path_get_basename ("/foo/file");
g_assert (strcmp (string, "file") == 0);
g_free (string);
#endif
if (g_test_verbose())
g_print ("checking g_path_get_dirname()...");
for (i = 0; i < n_dirname_checks; i++)
{
gchar *dirname = g_path_get_dirname (dirname_checks[i].filename);
if (strcmp (dirname, dirname_checks[i].dirname) != 0)
{
g_error ("\nfailed for \"%s\"==\"%s\" (returned: \"%s\")\n",
dirname_checks[i].filename,
dirname_checks[i].dirname,
dirname);
}
g_free (dirname);
}
if (g_test_verbose())
g_print ("ok\n");
if (g_test_verbose())
g_print ("checking g_path_skip_root()...");
for (i = 0; i < n_skip_root_checks; i++)
{
const gchar *skipped = g_path_skip_root (skip_root_checks[i].filename);
if ((skipped && !skip_root_checks[i].without_root) ||
(!skipped && skip_root_checks[i].without_root) ||
((skipped && skip_root_checks[i].without_root) &&
strcmp (skipped, skip_root_checks[i].without_root)))
{
g_error ("\nfailed for \"%s\"==\"%s\" (returned: \"%s\")\n",
skip_root_checks[i].filename,
(skip_root_checks[i].without_root ?
skip_root_checks[i].without_root : "<NULL>"),
(skipped ? skipped : "<NULL>"));
}
}
if (g_test_verbose())
g_print ("ok\n");
}
static void
test_file_functions (void)
{
const char hello[] = "Hello, World";
const int hellolen = sizeof (hello) - 1;
GError *error;
char template[32];
char *name_used, chars[62];
gint fd, n;
strcpy (template, "foobar");
fd = g_mkstemp (template);
if (g_test_verbose() && fd != -1)
g_print ("g_mkstemp works even if template doesn't end in XXXXXX\n");
close (fd);
strcpy (template, "fooXXXXXX");
fd = g_mkstemp (template);
if (fd == -1)
g_error ("g_mkstemp didn't work for template %s\n", template);
n = write (fd, hello, hellolen);
if (n == -1)
g_error ("write() failed: %s\n", g_strerror (errno));
else if (n != hellolen)
g_error ("write() should have written %d bytes, wrote %d\n", hellolen, n);
lseek (fd, 0, 0);
n = read (fd, chars, sizeof (chars));
if (n == -1)
g_error ("read() failed: %s\n", g_strerror (errno));
else if (n != hellolen)
g_error ("read() should have read %d bytes, got %d\n", hellolen, n);
chars[n] = 0;
if (strcmp (chars, hello) != 0)
g_error ("wrote '%s', but got '%s'\n", hello, chars);
close (fd);
remove (template);
error = NULL;
strcpy (template, "zap" G_DIR_SEPARATOR_S "barXXXXXX");
fd = g_file_open_tmp (template, &name_used, &error);
if (g_test_verbose())
{
if (fd != -1)
g_print ("g_file_open_tmp works even if template contains '%s'\n", G_DIR_SEPARATOR_S);
else
g_print ("g_file_open_tmp correctly returns error: %s\n", error->message);
}
close (fd);
g_clear_error (&error);
#ifdef G_OS_WIN32
strcpy (template, "zap/barXXXXXX");
fd = g_file_open_tmp (template, &name_used, &error);
if (g_test_verbose())
{
if (fd != -1)
g_print ("g_file_open_tmp works even if template contains '/'\n");
else
g_print ("g_file_open_tmp correctly returns error: %s\n", error->message);
}
close (fd);
g_clear_error (&error);
#endif
strcpy (template, "zapXXXXXX");
fd = g_file_open_tmp (template, &name_used, &error);
if (fd == -1)
g_error ("g_file_open_tmp didn't work for template '%s': %s\n", template, error->message);
else if (g_test_verbose())
g_print ("g_file_open_tmp for template '%s' used name '%s'\n", template, name_used);
close (fd);
g_clear_error (&error);
remove (name_used);
fd = g_file_open_tmp (NULL, &name_used, &error);
if (fd == -1)
g_error ("g_file_open_tmp didn't work for a NULL template: %s\n", error->message);
close (fd);
g_clear_error (&error);
remove (name_used);
}
static void
test_arrays (void)
{
GByteArray *gbarray;
GPtrArray *gparray;
GArray *garray;
guint i;
gparray = g_ptr_array_new ();
for (i = 0; i < 10000; i++)
g_ptr_array_add (gparray, GINT_TO_POINTER (i));
for (i = 0; i < 10000; i++)
if (g_ptr_array_index (gparray, i) != GINT_TO_POINTER (i))
g_error ("array fails: %p ( %p )\n", g_ptr_array_index (gparray, i), GINT_TO_POINTER (i));
g_ptr_array_free (gparray, TRUE);
gbarray = g_byte_array_new ();
for (i = 0; i < 10000; i++)
g_byte_array_append (gbarray, (guint8*) "abcd", 4);
for (i = 0; i < 10000; i++)
{
g_assert (gbarray->data[4*i] == 'a');
g_assert (gbarray->data[4*i+1] == 'b');
g_assert (gbarray->data[4*i+2] == 'c');
g_assert (gbarray->data[4*i+3] == 'd');
}
g_byte_array_free (gbarray, TRUE);
garray = g_array_new (FALSE, FALSE, sizeof (gint));
for (i = 0; i < 10000; i++)
g_array_append_val (garray, i);
for (i = 0; i < 10000; i++)
if (g_array_index (garray, gint, i) != i)
g_error ("failure: %d ( %d )\n", g_array_index (garray, gint, i), i);
g_array_free (garray, TRUE);
garray = g_array_new (FALSE, FALSE, sizeof (gint));
for (i = 0; i < 100; i++)
g_array_prepend_val (garray, i);
for (i = 0; i < 100; i++)
if (g_array_index (garray, gint, i) != (100 - i - 1))
g_error ("failure: %d ( %d )\n", g_array_index (garray, gint, i), 100 - i - 1);
g_array_free (garray, TRUE);
}
static void
hash_table_tests (void)
{
GHashTable *hash_table;
int array[10000];
gint *pvalue = NULL;
gint value = 120;
guint i;
hash_table = g_hash_table_new (my_hash, my_hash_equal);
for (i = 0; i < 10000; i++)
{
array[i] = i;
g_hash_table_insert (hash_table, &array[i], &array[i]);
}
pvalue = g_hash_table_find (hash_table, find_first_that, &value);
if (*pvalue != value)
g_error ("g_hash_table_find failed");
g_hash_table_foreach (hash_table, my_hash_callback, NULL);
for (i = 0; i < 10000; i++)
if (array[i] == 0)
g_error ("hashtable-test: wrong value: %d\n", i);
for (i = 0; i < 10000; i++)
g_hash_table_remove (hash_table, &array[i]);
for (i = 0; i < 10000; i++)
{
array[i] = i;
g_hash_table_insert (hash_table, &array[i], &array[i]);
}
if (g_hash_table_foreach_remove (hash_table, my_hash_callback_remove, NULL) != 5000 ||
g_hash_table_size (hash_table) != 5000)
g_error ("hashtable removal failed\n");
g_hash_table_foreach (hash_table, my_hash_callback_remove_test, NULL);
g_hash_table_destroy (hash_table);
}
static void
relation_test (void)
{
GRelation *relation = g_relation_new (2);
GTuples *tuples;
gint data [1024];
guint i;
g_relation_index (relation, 0, g_int_hash, g_int_equal);
g_relation_index (relation, 1, g_int_hash, g_int_equal);
for (i = 0; i < 1024; i += 1)
data[i] = i;
for (i = 1; i < 1023; i += 1)
{
g_relation_insert (relation, data + i, data + i + 1);
g_relation_insert (relation, data + i, data + i - 1);
}
for (i = 2; i < 1022; i += 1)
{
g_assert (! g_relation_exists (relation, data + i, data + i));
g_assert (! g_relation_exists (relation, data + i, data + i + 2));
g_assert (! g_relation_exists (relation, data + i, data + i - 2));
}
for (i = 1; i < 1023; i += 1)
{
g_assert (g_relation_exists (relation, data + i, data + i + 1));
g_assert (g_relation_exists (relation, data + i, data + i - 1));
}
for (i = 2; i < 1022; i += 1)
{
g_assert (g_relation_count (relation, data + i, 0) == 2);
g_assert (g_relation_count (relation, data + i, 1) == 2);
}
g_assert (g_relation_count (relation, data, 0) == 0);
g_assert (g_relation_count (relation, data + 42, 0) == 2);
g_assert (g_relation_count (relation, data + 43, 1) == 2);
g_assert (g_relation_count (relation, data + 41, 1) == 2);
g_relation_delete (relation, data + 42, 0);
g_assert (g_relation_count (relation, data + 42, 0) == 0);
g_assert (g_relation_count (relation, data + 43, 1) == 1);
g_assert (g_relation_count (relation, data + 41, 1) == 1);
tuples = g_relation_select (relation, data + 200, 0);
g_assert (tuples->len == 2);
#if 0
for (i = 0; i < tuples->len; i += 1)
{
printf ("%d %d\n",
*(gint*) g_tuples_index (tuples, i, 0),
*(gint*) g_tuples_index (tuples, i, 1));
}
#endif
g_assert (g_relation_exists (relation, data + 300, data + 301));
g_relation_delete (relation, data + 300, 0);
g_assert (!g_relation_exists (relation, data + 300, data + 301));
g_tuples_destroy (tuples);
g_relation_destroy (relation);
relation = NULL;
}
static void
gstring_tests (void)
{
GString *string1, *string2;
guint i;
if (g_test_verbose())
g_print ("test GString basics\n");
string1 = g_string_new ("hi pete!");
string2 = g_string_new ("");
g_assert (strcmp ("hi pete!", string1->str) == 0);
for (i = 0; i < 10000; i++)
g_string_append_c (string1, 'a'+(i%26));
#ifndef G_OS_WIN32
/* MSVC, mingw32 and LCC use the same run-time C library, which doesn't like
the %10000.10000f format... */
g_string_printf (string2, "%s|%0100d|%s|%s|%0*d|%*.*f|%10000.10000f",
"this pete guy sure is a wuss, like he's the number ",
1,
" wuss. everyone agrees.\n",
string1->str,
10, 666, 15, 15, 666.666666666, 666.666666666);
#else
g_string_printf (string2, "%s|%0100d|%s|%s|%0*d|%*.*f|%100.100f",
"this pete guy sure is a wuss, like he's the number ",
1,
" wuss. everyone agrees.\n",
string1->str,
10, 666, 15, 15, 666.666666666, 666.666666666);
#endif
if (g_test_verbose())
g_print ("string2 length = %lu...\n", (gulong)string2->len);
string2->str[70] = '\0';
if (g_test_verbose())
g_print ("first 70 chars:\n%s\n", string2->str);
string2->str[141] = '\0';
if (g_test_verbose())
g_print ("next 70 chars:\n%s\n", string2->str+71);
string2->str[212] = '\0';
if (g_test_verbose())
g_print ("and next 70:\n%s\n", string2->str+142);
if (g_test_verbose())
g_print ("last 70 chars:\n%s\n", string2->str+string2->len - 70);
g_string_free (string1, TRUE);
g_string_free (string2, TRUE);
/* append */
string1 = g_string_new ("firsthalf");
g_string_append (string1, "lasthalf");
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* append_len */
string1 = g_string_new ("firsthalf");
g_string_append_len (string1, "lasthalfjunkjunk", strlen ("lasthalf"));
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* prepend */
string1 = g_string_new ("lasthalf");
g_string_prepend (string1, "firsthalf");
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* prepend_len */
string1 = g_string_new ("lasthalf");
g_string_prepend_len (string1, "firsthalfjunkjunk", strlen ("firsthalf"));
g_assert (strcmp (string1->str, "firsthalflasthalf") == 0);
g_string_free (string1, TRUE);
/* insert */
string1 = g_string_new ("firstlast");
g_string_insert (string1, 5, "middle");
g_assert (strcmp (string1->str, "firstmiddlelast") == 0);
g_string_free (string1, TRUE);
/* insert with pos == end of the string */
string1 = g_string_new ("firstmiddle");
g_string_insert (string1, strlen ("firstmiddle"), "last");
g_assert (strcmp (string1->str, "firstmiddlelast") == 0);
g_string_free (string1, TRUE);
/* insert_len */
string1 = g_string_new ("firstlast");
g_string_insert_len (string1, 5, "middlejunkjunk", strlen ("middle"));
g_assert (strcmp (string1->str, "firstmiddlelast") == 0);
g_string_free (string1, TRUE);
/* insert_len with magic -1 pos for append */
string1 = g_string_new ("first");
g_string_insert_len (string1, -1, "lastjunkjunk", strlen ("last"));
g_assert (strcmp (string1->str, "firstlast") == 0);
g_string_free (string1, TRUE);
/* insert_len with magic -1 len for strlen-the-string */
string1 = g_string_new ("first");
g_string_insert_len (string1, 5, "last", -1);
g_assert (strcmp (string1->str, "firstlast") == 0);
g_string_free (string1, TRUE);
/* g_string_equal */
string1 = g_string_new ("test");
string2 = g_string_new ("te");
g_assert (! g_string_equal(string1, string2));
g_string_append (string2, "st");
g_assert (g_string_equal(string1, string2));
g_string_free (string1, TRUE);
g_string_free (string2, TRUE);
/* Check handling of embedded ASCII 0 (NUL) characters in GString. */
if (g_test_verbose())
g_print ("test embedded ASCII 0 (NUL) characters in GString\n");
string1 = g_string_new ("fiddle");
string2 = g_string_new ("fiddle");
g_assert (g_string_equal(string1, string2));
g_string_append_c(string1, '\0');
g_assert (! g_string_equal(string1, string2));
g_string_append_c(string2, '\0');
g_assert (g_string_equal(string1, string2));
g_string_append_c(string1, 'x');
g_string_append_c(string2, 'y');
g_assert (! g_string_equal(string1, string2));
g_assert (string1->len == 8);
g_string_append(string1, "yzzy");
g_assert (string1->len == 12);
g_assert ( memcmp(string1->str, "fiddle\0xyzzy", 13) == 0);
g_string_insert(string1, 1, "QED");
g_assert ( memcmp(string1->str, "fQEDiddle\0xyzzy", 16) == 0);
g_string_free (string1, TRUE);
g_string_free (string2, TRUE);
}
static void
various_string_tests (void)
{
GStringChunk *string_chunk;
GTimeVal ref_date, date;
gchar *tmp_string = NULL, *tmp_string_2, *string, *date_str;
guint i;
if (g_test_verbose())
g_print ("checking string chunks...");
string_chunk = g_string_chunk_new (1024);
for (i = 0; i < 100000; i ++)
{
tmp_string = g_string_chunk_insert (string_chunk, "hi pete");
if (strcmp ("hi pete", tmp_string) != 0)
g_error ("string chunks are broken.\n");
}
tmp_string_2 = g_string_chunk_insert_const (string_chunk, tmp_string);
g_assert (tmp_string_2 != tmp_string && strcmp (tmp_string_2, tmp_string) == 0);
tmp_string = g_string_chunk_insert_const (string_chunk, tmp_string);
g_assert (tmp_string_2 == tmp_string);
g_string_chunk_free (string_chunk);
if (g_test_verbose())
g_print ("test positional printf formats (not supported):");
string = g_strdup_printf ("%.*s%s", 5, "a", "b");
tmp_string = g_strdup_printf ("%2$*1$s", 5, "c");
if (g_test_verbose())
g_print ("%s%s\n", string, tmp_string);
g_free (tmp_string);
g_free (string);
#define REF_INVALID "Wed Dec 19 17:20:20 GMT 2007"
#define REF_SEC_UTC 320063760
#define REF_STR_UTC "1980-02-22T10:36:00Z"
#define REF_STR_CEST "1980-02-22T12:36:00+02:00"
#define REF_STR_EST "1980-02-22T05:36:00-05:00"
if (g_test_verbose())
g_print ("checking g_time_val_from_iso8601...\n");
ref_date.tv_sec = REF_SEC_UTC;
ref_date.tv_usec = 0;
g_assert (g_time_val_from_iso8601 (REF_INVALID, &date) == FALSE);
g_assert (g_time_val_from_iso8601 (REF_STR_UTC, &date) != FALSE);
if (g_test_verbose())
g_print ("\t=> UTC stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec);
g_assert (date.tv_sec == ref_date.tv_sec);
g_assert (g_time_val_from_iso8601 (REF_STR_CEST, &date) != FALSE);
if (g_test_verbose())
g_print ("\t=> CEST stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec);
g_assert (date.tv_sec == ref_date.tv_sec);
g_assert (g_time_val_from_iso8601 (REF_STR_EST, &date) != FALSE);
if (g_test_verbose())
g_print ("\t=> EST stamp = %ld (should be: %ld) (%ld off)\n", date.tv_sec, ref_date.tv_sec, date.tv_sec - ref_date.tv_sec);
g_assert (date.tv_sec == ref_date.tv_sec);
if (g_test_verbose())
g_print ("checking g_time_val_to_iso8601...\n");
ref_date.tv_sec = REF_SEC_UTC;
ref_date.tv_usec = 1;
date_str = g_time_val_to_iso8601 (&ref_date);
g_assert (date_str != NULL);
if (g_test_verbose())
g_print ("\t=> date string = %s (should be: %s)\n", date_str, REF_STR_UTC);
g_assert (strcmp (date_str, REF_STR_UTC) == 0);
g_free (date_str);
if (g_test_verbose())
g_print ("checking g_ascii_strcasecmp...");
g_assert (g_ascii_strcasecmp ("FroboZZ", "frobozz") == 0);
g_assert (g_ascii_strcasecmp ("frobozz", "frobozz") == 0);
g_assert (g_ascii_strcasecmp ("frobozz", "FROBOZZ") == 0);
g_assert (g_ascii_strcasecmp ("FROBOZZ", "froboz") > 0);
g_assert (g_ascii_strcasecmp ("", "") == 0);
g_assert (g_ascii_strcasecmp ("!#%&/()", "!#%&/()") == 0);
g_assert (g_ascii_strcasecmp ("a", "b") < 0);
g_assert (g_ascii_strcasecmp ("a", "B") < 0);
g_assert (g_ascii_strcasecmp ("A", "b") < 0);
g_assert (g_ascii_strcasecmp ("A", "B") < 0);
g_assert (g_ascii_strcasecmp ("b", "a") > 0);
g_assert (g_ascii_strcasecmp ("b", "A") > 0);
g_assert (g_ascii_strcasecmp ("B", "a") > 0);
g_assert (g_ascii_strcasecmp ("B", "A") > 0);
if (g_test_verbose())
g_print ("checking g_strdup...\n");
g_assert (g_strdup (NULL) == NULL);
string = g_strdup (GLIB_TEST_STRING);
g_assert (string != NULL);
g_assert (strcmp(string, GLIB_TEST_STRING) == 0);
g_free (string);
if (g_test_verbose())
g_print ("checking g_strconcat...\n");
string = g_strconcat (GLIB_TEST_STRING, NULL);
g_assert (string != NULL);
g_assert (strcmp (string, GLIB_TEST_STRING) == 0);
g_free (string);
string = g_strconcat (GLIB_TEST_STRING, GLIB_TEST_STRING,
GLIB_TEST_STRING, NULL);
g_assert (string != NULL);
g_assert (strcmp (string, GLIB_TEST_STRING GLIB_TEST_STRING
GLIB_TEST_STRING) == 0);
g_free (string);
if (g_test_verbose())
g_print ("checking g_strlcpy/g_strlcat...");
/* The following is a torture test for strlcpy/strlcat, with lots of
* checking; normal users wouldn't use them this way!
*/
string = g_malloc (6);
*(string + 5) = 'Z'; /* guard value, shouldn't change during test */
*string = 'q';
g_assert (g_strlcpy(string, "" , 5) == 0);
g_assert ( *string == '\0' );
*string = 'q';
g_assert (g_strlcpy(string, "abc" , 5) == 3);
g_assert ( *(string + 3) == '\0' );
g_assert (g_str_equal(string, "abc"));
g_assert (g_strlcpy(string, "abcd" , 5) == 4);
g_assert ( *(string + 4) == '\0' );
g_assert ( *(string + 5) == 'Z' );
g_assert (g_str_equal(string, "abcd"));
g_assert (g_strlcpy(string, "abcde" , 5) == 5);
g_assert ( *(string + 4) == '\0' );
g_assert ( *(string + 5) == 'Z' );
g_assert (g_str_equal(string, "abcd"));
g_assert (g_strlcpy(string, "abcdef" , 5) == 6);
g_assert ( *(string + 4) == '\0' );
g_assert ( *(string + 5) == 'Z' );
g_assert (g_str_equal(string, "abcd"));
*string = 'Y';
*(string + 1)= '\0';
g_assert (g_strlcpy(string, "Hello" , 0) == 5);
g_assert (*string == 'Y');
*string = '\0';
g_assert (g_strlcat(string, "123" , 5) == 3);
g_assert ( *(string + 3) == '\0' );
g_assert (g_str_equal(string, "123"));
g_assert (g_strlcat(string, "" , 5) == 3);
g_assert ( *(string + 3) == '\0' );
g_assert (g_str_equal(string, "123"));
g_assert (g_strlcat(string, "4", 5) == 4);
g_assert (g_str_equal(string, "1234"));
g_assert (g_strlcat(string, "5", 5) == 5);
g_assert ( *(string + 4) == '\0' );
g_assert (g_str_equal(string, "1234"));
g_assert ( *(string + 5) == 'Z' );
*string = 'Y';
*(string + 1)= '\0';
g_assert (g_strlcat(string, "123" , 0) == 3);
g_assert (*string == 'Y');
/* A few more tests, demonstrating more "normal" use */
g_assert (g_strlcpy(string, "hi", 5) == 2);
g_assert (g_str_equal(string, "hi"));
g_assert (g_strlcat(string, "t", 5) == 3);
g_assert (g_str_equal(string, "hit"));
g_free(string);
if (g_test_verbose())
g_print ("checking g_strdup_printf...\n");
string = g_strdup_printf ("%05d %-5s", 21, "test");
g_assert (string != NULL);
g_assert (strcmp(string, "00021 test ") == 0);
g_free (string);
/* g_debug (argv[0]); */
}
static void
test_mem_chunks (void)
{
GMemChunk *mem_chunk = g_mem_chunk_new ("test mem chunk", 50, 100, G_ALLOC_AND_FREE);
gchar *mem[10000];
guint i;
for (i = 0; i < 10000; i++)
{
guint j;
mem[i] = g_chunk_new (gchar, mem_chunk);
for (j = 0; j < 50; j++)
mem[i][j] = i * j;
}
for (i = 0; i < 10000; i++)
g_mem_chunk_free (mem_chunk, mem[i]);
}
int
main (int argc,
char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/testglib/Infos", test_info);
g_test_add_func ("/testglib/Types Sizes", type_sizes);
g_test_add_func ("/testglib/GStrings", gstring_tests);
g_test_add_func ("/testglib/Various Strings", various_string_tests);
g_test_add_func ("/testglib/GList", glist_test);
g_test_add_func ("/testglib/GSList", gslist_test);
g_test_add_func ("/testglib/GNode", gnode_test);
g_test_add_func ("/testglib/GTree", binary_tree_test);
g_test_add_func ("/testglib/Arrays", test_arrays);
g_test_add_func ("/testglib/GHashTable", hash_table_tests);
g_test_add_func ("/testglib/Relation", relation_test);
g_test_add_func ("/testglib/File Paths", test_paths);
g_test_add_func ("/testglib/File Functions", test_file_functions);
g_test_add_func ("/testglib/Mkdir", test_g_mkdir_with_parents);
g_test_add_func ("/testglib/Parse Debug Strings", test_g_parse_debug_string);
g_test_add_func ("/testglib/GMemChunk (deprecated)", test_mem_chunks);
g_test_add_func ("/testglib/Warnings & Errors", log_warning_error_tests);
g_test_add_func ("/testglib/Timers (slow)", timer_tests);
return g_test_run();
}
view raw testglibc.c hosted with ❤ by GitHub


Monday, April 11, 2016

Creating and using a .this pointer in C language structs



This blog post contains examples of how to create a this pointer in C. You may know "this.xxxx" is a C++ attribute of objects. Well like many things you can emulate it in C because C is far more flexible without the bloating that object oriented languages provide for lazy programmers to make mistakes.

Of course, in order to emulate in C it requires more knowledge/experience than the average starting programmer can do. And as websites develop bit rot for older technology these things get lost unless others decide to pick up the challenge to maintain them.  I do not claim to be the first to do this, I am sure it's been done, but I am the one making a clear explanation and re-introducing it as it were.

I tried to post to stack exchange but I do not meet the process nazi's interpretation of a good question so I won't bother providing them my help. I would rather be helpful than process-correct and I don't get paid to educate so I stick to the facts without superfluity in a non-educational idiom.

<\whining>

One can make a resident pointer to a struct of the same type like so:
/*! \def cca_t continous cellular automata type
*
* state and cell variables are disconnected in order to
* uniformly maintain a common interface going forward.
* continuous / non totalistic / totalistic can augment this original struct
* dynamic variables can be integers converted or reals.
*
* convention is one state variable per neighbour starting at 0 will align
* with the number of neighboiurs and any above vars can be above n-1
*
* boundary cells can point at "zero" cell that provides no data save zeros
*
* neighbor number and state number reflect the same cell adjacent when assigning and using
* variables related to respective
* this allows each cell to be independent in orientation and position
* ( maybe a tunnel from far away lets out nearby)
*
* */
typedef struct cca_t
{
struct cca_t * this; //! point to myself.
unsigned int number; //! number of this cell
float x; //!
float y; //! absolute global X Y Z coords of cell centre
float z; //!
unsigned int neighbour_number; //! [0...N-1] number of cells surrouding this one
struct cca_t ** neighbours; //! dynamic pointers to surrounding cells.
int num_states; //! dynamic number of state variables per cell
cell_surface_t * state; //! dynamic state variables per cell
}cca_t;
view raw gistfile1.txt hosted with ❤ by GitHub


You don't need the first cca_t I just use it to make my understanding easier.

Before a struct has been defined one can't refer to it. Some sort of causal loop error in programming form.  But one can declare it as a struct while defining the struct- itself?!? Another metaphysical argument I don't need to go into. I know this will work on GCC with default warnings and error flags and that's all I care about. I cannot confirm if it works with C-XXXX standard in general terms. CAVEAT EMPTOR. If you do find out it works I would appreciate 40 ms of your time to respond back below and we can leave all the comments in one spot for people to find them. Like me in 6 months when I return.

So the "this" pointer is a pointer without an array. Notice I include a pointer to a pointer to  the same struct being defined. In this case I am making a same-typed pointer (to pointer) that will be the receptor of similar pointers from neighbouring cells. I was making a cellular automata at the time so that was the context. I wanted to be able to define modular units that might end up on different threads in this version. So they can access in the same program frame addressing but not necessarily the same operation unit / lightweight process.

So far so good. Now, when you initialize / construct the dynamic struct with malloc:

/*
* number of neighbors = num
* real coords x y z
* states = equals number of cell states should be more than num to reflect all
* */
cca_t *CCAInit(int num, float x, float y, float z, int states )
{
cca_t * ret;
int i;
ret = malloc(sizeof(cca_t) );
// point at self
ret->this = (cca_t *) ret;
ret->number = num;
// reset neighbors to none assigned
ret->neighbour_number = 0;
ret->x = x;
ret->y = y;
ret->z = z;
// allocate for a set of pointer to other cells
ret->neighbours = (struct cca_t ** )malloc(num * sizeof(struct cca_t *) );
// allocate for internal / external state pairs
ret->state = ( cell_surface_t * ) malloc(states * sizeof(cell_surface_t ) );
// number of states reflects the current empty cell surface
ret->num_states = 0;
return ret;
}
view raw gistfile1.txt hosted with ❤ by GitHub



You point the this at the pointer you will return at the end of the function.

In this instantiation, I make cell surfaces dependent on the same neighbour number to 0 cell means neighbour 0 and also state[0] for any values passed back and forth. Now I can iterate once and get it all.  I don't make a set pattern for cell walls because they might have arbitrary connectedness.

Now when I want to assign a neighbour to a cell it works something like this:

//! Assign neighbours to respective cell
int CCAAssignNeighbor ( cca_t *a, cca_t *b )
{
float * ptr;
// aim values - wow screwed this up a lot
// aim a's neighbour at b's this
a->neighbours[a->neighbour_number] = (struct cca_t * )b->this;
// vice versa
b->neighbours[b->neighbour_number] = (struct cca_t * )a->this;
// surrogate pointer assignment for a cell state to b cell state
ptr = &(b->state[b->neighbour_number]).st;
AssignCellSurface ( &(a->state[a->neighbour_number]), (const float *)ptr );
// vice versa
ptr = &(a->state[a->neighbour_number]).st;
AssignCellSurface ( &(b->state[b->neighbour_number]), (const float *)ptr );
a->neighbour_number++;
b->neighbour_number++;
return 0;
}
view raw gistfile1.txt hosted with ❤ by GitHub



I assign b's b->this variable to the neighbour slot open in a's neighbour list.
And likewise assign a's this to b. I also assign the cell walls (neighbour connection) using the ADDRESS OF of the pointer not the VALUE of the pointer. Since the neighbour is inside the larger struct passed and we are using  a pointer -> pointer -> struct we are accessing the value by default when we aim at the second struct within a struct that was passed inside a function variable declaration because these are nested structs within structs.  I know that sounds like double talk but I am unrolling the order of operations and presenting it in a way that when you look at other explanations will make sense.

Pointers take time to understand and remember that pointers give a value or an address, not an array, when accessed. That's why a pointer to element with offset indexing leads to another pointed-to element. An array is guaranteed for your program's sake (as far as it can see) to be a contiguous length of memory of type.

The struct is a value when accessed one way, it is an address when accessed the other.

I don't like the *( a + i).element method of writing C structs because it is a babyish technique taught by naive professors that have never dared to make more complicated nested code. Some claim it is faster and I don't dispute that but I doubt they've tried harder because they stop teaching at the semester end. Probably because when you present the same material every year no one challenges you to prove you can do more. It makes sense for basic applications which is what they can show you. But working code lives and gets more complex. Programmers don't.

I claim it's babyish because I dare them to keep this kind of nested logic straight in their mind when writing it.

*((((a+i)+j)+k)+l)+m).element

So

&(b->state[b->neighbour_number]).st

gives us the address of the pointed to state address (.st) inside the nested pointers and this can continue with a rational understanding of the content within like so:

&(b->state[b->neighbour_number]->neuron[b->cell_number]->synapse[b->geo_number]).st

ad infinitum with a rational way to understand what was written. I can make iterator names that make sense and I can understand 10 files all at once. All the element indices can be maintained at the top level and iterated which is really the operation that most code undergoes. But what is different here is people can make a direct understanding of the things operating on things because that is the level humans work at. That is what costs the most time; understanding what code does not writing perfect code. Machines are better in most cases for optimization except for ATLAS as far as I know. R. Clint Whaley is an uber nerd and he writes sparse matrix optimizations by hand.

I follow the unix philosophy  writing understandable code and leaving optimization as another step:

"Worse is better"

I work in research so I don't worry about development as much as making sure it works. But with a top level iteration mechanism the GCC compiler can then unroll for loops for speed or optimize for memory. But the human being writes code and then goes off for 3 months and then has to come back to it.

This is why writing things like a .this pointer make it easier to grasp what you wrote and then evolve it quickly with patterns that make sense.







Sunday, March 20, 2016

Writing time to vector data output in ngspice control block


If you want to store the time as a vector alongside the columns of data in an ngspice simulation output, then you call the vector 't' which is the internal name for the time-stepped finite difference. 

For example, this control block prints out the time into a wrdata command.

**************************************************************
*  CONTROL BLOCK                                             *
**************************************************************
.control
set hcopydevtype=postscript
* allow color and set background color if set to value > 0
set hcopypscolor=1
*color0 is background color
*color1 is the grid and text color
*colors 2-15 are for the vectors
set color0=rgb:f/f/f
set color1=rgb:0/0/0
set color2=rgb:5/5/5
set color3=rgb:0/9/9
set color4=rgb:7/9/0
op
run
*  V(1) V(2) V(3) I(vss) V(7) V(6) V(5)
gnuplot   V(1) V(2) V(3) I(vss) V(7) V(6) V(5)
hardcopy multi-switch.ps  V(1) V(2) V(3) I(vss) V(7) V(6) V(5)
wrdata multiswitch.data  t V(1) V(2) V(3) I(vss) V(7) V(6) V(5)
.endc

which resulted in the following output:


 

Friday, March 4, 2016

Example of a MOSFET switch in SPICE model

I was reading this OK tutorial to refresh myself on how to make a MOSFET switch. They do a good job explaining theory but they dont' take it that one step further. You can find more information here.




I took a n-channel depletion MOSFET model from here Simon Bramble
and I made a SPICE model circuit around it.

I stepped the voltage on the gate - look at the V(1) voltage in grey - and it activates the MOSFET conducting channel via the field effect when the blue line V(3) (which is the drain node ) drops from 6 volts down to zero.   This causes the current to flow through the drain to source and this turns on the load which is flowing through I(Vss).


Erratum: First version explained effect as transconductance, that's not accurate I've changed it to field effect. That's how rusty I am I mixed the phenomena.Transconductance is a generalization relating transfer function in this case of gate voltage to compared to source current / voltage but the phenomena is the field effect. My first explanation was partly correct but the second one is completely accurate.