xref: /third_party/toybox/www/design.html (revision 0f66f451)
10f66f451Sopenharmony_ci<html><head><title>The design of toybox</title></head>
20f66f451Sopenharmony_ci<!--#include file="header.html" -->
30f66f451Sopenharmony_ci
40f66f451Sopenharmony_ci<a name="goals"><b><h2><a href="#goals">Design goals</a></h2></b>
50f66f451Sopenharmony_ci
60f66f451Sopenharmony_ci<p>Toybox should be simple, small, fast, and full featured. In that order.</p>
70f66f451Sopenharmony_ci
80f66f451Sopenharmony_ci<p>When these goals need to be balanced off against each other, keeping the code
90f66f451Sopenharmony_cias simple as it can be to do what it does is the most important (and hardest)
100f66f451Sopenharmony_cigoal. Then keeping it small is slightly more important than making it fast.
110f66f451Sopenharmony_ciFeatures are the reason we write code in the first place but this has all
120f66f451Sopenharmony_cibeen implemented before so if we can't do a better job why bother?</p>
130f66f451Sopenharmony_ci
140f66f451Sopenharmony_ci<p>It should be possible to get 80% of the way to each goal
150f66f451Sopenharmony_cibefore they really start to fight. Here they are in reverse order
160f66f451Sopenharmony_ciof importance:</p>
170f66f451Sopenharmony_ci
180f66f451Sopenharmony_ci<b><h3>Features</h3></b>
190f66f451Sopenharmony_ci
200f66f451Sopenharmony_ci<p>These days toybox is the command line of Android, so anything the android
210f66f451Sopenharmony_ciguys say to do gets at the very least closely listened to.</p>
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ci<p>Toybox should provide the command line utilities of a build
240f66f451Sopenharmony_cienvironment capable of recompiling itself under itself from source code.
250f66f451Sopenharmony_ciThis minimal build system conceptually consists of 4 parts: toybox,
260f66f451Sopenharmony_cia C library, a compiler, and a kernel. Toybox needs to provide all the
270f66f451Sopenharmony_cicommands (with all the behavior) necessary to run the configure/make/install
280f66f451Sopenharmony_ciof each package and boot the resulting system into a usable state.</p>
290f66f451Sopenharmony_ci
300f66f451Sopenharmony_ci<p>In addition, it should be possible to bootstrap up to arbitrary complexity
310f66f451Sopenharmony_ciunder the result by compiling and installing additional packages into this
320f66f451Sopenharmony_ciminimal system, as measured by building both Linux From Scratch and the
330f66f451Sopenharmony_ciAndroid Open Source Project under the result. Any "circular dependencies"
340f66f451Sopenharmony_cishould be solved by toybox including the missing dependencies itself
350f66f451Sopenharmony_ci(see "Shared Libraries" below).</p>
360f66f451Sopenharmony_ci
370f66f451Sopenharmony_ci<p>Finally, toybox may provide some "convenience" utilties
380f66f451Sopenharmony_cilike top and vi that aren't necessarily used in a build but which turn
390f66f451Sopenharmony_cithe minimal build environment into a minimal development environment
400f66f451Sopenharmony_ci(supporting edit/compile/test cycles in a text console), configure
410f66f451Sopenharmony_cinetwork infrastructure for communication with other systems (in a build
420f66f451Sopenharmony_cicluster), and so on.</p>
430f66f451Sopenharmony_ci
440f66f451Sopenharmony_ci<p>The hard part is deciding what NOT to include.
450f66f451Sopenharmony_ciA project without boundaries will bloat itself
460f66f451Sopenharmony_cito death. One of the hardest but most important things a project must
470f66f451Sopenharmony_cido is draw a line and say "no, this is somebody else's problem, not
480f66f451Sopenharmony_cisomething we should do."
490f66f451Sopenharmony_ciSome things are simply outside the scope of the project: even though
500f66f451Sopenharmony_ciposix defines commands for compiling and linking, we're not going to include
510f66f451Sopenharmony_cia compiler or linker (and support for a potentially infinite number of hardware
520f66f451Sopenharmony_citargets). And until somebody comes up with a ~30k ssh implementation (with
530f66f451Sopenharmony_cia crypto algorithm that won't need replacing every 5 years), we're
540f66f451Sopenharmony_cigoing to point you at dropbear or bearssl.</p>
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ci<p>The <a href=roadmap.html>roadmap</a> has the list of features we're
570f66f451Sopenharmony_citrying to implement, and the reasons why we decided to include those
580f66f451Sopenharmony_cifeatures. After the 1.0 release some of that material may get moved here,
590f66f451Sopenharmony_cibut for now it needs its own page. The <a href=status.html>status</a>
600f66f451Sopenharmony_cipage shows the project's progress against the roadmap.</p>
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ci<p>There are potential features (such as a screen/tmux implementation)
630f66f451Sopenharmony_cithat might be worth adding after 1.0, in part because they could share
640f66f451Sopenharmony_ciinfrastructure with things like "less" and "vi" so might be less work for
650f66f451Sopenharmony_cius to do than for an external from scratch implementation. But for now, major
660f66f451Sopenharmony_cinew features outside posix, android's existing commands, and the needs of
670f66f451Sopenharmony_cidevelopment systems, are a distraction from the 1.0 release.</p>
680f66f451Sopenharmony_ci
690f66f451Sopenharmony_ci<b><h3>Speed</h3></b>
700f66f451Sopenharmony_ci
710f66f451Sopenharmony_ci<p>It's easy to say lots about optimizing for speed (which is why this section
720f66f451Sopenharmony_ciis so long), but at the same time it's the optimization we care the least about.
730f66f451Sopenharmony_ciThe essence of speed is being as efficient as possible, which means doing as
740f66f451Sopenharmony_cilittle work as possible.  A design that's small and simple gets you 90% of the
750f66f451Sopenharmony_ciway there, and most of the rest is either fine-tuning or more trouble than
760f66f451Sopenharmony_ciit's worth (and often actually counterproductive).  Still, here's some
770f66f451Sopenharmony_ciadvice:</p>
780f66f451Sopenharmony_ci
790f66f451Sopenharmony_ci<p>First, understand the darn problem you're trying to solve.  You'd think
800f66f451Sopenharmony_ciI wouldn't have to say this, but I do.  Trying to find a faster sorting
810f66f451Sopenharmony_cialgorithm is no substitute for figuring out a way to skip the sorting step
820f66f451Sopenharmony_cientirely.  The fastest way to do anything is not to have to do it at all,
830f66f451Sopenharmony_ciand _all_ optimization boils down to avoiding unnecessary work.</p>
840f66f451Sopenharmony_ci
850f66f451Sopenharmony_ci<p>Speed is easy to measure; there are dozens of profiling tools for Linux
860f66f451Sopenharmony_ci(although personally I find the "time" command a good starting place).
870f66f451Sopenharmony_ciDon't waste too much time trying to optimize something you can't measure,
880f66f451Sopenharmony_ciand there's no much point speeding up things you don't spend much time doing
890f66f451Sopenharmony_cianyway.</p>
900f66f451Sopenharmony_ci
910f66f451Sopenharmony_ci<p>Understand the difference between throughput and latency.  Faster
920f66f451Sopenharmony_ciprocessors improve throughput, but don't always do much for latency.
930f66f451Sopenharmony_ciAfter 30 years of Moore's Law, most of the remaining problems are latency,
940f66f451Sopenharmony_cinot throughput.  (There are of course a few exceptions, like data compression
950f66f451Sopenharmony_cicode, encryption, rsync...)  Worry about throughput inside long-running
960f66f451Sopenharmony_ciloops, and worry about latency everywhere else.  (And don't worry too much
970f66f451Sopenharmony_ciabout avoiding system calls or function calls or anything else in the name
980f66f451Sopenharmony_ciof speed unless you are in the middle of a tight loop that's you've already
990f66f451Sopenharmony_ciproven isn't running fast enough.)</p>
1000f66f451Sopenharmony_ci
1010f66f451Sopenharmony_ci<p>"Locality of reference" is generally nice, in all sorts of contexts.
1020f66f451Sopenharmony_ciIt's obvious that waiting for disk access is 1000x slower than doing stuff in
1030f66f451Sopenharmony_ciRAM (and making the disk seek is 10x slower than sequential reads/writes),
1040f66f451Sopenharmony_cibut it's just as true that a loop which stays in L1 cache is many times faster
1050f66f451Sopenharmony_cithan a loop that has to wait for a DRAM fetch on each iteration.  Don't worry
1060f66f451Sopenharmony_ciabout whether "&" is faster than "%" until your executable loop stays in L1
1070f66f451Sopenharmony_cicache and the data access is fetching cache lines intelligently.  (To
1080f66f451Sopenharmony_ciunderstand DRAM, L1, and L2 cache, read Hannibal's marvelous ram guide at Ars
1090f66f451Sopenharmony_ciTechnica:
1100f66f451Sopenharmony_ci<a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part1-2.html>part one</a>,
1110f66f451Sopenharmony_ci<a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part2-1.html>part two</a>,
1120f66f451Sopenharmony_ci<a href=http://arstechnica.com/paedia/r/ram_guide/ram_guide.part3-1.html>part three</a>,
1130f66f451Sopenharmony_ciplus this
1140f66f451Sopenharmony_ci<a href=http://arstechnica.com/articles/paedia/cpu/caching.ars/1>article on
1150f66f451Sopenharmony_cicacheing</a>, and this one on
1160f66f451Sopenharmony_ci<a href=http://arstechnica.com/articles/paedia/cpu/bandwidth-latency.ars>bandwidth
1170f66f451Sopenharmony_ciand latency</a>.
1180f66f451Sopenharmony_ciAnd there's <a href=http://arstechnica.com/paedia/index.html>more where that came from</a>.)
1190f66f451Sopenharmony_ciRunning out of L1 cache can execute one instruction per clock cycle, going
1200f66f451Sopenharmony_cito L2 cache costs a dozen or so clock cycles, and waiting for a worst case dram
1210f66f451Sopenharmony_cifetch (round trip latency with a bank switch) can cost thousands of
1220f66f451Sopenharmony_ciclock cycles.  (Historically, this disparity has gotten worse with time,
1230f66f451Sopenharmony_cijust like the speed hit for swapping to disk.  These days, a _big_ L1 cache
1240f66f451Sopenharmony_ciis 128k and a big L2 cache is a couple of megabytes.  A cheap low-power
1250f66f451Sopenharmony_ciembedded processor may have 8k of L1 cache and no L2.)</p>
1260f66f451Sopenharmony_ci
1270f66f451Sopenharmony_ci<p>Learn how <a href=http://nommu.org/memory-faq.txt>virtual memory and
1280f66f451Sopenharmony_cimemory managment units work</a>.  Don't touch
1290f66f451Sopenharmony_cimemory you don't have to.  Even just reading memory evicts stuff from L1 and L2
1300f66f451Sopenharmony_cicache, which may have to be read back in later.  Writing memory can force the
1310f66f451Sopenharmony_cioperating system to break copy-on-write, which allocates more memory.  (The
1320f66f451Sopenharmony_cimemory returned by malloc() is only a virtual allocation, filled with lots of
1330f66f451Sopenharmony_cicopy-on-write mappings of the zero page.  Actual physical pages get allocated
1340f66f451Sopenharmony_ciwhen the copy-on-write gets broken by writing to the virtual page.  This
1350f66f451Sopenharmony_ciis why checking the return value of malloc() isn't very useful anymore, it
1360f66f451Sopenharmony_cionly detects running out of virtual memory, not physical memory.  Unless
1370f66f451Sopenharmony_ciyou're using a <a href=http://nommu.org>NOMMU system</a>, where all bets
1380f66f451Sopenharmony_ciare off.)</p>
1390f66f451Sopenharmony_ci
1400f66f451Sopenharmony_ci<p>Don't think that just because you don't have a swap file the system can't
1410f66f451Sopenharmony_cistart swap thrashing: any file backed page (ala mmap) can be evicted, and
1420f66f451Sopenharmony_cithere's a reason all running programs require an executable file (they're
1430f66f451Sopenharmony_cimmaped, and can be flushed back to disk when memory is short).  And long
1440f66f451Sopenharmony_cibefore that, disk cache gets reclaimed and has to be read back in.  When the
1450f66f451Sopenharmony_cioperating system really can't free up any more pages it triggers the out of
1460f66f451Sopenharmony_cimemory killer to free up pages by killing processes (the alternative is the
1470f66f451Sopenharmony_cientire OS freezing solid).  Modern operating systems seldom run out of
1480f66f451Sopenharmony_cimemory gracefully.</p>
1490f66f451Sopenharmony_ci
1500f66f451Sopenharmony_ci<p>Also, it's better to be simple than clever.  Many people think that mmap()
1510f66f451Sopenharmony_ciis faster than read() because it avoids a copy, but twiddling with the memory
1520f66f451Sopenharmony_cimanagement is itself slow, and can cause unnecessary CPU cache flushes.  And
1530f66f451Sopenharmony_ciif a read faults in dozens of pages sequentially, but your mmap iterates
1540f66f451Sopenharmony_cibackwards through a file (causing lots of seeks, each of which your program
1550f66f451Sopenharmony_ciblocks waiting for), the read can be many times faster.  On the other hand, the
1560f66f451Sopenharmony_cimmap can sometimes use less memory, since the memory provided by mmap
1570f66f451Sopenharmony_cicomes from the page cache (allocated anyway), and it can be faster if you're
1580f66f451Sopenharmony_cidoing a lot of different updates to the same area.  The moral?  Measure, then
1590f66f451Sopenharmony_citry to speed things up, and measure again to confirm it actually _did_ speed
1600f66f451Sopenharmony_cithings up rather than made them worse.  (And understanding what's really going
1610f66f451Sopenharmony_cion underneath is a big help to making it happen faster.)</p>
1620f66f451Sopenharmony_ci
1630f66f451Sopenharmony_ci<p>In general, being simple is better than being clever.  Optimization
1640f66f451Sopenharmony_cistrategies change with time.  For example, decades ago precalculating a table
1650f66f451Sopenharmony_ciof results (for things like isdigit() or cosine(int degrees)) was clearly
1660f66f451Sopenharmony_cifaster because processors were so slow.  Then processors got faster and grew
1670f66f451Sopenharmony_cimath coprocessors, and calculating the value each time became faster than
1680f66f451Sopenharmony_cithe table lookup (because the calculation fit in L1 cache but the lookup
1690f66f451Sopenharmony_cihad to go out to DRAM).  Then cache sizes got bigger (the Pentium M has
1700f66f451Sopenharmony_ci2 megabytes of L2 cache) and the table fit in cache, so the table became
1710f66f451Sopenharmony_cifast again...  Predicting how changes in hardware will affect your algorithm
1720f66f451Sopenharmony_ciis difficult, and using ten year old optimization advice and produce
1730f66f451Sopenharmony_cilaughably bad results.  But being simple and efficient is always going to
1740f66f451Sopenharmony_cigive at least a reasonable result.</p>
1750f66f451Sopenharmony_ci
1760f66f451Sopenharmony_ci<p>The famous quote from Ken Thompson, "When in doubt, use brute force",
1770f66f451Sopenharmony_ciapplies to toybox.  Do the simple thing first, do as little of it as possible,
1780f66f451Sopenharmony_ciand make sure it's right.  You can always speed it up later.</p>
1790f66f451Sopenharmony_ci
1800f66f451Sopenharmony_ci<b><h3>Size</h3></b>
1810f66f451Sopenharmony_ci<p>Again, being simple gives you most of this. An algorithm that does less work
1820f66f451Sopenharmony_ciis generally smaller.  Understand the problem, treat size as a cost, and
1830f66f451Sopenharmony_ciget a good bang for the byte.</p>
1840f66f451Sopenharmony_ci
1850f66f451Sopenharmony_ci<p>Understand the difference between binary size, heap size, and stack size.
1860f66f451Sopenharmony_ciYour binary is the executable file on disk, your heap is where malloc() memory
1870f66f451Sopenharmony_cilives, and your stack is where local variables (and function call return
1880f66f451Sopenharmony_ciaddresses) live.  Optimizing for binary size is generally good: executing
1890f66f451Sopenharmony_cifewer instructions makes your program run faster (and fits more of it in
1900f66f451Sopenharmony_cicache).  On embedded systems, binary size is especially precious because
1910f66f451Sopenharmony_ciflash is expensive (and its successor, MRAM, even more so).  Small stack size
1920f66f451Sopenharmony_ciis important for nommu systems because they have to preallocate their stack
1930f66f451Sopenharmony_ciand can't make it bigger via page fault.  And everybody likes a small heap.</p>
1940f66f451Sopenharmony_ci
1950f66f451Sopenharmony_ci<p>Measure the right things.  Especially with modern optimizers, expecting
1960f66f451Sopenharmony_cisomething to be smaller is no guarantee it will be after the compiler's done
1970f66f451Sopenharmony_ciwith it.  Binary size isn't the most accurate indicator of the impact of a
1980f66f451Sopenharmony_cigiven change, because lots of things get combined and rounded during
1990f66f451Sopenharmony_cicompilation and linking.  Matt Mackall's bloat-o-meter is a python script
2000f66f451Sopenharmony_ciwhich compares two versions of a program, and shows size changes in each
2010f66f451Sopenharmony_cisymbol (using the "nm" command behind the scenes).  To use this, run
2020f66f451Sopenharmony_ci"make baseline" to build a baseline version to compare against, and
2030f66f451Sopenharmony_cithen "make bloatometer" to compare that baseline version against the current
2040f66f451Sopenharmony_cicode.</p>
2050f66f451Sopenharmony_ci
2060f66f451Sopenharmony_ci<p>Avoid special cases.  Whenever you see similar chunks of code in more than
2070f66f451Sopenharmony_cione place, it might be possible to combine them and have the users call shared
2080f66f451Sopenharmony_cicode. (This is the most commonly cited trick, which doesn't make it easy. If
2090f66f451Sopenharmony_ciseeing two lines of code do the same thing makes you slightly uncomfortable,
2100f66f451Sopenharmony_ciyou've got the right mindset.)</p>
2110f66f451Sopenharmony_ci
2120f66f451Sopenharmony_ci<p>Some specific advice: Using a char in place of an int when doing math
2130f66f451Sopenharmony_ciproduces significantly larger code on some platforms (notably arm),
2140f66f451Sopenharmony_cibecause each time the compiler has to emit code to convert it to int, do the
2150f66f451Sopenharmony_cimath, and convert it back.  Bitfields have this problem on most platforms.
2160f66f451Sopenharmony_ciBecause of this, using char to index a for() loop is probably not a net win,
2170f66f451Sopenharmony_cialthough using char (or a bitfield) to store a value in a structure that's
2180f66f451Sopenharmony_cirepeated hundreds of times can be a good tradeoff of binary size for heap
2190f66f451Sopenharmony_cispace.</p>
2200f66f451Sopenharmony_ci
2210f66f451Sopenharmony_ci<b><h3>Simplicity</h3></b>
2220f66f451Sopenharmony_ci
2230f66f451Sopenharmony_ci<p>Complexity is a cost, just like code size or runtime speed. Treat it as
2240f66f451Sopenharmony_cia cost, and spend your complexity budget wisely. (Sometimes this means you
2250f66f451Sopenharmony_cican't afford a feature because it complicates the code too much to be
2260f66f451Sopenharmony_ciworth it.)</p>
2270f66f451Sopenharmony_ci
2280f66f451Sopenharmony_ci<p>Simplicity has lots of benefits.  Simple code is easy to maintain, easy to
2290f66f451Sopenharmony_ciport to new processors, easy to audit for security holes, and easy to
2300f66f451Sopenharmony_ciunderstand.</p>
2310f66f451Sopenharmony_ci
2320f66f451Sopenharmony_ci<p>Simplicity itself can have subtle non-obvious aspects requiring a tradeoff
2330f66f451Sopenharmony_cibetween one kind of simplicity and another: simple for the computer to
2340f66f451Sopenharmony_ciexecute and simple for a human reader to understand aren't always the
2350f66f451Sopenharmony_cisame thing. A compact and clever algorithm that does very little work may
2360f66f451Sopenharmony_cinot be as easy to explain or understand as a larger more explicit version
2370f66f451Sopenharmony_cirequiring more code, memory, and CPU time. When balancing these, err on the
2380f66f451Sopenharmony_ciside of doing less work, but add comments describing how you
2390f66f451Sopenharmony_cicould be more explicit.</p>
2400f66f451Sopenharmony_ci
2410f66f451Sopenharmony_ci<p>In general, comments are not a substitute for good code (or well chosen
2420f66f451Sopenharmony_civariable or function names). Commenting "x += y;" with "/* add y to x */"
2430f66f451Sopenharmony_cican actually detract from the program's readability. If you need to describe
2440f66f451Sopenharmony_ciwhat the code is doing (rather than _why_ it's doing it), that means the
2450f66f451Sopenharmony_cicode itself isn't very clear.</p>
2460f66f451Sopenharmony_ci
2470f66f451Sopenharmony_ci<p>Environmental dependencies are another type of complexity, so needing other
2480f66f451Sopenharmony_cipackages to build or run is a big downside. For example, we don't use curses
2490f66f451Sopenharmony_ciwhen we can simply output ansi escape sequences and trust all terminal
2500f66f451Sopenharmony_ciprograms written in the past 30 years to be able to support them. Regularly
2510f66f451Sopenharmony_citesting that we work with C libraries which support static linking (musl does,
2520f66f451Sopenharmony_ciglibc doesn't) is another way to be self-contained with known boundaries:
2530f66f451Sopenharmony_ciit doesn't have to be the only way to build the project, but should be regularly
2540f66f451Sopenharmony_citested and supported.</p>
2550f66f451Sopenharmony_ci
2560f66f451Sopenharmony_ci<p>Prioritizing simplicity tends to serve our other goals: simplifying code
2570f66f451Sopenharmony_cigenerally reduces its size (both in terms of binary size and runtime memory
2580f66f451Sopenharmony_ciusage), and avoiding unnecessary work makes code run faster. Smaller code
2590f66f451Sopenharmony_cialso tends to run faster on modern hardware due to CPU cacheing: fitting your
2600f66f451Sopenharmony_cicode into L1 cache is great, and staying in L2 cache is still pretty good.</p>
2610f66f451Sopenharmony_ci
2620f66f451Sopenharmony_ci<p>But a simple implementation is not always the smallest or fastest, and
2630f66f451Sopenharmony_cibalancing simplicity vs the other goals can be difficult. For example, the
2640f66f451Sopenharmony_ciatolx_range() function in lib/lib.c always uses the 64 bit "long long" type,
2650f66f451Sopenharmony_ciwhich produces larger and slower code on 32 bit platforms and
2660f66f451Sopenharmony_cioften assigned into smaller interger types. Although libc has parallel
2670f66f451Sopenharmony_ciimplementations for different data sizes (atoi, atol, atoll) we chose a
2680f66f451Sopenharmony_cicommon codepath which can cover all cases (every user goes through the
2690f66f451Sopenharmony_cisame codepath, with the maximum amount of testing and minimum and avoids
2700f66f451Sopenharmony_cisurprising variations in behavior).</p>
2710f66f451Sopenharmony_ci
2720f66f451Sopenharmony_ci<p>On the other hand, the "tail" command has two codepaths, one for seekable
2730f66f451Sopenharmony_cifiles and one for nonseekable files. Although the nonseekable case can handle
2740f66f451Sopenharmony_ciall inputs (and is required when input comes from a pipe or similar, so cannot
2750f66f451Sopenharmony_cibe removed), reading through multiple gigabytes of data to reach the end of
2760f66f451Sopenharmony_ciseekable files was both a common case and hugely penalized by a nonseekable
2770f66f451Sopenharmony_ciapproach (half-minute wait vs instant results). This is one example
2780f66f451Sopenharmony_ciwhere performance did outweigh simplicity of implementation.</p>
2790f66f451Sopenharmony_ci
2800f66f451Sopenharmony_ci<p><a href=http://www.joelonsoftware.com/articles/fog0000000069.html>Joel
2810f66f451Sopenharmony_ciSpolsky argues against throwing code out and starting over</a>, and he has
2820f66f451Sopenharmony_cigood points: an existing debugged codebase contains a huge amount of baked
2830f66f451Sopenharmony_ciin knowledge about strange real-world use cases that the designers didn't
2840f66f451Sopenharmony_ciknow about until users hit the bugs, and most of this knowledge is never
2850f66f451Sopenharmony_ciexplicitly stated anywhere except in the source code.</p>
2860f66f451Sopenharmony_ci
2870f66f451Sopenharmony_ci<p>That said, the Mythical Man-Month's "build one to throw away" advice points
2880f66f451Sopenharmony_ciout that until you've solved the problem you don't properly understand it, and
2890f66f451Sopenharmony_ciabout the time you finish your first version is when you've finally figured
2900f66f451Sopenharmony_ciout what you _should_ have done.  (The corrolary is that if you build one
2910f66f451Sopenharmony_ciexpecting to throw it away, you'll actually wind up throwing away two.  You
2920f66f451Sopenharmony_cidon't understand the problem until you _have_ solved it.)</p>
2930f66f451Sopenharmony_ci
2940f66f451Sopenharmony_ci<p>Joel is talking about what closed source software can afford to do: Code
2950f66f451Sopenharmony_cithat works and has been paid for is a corporate asset not lightly abandoned.
2960f66f451Sopenharmony_ciOpen source software can afford to re-implement code that works, over and
2970f66f451Sopenharmony_ciover from scratch, for incremental gains.  Before toybox, the unix command line
2980f66f451Sopenharmony_cihas already been reimplemented from scratch several times (the
2990f66f451Sopenharmony_cioriginal AT&amp;T Unix command line in assembly and then in C, the BSD
3000f66f451Sopenharmony_civersions, Coherent was the first full from-scratch Unix clone in 1980,
3010f66f451Sopenharmony_ciMinix was another clone which Linux was inspired by and developed under,
3020f66f451Sopenharmony_cithe GNU tools were yet another rewrite intended for use in the stillborn
3030f66f451Sopenharmony_ci"Hurd" project, BusyBox was still another rewrite, and more versions
3040f66f451Sopenharmony_ciwere written in Plan 9, uclinux, klibc, sash, sbase, s6, and of course
3050f66f451Sopenharmony_ciandroid toolbox...). But maybe toybox can do a better job. :)</p>
3060f66f451Sopenharmony_ci
3070f66f451Sopenharmony_ci<p>As Antoine de St. Exupery (author of "The Little Prince" and an early
3080f66f451Sopenharmony_ciaircraft designer) said, "Perfection is achieved, not when there
3090f66f451Sopenharmony_ciis nothing left to add, but when there is nothing left to take away."
3100f66f451Sopenharmony_ciAnd Ken Thompson (creator of Unix) said "One of my most productive
3110f66f451Sopenharmony_cidays was throwing away 1000 lines of code." It's always possible to
3120f66f451Sopenharmony_cicome up with a better way to do it.</p>
3130f66f451Sopenharmony_ci
3140f66f451Sopenharmony_ci<p>P.S. How could I resist linking to an article about
3150f66f451Sopenharmony_ci<a href=http://blog.outer-court.com/archive/2005-08-24-n14.html>why
3160f66f451Sopenharmony_ciprogrammers should strive to be lazy and dumb</a>?</p>
3170f66f451Sopenharmony_ci
3180f66f451Sopenharmony_ci<a name="portability"><b><h2><a href="#portability">Portability issues</a></h2></b>
3190f66f451Sopenharmony_ci
3200f66f451Sopenharmony_ci<b><h3>Platforms</h3></b>
3210f66f451Sopenharmony_ci<p>Toybox should run on Android (all commands with musl-libc, as large a subset
3220f66f451Sopenharmony_cias practical with bionic), and every other hardware platform Linux runs on.
3230f66f451Sopenharmony_ciOther posix/susv4 environments (perhaps MacOS X or newlib+libgloss) are vaguely
3240f66f451Sopenharmony_ciinteresting but only if they're easy to support; I'm not going to spend much
3250f66f451Sopenharmony_cieffort on them.</p>
3260f66f451Sopenharmony_ci
3270f66f451Sopenharmony_ci<p>I don't do windows.</p>
3280f66f451Sopenharmony_ci
3290f66f451Sopenharmony_ci<p>We depend on C99 and posix-2008 libc features such as the openat() family of
3300f66f451Sopenharmony_cifunctions. We also root around in the linux /proc directory a lot (no other
3310f66f451Sopenharmony_ciway to implement "ps" at the moment), and assume certain "modern" linux kernel
3320f66f451Sopenharmony_cibehavior such as large environment sizes (<a href=https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b6a2fea39318>linux commit b6a2fea39318</a>, went into 2.6.22
3330f66f451Sopenharmony_cireleased <a href=faq.html#support_horizon>July 2007</a>, expanding the 128k
3340f66f451Sopenharmony_cilimit to 2 gigabytes. But it was then
3350f66f451Sopenharmony_citrimmed back down to 10 megabytes, and when I asked for a way to query the
3360f66f451Sopenharmony_ciactual value from the kernel if it was going to keep changing
3370f66f451Sopenharmony_cilike that, <a href=https://lkml.org/lkml/2017/11/5/204>Linus declined</a>).
3380f66f451Sopenharmony_ciIn theory this shouldn't prevent us from working on
3390f66f451Sopenharmony_ciolder kernels or other implementations (ala BSD), but we don't police their
3400f66f451Sopenharmony_cicorner cases.</p>
3410f66f451Sopenharmony_ci
3420f66f451Sopenharmony_ci<a name="bits" />
3430f66f451Sopenharmony_ci<b><h3>32/64 bit</h3></b>
3440f66f451Sopenharmony_ci<p>Toybox should work on both 32 bit and 64 bit systems. 64 bit desktop
3450f66f451Sopenharmony_cihardware went mainstream in <a href=https://web.archive.org/web/20040307000108mp_/http://developer.intel.com/technology/64bitextensions/faq.htm>in 2005</a>
3460f66f451Sopenharmony_ciand was essentially ubiquitous <a href=faq.html#support_horizon>by 2012</a>,
3470f66f451Sopenharmony_cibut 32 bit hardware will continue to be important in embedded devices for years to come.</p>
3480f66f451Sopenharmony_ci
3490f66f451Sopenharmony_ci<p>Toybox relies on the
3500f66f451Sopenharmony_ci<a href=http://archive.opengroup.org/public/tech/aspen/lp64_wp.htm>LP64 standard</a>
3510f66f451Sopenharmony_ciwhich Linux, MacOS X, and BSD all implement, and which modern 64 bit processors such as
3520f66f451Sopenharmony_cix86-64 were <a href=http://www.pagetable.com/?p=6>explicitly designed to
3530f66f451Sopenharmony_cisupport</a>. (Here's the original <a href=https://web.archive.org/web/20020905181545/http://www.unix.org/whitepapers/64bit.html>LP64 white paper</a>.)</p>
3540f66f451Sopenharmony_ci
3550f66f451Sopenharmony_ci<p>LP64 defines explicit sizes for all the basic C integer types, and
3560f66f451Sopenharmony_ciguarantees that on any Unix-like platform "long" and "pointer" types
3570f66f451Sopenharmony_ciare always the same size. This means it's safe to assign pointers into
3580f66f451Sopenharmony_cilongs and vice versa without losing data: on 32 bit systems both are 32 bit,
3590f66f451Sopenharmony_cion 64 bit systems both are 64 bit.</p>
3600f66f451Sopenharmony_ci
3610f66f451Sopenharmony_ci<table border=1 cellpadding=10 cellspacing=2>
3620f66f451Sopenharmony_ci<tr><td>C type</td><td>32 bit<br />sizeof</td><td>64 bit<br />sizeof</td></tr>
3630f66f451Sopenharmony_ci<tr><td>char</td><td>1 byte</td><td>1 byte</td></tr>
3640f66f451Sopenharmony_ci<tr><td>short</td><td>2 bytes</td><td>2 bytes</td></tr>
3650f66f451Sopenharmony_ci<tr><td>int</td><td>4 bytes</td><td>4 bytes</td></tr>
3660f66f451Sopenharmony_ci<tr><td>long</td><td>4 bytes</td><td>8 bytes</td></tr>
3670f66f451Sopenharmony_ci<tr><td>long long</td><td>8 bytes</td><td>8 bytes</td></tr>
3680f66f451Sopenharmony_ci</table>
3690f66f451Sopenharmony_ci
3700f66f451Sopenharmony_ci<p>LP64 eliminates the need to use c99 "uint32_t" and friends: the basic
3710f66f451Sopenharmony_ciC types all have known size/behavior, and the only type whose
3720f66f451Sopenharmony_cisize varies is "long", which is the natural register size of the processor.</p>
3730f66f451Sopenharmony_ci
3740f66f451Sopenharmony_ci<p>Note that Windows doesn't work like this, and I don't care, but if you're
3750f66f451Sopenharmony_cicurious here are <a href=https://devblogs.microsoft.com/oldnewthing/20050131-00/?p=36563>the insane legacy reasons why this is broken on Windows</a>.</a></p>
3760f66f451Sopenharmony_ci
3770f66f451Sopenharmony_ci<b><h3>Signedness of char</h3></b>
3780f66f451Sopenharmony_ci<p>On platforms like x86, variables of type char default to unsigned.  On
3790f66f451Sopenharmony_ciplatforms like arm, char defaults to signed.  This difference can lead to
3800f66f451Sopenharmony_cisubtle portability bugs, and to avoid them we specify which one we want by
3810f66f451Sopenharmony_cifeeding the compiler -funsigned-char.</p>
3820f66f451Sopenharmony_ci
3830f66f451Sopenharmony_ci<p>The reason to pick "unsigned" is that way char strings are 8-bit clean by
3840f66f451Sopenharmony_cidefault, which makes UTF-8 support easier.</p>
3850f66f451Sopenharmony_ci
3860f66f451Sopenharmony_ci<p><h3>Error messages and internationalization:</h3></p>
3870f66f451Sopenharmony_ci
3880f66f451Sopenharmony_ci<p>Error messages are extremely terse not just to save bytes, but because we
3890f66f451Sopenharmony_cidon't use any sort of _("string") translation infrastructure. (We're not
3900f66f451Sopenharmony_citranslating the command names themselves, so we must expect a minimum amount of
3910f66f451Sopenharmony_cienglish knowledge from our users, but let's keep it to a minimum.)</p>
3920f66f451Sopenharmony_ci
3930f66f451Sopenharmony_ci<p>Thus "bad -A '%c'" is
3940f66f451Sopenharmony_cipreferable to "Unrecognized address base '%c'", because a non-english speaker
3950f66f451Sopenharmony_cican see that -A was the problem (giving back the command line argument they
3960f66f451Sopenharmony_cisupplied). A user with a ~20 word english vocabulary is
3970f66f451Sopenharmony_cimore likely to know (or guess) "bad" than the longer message, and you can
3980f66f451Sopenharmony_ciuse "bad" in place of "invalid", "inappropriate", "unrecognized"...
3990f66f451Sopenharmony_ciSimilarly when atolx_range() complains about range constraints with
4000f66f451Sopenharmony_ci"4 < 17" or "12 > 5", it's intentional: those don't need to be translated.</p>
4010f66f451Sopenharmony_ci
4020f66f451Sopenharmony_ci<p>The strerror() messages produced by perror_exit() and friends should be
4030f66f451Sopenharmony_cilocalized by libc, and our error functions also prepend the command name
4040f66f451Sopenharmony_ci(which non-english speakers can presumably recognize already). Keep the
4050f66f451Sopenharmony_ciexplanation in between to a minimum, and where possible feed back the values
4060f66f451Sopenharmony_cithey passed in to identify _what_ we couldn't process.
4070f66f451Sopenharmony_ciIf you say perror_exit("setsockopt"), you've identified the action you
4080f66f451Sopenharmony_ciwere trying to take, and the perror gives a translated error message (from libc)
4090f66f451Sopenharmony_ciexplaining _why_ it couldn't do it, so you probably don't need to add english
4100f66f451Sopenharmony_ciwords like "failed" or "couldn't assign".</p>
4110f66f451Sopenharmony_ci
4120f66f451Sopenharmony_ci<p>All commands should be 8-bit clean, with explicit
4130f66f451Sopenharmony_ci<a href=http://yarchive.net/comp/linux/utf8.html>UTF-8</a> support where
4140f66f451Sopenharmony_cinecessary. Assume all input data might be utf8, and at least preserve
4150f66f451Sopenharmony_ciit and pass it through. (For this reason, our build is -funsigned-char on
4160f66f451Sopenharmony_ciall architectures; "char" is unsigned unless you stick "signed" in front
4170f66f451Sopenharmony_ciof it.)</p>
4180f66f451Sopenharmony_ci
4190f66f451Sopenharmony_ci<p>Locale support isn't currently a goal; that's a presentation layer issue
4200f66f451Sopenharmony_ci(I.E. a GUI problem).</p>
4210f66f451Sopenharmony_ci
4220f66f451Sopenharmony_ci<p>Someday we should probably have translated --help text, but that's a
4230f66f451Sopenharmony_cipost-1.0 issue.</p>
4240f66f451Sopenharmony_ci
4250f66f451Sopenharmony_ci<p><h3>Shared Libraries</h3></p>
4260f66f451Sopenharmony_ci
4270f66f451Sopenharmony_ci<p>Toybox's policy on shared libraries is that they should never be
4280f66f451Sopenharmony_cirequired, but can optionally be used to improve performance.</p>
4290f66f451Sopenharmony_ci
4300f66f451Sopenharmony_ci<p>Toybox should provide the command line utilities for
4310f66f451Sopenharmony_ci<a href=roadmap.html#dev_env>self-hosting development envirionments</a>,
4320f66f451Sopenharmony_ciand an easy way to set up "hermetic builds" (I.E. builds which provide
4330f66f451Sopenharmony_citheir own dependencies, isolating the build logic from host command version
4340f66f451Sopenharmony_ciskew with a simple known build environment). In both cases, external
4350f66f451Sopenharmony_cidependencies defeat the purpose.</p>
4360f66f451Sopenharmony_ci
4370f66f451Sopenharmony_ci<p>This means toybox should provide full functionality without relying
4380f66f451Sopenharmony_cion any external dependencies (other than libc). But toybox may optionally use
4390f66f451Sopenharmony_cilibraries such as zlib and openssl to improve performance for things like
4400f66f451Sopenharmony_cideflate and sha1sum, which lets the corresponding built-in implementations
4410f66f451Sopenharmony_cibe simple (and thus slow). But the built-in implementations need to exist and
4420f66f451Sopenharmony_ciwork.</p>
4430f66f451Sopenharmony_ci
4440f66f451Sopenharmony_ci<p>(This is why we use an external https wrapper program, because depending on
4450f66f451Sopenharmony_ciopenssl or similar to be linked in would change the behavior of toybox.)</p>
4460f66f451Sopenharmony_ci
4470f66f451Sopenharmony_ci<a name="license" />
4480f66f451Sopenharmony_ci<h2>License</h2>
4490f66f451Sopenharmony_ci
4500f66f451Sopenharmony_ci<p>Toybox is licensed <a href=license.html>0BSD</a>, which is a public domain
4510f66f451Sopenharmony_ciequivalent license approved by <a href=https://spdx.org/licenses/0BSD.html>SPDX</a>. This works like other BSD licenses except that it doesn't
4520f66f451Sopenharmony_cirequire copying specific license text into the resulting project when
4530f66f451Sopenharmony_ciyou copy code. (We care about attribution, not ownership, and the internet's
4540f66f451Sopenharmony_cireally good at pointing out plagiarism.)</p>
4550f66f451Sopenharmony_ci
4560f66f451Sopenharmony_ci<p>This means toybox usually can't use external code contributions, and must
4570f66f451Sopenharmony_ciimplement new versions of everything unless the external code's original
4580f66f451Sopenharmony_ciauthor (and any additional contributors) grants permission to relicense.
4590f66f451Sopenharmony_ciJust as a GPLv2 project can't incorporate GPLv3 code and a BSD-licensed
4600f66f451Sopenharmony_ciproject can't incorporate either kind of GPL code, we can't incorporate
4610f66f451Sopenharmony_cimost BSD or Apache licensed code without changing our license terms.</p>
4620f66f451Sopenharmony_ci
4630f66f451Sopenharmony_ci<p>The exception to this is code under an existing public domain equivalent
4640f66f451Sopenharmony_cilicense, such as the xz decompressor or
4650f66f451Sopenharmony_ci<a href=https://github.com/mkj/dropbear/blob/master/libtommath/LICENSE>libtommath</a> and <a href=https://github.com/mkj/dropbear/blob/master/libtomcrypt/LICENSE>libtomcrypt</a>.</p>
4660f66f451Sopenharmony_ci
4670f66f451Sopenharmony_ci<a name="codestyle" />
4680f66f451Sopenharmony_ci<h2>Coding style</h2>
4690f66f451Sopenharmony_ci
4700f66f451Sopenharmony_ci<p>The real coding style holy wars are over things that don't matter
4710f66f451Sopenharmony_ci(whitespace, indentation, curly bracket placement...) and thus have no
4720f66f451Sopenharmony_ciobviously correct answer. As in academia, "the fighting is so vicious because
4730f66f451Sopenharmony_cithe stakes are so small". That said, being consistent makes the code readable,
4740f66f451Sopenharmony_ciso here's how to make toybox code look like other toybox code.</p>
4750f66f451Sopenharmony_ci
4760f66f451Sopenharmony_ci<p>Toybox source uses two spaces per indentation level, and wraps at 80
4770f66f451Sopenharmony_cicolumns. (Indentation of continuation lines is awkward no matter what
4780f66f451Sopenharmony_ciyou do, sometimes two spaces looks better, sometimes indenting to the
4790f66f451Sopenharmony_cicontents of a parentheses looks better.)</p>
4800f66f451Sopenharmony_ci
4810f66f451Sopenharmony_ci<p>I'm aware this indentation style creeps some people out, so here's
4820f66f451Sopenharmony_cithe sed invocation to convert groups of two leading spaces to tabs:</p>
4830f66f451Sopenharmony_ci<blockquote><pre>
4840f66f451Sopenharmony_cised -i ':loop;s/^\( *\)  /\1\t/;t loop' filename
4850f66f451Sopenharmony_ci</pre></blockquote>
4860f66f451Sopenharmony_ci
4870f66f451Sopenharmony_ci<p>And here's the sed invocation to convert leading tabs to two spaces each:</p>
4880f66f451Sopenharmony_ci<blockquote><pre>
4890f66f451Sopenharmony_cised -i ':loop;s/^\( *\)\t/\1  /;t loop' filename
4900f66f451Sopenharmony_ci</pre></blockquote>
4910f66f451Sopenharmony_ci
4920f66f451Sopenharmony_ci<p>There's a space after C flow control statements that look like functions, so
4930f66f451Sopenharmony_ci"if (blah)" instead of "if(blah)". (Note that sizeof is actually an
4940f66f451Sopenharmony_cioperator, so we don't give it a space for the same reason ++ doesn't get
4950f66f451Sopenharmony_cione. Yeah, it doesn't need the parentheses either, but it gets them.
4960f66f451Sopenharmony_ciThese rules are mostly to make the code look consistent, and thus easier
4970f66f451Sopenharmony_cito read.) We also put a space around assignment operators (on both sides),
4980f66f451Sopenharmony_ciso "int x = 0;".</p>
4990f66f451Sopenharmony_ci
5000f66f451Sopenharmony_ci<p>Blank lines (vertical whitespace) go between thoughts. "We were doing that,
5010f66f451Sopenharmony_cinow we're doing this." (Not a hard and fast rule about _where_ it goes,
5020f66f451Sopenharmony_cibut there should be some for the same reason writing has paragraph breaks.)</p>
5030f66f451Sopenharmony_ci
5040f66f451Sopenharmony_ci<p>Variable declarations go at the start of blocks, with a blank line between
5050f66f451Sopenharmony_cithem and other code. Yes, c99 allows you to put them anywhere, but they're
5060f66f451Sopenharmony_ciharder to find if you do that. If there's a large enough distance between
5070f66f451Sopenharmony_cithe declaration and the code using it to make you uncomfortable, maybe the
5080f66f451Sopenharmony_cifunction's too big, or is there an if statement or something you can
5090f66f451Sopenharmony_ciuse as an excuse to start a new closer block? Use a longer variable name
5100f66f451Sopenharmony_cithat's easier to search for perhaps?</p>
5110f66f451Sopenharmony_ci
5120f66f451Sopenharmony_ci<p>An * binds to a variable name not a type name, so space it that way.
5130f66f451Sopenharmony_ci(In C "char *a, b;" and "char* a, b;" mean the same thing: "a" is a pointer
5140f66f451Sopenharmony_cibut "b" is not. Spacing it the second way is not how C works.)</p>
5150f66f451Sopenharmony_ci
5160f66f451Sopenharmony_ci<p>If statements with a single line body go on the same line if the result
5170f66f451Sopenharmony_cifits in 80 columns, on a second line if it doesn't. We usually only use
5180f66f451Sopenharmony_cicurly brackets if we need to, either because the body is multiple lines or
5190f66f451Sopenharmony_cibecause we need to distinguish which if an else binds to. Curly brackets go
5200f66f451Sopenharmony_cion the same line as the test/loop statement. The exception to both cases is
5210f66f451Sopenharmony_ciif the test part of an if statement is long enough to split into multiple
5220f66f451Sopenharmony_cilines, then we put the curly bracket on its own line afterwards (so it doesn't
5230f66f451Sopenharmony_ciget lost in the multple line variably indented mess), and we put it there
5240f66f451Sopenharmony_cieven if it's only grouping one line (because the indentation level is not
5250f66f451Sopenharmony_ciproviding clear information in that case).</p>
5260f66f451Sopenharmony_ci
5270f66f451Sopenharmony_ci<p>I.E.</p>
5280f66f451Sopenharmony_ci
5290f66f451Sopenharmony_ci<blockquote>
5300f66f451Sopenharmony_ci<pre>
5310f66f451Sopenharmony_ciif (thingy) thingy;
5320f66f451Sopenharmony_cielse thingy;
5330f66f451Sopenharmony_ci
5340f66f451Sopenharmony_ciif (thingy) {
5350f66f451Sopenharmony_ci  thingy;
5360f66f451Sopenharmony_ci  thingy;
5370f66f451Sopenharmony_ci} else thingy;
5380f66f451Sopenharmony_ci
5390f66f451Sopenharmony_ciif (blah blah blah...
5400f66f451Sopenharmony_ci    && blah blah blah)
5410f66f451Sopenharmony_ci{
5420f66f451Sopenharmony_ci  thingy;
5430f66f451Sopenharmony_ci}
5440f66f451Sopenharmony_ci</pre></blockquote>
5450f66f451Sopenharmony_ci
5460f66f451Sopenharmony_ci<p>Gotos are allowed for error handling, and for breaking out of
5470f66f451Sopenharmony_cinested loops. In general, a goto should only jump forward (not back), and
5480f66f451Sopenharmony_cishould either jump to the end of an outer loop, or to error handling code
5490f66f451Sopenharmony_ciat the end of the function. Goto labels are never indented: they override the
5500f66f451Sopenharmony_ciblock structure of the file. Putting them at the left edge makes them easy
5510f66f451Sopenharmony_cito spot as overrides to the normal flow of control, which they are.</p>
5520f66f451Sopenharmony_ci
5530f66f451Sopenharmony_ci<p>When there's a shorter way to say something, we tend to do that for
5540f66f451Sopenharmony_ciconsistency. For example, we tend to say "*blah" instead of "blah[0]" unless
5550f66f451Sopenharmony_ciwe're referring to more than one element of blah. Similarly, NULL is
5560f66f451Sopenharmony_cireally just 0 (and C will automatically typecast 0 to anything, except in
5570f66f451Sopenharmony_civarargs), "if (function() != NULL)" is the same as "if (function())",
5580f66f451Sopenharmony_ci"x = (blah == NULL);" is "x = !blah;", and so on.</p>
5590f66f451Sopenharmony_ci
5600f66f451Sopenharmony_ci<p>The goal is to be
5610f66f451Sopenharmony_ciconcise, not cryptic: if you're worried about the code being hard to
5620f66f451Sopenharmony_ciunderstand, splitting it to multiple steps on multiple lines is
5630f66f451Sopenharmony_cibetter than a NOP operation like "!= NULL". A common sign of trying too
5640f66f451Sopenharmony_cihard is nesting ? : three levels deep, sometimes if/else and a temporary
5650f66f451Sopenharmony_civariable is just plain easier to read. If you think you need a comment,
5660f66f451Sopenharmony_ciyou may be right.</p>
5670f66f451Sopenharmony_ci
5680f66f451Sopenharmony_ci<p>Comments are nice, but don't overdo it. Comments should explain _why_,
5690f66f451Sopenharmony_cinot how. If the code doesn't make the how part obvious, that's a problem with
5700f66f451Sopenharmony_cithe code. Sometimes choosing a better variable name is more revealing than a
5710f66f451Sopenharmony_cicomment. Comments on their own line are better than comments on the end of
5720f66f451Sopenharmony_cilines, and they usually have a blank line before them. Most of toybox's
5730f66f451Sopenharmony_cicomments are c99 style // single line comments, even when there's more than
5740f66f451Sopenharmony_cione of them. The /* multiline */ style is used at the start for the metadata,
5750f66f451Sopenharmony_cibut not so much in the code itself. They don't nest cleanly, are easy to leave
5760f66f451Sopenharmony_ciaccidentally unterminated, need extra nonfunctional * to look right, and if
5770f66f451Sopenharmony_ciyou need _that_ much explanation maybe what you really need is a URL citation
5780f66f451Sopenharmony_cilinking to a standards document? Long comments can fall out of sync with what
5790f66f451Sopenharmony_cithe code is doing. Comments do not get regression tested. There's no such
5800f66f451Sopenharmony_cithing as self-documenting code (if nothing else, code with _no_ comments
5810f66f451Sopenharmony_ciis a bit unfriendly to new readers), but "chocolate sauce isn't the answer
5820f66f451Sopenharmony_cito bad cooking" either. Don't use comments as a crutch to explain unclear
5830f66f451Sopenharmony_cicode if the code can be fixed.</p>
5840f66f451Sopenharmony_ci
5850f66f451Sopenharmony_ci<!--#include file="footer.html" -->
586