1996 fixes and updates

Update the README.md files - at least with those not in a previous
commit. The updates mostly include formatting (make them markdown) but
probably also typos here and there. More probably has to be done with
these files.

The 1996/august/.gitignore was updated as well with a file that is
generated by one of the suggested commands.
This commit is contained in:
Cody Boone Ferguson committed 2023-03-20 09:01:46 -07:00
1 parent 2d6d05e053
commit 8870040156
9 files changed
+333 -314

No files matched your search

+1
View File
@@ -1,2 +1,3 @@
august august
august.oc august.oc
test.oo
+87 -86
View File
@@ -10,7 +10,7 @@
make all make all
### To run ## To run:
cat august.c test.oc | ./august > test.oo cat august.c test.oc | ./august > test.oo
./august < test.oo ./august < test.oo
@@ -19,158 +19,159 @@
If the above cat line does not execute in a very short amount If the above cat line does not execute in a very short amount
of time, then you may need to fix your compiler or use gcc. of time, then you may need to fix your compiler or use gcc.
## Judges' comments ## Try:
Try:
cat august.c fac.oc | ./august > fac.oo cat august.c fac.oc | ./august > fac.oo
./august < fac.oo ./august < fac.oo
Or try:
cat august.c fac.oc | ./august | ./august cat august.c fac.oc | ./august | ./august
And try:
cat august.c parse.oc | ./august > parse.oo cat august.c parse.oc | ./august > parse.oo
cat parse.oo fac.oc | ./august | ./august cat parse.oo fac.oc | ./august | ./august
This entry can feed on itself. If you C preprocess the source,
you compile the interpreter: ## Judges' comments:
This entry can feed on itself. If you C preprocess the source,
you compile the interpreter:
make august.oc make august.oc
We can now compile the interpreter as follows: We can now compile the interpreter as follows:
cat august.c august.oc | ./august > august.oo cat august.c august.oc | ./august > august.oo
and use the compiled interpreter to execute some previously and use the compiled interpreter to execute some previously
compiled code: compiled code:
cat august.oo test.oo | ./august cat august.oo test.oo | ./august
And we can have the compiled interpreter interpret itself which And we can have the compiled interpreter interpret itself which
inturn compiles the test program: in turn compiles the test program:
cat august.oo august.oo fac.oo | ./august cat august.oo august.oo fac.oo | ./august
And if you have lots of spare time, you can recurse one level deeper: And if you have lots of spare time, you can recurse one level deeper:
cat august.oo august.oo august.oo fac.oo | ./august cat august.oo august.oo august.oo fac.oo | ./august
We (the judges) recommend that you spend some time studying this We (the judges) recommend that you spend some time studying this
entry as it surely will make the 'best of the IOCCC' list. It was entry as it surely will make the 'best of the IOCCC' list. It was
very well received by those who attended the IOCCC BOF. very well received by those who attended the IOCCC BOF.
## Author's comments ## Author's comments
This program is a bytecode interpreter. This fact is not This program is a bytecode interpreter. This fact is not
particularly obfuscated; any experienced C programmer can see that particularly obfuscated; any experienced C programmer can see that
in about a minute. in about a minute.
The interpreter reads the bytecode from the standard input. What The interpreter reads the bytecode from the standard input. What
remains of the standard input after this is left as standard input for remains of the standard input after this is left as standard input for
the interpreted program. the interpreted program.
Having an interpreter but no code isn't any fun. But within a comment Having an interpreter but no code isn't any fun. But within a comment
in the beginning of the interpreter is a sequence of bytes that is in the beginning of the interpreter is a sequence of bytes that is
code that the interpreter understands. It is in fact the code for a code that the interpreter understands. It is in fact the code for a
compiler that compiles from OC (Obfuscated C, a subset of C) into the compiler that compiles from OC (Obfuscated C, a subset of C) into the
byte code that the interpreter understands. byte code that the interpreter understands.
The language the compiler understands is a subset of C (the grammar The language the compiler understands is a subset of C (the grammar
is in grammar.small) with the functions getchar() and putchar() is in grammar.small) with the functions getchar() and putchar()
available. The subset has the types `int', `char', and pointers to available. The subset has the types `int', `char', and pointers to
those (it has function pointers too, but not with the proper those (it has function pointers too, but not with the proper
syntax). Arrays can only be global. There is a rather limited set syntax). Arrays can only be global. There is a rather limited set
of operators, and control constructs. of operators, and control constructs.
The compiler has absolutely no error checking, so things The compiler has absolutely no error checking, so things
can go wrong. But this is what seasoned C progammers are used to. can go wrong. But this is what seasoned C programmers are used to.
Assume that the interpreter has been compiled (source in prog.c and Assume that the interpreter has been compiled (source in prog.c and
binary in prog) and that we have this in a file named test.oc: binary in prog) and that we have this in a file named test.oc:
main() { putchar('!'); putchar('\n'); exit(0); } main() { putchar('!'); putchar('\n'); exit(0); }
We can then compile by We can then compile by
cat august.c test.oc | ./august > test.oo cat august.c test.oc | ./august > test.oo
And run the compiled program by And run the compiled program by
./august < test.oo ./august < test.oo
Or even simpler: Or even simpler:
cat august.c test.oc | ./august | ./august cat august.c test.oc | ./august | ./august
A larger example is included fac.oc. A larger example is included fac.oc.
The compiler for OC is naturally written in OC (where did you think The compiler for OC is naturally written in OC (where did you think
the byte code came from?). It is in the file parse.oc. the byte code came from?). It is in the file parse.oc.
Hmm... So now we have an interpreter written in OC and a compiler Hmm... So now we have an interpreter written in OC and a compiler
for OC, can we compile the interpreter? Yes, we can! The OC for OC, can we compile the interpreter? Yes, we can! The OC
compiler does not have a preprocessor (hey, what do you expect from compiler does not have a preprocessor (hey, what do you expect from
a program that is this small), so we need to use an external one. a program that is this small), so we need to use an external one.
To preprocess august.c use the command from the build file, but add To preprocess august.c use the command from the build file, but add
the -E flag and change 60000 to 40000, i.e.: the -E flag and change 60000 to 40000, i.e.:
cc -E -DZ=40000 .... august.c > prog.oc cc -E -DZ=40000 .... august.c > prog.oc
If prog.oc contain some junk line starting with a `#' (most likely If prog.oc contain some junk line starting with a `#' (most likely
it does) then remove it. it does) then remove it.
OK, we can now compile the interpreter: OK, we can now compile the interpreter:
cat august.c prog.oc | ./august > prog.oo cat august.c prog.oc | ./august > prog.oo
And we can run it: And we can run it:
cat prog.oo test.oo | ./august cat prog.oo test.oo | ./august
Here we have the interpreter interpreting another interpreter that runs Here we have the interpreter interpreting another interpreter that runs
the program. Did you think that was too fast? Just throw in another the program. Did you think that was too fast? Just throw in another
level of interpretation: level of interpretation:
cat prog.oo prog.oo test.oo | ./august cat prog.oo prog.oo test.oo | ./august
And another... And another...
cat prog.oo prog.oo prog.oo test.oo | ./august cat prog.oo prog.oo prog.oo test.oo | ./august
***** *****
Fitting the byte code for the compiler into the initial comment Fitting the byte code for the compiler into the initial comment
was a challenge. I had to strip the compiler/interpreter of was a challenge. I had to strip the compiler/interpreter of
a lot of functionality to bring down their sizes. The initial a lot of functionality to bring down their sizes. The initial
version had a full set of binary operators as well as prefix &. version had a full set of binary operators as well as prefix &.
Given a handful more bytes it would be easy to put them back. Given a handful more bytes it would be easy to put them back.
Anyway, the bytecode from the compiler is first optimized in Anyway, the bytecode from the compiler is first optimized in
an external optimizer (not supplied here); this brings down the an external optimizer (not supplied here); this brings down the
size of it by about 30%. The remaining code is then LZ-compressed size of it by about 30%. The remaining code is then LZ-compressed
(roughly) with a sliding window of 255 bytes. The most common (roughly) with a sliding window of 255 bytes. The most common
occuring bytes are then encoded using " \n\t;{}" to avoid having occuring bytes are then encoded using " \n\t;{}" to avoid having
them counted as significant. About 200 bytes of the interpreter them counted as significant. About 200 bytes of the interpreter
handles the unpacking of the byte code (this is a lot, but it pays handles the unpacking of the byte code (this is a lot, but it pays
off). off).
Despite all this work I still had to put a lot of code in the Despite all this work I still had to put a lot of code in the
compilation command. This disturbs me. It is possible to compress compilation command. This disturbs me. It is possible to compress
the code further with a better entropy coder (e.g. an arithmetic the code further with a better entropy coder (e.g. an arithmetic
coder), but decompression then takes up too much code. Maybe this coder), but decompression then takes up too much code. Maybe this
would be a way to fit it all into the 1536 bytes, but I'm don't have would be a way to fit it all into the 1536 bytes, but I'm don't have
time to try it. time to try it.
***** *****
Execution starts at main(). Nothing can be used before it is Execution starts at main(). Nothing can be used before it is
defined. getchar() and putchar() are predefined functions. defined. getchar() and putchar() are predefined functions.
The following is a description of the OC grammar: The following is a description of the OC grammar:
OC grammar OC grammar
========== ==========
Terminals are in quotes, () is used for bracketing.
Terminals are in quotes, () is used for bracketing.
program: decl* program: decl*
@@ -228,6 +229,6 @@
stars: "*"* stars: "*"*
***** *****
Well, that is enough ranting for now. Well, that is enough ranting for now.
+1 -1
View File
@@ -51,7 +51,7 @@ We (the judges) recommend that you take the time needed to understand
how this program works. The source is small enough to make the effort how this program works. The source is small enough to make the effort
reasonable, and complex enough to make it interesting. reasonable, and complex enough to make it interesting.
## Author's comments ## Author's comments:
This program assumes that your terminal wraps lines automatically. This program assumes that your terminal wraps lines automatically.
If this is not the case, you may need to change the space in the If this is not the case, you may need to change the space in the
+4
View File
@@ -14,6 +14,10 @@
./eldby ./eldby
WARNING: on modern systems this is very fast and could be a problem for those
susceptible to photosensitivity. Please try this one with caution if this
applies to you! The same goes for people who can be overstimulated.
## Judges' comments ## Judges' comments
We were impressed by the author's ability to render spheres in 3D We were impressed by the author's ability to render spheres in 3D
+3
View File
@@ -17,6 +17,9 @@
echo 'Huffman Decoding' | ./huffman echo 'Huffman Decoding' | ./huffman
NOTE: this program uses `gets()` so you will likely get a warning when compiling
and/or running on modern systems.
## Try: ## Try:
echo 'seeing or feeling is believing' | ./huffman echo 'seeing or feeling is believing' | ./huffman
+6 -6
View File
@@ -12,7 +12,7 @@
make all make all
### To run ### To run:
./jonth ./jonth
@@ -20,18 +20,18 @@
./jonth host1:0 host2:0 ./jonth host1:0 host2:0
## Judges' comments ## Judges' comments:
For extra credit, figure out how you can cheat by taking over For extra credit, figure out how you can cheat by taking over
a square already occupied by an opponent. a square already occupied by an opponent.
## Author's comments ## Author's comments:
This is multi-user tic-tac-toe for X Windows. You may give one or two X This is multi-user tic-tac-toe for X Windows. You may give one or two X
displays as arguments to this game. displays as arguments to this game.
Known bugs: ### Known bugs:
Sometimes on Solaris X gets confused and this program core dumps, Sometimes on Solaris X gets confused and this program core dumps,
solution: give two display arguments (prog host:0 host:0) solution: give two display arguments (prog host:0 host:0)
+29 -29
View File
@@ -20,50 +20,50 @@ For more information try:
./rcm < rfc1952.gz ./rcm < rfc1952.gz
## Judges' comments ## Judges' comments:
For a good noop try: For a good noop try:
gzip -c < rcm.c | ./rcm gzip -c < rcm.c | ./rcm
## Author's comments ## Author's comments:
Except for some silly requirements regarding input validation, CRC Except for some silly requirements regarding input validation, CRC
checking, and similar unimportant fluff, this program is a fully checking, and similar unimportant fluff, this program is a fully
compliant RFC1951/RFC1952 (GNU Gzip) file uncompressor. Feed it a compliant RFC1951/RFC1952 (GNU Gzip) file uncompressor. Feed it a
"gzip" compressed file on standard input, and it will write the fully "gzip" compressed file on standard input, and it will write the fully
uncompressed original file to standard output. uncompressed original file to standard output.
ObJustifications for this entry: ### ObJustifications for this entry:
The Design Trade-off Argument #### The Design Trade-off Argument:
"The 184 characters worth of defines in the build file have been "The 184 characters worth of defines in the build file have been
carefully offset by 184 redundant and unecessary '{', '}', and ';' carefully offset by 184 redundant and unnecessary '{', '}', and ';'
characters in the source file." characters in the source file."
The Design Review Argument #### The Design Review Argument:
"Using only 216 characters out of a maximum of 256, the build file "Using only 216 characters out of a maximum of 256, the build file
meets and actually exceeds specification." meets and actually exceeds specification."
The Program Metrics Argument #### The Program Metrics Argument:
"The program source is less than 3,100 characters in length and "The program source is less than 3,100 characters in length and
uncompresses file `emacs-19.34b.tar.gz' in about 130 seconds on uncompresses file `emacs-19.34b.tar.gz' in about 130 seconds on
my HP 9000/735. The now obsolete source file `inflate.c' from my HP 9000/735. The now obsolete source file `inflate.c' from
the GNU gzip source tree is 31,613 characters in length and the GNU gzip source tree is 31,613 characters in length and
uncompresses the emacs distribution file in slightly under 25 uncompresses the emacs distribution file in slightly under 25
seconds. Thus: seconds. Thus:
3100 / 130 = 23.85 -vs- 31613 / 25 = 1264.52 3100 / 130 = 23.85 -vs- 31613 / 25 = 1264.52
which represents a better than 530% improvement in the ratio of which represents a better than 530% improvement in the ratio of
source file size to execution time." source file size to execution time."
The Whining Programmer's Argument #### The Whining Programmer's Argument:
"The inflate algorithm requires that over 400 characters worth "The inflate algorithm requires that over 400 characters worth
of constants and tables be defined. Which means the program of constants and tables be defined. Which means the program
itself had to be squeezed down to about 1,100 IOCCC countable itself had to be squeezed down to about 1,100 IOCCC countable
bytes." bytes."
+98 -86
View File
@@ -12,119 +12,131 @@
make all make all
### To run ## To run:
./schweikh2 ./schweikh2
## Judges' comments The program accepts up to 4 parameters:
Also try: 0. The number of operators in the expression [default: 9]
1. The value of the first operand [default: 1]
2. The (possibly negative) increment to the next operand [default: 1]
3. The goal value or 0/0 [default: 42]
## Try:
./schweikh2 15 1 0 0/0 ./schweikh2 15 1 0 0/0
The program accepts up to 4 parameters: What happens if you pass only three parameters like the below?
1) The number of operators in the expression [default: 9] ./schweikh2 15 1 0
2) The value of the first operand [default: 1] ## Judges' comments:
3) The (possibly negative) increment to the next operand [default: 1]
4) The goal value or 0/0 [default: 42] The original program, with only slight obfuscations, may be found
in `hunni.c`. The perl script, obfuscates it appropriately:
The original program, with only slight obfuscations, may be found
in hunni.c. The perl script, obfuscates it appropriately:
perl ./hunni.pl < hunni.c perl ./hunni.pl < hunni.c
This entry was another crowd pleaser at the IOCCC BOF. This entry was another crowd pleaser at the IOCCC BOF.
## Author's comments ## Author's comments:
Note: the source must be viewed with tabstop 8. To appreciate the Note: the source must be viewed with tabstop 8. To appreciate the
beauty have your eyes half closed. beauty have your eyes half closed.
Why I think this program is obfuscated: ### Why I think this program is obfuscated:
C is a free format language. It is therefore a question of style how C is a free format language. It is therefore a question of style how
variables, operators, declarations, functions and syntactic elements variables, operators, declarations, functions and syntactic elements
like braces and parentheses are grouped together. As a rule of thumb, like braces and parentheses are grouped together. As a rule of thumb,
I put spaces around operators, after opening braces and before closing I put spaces around operators, after opening braces and before closing
braces. I have generalized this rule by putting whitespace after *any* braces. I have generalized this rule by putting whitespace after *any*
non-white space character (with the exception of preprocessor directives). non-white space character (with the exception of preprocessor directives).
There are no recognizable keywords anymore. There are no recognizable There are no recognizable keywords anymore. There are no recognizable
string literals any more. The main function is invisible. All operators string literals any more. The main function is invisible. All operators
longer than one character have gone. They have been replaced by macro longer than one character have gone. They have been replaced by macro
calls with quite a number of arguments. The keys are the token pasting calls with quite a number of arguments. The keys are the token pasting
and stringizing macros e and O. The "arcane rules of token pasting" and stringizing macros e and O. The "arcane rules of token pasting"
[K&R2] are used up to three levels, H(O,r)R(O,r)! which is needed to make [K&R2] are used up to three levels, `H(O,r)R(O,r)` (!) which is needed to make
one 8-character token from 8 characters ('unsigned'). BTW, C has no 9 or one 8-character token from 8 characters ('unsigned'). BTW, C has no 9 or
more character reserved words, so a fourth level is only required if more character reserved words, so a fourth level is only required if
you intend to generate object names from 9 or more components. The final you intend to generate object names from 9 or more components. The final
round even swaps the tokens, so all keywords must be spelled backwards round even swaps the tokens, so all keywords must be spelled backwards
(sort of, can you spot printf in the P macro?). (sort of, can you spot printf in the P macro?).
Try to indent the source. The unbalanced parentheses confuse Solaris' Try to indent the source. The unbalanced parentheses confuse Solaris'
indent to the max (although the result does compile): indent to the max (although the result does compile):
Looking at the indented source is even *less* fun, because the nice Looking at the indented source is even *less* fun, because the nice
diamond pattern has been replaced by something that looks like, uhm, diamond pattern has been replaced by something that looks like, uhm,
modem noise. modem noise.
Lint the source. It's clean. How obfuscated... Lint the source. It's clean. How obfuscated...
What's the exit status? The program exits with J - 1 where J is the What's the exit status? The program exits with J - 1 where J is the
return value from several non-void functions (needed to shut up return value from several non-void functions (needed to shut up
lint). The last assignment is J = printf ("%c", 10); so the exit lint). The last assignment is J = printf ("%c", 10); so the exit
status is 0 unless the last printf failed, which is even a *useful* status is 0 unless the last printf failed, which is even a *useful*
status. A further obfuscation is the use of 10 instead of '\n'. status. A further obfuscation is the use of 10 instead of '\n'.
'\n' can not be constructed by token pasting, AFAIK. That's why schweikh2 '\n' can not be constructed by token pasting, AFAIK. That's why `schweikh2`
is likely to produce occasional garbage on non-ASCII execution character is likely to produce occasional garbage on non-ASCII execution character
set systems. set systems.
What this program does:
-----------------------
The program is an expression evaluator for a limited class of What this program does:
expressions. Known operators are + - * /, operands are real numbers -----------------------
(integers or fractions). The operands are restricted in that they must
differ by a constant value, for example 1 + 2 * 3 / 4 - 5. For any
combination of operators the expression is evaluated and compared to a
goal value. If they compare equal the expression is printed. If the goal
value is specified as 0/0 all expressions and results are printed. For
any fraction, the numerator and denominator are divided by their
greatest common divisor.
The program accepts up to 4 parameters: The program is an expression evaluator for a limited class of
1) The number of operators in the expression [default: 9] expressions. Known operators are + - * /, operands are real numbers
2) The value of the first operand [1] (integers or fractions). The operands are restricted in that they must
3) The (possibly negative) increment to the next operand [1] differ by a constant value, for example 1 + 2 * 3 / 4 - 5. For any
4) The goal value or 0/0 [42] combination of operators the expression is evaluated and compared to a
goal value. If they compare equal the expression is printed. If the goal
value is specified as 0/0 all expressions and results are printed. For
any fraction, the numerator and denominator are divided by their
greatest common divisor.
On ANSI/ISO C conformant systems (where long is at least 32 bits) The program accepts up to 4 parameters:
overflow will occur in the default case (first operand 1, increment 1) 1) The number of operators in the expression [default: 9]
only for more than 11 operators. Because a bit mask is used that needs 2) The value of the first operand [1]
twice the number of operands in bits (plus a sentinel value) on those 3) The (possibly negative) increment to the next operand [1]
systems the first parameter must not exceed 15. 4) The goal value or 0/0 [42]
If long is at least 64 bits overflow will occur for more than 19 On ANSI/ISO C conformant systems (where long is at least 32 bits)
operators but you will probably wait quite some time for every of the overflow will occur in the default case (first operand 1, increment 1)
4^19 = 274,877,906,944 combinations to be evaluated... While I'm at it, only for more than 11 operators. Because a bit mask is used that needs
on a 50 MHz sparc, 4^11 expressions are evaluated in about 3 cpu twice the number of operands in bits (plus a sentinel value) on those
minutes (gcc -O3). systems the first parameter must not exceed 15.
Sample arguments: If long is at least 64 bits overflow will occur for more than 19
----------------- operators but you will probably wait quite some time for every of the
$ schweikh2 6 4^19 = 274,877,906,944 combinations to be evaluated... While I'm at it,
$ schweikh2 7 0 on a 50 MHz sparc, 4^11 expressions are evaluated in about 3 cpu
$ schweikh2 7 7 0 minutes (gcc -O3).
$ schweikh2 8 9 -1 -67/21
$ schweikh2 8 2 2/5 -98/25 Sample arguments:
$ schweikh2 2 1/0 1 1/0 -----------------
$ ./schweikh2 6
$ ./schweikh2 7 0
$ ./schweikh2 7 7 0
$ ./schweikh2 8 9 -1 -67/21
$ ./schweikh2 8 2 2/5 -98/25
$ ./schweikh2 2 1/0 1 1/0
Miscellaneous
-------------
The diamond pattern can be continued without a break.
For a nice printout try this on your Bourne compatible shell:
% while :; do grep -v '#' schweikh2.c; done
Miscellaneous
-------------
The diamond pattern can be continued without a break.
For a nice printout try this on your Bourne compatible shell
% while :; do grep -v '#' schweikh2.c; done
+104 -106
View File
@@ -12,57 +12,58 @@
make all make all
### To run ## To run:
WARNING: Do not run this program without reading this text down to the end. **WARNING**: Do not run this program without reading this text down to the end!
It may render your system unusable for a limited amount of time It may render your system unusable for a limited amount of time
or force you to reboot using the Big Red Button! This is not a joke. or force you to reboot using the Big Red Button! This is **NOT** a joke.
You have been warned. You have been warned.
./schweikh3
## Judges' comments
The source fails to compile on compilers that recognize the inline
keyword by default. For gcc, you may need to use -ansi.
See the file, schweikh3.info for example output from various systems.
But still it is a useful utility. To use try:
./schweikh3 ./schweikh3
and thanks for the virtual memories! :-)
NOTE: On some systems such as SunOS, one may need to compile with: ## Judges' comments:
${CC} ${CFLAGS} \ The source fails to compile on compilers that recognize the inline
-I. -D_POSIX_SOURCE '-Ddifftime(a,b)=(double)(b-a)' \ keyword by default. For gcc, you may need to use -ansi.
schweikh3.c -o schweikh3
## Author's comments See the file, `schweikh3.info` for example output from various systems.
Why I think my entry is obfuscated But still it is a useful utility. To use try:
----------------------------------
There are some layout obfuscations that hinder readability, like ./schweikh3
some (but not all) one character tokens in column 1. Includes,
defines and declarations of data and functions appear intermixed.
iso646.h is even included in the middle of an expression, how
disgusting. Besides, requiring iso646.h will render the program
uncompilable on many systems that have not yet incorporated
Amendment One to The Standard. If you, dear reader, suffer from
this disease, just read iso646.h.
There are no `if' statements. They have been replaced by and thanks for the virtual memories! :-)
"switch () case 1:" or "switch () case 0:" etc. At least one
switch has a declaration after the opening brace, booh!
Identifiers are named after random programming languages, Basic,
Cobol, F77, awk etc.
You have to select maximum ANSI C conformance of your NOTE: On some systems such as SunOS, one may need to compile with:
compiler. The source fails to compile under a C++ compiler due to
the `new' identifier which is a C++ reserved word: ${CC} ${CFLAGS} \
-I. -D_POSIX_SOURCE '-Ddifftime(a,b)=(double)(b-a)' \
schweikh3.c -o schweikh3
## Author's comments:
Why I think my entry is obfuscated
----------------------------------
There are some layout obfuscations that hinder readability, like
some (but not all) one character tokens in column 1. Includes,
defines and declarations of data and functions appear intermixed.
iso646.h is even included in the middle of an expression, how
disgusting. Besides, requiring iso646.h will render the program
uncompilable on many systems that have not yet incorporated
Amendment One to The Standard. If you, dear reader, suffer from
this disease, just read iso646.h.
There are no `if' statements. They have been replaced by
"switch () case 1:" or "switch () case 0:" etc. At least one
switch has a declaration after the opening brace, booh!
Identifiers are named after random programming languages, Basic,
Cobol, F77, awk etc.
You have to select maximum ANSI C conformance of your
compiler. The source fails to compile under a C++ compiler due to
the `new' identifier which is a C++ reserved word:
$ g++ prog.c $ g++ prog.c
prog.c:9: parse error before `new' prog.c:9: parse error before `new'
@@ -73,8 +74,8 @@
[...] [...]
prog.c:91: confused by earlier errors, bailing out prog.c:91: confused by earlier errors, bailing out
The source fails to compile on compilers that recognize the inline The source fails to compile on compilers that recognize the inline
keyword by default, e.g. gcc: keyword by default, e.g. gcc:
$ gcc prog.c $ gcc prog.c
prog.c:46: parse error before `void' prog.c:46: parse error before `void'
@@ -83,84 +84,81 @@
prog.c: At top level: prog.c: At top level:
prog.c:97: parse error before `void' prog.c:97: parse error before `void'
The -ansi option to gcc fixes this. The `-ansi` option to gcc fixes this.
The source fails to compile on obsolete systems with obsolete The source fails to compile on obsolete systems with obsolete
memory models due to the identifier `far' (to be honest: this is memory models due to the identifier `far' (to be honest: this is
untested :-). untested :-).
Does your indent cope with backslash/newline pairs inbetween Does your indent cope with backslash/newline pairs inbetween
keywords like in "re\<newline>turn"? Solaris' indent inserts spaces keywords like in "re\<newline>turn"? Solaris' indent inserts spaces
and thereby renders the source uncompilable... and thereby renders the source uncompilable...
The indented source will exhibit different semantics when indenting The indented source will exhibit different semantics when indenting
changes the number of lines (due to using the __LINE__ macro in an changes the number of lines (due to using the __LINE__ macro in an
expression). expression).
And what about sizeof __TIME__? This one's for the philosophers And what about sizeof __TIME__? This one's for the philosophers
and physicists among us programmers. What is it? sizeof (char *) -- no. and physicists among us programmers. What is it? sizeof (char *)? -- no.
Strlen ("hh:mm:ss")? -- no. It's the same as sizeof __DATE__ - strlen ("hh:mm:ss")? -- no. It's the same as sizeof __DATE__ -
sizeof "no" :-) sizeof "no" :-)
The program is POSIX conformant and lints without warning under The program is POSIX conformant and lints without warning under
Solaris' Sunpro lint. Allocated memory is correctly freed on all Solaris' Sunpro lint. Allocated memory is correctly freed on all
possible execution paths. possible execution paths.
What this program does What this program does:
---------------------- ----------------------
This program tests your operating system's allocation honesty. It This program tests your operating system's allocation honesty. It
forks a child that will allocate as much as possible with byte forks a child that will allocate as much as possible with byte
granularity and then write to every 1024th byte, at the same time granularity and then write to every 1024th byte, at the same time
writing a status report on stdout. This way each page of the writing a status report on stdout. This way each page of the
allocated virtual memory will eventually be referenced. Without allocated virtual memory will eventually be referenced. Without
any arguments, malloc() will be used to allocate memory. If the any arguments, malloc() will be used to allocate memory. If the
first arg starts with c, calloc() will be used instead. first arg starts with c, calloc() will be used instead.
I distinguish three different behaviours: I distinguish three different behaviours:
1) OS without memory overcommit: 1. OS without memory overcommit: malloc does not lie -- you can use all memory
malloc does not lie -- you can use all memory the OS granted to you. the OS granted to you.
2) OS with memory overcommit: 2. OS with memory overcommit: malloc lies -- any program may get killed at
malloc lies -- any program may get killed at random when the random when the OS can not acquire a free page.
OS can not acquire a free page.
3) OS with pseudo memory overcommit: 3. OS with pseudo memory overcommit: malloc lies -- The OS puts processes to
malloc lies -- The OS puts processes to sleep for which it can sleep for which it can not acquire a free page. Because other processes are also
not acquire a free page. Because other processes are also affected this way, the system slows down and quickly becomes totally unusable
affected this way, the system slows down and quickly becomes (also known as "thrashing"). Because more and more processes wait for memory
totally unusable (also known as "thrashing"). Because more and returned by other exiting processes the system eventually enters a deadlock
more processes wait for memory returned by other exiting processes situation and effectively halts.
the system eventually enters a deadlock situation and effectively
halts.
I had the pleasure and pain to find all three behaviours on the I had the pleasure and pain to find all three behaviours on the
platforms I use regularly. What follows are the outputs for platforms I use regularly. What follows are the outputs for
Solaris, FreeBSD and Linux. The Linux case is interesting in that Solaris, FreeBSD and Linux. The Linux case is interesting in that
the behaviour changes when calloc is used instead of malloc. the behaviour changes when calloc is used instead of malloc.
For FreeBSD, the last two lines are not messages stemming from For FreeBSD, the last two lines are not messages stemming from
my program but issued by the syslog daemon. my program but issued by the syslog daemon.
Implementation remarks Implementation remarks
---------------------- ----------------------
How can we distinguish the three different OS types from the How can we distinguish the three different OS types from the
table above? table above?
Obviously, type 1), no overcommit, is easy. Obviously, type 1), no overcommit, is easy.
For type 2), random kill, we need to fork a child that can be killed For type 2), random kill, we need to fork a child that can be killed
and whose exit status the parent collects and decodes. Here's and whose exit status the parent collects and decodes. Here's
where POSIX enters the scene. where POSIX enters the scene.
Type 3), thrashing, was the hardest part. The first attempt was to Type 3), thrashing, was the hardest part. The first attempt was to
measure the elapsed processor time it takes to write a single byte measure the elapsed processor time it takes to write a single byte
(using clock()). If a certain threshold is reached I assume the (using clock()). If a certain threshold is reached I assume the
system is thrashing. When thrashing, however, the process does not system is thrashing. When thrashing, however, the process does not
consume processor time in a noticeable amount any longer and I lose. consume processor time in a noticeable amount any longer and I lose.
So I switched to measuring wall clock time using time(). On the one So I switched to measuring wall clock time using time(). On the one
system that I observe thrashing (Linux) this works (although the system that I observe thrashing (Linux) this works (although the
value returned by time seems to 'hang' sometimes when the OS thrashes. value returned by time seems to 'hang' sometimes when the OS thrashes.
Exceeding a threshold of 5 seconds may be detected only after half a Exceeding a threshold of 5 seconds may be detected only after half a
minute or so. minute or so.