OSDN Git Service

Merge tag 'android-7.1.2_r2' into cm-14.1
[android-x86/external-toybox.git] / www / design.html
1 <html><head><title>The design of toybox</title></head>
2 <!--#include file="header.html" -->
3
4 <b><h2>Design goals</h2></b>
5
6 <p>Toybox should be simple, small, fast, and full featured.  Often, these
7 things need to be balanced off against each other.  In general, keeping the
8 code simple the most important (and hardest) goal, and small is slightly more
9 important than fast. Features are the reason we write code in the first
10 place but this has all been implemented before so if we can't do a better
11 job why bother?  It should be possible to get 80% of the way to each goal
12 before they really start to fight.</p>
13
14 <p>Here they are in reverse order of importance:</p>
15
16 <b><h3>Features</h3></b>
17
18 <p>The <a href=roadmap.html>roadmap</a> has the list of features we're
19 trying to implement, and the reasons for them. After the 1.0 release
20 some of that material may get moved here.</p>
21
22 <p>Some things are simply outside the scope of the project: even though
23 posix defines commands for compiling and linking, we're not going to include
24 a compiler or linker (and support for a potentially infinite number of hardware
25 targets). And until somebody comes up with a ~30k ssh implementation, we're
26 going to point you at dropbear or polarssl.</p>
27
28 <p>Environmental dependencies are a type of complexity, so needing other
29 packages to build or run is a big downside.  For example, we don't use curses
30 when we can simply output ansi escape sequences and trust all terminal
31 programs written in the past 30 years to be able to support them. (A common
32 use case is to download a statically linked toybox binary to an arbitrary
33 Linux system, and use it in an otherwise unknown environment; being
34 self-contained helps support this.)</p>
35
36 <b><h3>Speed</h3></b>
37
38 <p>It's easy to say lots about optimizing for speed (which is why this section
39 is so long), but at the same time it's the optimization we care the least about.
40 The essence of speed is being as efficient as possible, which means doing as
41 little work as possible.  A design that's small and simple gets you 90% of the
42 way there, and most of the rest is either fine-tuning or more trouble than
43 it's worth (and often actually counterproductive).  Still, here's some
44 advice:</p>
45
46 <p>First, understand the darn problem you're trying to solve.  You'd think
47 I wouldn't have to say this, but I do.  Trying to find a faster sorting
48 algorithm is no substitute for figuring out a way to skip the sorting step
49 entirely.  The fastest way to do anything is not to have to do it at all,
50 and _all_ optimization boils down to avoiding unnecessary work.</p>
51
52 <p>Speed is easy to measure; there are dozens of profiling tools for Linux
53 (although personally I find the "time" command a good starting place).
54 Don't waste too much time trying to optimize something you can't measure,
55 and there's no much point speeding up things you don't spend much time doing
56 anyway.</p>
57
58 <p>Understand the difference between throughput and latency.  Faster
59 processors improve throughput, but don't always do much for latency.
60 After 30 years of Moore's Law, most of the remaining problems are latency,
61 not throughput.  (There are of course a few exceptions, like data compression
62 code, encryption, rsync...)  Worry about throughput inside long-running
63 loops, and worry about latency everywhere else.  (And don't worry too much
64 about avoiding system calls or function calls or anything else in the name
65 of speed unless you are in the middle of a tight loop that's you've already
66 proven isn't running fast enough.)</p>
67
68 <p>"Locality of reference" is generally nice, in all sorts of contexts.
69 It's obvious that waiting for disk access is 1000x slower than doing stuff in
70 RAM (and making the disk seek is 10x slower than sequential reads/writes),
71 but it's just as true that a loop which stays in L1 cache is many times faster
72 than a loop that has to wait for a DRAM fetch on each iteration.  Don't worry
73 about whether "&" is faster than "%" until your executable loop stays in L1
74 cache and the data access is fetching cache lines intelligently.  (To
75 understand DRAM, L1, and L2 cache, read Hannibal's marvelous ram guide at Ars
76 Technica:
77 <a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part1-2.html>part one</a>,
78 <a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part2-1.html>part two</a>,
79 <a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part3-1.html>part three</a>,
80 plus this
81 <a href=http://arstechnica.com/articles/paedia/cpu/caching.ars/1>article on
82 cacheing</a>, and this one on
83 <a href=http://arstechnica.com/articles/paedia/cpu/bandwidth-latency.ars>bandwidth
84 and latency</a>.
85 And there's <a href=http://arstechnica.com/paedia/index.html>more where that came from</a>.)
86 Running out of L1 cache can execute one instruction per clock cycle, going
87 to L2 cache costs a dozen or so clock cycles, and waiting for a worst case dram
88 fetch (round trip latency with a bank switch) can cost thousands of
89 clock cycles.  (Historically, this disparity has gotten worse with time,
90 just like the speed hit for swapping to disk.  These days, a _big_ L1 cache
91 is 128k and a big L2 cache is a couple of megabytes.  A cheap low-power
92 embedded processor may have 8k of L1 cache and no L2.)</p>
93
94 <p>Learn how <a href=http://nommu.org/memory-faq.txt>virtual memory and
95 memory managment units work</a>.  Don't touch
96 memory you don't have to.  Even just reading memory evicts stuff from L1 and L2
97 cache, which may have to be read back in later.  Writing memory can force the
98 operating system to break copy-on-write, which allocates more memory.  (The
99 memory returned by malloc() is only a virtual allocation, filled with lots of
100 copy-on-write mappings of the zero page.  Actual physical pages get allocated
101 when the copy-on-write gets broken by writing to the virtual page.  This
102 is why checking the return value of malloc() isn't very useful anymore, it
103 only detects running out of virtual memory, not physical memory.  Unless
104 you're using a <a href=http://nommu.org>NOMMU system</a>, where all bets are off.)</p>
105
106 <p>Don't think that just because you don't have a swap file the system can't
107 start swap thrashing: any file backed page (ala mmap) can be evicted, and
108 there's a reason all running programs require an executable file (they're
109 mmaped, and can be flushed back to disk when memory is short).  And long
110 before that, disk cache gets reclaimed and has to be read back in.  When the
111 operating system really can't free up any more pages it triggers the out of
112 memory killer to free up pages by killing processes (the alternative is the
113 entire OS freezing solid).  Modern operating systems seldom run out of
114 memory gracefully.</p>
115
116 <p>Also, it's better to be simple than clever.  Many people think that mmap()
117 is faster than read() because it avoids a copy, but twiddling with the memory
118 management is itself slow, and can cause unnecessary CPU cache flushes.  And
119 if a read faults in dozens of pages sequentially, but your mmap iterates
120 backwards through a file (causing lots of seeks, each of which your program
121 blocks waiting for), the read can be many times faster.  On the other hand, the
122 mmap can sometimes use less memory, since the memory provided by mmap
123 comes from the page cache (allocated anyway), and it can be faster if you're
124 doing a lot of different updates to the same area.  The moral?  Measure, then
125 try to speed things up, and measure again to confirm it actually _did_ speed
126 things up rather than made them worse.  (And understanding what's really going
127 on underneath is a big help to making it happen faster.)</p>
128
129 <p>In general, being simple is better than being clever.  Optimization
130 strategies change with time.  For example, decades ago precalculating a table
131 of results (for things like isdigit() or cosine(int degrees)) was clearly
132 faster because processors were so slow.  Then processors got faster and grew
133 math coprocessors, and calculating the value each time became faster than
134 the table lookup (because the calculation fit in L1 cache but the lookup
135 had to go out to DRAM).  Then cache sizes got bigger (the Pentium M has
136 2 megabytes of L2 cache) and the table fit in cache, so the table became
137 fast again...  Predicting how changes in hardware will affect your algorithm
138 is difficult, and using ten year old optimization advice and produce
139 laughably bad results.  But being simple and efficient is always going to
140 give at least a reasonable result.</p>
141
142 <p>The famous quote from Ken Thompson, "When in doubt, use brute force",
143 applies to toybox.  Do the simple thing first, do as little of it as possible,
144 and make sure it's right.  You can always speed it up later.</p>
145
146 <b><h3>Size</h3></b>
147 <p>Again, simple gives you most of this.  An algorithm that does less work
148 is generally smaller.  Understand the problem, treat size as a cost, and
149 get a good bang for the byte.</p>
150
151 <p>Understand the difference between binary size, heap size, and stack size.
152 Your binary is the executable file on disk, your heap is where malloc() memory
153 lives, and your stack is where local variables (and function call return
154 addresses) live.  Optimizing for binary size is generally good: executing
155 fewer instructions makes your program run faster (and fits more of it in
156 cache).  On embedded systems, binary size is especially precious because
157 flash is expensive (and its successor, MRAM, even more so).  Small stack size
158 is important for nommu systems because they have to preallocate their stack
159 and can't make it bigger via page fault.  And everybody likes a small heap.</p>
160
161 <p>Measure the right things.  Especially with modern optimizers, expecting
162 something to be smaller is no guarantee it will be after the compiler's done
163 with it.  Binary size isn't the most accurate indicator of the impact of a
164 given change, because lots of things get combined and rounded during
165 compilation and linking.  Matt Mackall's bloat-o-meter is a python script
166 which compares two versions of a program, and shows size changes in each
167 symbol (using the "nm" command behind the scenes).  To use this, run
168 "make baseline" to build a baseline version to compare against, and
169 then "make bloatometer" to compare that baseline version against the current
170 code.</p>
171
172 <p>Avoid special cases.  Whenever you see similar chunks of code in more than
173 one place, it might be possible to combine them and have the users call shared
174 code. (This is the most commonly cited trick, which doesn't make it easy. If
175 seeing two lines of code do the same thing makes you slightly uncomfortable,
176 you've got the right mindset.)</p>
177
178 <p>Some specific advice: Using a char in place of an int when doing math
179 produces significantly larger code on some platforms (notably arm),
180 because each time the compiler has to emit code to convert it to int, do the
181 math, and convert it back.  Bitfields have this problem on most platforms.
182 Because of this, using char to index a for() loop is probably not a net win,
183 although using char (or a bitfield) to store a value in a structure that's
184 repeated hundreds of times can be a good tradeoff of binary size for heap
185 space.</p>
186
187 <b><h3>Simple</h3></b>
188
189 <p>Complexity is a cost, just like code size or runtime speed. Treat it as
190 a cost, and spend your complexity budget wisely. (Sometimes this means you
191 can't afford a feature because it complicates the code too much to be
192 worth it.)</p>
193
194 <p>Simplicity has lots of benefits.  Simple code is easy to maintain, easy to
195 port to new processors, easy to audit for security holes, and easy to
196 understand.</p>
197
198 <p>Simplicity itself can have subtle non-obvious aspects requiring a tradeoff
199 between one kind of simplicity and another: simple for the computer to
200 execute and simple for a human reader to understand aren't always the
201 same thing. A compact and clever algorithm that does very little work may
202 not be as easy to explain or understand as a larger more explicit version
203 requiring more code, memory, and CPU time. When balancing these, err on the
204 side of doing less work, but add comments describing how you
205 could be more explicit.</p>
206
207 <p>In general, comments are not a substitute for good code (or well chosen
208 variable or function names). Commenting "x += y;" with "/* add y to x */"
209 can actually detract from the program's readability. If you need to describe
210 what the code is doing (rather than _why_ it's doing it), that means the
211 code itself isn't very clear.</p>
212
213 <p>Prioritizing simplicity tends to serve our other goals: simplifying code
214 generally reduces its size (both in terms of binary size and runtime memory
215 usage), and avoiding unnecessary work makes code run faster. Smaller code
216 also tends to run faster on modern hardware due to CPU cacheing: fitting your
217 code into L1 cache is great, and staying in L2 cache is still pretty good.</p>
218
219 <p>But a simple implementation is not always the smallest or fastest, and
220 balancing simplicity vs the other goals can be difficult. For example, the
221 atolx_range() function in lib/lib.c uses the always 64 bit "long long" type,
222 which produces larger and slower code on 32 bit platforms and
223 often assigned into smaller interger types. Although libc has parallel
224 implementations for different data sizes (atoi, atol, atoll) we only
225 used the largest, which can cover all cases.</p>
226
227 <p>On the other hand, the "tail" command has two codepaths, one for seekable
228 files and one for nonseekable files. Although the nonseekable case can handle
229 all inputs (and is required when input comes from a pipe or similar, so cannot
230 be removed), reading through multiple gigabytes of data to reach the end of
231 seekable files was both a common case and hugely penalized by a nonseekable
232 approach (half-minute wait vs instant results). This is one example
233 where performance did outweigh simplicity of implementation.</p>
234
235 <p><a href=http://www.joelonsoftware.com/articles/fog0000000069.html>Joel
236 Spolsky argues against throwing code out and starting over</a>, and he has
237 good points: an existing debugged codebase contains a huge amount of baked
238 in knowledge about strange real-world use cases that the designers didn't
239 know about until users hit the bugs, and most of this knowledge is never
240 explicitly stated anywhere except in the source code.</p>
241
242 <p>That said, the Mythical Man-Month's "build one to throw away" advice points
243 out that until you've solved the problem you don't properly understand it, and
244 about the time you finish your first version is when you've finally figured
245 out what you _should_ have done.  (The corrolary is that if you build one
246 expecting to throw it away, you'll actually wind up throwing away two.  You
247 don't understand the problem until you _have_ solved it.)</p>
248
249 <p>Joel is talking about what closed source software can afford to do: Code
250 that works and has been paid for is a corporate asset not lightly abandoned.
251 Open source software can afford to re-implement code that works, over and
252 over from scratch, for incremental gains.  Before toybox, the unix command line
253 has already been reimplemented from scratch several times (the
254 original AT&amp;T Unix command line in assembly and then in C, the BSD
255 versions, Coherent was the first full from-scratch Unix clone in 1980,
256 Minix was another clone which Linux was inspired by and developed under,
257 the GNU tools were yet another rewrite intended for use in the stillborn
258 "Hurd" project, BusyBox was still another rewrite, and more versions
259 were written in Plan 9, uclinux, klibc, sash, sbase, s6, and of course
260 android toolbox...) but maybe toybox can do a better job. :)</p>
261
262 <p>As Antoine de St. Exupery (author of "The Little Prince" and an early
263 aircraft designer) said, "Perfection is achieved, not when there
264 is nothing left to add, but when there is nothing left to take away."
265 And Ken Thompson (creator of Unix) said "One of my most productive
266 days was throwing away 1000 lines of code." It's always possible to
267 come up with a better way to do it.</p>
268
269 <p>P.S.  How could I resist linking to an article about
270 <a href=http://blog.outer-court.com/archive/2005-08-24-n14.html>why
271 programmers should strive to be lazy and dumb</a>?</p>
272
273 <b><h2>Portability issues</h2></b>
274
275 <b><h3>Platforms</h3></b>
276 <p>Toybox should run on Android (all commands with musl-libc, as large a subset
277 as practical with bionic), and every other hardware platform Linux runs on.
278 Other posix/susv4 environments (perhaps MacOS X or newlib+libgloss) are vaguely
279 interesting but only if they're easy to support; I'm not going to spend much
280 effort on them.</p>
281
282 <p>I don't do windows.</p>
283
284 <p>We depend on C99 and posix-2008 libc features such as the openat() family of
285 functions. We also assume certain "modern" linux kernel behavior such
286 as large environment sizes (linux commit b6a2fea39318, went into 2.6.22
287 released July 2007). In theory this shouldn't prevent us from working on
288 older kernels or other implementations (ala BSD), but we don't police their
289 corner cases.</p>
290
291 <b><h3>32/64 bit</h3></b>
292 <p>Toybox should work on both 32 bit and 64 bit systems. 64 bit desktop
293 hardware went mainstream in 2005 and was essentially ubiquitous
294 by the end of the decade, but 32 bit hardware will continue to be important
295 in embedded devices for several more years.</p>
296
297 <p>Toybox relies on the fact that on any Unix-like platform, pointer and long
298 are always the same size (on both 32 and 64 bit). Pointer and int are _not_
299 the same size on 64 bit systems, but pointer and long are.</p>
300
301 <p>This is guaranteed by the LP64 memory model, a Unix standard (which Linux
302 and MacOS X both implement, and which modern 64 bit processors such as
303 x86-64 were <a href=http://www.pagetable.com/?p=6>designed for</a>).  See
304 <a href=http://www.unix.org/whitepapers/64bit.html>the LP64 standard</a> and
305 <a href=http://www.unix.org/version2/whatsnew/lp64_wp.html>the LP64
306 rationale</a> for details.</p>
307
308 <p>Note that Windows doesn't work like this, and I don't care.
309 <a href=http://blogs.msdn.com/oldnewthing/archive/2005/01/31/363790.aspx>The
310 insane legacy reasons why this is broken on Windows are explained here.</a></p>
311
312 <b><h3>Signedness of char</h3></b>
313 <p>On platforms like x86, variables of type char default to unsigned.  On
314 platforms like arm, char defaults to signed.  This difference can lead to
315 subtle portability bugs, and to avoid them we specify which one we want by
316 feeding the compiler -funsigned-char.</p>
317
318 <p>The reason to pick "unsigned" is that way we're 8-bit clean by default.</p>
319
320 <p><h3>Error messages and internationalization:</h3></p>
321
322 <p>Error messages are extremely terse not just to save bytes, but because we
323 don't use any sort of _("string") translation infrastructure. (We're not
324 translating the command names themselves, so we must expect a minimum amount of
325 english knowledge from our users, but let's keep it to a minimum.)</p>
326
327 <p>Thus "bad -A '%c'" is
328 preferable to "Unrecognized address base '%c'", because a non-english speaker
329 can see that -A was the problem (giving back the command line argument they
330 supplied). A user with a ~20 word english vocabulary is
331 more likely to know (or guess) "bad" than the longer message, and you can
332 use "bad" in place of "invalid", "inappropriate", "unrecognized"...
333 Similarly when atolx_range() complains about range constraints with
334 "4 < 17" or "12 > 5", it's intentional: those don't need to be translated.</p>
335
336 <p>The strerror() messages produced by perror_exit() and friends should be
337 localized by libc, and our error functions also prepend the command name
338 (which non-english speakers can presumably recognize already). Keep the
339 explanation in between to a minimum, and where possible feed back the values
340 they passed in to identify _what_ we couldn't process.
341 If you say perror_exit("setsockopt"), you've identified the action you
342 were trying to take, and the perror gives a translated error message (from libc)
343 explaining _why_ it couldn't do it, so you probably don't need to add english
344 words like "failed" or "couldn't assign".</p>
345
346 <p>All commands should be 8-bit clean, with explicit
347 <a href=http://yarchive.net/comp/linux/utf8.html>UTF-8</a> support where
348 necessary. Assume all input data might be utf8, and at least preserve
349 it and pass it through. (For this reason, our build is -funsigned-char on
350 all architectures; "char" is unsigned unless you stick "signed" in front
351 of it.)</p>
352
353 <p>Locale support isn't currently a goal; that's a presentation layer issue
354 (I.E. a GUI problem).</p>
355
356 <p><h3>Shared Libraries</h3></p>
357
358 <p>Toybox's policy on shared libraries is that they should never be
359 required, but can optionally be used to improve performance.</p>
360
361 <p>Toybox should provide the command line utilities for
362 <a href=roadmap.html#dev_env>self-hosting development evirionments</a>,
363 and an easy way to set up "hermetic builds" (I.E. builds which provide
364 their own dependencies, isolating the build logic from host command version
365 skew with a simple known build environment). In both cases, external
366 dependencies defeat the purpose.</p>
367
368 <p>This means toybox should provide full functionality without relying
369 on any external dependencies (other than libc). But toybox may optionally use
370 libraries such as zlib and openssl to improve performance for things like
371 deflate and sha1sum, which lets the corresponding built-in implementations
372 be simple (and thus slow). But the built-in implementations need to exist and
373 work.</p>
374
375 <p>(This is why we use an external https wrapper program, because depending on
376 openssl or similar to be linked in would change the behavior of toybox.)</p>
377
378 <a name="codestyle" />
379 <h2>Coding style</h2>
380
381 <p>The real coding style holy wars are over things that don't matter
382 (whitespace, indentation, curly bracket placement...) and thus have no
383 obviously correct answer. As in academia, "the fighting is so vicious because
384 the stakes are so small". That said, being consistent makes the code readable,
385 so here's how to make toybox code look like other toybox code.</p>
386
387 <p>Toybox source uses two spaces per indentation level, and wraps at 80
388 columns. (Indentation of continuation lines is awkward no matter what
389 you do, sometimes two spaces looks better, sometimes indenting to the
390 contents of a parentheses looks better.)</p>
391
392 <p>I'm aware this indentation style creeps some people out, so here's
393 the sed invocation to convert groups of two leading spaces to tabs:</p>
394 <blockquote><pre>
395 sed -i ':loop;s/^\( *\)  /\1\t/;t loop' filename
396 </pre></blockquote>
397
398 <p>And here's the sed invocation to convert leading tabs to two spaces each:</p>
399 <blockquote><pre>
400 sed -i ':loop;s/^\( *\)\t/\1  /;t loop' filename
401 </pre></blockquote>
402
403 <p>There's a space after C flow control statements that look like functions, so
404 "if (blah)" instead of "if(blah)". (Note that sizeof is actually an
405 operator, so we don't give it a space for the same reason ++ doesn't get
406 one. Yeah, it doesn't need the parentheses either, but it gets them.
407 These rules are mostly to make the code look consistent, and thus easier
408 to read.) We also put a space around assignment operators (on both sides),
409 so "int x = 0;".</p>
410
411 <p>Blank lines (vertical whitespace) go between thoughts. "We were doing that,
412 now we're doing this." (Not a hard and fast rule about _where_ it goes,
413 but there should be some for the same reason writing has paragraph breaks.)</p>
414
415 <p>Variable declarations go at the start of blocks, with a blank line between
416 them and other code. Yes, c99 allows you to put them anywhere, but they're
417 harder to find if you do that. If there's a large enough distance between
418 the declaration and the code using it to make you uncomfortable, maybe the
419 function's too big, or is there an if statement or something you can
420 use as an excuse to start a new closer block?</p>
421
422 <p>If statments with a single line body go on the same line if the result
423 fits in 80 columns, on a second line if it doesn't. We usually only use
424 curly brackets if we need to, either because the body is multiple lines or
425 because we need to distinguish which if an else binds to. Curly brackets go
426 on the same line as the test/loop statement. The exception to both cases is
427 if the test part of an if statement is long enough to split into multiple
428 lines, then we put the curly bracket on its own line afterwards (so it doesn't
429 get lost in the multple line variably indented mess), and we put it there
430 even if it's only grouping one line (because the indentation level is not
431 providing clear information in that case).</p>
432
433 <p>I.E.</p>
434
435 <blockquote>
436 <pre>
437 if (thingy) thingy;
438 else thingy;
439
440 if (thingy) {
441   thingy;
442   thingy;
443 } else thingy;
444
445 if (blah blah blah...
446     && blah blah blah)
447 {
448   thingy;
449 }
450 </pre></blockquote>
451
452 <p>Gotos are allowed for error handling, and for breaking out of
453 nested loops. In general, a goto should only jump forward (not back), and
454 should either jump to the end of an outer loop, or to error handling code
455 at the end of the function. Goto labels are never indented: they override the
456 block structure of the file. Putting them at the left edge makes them easy
457 to spot as overrides to the normal flow of control, which they are.</p>
458
459 <p>When there's a shorter way to say something, we tend to do that for
460 consistency. For example, we tend to say "*blah" instead of "blah[0]" unless
461 we're referring to more than one element of blah. Similarly, NULL is
462 really just 0 (and C will automatically typecast 0 to anything, except in
463 varargs), "if (function() != NULL)" is the same as "if (function())",
464 "x = (blah == NULL);" is "x = !blah;", and so on.</p>
465
466 <p>The goal is to be
467 concise, not cryptic: if you're worried about the code being hard to
468 understand, splitting it to multiple steps on multiple lines is
469 better than a NOP operation like "!= NULL". A common sign of trying to
470 hard is nesting ? : three levels deep, sometimes if/else and a temporary
471 variable is just plain easier to read. If you think you need a comment,
472 you may be right.</p>
473
474 <p>Comments are nice, but don't overdo it. Comments should explain _why_,
475 not how. If the code doesn't make the how part obvious, that's a problem with
476 the code. Sometimes choosing a better variable name is more revealing than a
477 comment. Comments on their own line are better than comments on the end of
478 lines, and they usually have a blank line before them. Most of toybox's
479 comments are c99 style // single line comments, even when there's more than
480 one of them. The /* multiline */ style is used at the start for the metadata,
481 but not so much in the code itself. They don't nest cleanly, are easy to leave
482 accidentally unterminated, need extra nonfunctional * to look right, and if
483 you need _that_ much explanation maybe what you really need is a URL citation
484 linking to a standards document? Long comments can fall out of sync with what
485 the code is doing. Comments do not get regression tested. There's no such
486 thing as self-documenting code (if nothing else, code with _no_ comments
487 is a bit unfriendly to new readers), but "chocolate sauce isn't the answer
488 to bad cooking" either. Don't use comments as a crutch to explain unclear
489 code if the code can be fixed.</p>
490
491 <!--#include file="footer.html" -->