<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Preston L. Bannister  { random memes } &#187; Software</title>
	<atom:link href="http://bannister.us/weblog/of/software/feed/" rel="self" type="application/rss+xml" />
	<link>http://bannister.us/weblog</link>
	<description>A personal viewpoint about software, the web, and anything else of note.</description>
	<lastBuildDate>Sat, 31 Jul 2010 05:00:11 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>VirtualBox as free software</title>
		<link>http://bannister.us/weblog/2010/07/16/virtualbox-as-free-software/</link>
		<comments>http://bannister.us/weblog/2010/07/16/virtualbox-as-free-software/#comments</comments>
		<pubDate>Fri, 16 Jul 2010 20:23:35 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1853</guid>
		<description><![CDATA[File this under &#8220;Missing the Obvious&#8221;. I bought VMware Workstation several years back. Used virtual machines quite a lot in developing and testing software. The company I work for licensed the enterprise version of VMware, at least in part due to developer experience using VMware. A few of the VMs currently hosted under the enterprise [...]]]></description>
			<content:encoded><![CDATA[<p>File this under &#8220;Missing the Obvious&#8221;.</p>
<p>I bought VMware Workstation several years back. Used virtual machines quite a lot in developing and testing software. The company I work for licensed the enterprise version of VMware, at least in part due to developer experience using VMware. A few of the VMs currently hosted under the enterprise version of VMware started out running under VMware on my desktop.</p>
<p>A few years back &#8211; well after EMC acquired VMware &#8211; I got the distinct impression that the desktop version of VMware was somewhat neglected. I was losing increasing amounts of time to locked-up VMs, messed up keymaps, and even main desktop lockup. This provided an incentive to try Sun&#8217;s VirtualBox, which proved more reliable for my usage.</p>
<p>Oracle gained ownership of the VirtualBox software with the Sun acquisition. Is there any strong reason for Oracle to offer and develop the &#8220;free&#8221; version of VirtualBox, or will the desktop version of VirtualBox suffer the same apparent neglect as did the the desktop version of VMware? </p>
<p>Of late I was reviewing the documentation we sent to our customers with the last software release, as we are rolling up to a small update in the near future, and a larger release a bit later. In the documentation we send to customers, we have a prominent declaration of support for running under VMware. This was an easy declaration to make, as developers used VMware heavily in development and testing. We knew our product ran under VMware.</p>
<p>Now this is somewhat less true. Certainly the more elaborate test setups (mine) are all run under VirtualBox. I expect things all still work under VMware, but I personally no longer have a basis to make that assertion. For the next release, we will likely add an statement of support for VirtualBox.</p>
<p>As to the obvious bit I had missed: </p>
<p>Many of our customers are still not using virtualization software. Generally customers move from a physical machine to a VM at the time of a major version upgrade. At the next major version upgrade, we will offer a recommendation for VirtualBox.</p>
<p>Oh. Right. I had forgotten about that multiplier. Selling enterprise virtualization to our customers represents <b>many</b> more opportunities than any in-house usage at our development outfit. Nevermind my misgivings &#8211; offering free software to developers is about the best advertising Oracle could buy. </p>
<p>Does Oracle know this?</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/07/16/virtualbox-as-free-software/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Odds and ends in testing with Active Directory</title>
		<link>http://bannister.us/weblog/2010/07/12/odds-and-ends-in-testing-with-active-directory/</link>
		<comments>http://bannister.us/weblog/2010/07/12/odds-and-ends-in-testing-with-active-directory/#comments</comments>
		<pubDate>Tue, 13 Jul 2010 03:03:26 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1852</guid>
		<description><![CDATA[Of late, I have needed to test against Microsoft&#8217;s Active Directory &#8211; so I setup a virtual machine hosting Windows 2003 Server and configured as a domain controller. The domain controller is connected to small number of machines (also VMs) on a private network &#8211; pretty much what you would expect for a tightly controlled [...]]]></description>
			<content:encoded><![CDATA[<p>Of late, I have needed to test against Microsoft&#8217;s Active Directory &#8211; so I setup a virtual machine hosting Windows 2003 Server and configured as a domain controller. The domain controller is connected to small number of machines (also VMs) on a private network &#8211; pretty much what you would expect for a tightly controlled test setup.</p>
<p>One surprise in the experience was the difficulty in getting the domain controller (and DHCP setup) properly working. Admittedly, setting up a domain controller is a somewhat unusual task. As a rare task perhaps not so important to Microsoft as a process that needs the user experience optimized. Still, given I have long been well-acquainted with how this all works at a network level, I ended up wasting a surprising amount of time trying to find the right knobs to turn. (Much searching with Google to get a key Microsoft product to work &#8230; the irony.)</p>
<p>Oddly, all the nice GUI interfaces to administering a domain server actually make the process <b>more obscure</b> than the equivalent activity on Unix (where I am accustomed to configuration kept in text files). I do now better understand my past conversations with network administrators who did not know how to do (what I considered) simple tasks with Microsoft&#8217;s domain controller &#8230; and why they often got it wrong. </p>
<p>Since my aim was testing, I wrote programs to populate Active Directory with large/variable numbers of users and groups, and to cleanup after. Though you could make direct Active Directory or LDAP calls, I find Microsoft&#8217;s <b>ldifde</b> utility is quite useful for this purpose. Note that Active Directory uses tombstones to support distributed replication (which is good), and that scripted changes will leave behind many tombstones &#8211; which may perturb your testing. You can limit the scope of the impact by changing the <a href="http://msdn.microsoft.com/en-us/library/ms680306(VS.85).aspx">tombstone lifetime</a> &#8211; the period of time tombstones are kept before cleanup &#8211; from the default down to 2(?) days. (Finding and <a href="http://www.petri.co.il/changing_the_tombstone_lifetime_windows_ad.htm">changing</a> that single setting ate up the large part of day.)</p>
<p>(Had a flashback to the old original text-based <a href="http://en.wikipedia.org/wiki/Colossal_Cave_Adventure">Adventure</a> game. &#8220;You are in a maze of twisty passages, all alike.&#8221;)</p>
<p>Another approach is to take a snapshot of the VM when Active Directory is in a &#8220;clean&#8221; state, and restore the snapshot later. In principle this could be simpler/faster than the scripted restore (using delete operations fed via <b>ldifde</b>). Found a quite thorough article on <a href="http://blogs.technet.com/b/askds/archive/2010/06/10/how-to-virtualize-active-directory-domain-controllers-part-1.aspx">virtualizing Active Directory</a>, and ran across the <a href="http://www.google.com/search?sourceid=chrome&#038;ie=UTF-8&#038;q=USN+ROLLBACK">USN ROLLBACK</a> problem.</p>
<p>To aid in distributed replication, Active Directory keeps a count of local changes (the USN number) to track replication of remote changes. All quite usual and normal for this problem domain. Given that Active Directory has been around for a decade, you would expect the obvious use-cases to be covered, but not quite&#8230;.</p>
<p>Turns out if you distribute changes among more than one Active Directory controller, you have a problem in the not-unusual case of taking a backup (or a snapshot) and restoring it later. Thus the many items returned by the above web search, including <a href="http://msexchangeteam.com/archive/2005/11/30/415345.aspx">developers at Microsoft</a>.</p>
<p>Um &#8230; what?!? Seems to me this is a pretty common case. That Microsoft would have got this wrong in the first version back in 2000 is only slightly disappointing. That this is still a problem a decade later is very surprising indeed.</p>
<p>Given that I may for future test purposes need to add a second domain controller VM, this looks to be another problem to avoid.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/07/12/odds-and-ends-in-testing-with-active-directory/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java for Business = Zinger from Oracle</title>
		<link>http://bannister.us/weblog/2010/06/22/java-for-business-zinger-from-oracle/</link>
		<comments>http://bannister.us/weblog/2010/06/22/java-for-business-zinger-from-oracle/#comments</comments>
		<pubDate>Wed, 23 Jun 2010 06:08:13 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1851</guid>
		<description><![CDATA[Last year Marsh Ray found a potential vulnerability in TLS renegotiation. The Oracle Java folk responded by disabling renegotiation entirely in the Java Runtime. This is not a fix, as it breaks usage for a large class of security-oriented sites. A rather large group of folk now find that their once-secure applications no longer work [...]]]></description>
			<content:encoded><![CDATA[<p>Last year Marsh Ray found a <a href="http://extendedsubset.com/?p=8">potential vulnerability in TLS renegotiation</a>. The Oracle Java folk responded by <a href="http://java.sun.com/javase/javaseforbusiness/docs/TLSReadme.html">disabling renegotiation entirely in the Java Runtime</a>. This is not a fix, as it breaks usage for a large class of security-oriented sites. A rather large group of folk now find that their once-secure applications no longer work &#8211; and are not happy.</p>
<p>At first, there was no word from Oracle on a real fix &#8230; then I received this email. </p>
<blockquote><p>
Hi Preston,<br />
Good to hear from you. Have I told you what a fine group of folks there is at Oracle Java? No? That&#8217;s because there isn&#8217;t.<br />
<i>[snip]</i></p>
<p>Anyway I ended up talking with some people in Oracle (our SES in DC called the president of Oracle) and several people later a VP of security called me. Anyway the fix will contain 3 implementations low, medium and high security and he claims it will take a significant amount of work in the application software to implement any of them. The release is expected by September. <b>The gotcha is the VP told me that they will only release the JRE to licensed JRE users.</b> I told him the JRE has always been a free download and he responded with &#8220;free download not free use&#8221;. In order to use the JRE you must have a support contract in place and it appeared to him the DoD was a couple million licenses out of compliance. Apparently it was only free until they came up with this Java for Business thing they have now. In my experience it&#8217;s a very Oracle response. Quite the opposite to my dealings with Microsoft.</p>
<p>I guess time will tell&#8230;.<br />
<i>[snip]</i></p></blockquote>
<p>(Emphasis is mine. All identifying information removed.)</p>
<p>Well, we have an answer. Yikes.</p>
<p>To be clear, I believe Oracle is entitled to do whatever they want to make money from Java. Oracle bought Sun, and now they own Java. Sun had a different approach, but Sun failed. Also what Oracle does in the long term may or may not be different from what the above message indicates.</p>
<p>On the flip side, I have to re-consider whether it makes sense to write code for the Java platform. If Oracle is fragmenting the Java platform, and changing the cost to customers &#8230; using Java may no longer make sense. This may be the point where I start planning to move all future work off Java and away from the JVM.</p>
<p>Of course, there is open-source Java, but I am not convinced there is the critical mass of interest required to make open-source Java a viable option. I would very much like to be wrong on this point, but for now, I am skeptical.</p>
<p>Is Google&#8217;s Dalvik VM potentially a viable option? Might IBM&#8217;s investment in Java translate into backing for open-source Java? Is it time to move off the Sun/Oracle JVM entirely?</p>
<p>Time to re-visit choices made long ago&#8230;.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/06/22/java-for-business-zinger-from-oracle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Anachronisms: Icon for &#8220;Save&#8230;&#8221; is a floppy, HDTV sized in inches</title>
		<link>http://bannister.us/weblog/2010/05/20/anachronisms-icon-for-save-is-a-floppy-hdtv-sized-in-inches/</link>
		<comments>http://bannister.us/weblog/2010/05/20/anachronisms-icon-for-save-is-a-floppy-hdtv-sized-in-inches/#comments</comments>
		<pubDate>Fri, 21 May 2010 07:23:02 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Humor]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[user interface]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1848</guid>
		<description><![CDATA[Went looking for an icon to represent the &#8220;Save&#8230;&#8221; or &#8220;Save As&#8230;&#8221; operation in an application. The icons I am finding are all images of a floppy disk. For long-time GUI users, this image is familiar and requires no thought. For newer users &#8230; they may never have seen a floppy disk. The image has [...]]]></description>
			<content:encoded><![CDATA[<p>Went looking for an icon to represent the &#8220;Save&#8230;&#8221; or &#8220;Save As&#8230;&#8221; operation in an application. The icons I am finding are all images of a floppy disk. For long-time GUI users, this image is familiar and requires no thought. For newer users &#8230; they may never have seen a floppy disk. The image has become antique. </p>
<p>When was the last time you used a computer with a floppy disk drive? When did you last save a file to a floppy disk? </p>
<p>Seems rather past time for a new icon. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Funny bit is that the square-with-notched-corner shape of a floppy looks rather like an SD memory card. Still not a good choice for file-save, as more likely use is to copy files <i>from</i> the memory card.</p>
<p>Checked to see if Microsoft had any good ideas. Office 2007 uses a image of a floppy for &#8220;Save&#8230;&#8221; and &#8220;Save As&#8230;&#8221; &#8211; adding a pencil writing on the floppy label (an antique behavior) for the &#8220;Save As&#8230;&#8221; icon. Internet Explorer changes &#8220;Save As&#8230;&#8221; so the pencil is writing on the sliding metal cover that protected the magnetic film (not really a good idea).</p>
<p>Went searching through GUI Style Guides looking to see if Microsoft (nope) or Apple (nope) had an updated replacement. Along the way ran across the style guide sections on designing for differing display resolutions. Display resolution is measured in DPI (dots-per-inch) and display sizes are measured in inches. Because both Microsoft Windows and Apple Macintosh software were originally developed on the west coast of the United States, entire generations of programmers and users have learned to think about screen sizes in inches.</p>
<p>Have you noticed that all the HDTVs sold have sizes measured in inches, not in Metric System units?</p>
<p>This is odd, as only the United States still holds firmly to the old &#8220;British Imperial System&#8221; of measures, and HDTVs are not made here. Even odder, in countries that do use the Metric measures, the HDTV sizes are still expressed in inches. (Don&#8217;t believe me? Check out the <a href="http://amazon.fr/">Amazon site in France</a> &#8211; home of the Metric System.)</p>
<p>If only the French had developed Windows &#8230; then the Metric System would have had a chance. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/05/20/anachronisms-icon-for-save-is-a-floppy-hdtv-sized-in-inches/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress hacked (again)</title>
		<link>http://bannister.us/weblog/2010/05/19/wordpress-hacked-again/</link>
		<comments>http://bannister.us/weblog/2010/05/19/wordpress-hacked-again/#comments</comments>
		<pubDate>Wed, 19 May 2010 22:47:46 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1846</guid>
		<description><![CDATA[On around May 13 someone subverted my weblog to serve pharmacy ads. Annoying, but not otherwise a big deal, given regular backups. This hack was more clever than prior incidents Took me longer to find and remove the problem. I expect WordPress to be insecure. Looked at the source code early on. Like most PHP [...]]]></description>
			<content:encoded><![CDATA[<p>On around May 13 someone subverted my weblog to serve pharmacy ads. Annoying, but not otherwise a big deal, given regular backups. This hack was more clever than prior incidents Took me longer to find and remove the problem.</p>
<p>I expect WordPress to be insecure. Looked at the source code early on. Like most PHP applications, the potential attack surface is very large.</p>
<p>Will be a bit before things are entirely in order. (Ick. Using a stock WordPress theme.)</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/05/19/wordpress-hacked-again/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Do you trust Oracle?</title>
		<link>http://bannister.us/weblog/2010/03/31/do-you-trust-oracle/</link>
		<comments>http://bannister.us/weblog/2010/03/31/do-you-trust-oracle/#comments</comments>
		<pubDate>Thu, 01 Apr 2010 05:45:53 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1809</guid>
		<description><![CDATA[The death of Sun, and the submission to Oracle &#8211; as a developer &#8211; leaves me with doubts about Java. Sun was always a bit of a mixed bag. Some of their work was absolutely brilliant, and some &#8211; especially with software &#8211; was amazingly dumb. Never did really understand why this was the case. [...]]]></description>
			<content:encoded><![CDATA[<p>The death of Sun, and the submission to Oracle &#8211; as a developer &#8211; leaves me with doubts about Java.</p>
<p>Sun was always a bit of a mixed bag. Some of their work was absolutely brilliant, and some &#8211; especially with software &#8211; was amazingly dumb. Never did really understand why this was the case. (Lots of theories, of course, but no certain grasp.)</p>
<p>Oracle is a very different mixed bag. The Oracle database is both a very solid piece of work, and old not-quite-irrelevant to the present and future (an intentional over-simplification.) The Oracle database is core to Oracle the company. On the other hand, Oracle (the company) is also heavily invested in applications and software that cluster around the Oracle database. Much of this software is written in Java (for very logical reasons). This would seem to guarantee the future of Java (as a platform).</p>
<p>But &#8230; the Oracle-core has a diminishing (if very fat) future. That fat future can stretch out quite a while &#8211; beyond obvious reason &#8211; as the IBM mainframe market proved.</p>
<p>As a developer, I find that I crossed a threshold some time back, and am impatient with Java when I can express more elegant solutions (to complex problems) in Javascript. (Yes, really &#8211; Javascript. May have something to do with early heavy exposure to Lisp in school.) Hearing that C# incorporates lambdas &#8230; is C# a better base than Java (with appropriate wariness of stealth Microsoft patents), or rather an extraneous step before bundling a Javascript interpreter? </p>
<p>Do you trust Oracle?</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/03/31/do-you-trust-oracle/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Re-thinking the IDE &#8211; a starting point</title>
		<link>http://bannister.us/weblog/2010/03/15/re-thinking-the-ide-a-starting-point/</link>
		<comments>http://bannister.us/weblog/2010/03/15/re-thinking-the-ide-a-starting-point/#comments</comments>
		<pubDate>Tue, 16 Mar 2010 05:27:37 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1753</guid>
		<description><![CDATA[Time for another user-interface rant. Use of multi-column lists One of my favorite annoyances is multi-column lists. When they first appeared in the early 1990&#8242;s, multi-column lists were cool. Add in user-sortable and re-sizable columns, and your application could offer better eye-candy than the competitors. The usual problem with multi-column lists was that the columns [...]]]></description>
			<content:encoded><![CDATA[<p>Time for another user-interface rant. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Use of multi-column lists</h3>
<p>One of my favorite annoyances is multi-column lists. When they first appeared in the early 1990&#8242;s, multi-column lists were cool. Add in user-sortable and re-sizable columns, and your application could offer better eye-candy than the competitors.</p>
<p>The usual problem with multi-column lists was that the columns came up the wrong size. Some applications were smart enough to auto-size columns &#8211; but not all &#8211; and auto-sizing was not always what the user wanted. The next logical enhancement was to save user preferences for column sizes, so that the next time the application came up, the user&#8217;s prior manual adjustments to column width were preserved. If the most-useful column widths change with different data, the users spend a lot of time manually fiddling with column widths.</p>
<p>As an example, we have the (really lame) Services view in Microsoft Windows. When this first appeared (in the mid-1990&#8242;s) the columns both (1) came up the wrong size, (2) in the wrong order (another topic), and (3) did not save user preferences. At the time, I figured this was a work in progress, and the Microsoft folk would fix this in a couple years.  </p>
<p>The screenshot below is from Windows 7, so &#8230; apparently not.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/windows-services.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/windows-services-300x219.png" width="300" height="219" /></a></pre>
<p>The Eclipse IDE uses multi-column lists for compiler error messages (and the like). Eclipse might be my current-favorite IDE, but this is one of my least-favorite aspects. </p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-1-console.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-1-console-300x187.png" width="300" height="187" /></a></pre>
<p>Note that basically all the columns are the wrong size. The error message text is most important, and is too small, with only a fraction visible. That makes any less-important column too-large, by definition. The column with the most excess whitespace &#8211; &#8220;Type&#8221; &#8211; is one of the least important. The &#8220;Path&#8221; column occupies a lot of space, but is of at best secondary importance. </p>
<p>The most important bit of the &#8220;Problems&#8221; panel is tiny.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-1-console-effective.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-1-console-effective-300x187.png" width="300" height="187"  /></a></pre>
<p>Note also that in the above screenshot, the overhead elements in the panel eat up about half the vertical space &#8211; far more space than the most-meaningful message texts. </p>
<p>This is not efficient &#8230;  on a large 1920&#215;1200 panel. On smaller panels, things are much worse.</p>
<h3>Presenting program texts</h3>
<p>The main payload in an IDE is the display of program text. An IDE may put up supplemental panels to help discover structure and aid navigation within the program, but the central bit is the program text. In Eclipse only a fraction of the screen is usable for the display of program text.</p>
<p>Current screens are more than wide enough for pretty much all program texts, but those same texts are far taller than they are wide. As screens are far shorter than program texts, you want to make the best use of the vertical space available. Only about 2/3 of the screen height is text, in the typical example below.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-1-text1.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-1-text1-300x187.png" width="300" height="187" /></a></pre>
<p>With Eclipse you can &#8220;maximize&#8221; the text panel, but there is still substantial vertical space lost to overhead elements.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-2-text.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/eclipse-big-2-text-300x187.png" width="300" height="187" /></a></pre>
<p>Note also that while there is a shortage of vertical space, there is an excess of unused horizontal space. </p>
<p>This is not efficient &#8230;  on a large 1920&#215;1200 panel. On smaller panels, things are much worse.</p>
<h3>Alternate presentations</h3>
<p>To make the best use of scarce vertical space &#8211; especially on smaller panels &#8211; you want to excise vertical overhead. Program texts tend to be dense on the left edge, and sparse on the right. For the display of error messages or navigation aids, if you place panels on the right, there is good chance you will overlap little or no program text. If you use fly-away panels, you can quickly view even very long lines of program text on the smallest-current laptop displays.</p>
<p>There is a simple/clever way around the presentation of variable-sized columns &#8211; concatenate all the variable size bits into one column. You can keep the fixed sized bits as columns (or not). If the resulting text is too wide for the available column space, allow the text to wrap. Excise the less-useful fields, and present on explicit user action (like hovering or clicking on the row).</p>
<p>Note that too-wide message texts can be hard (or slow) to read. (Ever lose track of the line you are on when looking across a very wide display?) There is a good argument for limiting the width of message texts to about the width of a typical piece of paper. (Funny how that works out &#8211; common paper width tends to be about the efficient maximum for text width.)</p>
<p>Combine the notions for maximal program text and complete-but-constrained message texts, and you might end up with something like this <a href="http://bannister.us/examples/concept-demo-1/">simple IDE mock-up</a>. (Note this is very much a quick-and-dirty first iteration.)</p>
<p>At 1920&#215;1200 &#8211; on a 24&#8243; desktop panel.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/ide-concept-demo-1-full-large.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/ide-concept-demo-1-full-large-300x187.png" width="300" height="187" /></a></pre>
<p>(Best to pull this up in Google Chrome, as only Chrome does a really good maximum size full-screen. Works in Safari. Renders in Firefox, though event handling is wrong. IE is pretty much hopeless. As this is a mockup, my aim was not to make it work across web browsers.)</p>
<p>Note that as the screen size drops, the use of screen space becomes radically better than conventional IDEs.</p>
<p>At 1440&#215;900 &#8211; the resolution of my 17&#8243; laptop panel.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1440x900.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1440x900-300x187.png" width="300" height="187"  /></a></pre>
<p>(Imagine the vertical overhead of Chrome as a part of the text-space.)</p>
<p>At 1280&#215;800 &#8211; the resolution of common basic laptop panels.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1280x800.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1280x800-300x187.png" width="300" height="187" /></a></pre>
<p>Note that on this smallish laptop we are getting both more vertical program text, and more complete message texts, than on my big 1920&#215;1200 panel using Eclipse.</p>
<p>At 1024&#215;600 &#8211; the resolution of a netbook panel.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1024x600.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1024x600-300x175.png" width="300" height="175" /></a></pre>
<p>With the fly-away message panel toggled off, even a netbook screen shows a good amount of program text.</p>
<pre><a href="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1024x600-max.png"><img src="http://bannister.us/weblog/wp-content/uploads/2010/03/concept-demo-1024x600-max-300x175.png" width="300" height="175" /></a></pre>
<h3>Topic for later &#8211; navigation</h3>
<p>After using Eclipse for several years, I have become pretty comfortable. Oddly, I have come to realize that in <b>one</b> aspect &#8211; navigation &#8211; Eclipse is inferior to my old use of Emacs with ctags/mkid/calls/grep. Under the hood, the functions for introspecting program texts are a dream compared to what I had twenty years back &#8230; but navigation still feels a bit clunky.</p>
<p>The next exercise will be to try and map out what is different, and take a stab at how to do better.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/03/15/re-thinking-the-ide-a-starting-point/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Multiplexed FastCGI connections?</title>
		<link>http://bannister.us/weblog/2010/01/31/multiplexed-fastcgi-connections/</link>
		<comments>http://bannister.us/weblog/2010/01/31/multiplexed-fastcgi-connections/#comments</comments>
		<pubDate>Sun, 31 Jan 2010 20:22:16 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1736</guid>
		<description><![CDATA[Does anyone use FastCGI with FCGI_MPXS_CONNS set to &#8220;1&#8243; (for multiplexed connections)? Most FastCGI backends seems to be written for non-multiplexed connections. (Much simpler, so understandable.) The IIS FastCGI connector apparently does not support multiplexed connections. Writing a FastCGI backend that allows for multiplexed connections. Would be a waste of time if not supported by [...]]]></description>
			<content:encoded><![CDATA[<p>Does anyone use FastCGI with <a href="http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S4.1">FCGI_MPXS_CONNS</a> set to &#8220;1&#8243; (for multiplexed connections)?</p>
<p>Most FastCGI backends seems to be written for non-multiplexed connections. (Much simpler, so understandable.) The IIS FastCGI connector apparently <a href="https://forums.iis.net/p/1154348/1890231.aspx#1890231">does not support multiplexed connections</a>. </p>
<p>Writing a FastCGI backend that allows for multiplexed connections. Would be a waste of time  if not supported by a frontend, or if existing frontends are buggy.</p>
<p>(Not really expecting a response, but have to state the question.)</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/01/31/multiplexed-fastcgi-connections/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Giving up HTML@W3C</title>
		<link>http://bannister.us/weblog/2010/01/17/giving-up-htmlw3c/</link>
		<comments>http://bannister.us/weblog/2010/01/17/giving-up-htmlw3c/#comments</comments>
		<pubDate>Mon, 18 Jan 2010 06:00:46 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[html@w3c]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1729</guid>
		<description><![CDATA[Got the &#8220;status as Invited Expert in HTML Working Group&#8221; email. This I will let expire. Spent my time tilting at windmills, and do not see any point in continuing. The HTML Working Group at W3C is &#8230; far too much noise. The HTML5 &#8220;standard&#8221; is going to be a bloated monster, and there is [...]]]></description>
			<content:encoded><![CDATA[<p>Got the &#8220;status as Invited Expert in HTML Working Group&#8221; email. This I will let expire. Spent my time tilting at windmills, and do not see any point in continuing.</p>
<p>The HTML Working Group at W3C is &#8230; far too much noise. The HTML5 &#8220;standard&#8221; is going to be a bloated monster, and there is no chance I can change that. Time to stop the pretense of trying.</p>
<p>Not that all the work is bad, or that there is any shortage of well-intentioned folk. What the group lacks is any sense of minimalism, and enough strong voices able to say &#8220;no&#8221;. What we will get is going to be even harder to digest than HTML4. This is sad. Future developers are going to have an even harder time, for no good reason.</p>
<p>The wildcard here is that the mainstream browser implementations may not follow all the half-thought ideas thrown in by the HTML5 Working Group. No idea how this will work out.</p>
<p>At the core HTML is pretty damn simple &#8211; or could be. HTML4 got stuffed with a bunch of half-thought notions, most of which have since proved of no value, and were ignored by developers. Ideally we would learn from experience, omit the fuzzy disused bits, and trim HTML down to the useful core. There are well-known (though not universally known) means to achieve this aim. This is not going to happen.  </p>
<p>I cannot keep up with the herd of Energizer-bunnies eager to make their mark, and with too-limited experience.<br />
Time to stop pretending.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/01/17/giving-up-htmlw3c/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Efficient UTF-8 recoding and secure processing</title>
		<link>http://bannister.us/weblog/2010/01/16/efficient-utf-8-recoding-and-secure-processing/</link>
		<comments>http://bannister.us/weblog/2010/01/16/efficient-utf-8-recoding-and-secure-processing/#comments</comments>
		<pubDate>Sat, 16 Jan 2010 21:20:20 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1717</guid>
		<description><![CDATA[An attempt to make a point&#8230; The use of UTF-8 on the web is common and increasing. Lots of data comes in as UTF-8, and inefficiency in UTF-8 data handling is going to have pretty pervasive impact. On the flip side, the creators of UTF-8 did a good job. There is nothing really complicated about [...]]]></description>
			<content:encoded><![CDATA[<p>An attempt to make a point&#8230;</p>
<p>The use of <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8</a> on the web is common and increasing. Lots of data comes in as UTF-8, and inefficiency in UTF-8 data handling is going to have pretty pervasive impact.</p>
<p>On the flip side, the creators of UTF-8 did a good job. There is nothing really complicated about the UTF-8 format, and processing is simple. </p>
<p>So I was surprised (or rather shocked) to find <a href="http://bannister.us/weblog/2009/10/05/example-general-purpose-trie-in-java/">in an earlier experiment</a> that Java performed UTF-8 conversion slowly. In fact, I was able to write a faster UTF-8 decoder <b>in Java</b> than the stock decoder. This is just plain wrong. Conversion between encodings is a primitive/simple operation best written in C/C++ and run as native code (and this is the sort of processing where C/C++ is probably always going to be much faster than Java). </p>
<p>There is a problem in that malicious external parties can send oddly-encoded UTF-8, and bypass simple-minded malware detection software. Ordinary ASCII characters can coded as an alternate multi-byte sequences, and simple scanners miss the alternate encoding.</p>
<p>This is a problem. There is a simple solution. One of a set of <a href="http://bannister.us/weblog/2009/02/02/long-term-compatible-protocols/">principles</a> I adopted a long time ago is <a href="http://bannister.us/weblog/2008/03/21/convert-at-the-edges/">&#8220;convert at the edges&#8221;</a>. If you have data coming in from an untrusted source, then you perform conversion and validation at the &#8220;edge&#8221; where the data is first received.</p>
<p>In the case of UTF-8 coming from an untrusted source, to make all later processing simpler, you must <b>recode</b> to eliminate any alternate encodings. This is quite simple, as recoded UTF-8 will always be the same size or smaller, and so can be done in-place. The <a href="http://bannister.us/weblog/2010/01/12/utf8ucs-conversion-benchmark/">prior experiment</a> measured the cost of UTF-8 recoding. Looks like we can drive a 1-Gbit network link at full speed (with efficient code), while recoding the entire contents. Since UTF-8 data usually represents a smaller portion of traffic, and since other processing tends to take the larger part of the load, there is no reason to not perform recoding on any UTF-8 data coming from an untrusted source. </p>
<p>Combine &#8220;convert at the edges&#8221; with UTF-8 recoding, and we lose the basis for the requirement in <a href="http://www.apps.ietf.org/rfc/rfc3629.html">RFC 3629</a> for detection of &#8220;illegal&#8221; UTF-8 code sequences. In addition we allow all downstream processing to be simpler and more efficient &#8230; and we also can be tolerant of imperfect upstream software. (Yes, I am going to invoke <a href="http://bannister.us/weblog/2005/11/05/postel%e2%80%99s-law-liberal-in-what-you-accept-conservative-in-what-you-generate/">Postel</a>, again.)</p>
<p>The basis is good (secure processing with untrusted sources), but the requirement for detection of &#8220;illegal&#8221; sequences is not necessary and (most definitely!) not optimal. </p>
<p>Example of UTF-8 recoding (from <a href="http://svn.bannister.us/public//json-c/sources/String.cpp">String.cpp</a>).</p>
<pre>
void UTF8::String::recode() {
    // Iterate until all UTF8 characters are normalized.
    // UTF8 in canonical form can only be smaller, so work in-place.
    char* p1 = pBuffer;
    char* p2 = pBuffer;
    char* pEOS = pBuffer + nContent;
    while (p1 < pEOS) {
        int c = 255 &#038; *p1++;
        if (c < 0x80) {
            *p2++ = c;
            continue;
        }
        if (c < 0xE0) {
            c = (31 &#038; c) << 6;
            c |= 63 &#038; *p1++;
        } else if (c < 0xF0) {
            c = (15 &#038; c) << 12;
            c |= (63 &#038; *p1++) << 6;
            c |= 63 &#038; *p1++;
        } else if (c < 0xF8) {
            c = (7 &#038; c) << 18;
            c |= (63 &#038; *p1++) << 12;
            c |= (63 &#038; *p1++) << 6;
            c |= 63 &#038; *p1++;
        } else if (c < 0xFC) {
            c = (3 &#038; c) << 24;
            c |= (63 &#038; *p1++) << 18;
            c |= (63 &#038; *p1++) << 12;
            c |= (63 &#038; *p1++) << 6;
            c |= 63 &#038; *p1++;
        } else {
            c = (1 &#038; c) << 30;
            c |= (63 &#038; *p1++) << 24;
            c |= (63 &#038; *p1++) << 18;
            c |= (63 &#038; *p1++) << 12;
            c |= (63 &#038; *p1++) << 6;
            c |= 63 &#038; *p1++;
        }
        if (c < 0x80) {
            *p2++ = c;
        } else if (c < 0x800) {
            *p2++ = 0xC0 | (c >> 6);
            *p2++ = 0x80 | (63 &#038; c);
        } else if (c < 0x10000) {
            *p2++ = 0xE0 | (c >> 12);
            *p2++ = 0x80 | (63 &#038; (c >> 6));
            *p2++ = 0x80 | (63 &#038; c);
        } else if (c < 0x200000) {
            *p2++ = 0xF0 | (c >> 18);
            *p2++ = 0x80 | (63 &#038; (c >> 12));
            *p2++ = 0x80 | (63 &#038; (c >> 6));
            *p2++ = 0x80 | (63 &#038; c);
        } else if (c < 0x4000000) {
            *p2++ = 0xF8 | (c >> 24);
            *p2++ = 0x80 | (63 &#038; (c >> 18));
            *p2++ = 0x80 | (63 &#038; (c >> 12));
            *p2++ = 0x80 | (63 &#038; (c >> 6));
            *p2++ = 0x80 | (63 &#038; c);
        } else {
            *p2++ = 0xFC | (1 &#038; (c >> 30));
            *p2++ = 0x80 | (63 &#038; (c >> 24));
            *p2++ = 0x80 | (63 &#038; (c >> 18));
            *p2++ = 0x80 | (63 &#038; (c >> 12));
            *p2++ = 0x80 | (63 &#038; (c >> 6));
            *p2++ = 0x80 | (63 &#038; c);
        }
    }
    nContent = (int) (p2 - pBuffer);
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/01/16/efficient-utf-8-recoding-and-secure-processing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>UTF8/UCS conversion benchmark</title>
		<link>http://bannister.us/weblog/2010/01/12/utf8ucs-conversion-benchmark/</link>
		<comments>http://bannister.us/weblog/2010/01/12/utf8ucs-conversion-benchmark/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 05:12:39 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1709</guid>
		<description><![CDATA[Point of reference&#8230; UCS (Unicode) to UTF8 conversion, and the reverse, when efficiently coded in C++ clocks in well above 100MB/s on current generation CPUs. If you are getting something much less &#8211; enough to be a problem &#8211; then there are questions you should ask. The following run spans 1 to 6 byte UTF8 [...]]]></description>
			<content:encoded><![CDATA[<p>Point of reference&#8230;</p>
<p>UCS (Unicode) to UTF8 conversion, and the reverse, when efficiently coded in C++ clocks in <b>well above</b> 100MB/s on current generation CPUs. If you are getting something much less &#8211; enough to be a problem &#8211; then there are questions you should ask. The following run spans 1 to 6 byte UTF8 encodings.</p>
<p>Recoding a UTF8 string to a normalized form is a bit slower at 122-87 MB/s.</p>
<pre>
preston@athena:~/workspace/json-c$ time Release/json
base 00000010 :  <b>229.9 MB/s UCS to UTF8 conversion</b>
base 00000010 :  <b>253.7 MB/s UTF8 to UCS conversion</b>
base 00000010 :  <b>122.0 MB/s UTF8 recode</b>
base 02000010 :  196.8 MB/s UCS to UTF8 conversion
base 02000010 :  177.1 MB/s UTF8 to UCS conversion
base 02000010 :  100.6 MB/s UTF8 recode
base 04000010 :  173.8 MB/s UCS to UTF8 conversion
base 04000010 :  157.1 MB/s UTF8 to UCS conversion
base 04000010 :   89.2 MB/s UTF8 recode
base 06000010 :  174.0 MB/s UCS to UTF8 conversion
base 06000010 :  158.5 MB/s UTF8 to UCS conversion
base 06000010 :   89.2 MB/s UTF8 recode
base 08000010 :  174.0 MB/s UCS to UTF8 conversion
base 08000010 :  154.5 MB/s UTF8 to UCS conversion
base 08000010 :   89.6 MB/s UTF8 recode
base 0a000010 :  174.1 MB/s UCS to UTF8 conversion
base 0a000010 :  156.5 MB/s UTF8 to UCS conversion
base 0a000010 :   89.8 MB/s UTF8 recode
base 0c000010 :  174.0 MB/s UCS to UTF8 conversion
base 0c000010 :  155.1 MB/s UTF8 to UCS conversion
base 0c000010 :   89.6 MB/s UTF8 recode
base 0e000010 :  174.0 MB/s UCS to UTF8 conversion
base 0e000010 :  158.8 MB/s UTF8 to UCS conversion
base 0e000010 :   87.4 MB/s UTF8 recode
base 10000010 :  170.8 MB/s UCS to UTF8 conversion
base 10000010 :  158.2 MB/s UTF8 to UCS conversion
base 10000010 :   89.7 MB/s UTF8 recode
base 12000010 :  174.0 MB/s UCS to UTF8 conversion
base 12000010 :  158.8 MB/s UTF8 to UCS conversion
base 12000010 :   86.5 MB/s UTF8 recode
base 14000010 :  171.5 MB/s UCS to UTF8 conversion
base 14000010 :  153.9 MB/s UTF8 to UCS conversion
base 14000010 :   87.1 MB/s UTF8 recode
base 16000010 :  172.1 MB/s UCS to UTF8 conversion
base 16000010 :  158.1 MB/s UTF8 to UCS conversion
base 16000010 :   87.5 MB/s UTF8 recode
base 18000010 :  172.1 MB/s UCS to UTF8 conversion
base 18000010 :  158.2 MB/s UTF8 to UCS conversion
base 18000010 :   86.9 MB/s UTF8 recode
base 1a000010 :  171.3 MB/s UCS to UTF8 conversion
base 1a000010 :  158.2 MB/s UTF8 to UCS conversion
base 1a000010 :   86.5 MB/s UTF8 recode
base 1c000010 :  169.5 MB/s UCS to UTF8 conversion
base 1c000010 :  158.5 MB/s UTF8 to UCS conversion
base 1c000010 :   86.5 MB/s UTF8 recode
base 1e000010 :  173.0 MB/s UCS to UTF8 conversion
base 1e000010 :  157.8 MB/s UTF8 to UCS conversion
base 1e000010 :   86.1 MB/s UTF8 recode
base 20000010 :  173.1 MB/s UCS to UTF8 conversion
base 20000010 :  158.4 MB/s UTF8 to UCS conversion
base 20000010 :   87.2 MB/s UTF8 recode
base 22000010 :  173.1 MB/s UCS to UTF8 conversion
base 22000010 :  158.2 MB/s UTF8 to UCS conversion
base 22000010 :   88.2 MB/s UTF8 recode
base 24000010 :  173.3 MB/s UCS to UTF8 conversion
base 24000010 :  158.8 MB/s UTF8 to UCS conversion
base 24000010 :   87.6 MB/s UTF8 recode
base 26000010 :  173.8 MB/s UCS to UTF8 conversion
base 26000010 :  158.1 MB/s UTF8 to UCS conversion
base 26000010 :   87.9 MB/s UTF8 recode
base 28000010 :  172.0 MB/s UCS to UTF8 conversion
base 28000010 :  158.7 MB/s UTF8 to UCS conversion
base 28000010 :   87.6 MB/s UTF8 recode
base 2a000010 :  172.0 MB/s UCS to UTF8 conversion
base 2a000010 :  158.1 MB/s UTF8 to UCS conversion
base 2a000010 :   86.1 MB/s UTF8 recode
base 2c000010 :  172.1 MB/s UCS to UTF8 conversion
base 2c000010 :  158.5 MB/s UTF8 to UCS conversion
base 2c000010 :   86.3 MB/s UTF8 recode
base 2e000010 :  173.8 MB/s UCS to UTF8 conversion
base 2e000010 :  158.2 MB/s UTF8 to UCS conversion
base 2e000010 :   89.3 MB/s UTF8 recode
base 30000010 :  170.0 MB/s UCS to UTF8 conversion
base 30000010 :  158.7 MB/s UTF8 to UCS conversion
base 30000010 :   87.2 MB/s UTF8 recode
base 32000010 :  173.8 MB/s UCS to UTF8 conversion
base 32000010 :  158.4 MB/s UTF8 to UCS conversion
base 32000010 :   87.8 MB/s UTF8 recode
base 34000010 :  173.5 MB/s UCS to UTF8 conversion
base 34000010 :  158.5 MB/s UTF8 to UCS conversion
base 34000010 :   88.0 MB/s UTF8 recode
base 36000010 :  173.1 MB/s UCS to UTF8 conversion
base 36000010 :  158.4 MB/s UTF8 to UCS conversion
base 36000010 :   88.4 MB/s UTF8 recode
base 38000010 :  172.5 MB/s UCS to UTF8 conversion
base 38000010 :  158.5 MB/s UTF8 to UCS conversion
base 38000010 :   88.7 MB/s UTF8 recode
base 3a000010 :  170.5 MB/s UCS to UTF8 conversion
base 3a000010 :  158.4 MB/s UTF8 to UCS conversion
base 3a000010 :   87.4 MB/s UTF8 recode
base 3c000010 :  169.3 MB/s UCS to UTF8 conversion
base 3c000010 :  154.7 MB/s UTF8 to UCS conversion
base 3c000010 :   86.3 MB/s UTF8 recode
base 3e000010 :  172.0 MB/s UCS to UTF8 conversion
base 3e000010 :  156.9 MB/s UTF8 to UCS conversion
base 3e000010 :   87.9 MB/s UTF8 recode
base 40000010 :  171.8 MB/s UCS to UTF8 conversion
base 40000010 :  148.9 MB/s UTF8 to UCS conversion
base 40000010 :   83.7 MB/s UTF8 recode
base 42000010 :  173.1 MB/s UCS to UTF8 conversion
base 42000010 :  149.3 MB/s UTF8 to UCS conversion
base 42000010 :   88.1 MB/s UTF8 recode
base 44000010 :  173.7 MB/s UCS to UTF8 conversion
base 44000010 :  157.7 MB/s UTF8 to UCS conversion
base 44000010 :   87.4 MB/s UTF8 recode
base 46000010 :  172.1 MB/s UCS to UTF8 conversion
base 46000010 :  152.2 MB/s UTF8 to UCS conversion
base 46000010 :   87.3 MB/s UTF8 recode
base 48000010 :  174.0 MB/s UCS to UTF8 conversion
base 48000010 :  142.7 MB/s UTF8 to UCS conversion
base 48000010 :   87.8 MB/s UTF8 recode
base 4a000010 :  173.5 MB/s UCS to UTF8 conversion
base 4a000010 :  150.2 MB/s UTF8 to UCS conversion
base 4a000010 :   87.7 MB/s UTF8 recode
base 4c000010 :  174.0 MB/s UCS to UTF8 conversion
base 4c000010 :  158.4 MB/s UTF8 to UCS conversion
base 4c000010 :   88.4 MB/s UTF8 recode
base 4e000010 :  174.0 MB/s UCS to UTF8 conversion
base 4e000010 :  156.9 MB/s UTF8 to UCS conversion
base 4e000010 :   88.4 MB/s UTF8 recode

real	2m0.751s
user	2m0.540s
sys	0m0.190s
</pre>
<p>The above run is on a <b>AMD Phenom(tm) II X4 955 Processor</b> running at the stock clock rate. </p>
<p>Code is in: <a href="http://svn.bannister.us/public/json-c/">http://svn.bannister.us/public/json-c/</a><br />
(A start on an experiment in fastest-possible JSON conversion.)</p>
<p>Note that I intentionally allow proper conversion of &#8220;invalid&#8221; UTF8 code strings. I completely understand the reason for the disallowed conversions, and I disagree.</p>
<p><b>Update:</b> Converted to use pointer arithmetic, rather than array and index. Was not sure pointer math was still a win on current CPUs and compilers. Got a big boost in throughput, so it is!</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/01/12/utf8ucs-conversion-benchmark/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Musing about cumulative impact</title>
		<link>http://bannister.us/weblog/2010/01/08/musing-about-cumulative-impact/</link>
		<comments>http://bannister.us/weblog/2010/01/08/musing-about-cumulative-impact/#comments</comments>
		<pubDate>Fri, 08 Jan 2010 20:52:45 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1705</guid>
		<description><![CDATA[About 15 years back I was working on a C++ GUI application with a cyclic workload and a lot of string manipulation. For both performance and reliability I came up with a lightweight string class that did allocations off a free list. The class benchmarked well, and performed very well in practice. About 10 years [...]]]></description>
			<content:encoded><![CDATA[<p>About 15 years back I was working on a C++ GUI application with a cyclic workload and a lot of string manipulation. For both performance and reliability I came up with a lightweight string class that did allocations off a free list. The class benchmarked well, and performed very well in practice. About 10 years back I started writing a C++ back-end bulk processing application with <b>massive</b> string manipulation, and re-used the same lightweight string (with excellent results). Five years back I again ran benchmarks (with good results) and <a href="http://bannister.us/weblog/2005/01/17/how-to-build-a-better-string-class-and-a-surprise/">wrote up the results</a>.</p>
<p>The lightweight string class is simple enough to be reproduced from memory (nothing over-complicated), but can make quite a difference in performance. I had hoped to make a point.</p>
<p>Since posting, after the initial burst, the articles have collected what looks like a few new hits, <b>every</b> day, for the past five years. Counting hits is a pretty foggy indicator, but it seems possible quite a few folk have read the article (though a relatively small fraction of the programming community). Some may have copied the C++ string class in their code. (What I hoped!) </p>
<p>But there is no sure way of knowing. What is the cumulative impact of this steady trickle of readers?</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2010/01/08/musing-about-cumulative-impact/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Almost but not quite &#8230; server-side JavaScript</title>
		<link>http://bannister.us/weblog/2009/12/30/almost-but-not-quite-server-side-javascript/</link>
		<comments>http://bannister.us/weblog/2009/12/30/almost-but-not-quite-server-side-javascript/#comments</comments>
		<pubDate>Thu, 31 Dec 2009 04:05:14 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1678</guid>
		<description><![CDATA[Bit over three years back I looked at server-side Javascript, and was not enthused with the available choices. Three distinct usages I&#8217;d like to cover: optimal performance,Windows web server (IIS) interoperable, and webhosting. In addition, there are three interesting aspects of optimal performance: throughput, scalability, and stability. For serving static content, I really like the [...]]]></description>
			<content:encoded><![CDATA[<p>Bit over three years back I looked at <a href="http://bannister.us/weblog/2006/12/19/revisiting-server-side-javascript/">server-side Javascript</a>, and was not enthused with the available choices. </p>
<p>Three distinct usages I&#8217;d like to cover: optimal performance,Windows web server (IIS) interoperable, and webhosting.</p>
<p>In addition, there are three interesting aspects of optimal performance: throughput, scalability, and stability.</p>
<p>For serving static content, I really like the model of a single-threaded non-blocking web server, of which <a href="http://www.acme.com/software/thttpd/">thttpd</a> was an early example, and for which the <a href="http://www.kegel.com/c10k.html">C10K</a> question clarified the need. A small/simple web server has a much better chance to being very reliable. With the single-threaded non-blocking model, massive scalability is possible.</p>
<p>For serving dynamic content, I really like the isolation and load distribution possible with the <a href="http://en.wikipedia.org/wiki/FastCGI">FastCGI</a> model (or the like). Dynamic code tends to be complex. Javascript interpreters are complex. Complex code tends to fail more often. Complex code can use more compute throughput than possible on a single box. For intranet applications, a single front-end web server is often preferable, and load distribution via FastCGI offers more headroom. All of which tends to argue for the FastCGI model, with isolation from the front-end web service, and potential distribution of load across more than one machine.</p>
<p>For the widest possible usage, in additional to optimal deployments (when there is no restriction on the front-end web server), the engine on which the application runs should be deployable behind IIS (for Windows-only organizations), and at common web-hosting services (like <a href="http://dreamhost.com/">Dreamhost</a>). Microsoft&#8217;s recent support of <a href="http://www.iis.net/expand/FastCGI">FastCGI with IIS</a> is a big help.</p>
<p>At that time (three years back), none of the solutions were really optimal &#8211; and in fact were pretty far from optimal. The Java-based <a href="http://www.mozilla.org/rhino/">Rhino</a>Javascript interpreter was easiest to embed, but failed the webhosting case. The C++ based JavaScript interpreters were a pain to embed, and offered good (but not great) performance. </p>
<p>Fast forward to the present, and Google offers the <a href="http://code.google.com/p/v8/">V8 JavaScript Engine</a> that offers <b>great</b> performance, and is easy to embed. (Google as the good guys, riding to the rescue once again &#8230; you&#8217;d think they have white hats superglued to their brains.) Suddenly we have lots of <a href="http://code.google.com/hosting/search?q=label:v8">projects embedding the V8 engine</a>. In addition, seems most all the single-threaded non-blocking web servers have picked up support for FastCGI.</p>
<p>Oh &#8230; and I am pretty much fed up with the <a href="http://en.wikipedia.org/wiki/Java_Servlet">Java Servlet</a> model. After considerable time with the problem, I am of the opinion that the servlet model chose the wrong abstractions, and this makes for awkward solutions. (Of course, the servlet model appeared very early in the history of web applications, so the mistake is easy to understand.)</p>
<p>Which means the model offered by <a href="">node.js</a> makes a lot of sense. I <i>like</i> the notion of a naked node (running JavaScript on the V8 engine) performing request dispatch without any extra layers or abstractions. The main lack with <b>node.js</b> is the ability to work via FastCGI (and thus no means to be deployed behind IIS on Windows).</p>
<p>But there are as yet items unresolved and/or unclear.</p>
<ul>
<li>Projects like <a href="http://code.google.com/p/v8cgi/">v8cgi</a> offer the V8 JavaScript Engine connected via FastCGI.</li>
<li>The <b>node.js</b> project offers a single-threaded non-blocking web server &#8230; but can it work behind FastCGI?</li>
<li>Is the environment for server-side JavaScript the same (or sufficiently similar) between <b>node.js</b> and <b>v8cgi</b>?</li>
<li><a href="http://en.wikipedia.org/wiki/Comet_(programming)">Comet</a> is still a question. Can FastCGI work well with long-outstanding requests from applications?</li>
</ul>
<p>The good news is that we seem a lot closer to attractive and well-supported server-side JavaScript for web applications &#8230; but it seems we are not quite fully there, as yet.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/12/30/almost-but-not-quite-server-side-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wireless network and Linux</title>
		<link>http://bannister.us/weblog/2009/12/24/wireless-network-and-linux/</link>
		<comments>http://bannister.us/weblog/2009/12/24/wireless-network-and-linux/#comments</comments>
		<pubDate>Fri, 25 Dec 2009 03:40:33 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1672</guid>
		<description><![CDATA[A signpost of sort &#8211; wireless network support on Linux, at least for the Intel 4965AGN adaptor &#8211; sucks. Went with the Intel adaptor when I ordered this notebook, in part as Intel seems to be actively supporting the development of Linux drivers. In practice, my laptop wireless connection is mostly unreliable, and often near-useless. [...]]]></description>
			<content:encoded><![CDATA[<p>A signpost of sort &#8211; wireless network support on Linux, at least for the <a href="http://www.intel.com/network/connectivity/products/wireless/wireless_n/overview.htm">Intel 4965AGN adaptor</a> &#8211; sucks.</p>
<p>Went with the Intel adaptor when I ordered this notebook, in part as Intel seems to be actively supporting the <a href="http://intellinuxwireless.org/">development of Linux drivers</a>. In practice, my laptop wireless connection is mostly unreliable, and often near-useless.</p>
<p>I used to think the problem was interference from other nearby wireless routers, but my daughter&#8217;s laptop seems to work well when mine does not. I have a simple <a href="http://bannister.us/examples/json/bounce.html">&#8220;bounce&#8221;</a> test for network performance. My daughter&#8217;s cheap Toshiba, on the same &#8220;bounce&#8221; page, gets steady/fast performance when side-by-side to my laptop &#8230; which gets slower/unsteady/unreliable performance.</p>
<p>Not specific to the wireless router or crowded neighborhood either &#8211; I get similar poor performance when at my father&#8217;s place in Colorado (different brand and generation of router, and fewer/further neighbors).</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/12/24/wireless-network-and-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Trie in Java &#8211; revisited</title>
		<link>http://bannister.us/weblog/2009/12/23/trie-in-java-revisited/</link>
		<comments>http://bannister.us/weblog/2009/12/23/trie-in-java-revisited/#comments</comments>
		<pubDate>Thu, 24 Dec 2009 01:12:51 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1666</guid>
		<description><![CDATA[An earlier attempt at writing a fast general purpose Trie in Java gave huge memory use, and disappointing results. Seems a Trie implementation that is both fast and general purpose is not possible. (Translation: For most use a Trie cannot replace a hash table.) After the prior results, I wanted to see if a less [...]]]></description>
			<content:encoded><![CDATA[<p>An earlier attempt at writing a fast <a href="http://bannister.us/weblog/2009/10/05/example-general-purpose-trie-in-java/">general purpose Trie in Java</a> gave <b>huge</b> memory use, and disappointing results. Seems a Trie implementation that is both fast and general purpose is not possible. (Translation: For most use a Trie cannot replace a hash table.)</p>
<p>After the prior results, I wanted to see if a less general-purpose implementation would perform better. Given enough advantages, could a Trie out-perform a hash table? (Again, this visits to some extent the question asked in a <a href="http://www.daniel-lemire.com/blog/archives/2009/10/02/sensible-hashing-of-variable-length-strings-is-impossible/">prior discussion</a>.)</p>
<p>For the current exercise I built two Trie implementations. The <a href="http://svn.bannister.us/public/Trie/sources/us/bannister/structures/trie2/LinkedTrie.java">LinkedTrie</a> is cheap to build, minimal in use of memory, but not especially fast to access. The <a href="http://svn.bannister.us/public/Trie/sources/us/bannister/structures/trie2/FixedTrie.java">FixedTrie</a> implementation should be pretty close to optimal in access time (for a Trie), but expensive to build (in fact the <a href="http://svn.bannister.us/public/Trie/sources/us/bannister/structures/trie2/FixedTrieBuilder.java">FixedTrieBuilder</a> transforms a LinkedTrie into an optimized FixedTrie).</p>
<p>As before, the sources are in (as an Eclipse project): <a href="http://svn.bannister.us/public/Trie/">http://svn.bannister.us/public/Trie/</a>.</p>
<p>The performance numbers make sense. The older <b>TallTrie</b> and <b>WideTrie</b> implementations (that traded increased memory use for speed) are indeed faster, though the <b>LinkedTrie</b> uses much(!) less memory. The new <b>FixedTrie</b> is fastest (hurrah!) and uses the least memory (a shade less than <b>LinkedTrie</b>). </p>
<p>But even the <b>FixedTrie</b> is slower than a generic hash table, with read-only access about 3 times as expensive.</p>
<p>Sample measurements&#8230;
<pre>
=== words
21 ms - 98569 words loaded

=== hash
3935/second - hash map loaded {4007 ms, 15771040 operations = 254 ns/op}
8554/second - hash map re-loaded {4010 ms, 34302012 operations = 116 ns/op}
<b>10768/second</b> - access each item in hash map {4000 ms, 43074653 operations = 92 ns/op}

=== trie (linked)
870/second - loaded trie (linked) {4078 ms, 3548484 operations = 1149 ns/op}
1204/second - re-loaded trie (linked) {4010 ms, 4829881 operations = 830 ns/op}
<b>1431/second</b> - access each item in trie (linked) {4063 ms, 5815571 operations = 698 ns/op}
98569 slots of trie (linked)
225791 nodes of trie (linked)

=== build fixed trie

=== trie (fixed)
85779/second - loaded trie (fixed) {4000 ms, 343118689 operations = 11 ns/op}
87652/second - re-loaded trie (fixed) {4000 ms, 350609933 operations = 11 ns/op}
<b>3438/second</b> - access each item in trie (fixed) {4013 ms, 13799660 operations = 290 ns/op}
98569 slots of trie (fixed)
225791 nodes of trie (fixed)

=== trie (wide)
65/second - loaded trie (wide) {4502 ms, 295707 operations = 15224 ns/op}
1659/second - re-loaded trie (wide) {4040 ms, 6702692 operations = 602 ns/op}
<b>1672/second</b> - access each item in trie (wide) {4007 ms, 6702692 operations = 597 ns/op}
41533124 slots of trie (wide)
225890 nodes of trie (wide)

=== trie (tall)
682/second - loaded trie (tall) {4043 ms, 2759932 operations = 1464 ns/op}
1733/second - re-loaded trie (tall) {4036 ms, 6998399 operations = 576 ns/op}
<b>1913/second</b> - access each item in trie (tall) {4019 ms, 7688382 operations = 522 ns/op}
6982694 slots of trie (tall)
446075 nodes of trie (tall)

=== string to UTF8 conversion
1393/second - word to UTF8 (stock) {4033 ms, 5618433 operations = 717 ns/op}
9585/second - word to UTF8 (fast) {4000 ms, 38343341 operations = 104 ns/op}
</pre>
<p>(Note that the <b>FixedTrie</b> ignores re-load, so the times for load and re-load are bogus.)</p>
<p>I suspect a C++ Trie implementation could do a bit better &#8230; but not necessarily outperform &#8230; compared to hash tables.</p>
<p>Looks very much like even a specialized read-only Trie cannot match the performance of a generic hash table (at least in Java).</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/12/23/trie-in-java-revisited/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Identifying and documenting bugs in Sun Java Printing</title>
		<link>http://bannister.us/weblog/2009/11/30/identifying-and-documenting-bugs-in-sun-java-printing/</link>
		<comments>http://bannister.us/weblog/2009/11/30/identifying-and-documenting-bugs-in-sun-java-printing/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 21:26:57 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1645</guid>
		<description><![CDATA[I have had a fair amount of trouble with odd behaviors and bugs in printing with Java. The time I have had to spend at work to eliminate troublesome behavior in a heavily used Java application (both before customers see the problem, and after customers report problems) &#8230; is embarrassing. After getting stuck on the [...]]]></description>
			<content:encoded><![CDATA[<p>I have had a fair amount of trouble with odd behaviors and bugs in printing with Java. The time I have had to spend at work to eliminate troublesome behavior in a heavily used Java application (both before customers see the problem, and after customers report problems) &#8230; is embarrassing.</p>
<p>After getting stuck on the latest problem, I finally built a &#8220;PrintExplorer&#8221; test program. The sources are up on <a href="http://kenai.com/projects/print-explorer">Kenai</a>, and on <a href="http://hg.bannister.us/public/PrintExplorer/">my website</a>. In both cases the sources are in a Mercurial repository, and I &#8220;push&#8221; changes to both (one of the nice features of Mercurial). The project was built using NetBeans 6.7, and you can download the built <a href="http://hg.bannister.us/public/PrintExplorer/file/d5468266772f/dist/PrintExplorer.jar">PrintExplorer.jar</a> file. </p>
<p>Run as usual:
<pre>
java -jar PrintExplorer.jar
</pre>
<p>(On Windows you can just double-click the JAR file.)</p>
<p>I filed two reports (so far, with review ids: 1660182 and 1660234) with <a href="http://bugs.sun.com/">bugs.sun.com</a>, though for all I know that may be a black hole (or not &#8230; I have no idea). With the <a href="http://openjdk.java.net/">OpenJDK</a> and <a href="https://jdk7.dev.java.net/">JDK7</a> work there may be better places to report/record this &#8230; I do not know. (If you know a better path, please <a href="mailto:preston@bannister.us">let me know</a>.)</p>
<p>Found so far:
<ul>
<li>PrinterJob.getPageDialog() makes invalid change to page size. <a href='http://bannister.us/weblog/wp-content/uploads/2009/11/print-test-log.html'>(log)</a></li>
<li>Toolkit.getPrintJob() changes JobAttributes.getPrinter() to default(?) printer. <a href='http://bannister.us/weblog/wp-content/uploads/2009/11/print-test-log1.html'>(log)</a></li>
<li>Quarter-sized pages sometimes printed on an HP DeskJet 5550 on Windows 7. <a href='http://bannister.us/weblog/wp-content/uploads/2009/11/print-test-log2.html'>(log)</a></li>
</ul>
<p>I am sure there are more. :/</p>
<p>Note that PrintExplorer always generates a log file. The log file contains the parameter values and print operations performed. (You need to be acquainted with Java Printing, otherwise the test program will be entirely obscure.) If you can reproduce a problem, then you can see in the log file <b>exactly</b> what you did, and what was returned. (Useful when you lost track of the exact sequence of steps.) When you send a log file, anyone can verify the exact sequence of steps you followed (a mistake in the problem description can be caught).</p>
<p>At this point the <b>DocPrinterJob</b> works for an empty Doc and a Pageable Doc, but not for a Reader Doc. (Unclear if the last is my bug, or a Java bug.)</p>
<p><b>Update &#8211; 2010.02.01</b><br />
Today the filed reports became public-visible bugs <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6921596">6921596</a> and <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6921597">6921597</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/11/30/identifying-and-documenting-bugs-in-sun-java-printing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tilting at windmills</title>
		<link>http://bannister.us/weblog/2009/11/23/tilting-at-windmills/</link>
		<comments>http://bannister.us/weblog/2009/11/23/tilting-at-windmills/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 07:10:13 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1635</guid>
		<description><![CDATA[Bought a Canon MX860. Did my research beforehand, and was able to force-install the 32-bit Canon drivers (for printing) on my 64-bit Ubuntu desktop. Getting scanning to work is another exercise as yet not done. The fact that Canon has published sources that (with the exception of a missing library) could be compiled as 64-bit [...]]]></description>
			<content:encoded><![CDATA[<p>Bought a Canon MX860. Did my research beforehand, and was able to force-install the 32-bit Canon drivers (for printing) on my 64-bit Ubuntu desktop. Getting scanning to work is another exercise as yet not done. The fact that Canon has published sources that (with the exception of a missing library) could be compiled as 64-bit native, lead me to poke Canon USA support.</p>
<p>My original query as represented in Canon&#8217;s support system (and trimmed):</p>
<blockquote><p>
Original Message Follows:<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Email Support Form Message<br />
Product Type: PIXMA MX860<br />
Product Serial Number: absx22394<br />
Date of Purchase: 11/18/2009<br />
First Name: Preston<br />
Last Name: Bannister<br />
Address: 14 Vallecito<br />
City: Foothill Ranch<br />
State: CA<br />
Zip: 92610<br />
Phone Number: 9495880872<br />
Email Address: preston@bannister.us<br />
INQUIRY: Need drivers for 64-bit Ubuntu (Debian Linux).<br />
Found the 32-bit drivers and sources on the Canon European site, but the sources are incomplete, so I cannot compile a 64-bit version.<br />
Either compiled 64-bit drivers, or a complete set of sources would be acceptable.
</p></blockquote>
<p>The response was pretty predictable, if not exactly addressing the question as posed.</p>
<blockquote><p>
On Fri, Nov 20, 2009 at 5:09 AM, Canon Support &#8211; MFP &lt;mfp @cits.canon.com&gt; wrote:</p>
<p>Dear Preston Bannister:</p>
<p>Thank you for contacting Canon product support.  We value you as a Canon customer and appreciate the opportunity to assist you.  We regret the issues experienced with locating Linux drivers for the MX860.</p>
<p>Canon USA does not support Linux with its consumer products.  The only recommendation I can offer would be to check the various Linux enthusiast websites to see if anyone has successfully attempted writing a third party driver.</p>
<p>We apologize for any inconvenience.  Please let us know if we can be of any further assistance with your MX860.</p>
<p>Sincerely,</p>
<p>James<br />
Technical Support Representative
</p></blockquote>
<p>Since there is always a chance (if small) of a constructive outcome, I tried again.</p>
<blockquote><p>
What I did was find that the Canon Europe and Canon Australia sites distribute Canon-developed Linux drivers for the MX860 (and other printers).</p>
<p>The software is pretty well done, has an active documented development history that goes at least to the year 2000, and is distributed as both binaries and source.</p>
<p>Odd that Canon USA does not distribute Canon-developed drivers (in which Canon has made a significant investment).</p>
<p>This is a case of so-close-but-not-quite there. The effort required for Canon to have a complete offering is pretty small, if someone can find the right branch of the company hierarchy to nudge.</p>
<p>Any chance of finding who within Canon sponsored development of the existing drivers for Linux?
</p></blockquote>
<p>The last response, which sounds dangerously close to &#8220;Fuck you. Go away.&#8221; (Though I would gladly be proven wrong.)</p>
<blockquote><p>
Dear Preston Bannister:</p>
<p>We appreciate your continued correspondence regarding Linux drivers for your PIXMA MX860.</p>
<p>I have forwarded your comments to Canon USA through our Customer Feedback process.  This process allows us to capture important feedback from our valued customers.  As we constantly strive to improve our products and services, your comments are vital to our continued success.</p>
<p>We hope this information is helpful to you.  Please let us know if we can be of any further assistance with your PIXMA MX860.</p>
<p>Thank you for choosing Canon.</p>
<p>Sincerely,</p>
<p>George<br />
Technical Support Representative
</p></blockquote>
<p>Had to try&#8230;.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/11/23/tilting-at-windmills/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>First impressions &#8211; Google Wave</title>
		<link>http://bannister.us/weblog/2009/10/24/first-impressions-google-wave/</link>
		<comments>http://bannister.us/weblog/2009/10/24/first-impressions-google-wave/#comments</comments>
		<pubDate>Sat, 24 Oct 2009 19:09:03 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1584</guid>
		<description><![CDATA[Basically, Google Wave is a full and natural merger of messaging, on the web. Before you say &#8220;oh, only that&#8221;, think this through. This is a pretty big deal. Put differently, this is messaging where: Conversations are a first-class concept, and each message is a part of a conversation. Each message can be presented by [...]]]></description>
			<content:encoded><![CDATA[<p>Basically, Google Wave is a full and natural merger of messaging, on the web. Before you say &#8220;oh, only that&#8221;, think this through. This is a pretty big deal. Put differently, this is messaging where:</p>
<ul>
<li>Conversations are a first-class concept, and each message is a part of a conversation.</li>
<li>Each message can be presented by a different application.</li>
</ul>
<p>Because Wave is on the web, the set of applications is come from <i>all applications in the world</i> (on the web).</p>
<p>E-mail clients can present &#8220;threads&#8221; that are something like conversations, but this requires a bit of guessing and fakery on the part of the client, and is easily confused. IM clients can present a log of messages &#8211; that looks something like a conversation &#8211; but does not work when you switch between devices. Given that email and IM servers know nothing about conversations, the clients do pretty much the best they can. If you know about conversations on the server, you can do a better job on the client.</p>
<p>When conversations are a first-class concept, there are lots of new possible functions. </p>
<p>On the flip side, the Google Wave folk have made some (common) missteps. </p>
<ul>
<li>Using new names for existing notions is sometimes useful, but more often not. Calling a conversation a &#8220;wave&#8221;, calling items (messages) within a conversation &#8220;blits&#8221; &#8230; I find this more confusing than useful. This reminds me more of talk from marketing folk than engineers.</li>
<li>It&#8217;s <a href="http://en.wikipedia.org/wiki/Talk_(software)">Talk</a>, on the web. As a programmer, I get it &#8211; this is <i>kewl!</i> Writing a web application that allows all participants to see each character typed &#8211; is tricky. As a demo of tricky programming, this is sure to impress. As a useful feature in a general use application &#8211; not so much. Character-at-a-time message traffic is a huge load increase on the server, and a disservice to the users. Think about:
<ul>
<li><b>Locus of attention</b> &#8211; the on-screen activity from updates to other users&#8217; incomplete messages is sure to distract.</li>
<li><b>Quality of expression</b> &#8211; how often have you started writing a message, then revised what you said and how you said it, before sending? Do you really want everyone to see your missteps &#8230; always?</li>
</ul>
<p>Note that both are a disservice to <b>all</b> participants &#8211; cool in a programming demo, not cool in a deployed application.
</li>
</ul>
<p>Other bits&#8230;.</p>
<ul>
<li>It looks like Outlook. This was new and cool in 1997, but not so much now.  Not at all sure this layout is efficient, especially given the changes in most-common screen layouts. In 1997 you would target 640&#215;480, 800&#215;600, and 1024&#215;768 on 14&#8243; to 17&#8243; as most common screen sizes. Today you are looking 1280&#215;800 on 14&#8243; or 15&#8243; as your minimum size, and 1920&#215;1080 at 20&#8243; or bigger as common. Present and future screens have not so much new height, with lots of new width &#8211; in fact too much new width for text content. Seems a different presentation is in order.
</li>
<li>Mixed applications in one web page is cool and exciting as <a href="http://en.wikipedia.org/wiki/ActiveX">ActiveX</a> was in the web browser in 1996. Viewed in terms of the good things it can do, ActiveX allows an open-ended mix of applications to present within a web page &#8211; very cool. The Microsoft folk were blindsided (as constructive, well-intentioned folk) as they did not consider all the bad things that could be done (and with ActiveX, <i>very</i> bad things are possible). The Google folk have lots of history to make them wary, and the scope for harm is much smaller, so likely little bad stuff get through. Still &#8211; do not underestimate the black hats. There are bound to be some exploits (though likely few and quickly suppressed).</li>
</ul>
<p>In sum, a decent start on a great idea. I&#8217;d want to be pretty ruthless about overhead that takes height away from the conversation view. I&#8217;d drop the per-character updates. I&#8217;d drop the use of funky names for existing ideas. None are fundamental problems, and all are easy to address.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/10/24/first-impressions-google-wave/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PrinterJob.pageDialog() in Java is broken?</title>
		<link>http://bannister.us/weblog/2009/10/16/printerjob-pagedialog-in-java-is-broken/</link>
		<comments>http://bannister.us/weblog/2009/10/16/printerjob-pagedialog-in-java-is-broken/#comments</comments>
		<pubDate>Sat, 17 Oct 2009 06:05:33 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1579</guid>
		<description><![CDATA[More specifically, the handling of margins, PageFormat, and the PrinterJob.pageDialog(PageFormat) seems to be broken. Fixing up an old Java desktop application for viewing old &#8220;green-bar&#8221; reports. Should be pretty simple &#8211; the reports are all fixed pitch text. Given I like to do things that &#8220;just work&#8221; (from the users perspective), I&#8217;d added live &#8220;smart&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>More specifically, the handling of margins, PageFormat, and the PrinterJob.pageDialog(PageFormat) seems to be broken.</p>
<p>Fixing up an old Java desktop application for viewing old &#8220;green-bar&#8221; reports. Should be pretty simple &#8211; the reports are all fixed pitch text. Given I like to do things that &#8220;just work&#8221; (from the users perspective), I&#8217;d added live &#8220;smart&#8221; definition of controllable font-auto-sizing, empty page suppression, and margins. Given that old printed reports may be formatted for paper sizes different than in your desktop (or network) printer, this was slightly tricky, but only that. </p>
<p>Of course, many of those old reports &#8211; meant for old line-printer green-bar paper &#8211; are best printed in landscape orientation. To my surprise, I find when in landscape orientation, the return from PrinterJob.pageDialog(PageFormat) consistently alters the margins, even when no changes were made, and the error is cumulative.</p>
<p>Um, what?</p>
<p>At first I assumed the error was in my code, but this bit of logged output caught the problem:
<pre>
PageFormat validate::
	paper dx: 792.0 dy: 612.0
	margins top: 72 left: 72 bottom: 72 right: 72
	orientation : LANDSCAPE
PageFormat after pageDialog::
	paper dx: 794.0 dy: 613.0
	margins top: 72 left: 74 bottom: 73 right: 72
	orientation : LANDSCAPE
</pre>
<p>The first is the contents of the PageFormat before calling PrinterJob.pageDialog(PageFormat), and the second is the new PageFormat returned. Note the +1 dy bump and +2 dx bump to the page extents (and the matching errors introduced to the left/bottom margins).</p>
<p>The code I used to remove the error:
<pre>
            controller.pageFormat = job.pageDialog(controller.pageFormat);

            // Work around an interesting(?) bug in PrinterJob.pageDialog().
            // For some reason the returned Paper is over-sized. (Why? Dunno.)
            // Use the Paper returned from PrinterJob.defaultPage() to get a proper size.
            // If the Java folk remove the bug, this code should change nothing.
            PageFormat pf = job.defaultPage();
            Paper paper1 = controller.pageFormat.getPaper();
            Paper paper2 = pf.getPaper();
            double x = paper1.getImageableX();
            double y = paper1.getImageableY();
            double dx = paper1.getImageableWidth();
            double dy = paper1.getImageableHeight();
            paper2.setImageableArea(x,y,dx,dy);
            controller.pageFormat.setPaper(paper2);
</pre>
<p>The bug was encountered using Java 1.6.0_15 (64-bit) on Ubuntu (Linux 2.6.28-15-generic #52-Ubuntu SMP Wed Sep 9 10:48:52 UTC 2009 x86_64 GNU/Linux).</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/10/16/printerjob-pagedialog-in-java-is-broken/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Example &#8211; general purpose Trie in Java</title>
		<link>http://bannister.us/weblog/2009/10/05/example-general-purpose-trie-in-java/</link>
		<comments>http://bannister.us/weblog/2009/10/05/example-general-purpose-trie-in-java/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 16:14:25 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1569</guid>
		<description><![CDATA[Run across mention of the Trie data structure in a slightly random discussion. Had no notion of how performance of a Trie compares with the usual hash table, so wrote a general-purpose Trie implementation in Java (sources in an Eclipse project) with a bias toward performance. The results are not encouraging. From a test run: [...]]]></description>
			<content:encoded><![CDATA[<p>Run across mention of the <a href="http://en.wikipedia.org/wiki/Trie">Trie</a> data structure in a <a href="http://www.daniel-lemire.com/blog/archives/2009/10/02/sensible-hashing-of-variable-length-strings-is-impossible/">slightly random discussion</a>. Had no notion of how performance of a Trie compares with the usual hash table, so wrote a general-purpose Trie implementation in Java (<a href="http://svn.bannister.us/public/Trie/">sources in an Eclipse project</a>) with a bias toward performance.</p>
<p>The results are not encouraging. From a test run:
<pre>
=== words
14 ms - 98569 words loaded

=== hash
3837/second - hash map loaded {4007 ms, 15376764 operations = 260 ns/op}
7953/second - hash map re-loaded {4003 ms, 31837787 operations = 125 ns/op}
<b>12958/second - access each item in hash map {4001 ms, 51847294 operations = 77 ns/op}</b>

=== trie (wide)
343/second - loaded trie (wide) {4022 ms, 1379966 operations = 2914 ns/op}
2089/second - re-loaded trie (wide) {4010 ms, 8378365 operations = 478 ns/op}
<b>2165/second - access each item in trie (wide) {4005 ms, 8674072 operations = 461 ns/op}</b>
41533124 slots of trie (wide)
225890 nodes of trie (wide)

=== trie (tall)
2254/second - loaded trie (tall) {4022 ms, 9068348 operations = 443 ns/op}
2446/second - re-loaded trie (tall) {4029 ms, 9856900 operations = 408 ns/op}
<b>2429/second - access each item in trie (tall) {4017 ms, 9758331 operations = 411 ns/op}</b>
6982694 slots of trie (tall)
446075 nodes of trie (tall)

=== string to UTF8 conversion
4708/second - word to UTF8 (stock) {4019 ms, 18925248 operations = 212 ns/op}
10503/second - word to UTF8 (fast) {4007 ms, 42088963 operations = 95 ns/op}
</pre>
<p>The test data is a file of 98569 words found in Linux installation. The words are read from a file, and loaded into an array of strings. The words are loaded into a HashMap (a library class) for comparison, and loaded into both a &#8220;tall&#8221; and &#8220;wide&#8221; Trie (differing space/time trade-offs).</p>
<p>The notion used to generate a &#8220;fast&#8221; Trie is for each node in the Trie to index off a byte in the UTF8 encoding of key String. Along the way, found that the stock String to UTF8 conversion to be a bit slow, so wrote a <a href="http://svn.bannister.us/public/Trie/sources/us/bannister/encoders/EncoderUTF8.java">faster version</a>. Even with the faster encoding, measured Trie performance is not good. </p>
<p>(Bit of a shock to find the stock String-to-UTF8 conversion slow in Java, as this is used a lot &#8211; especially in web applications &#8211; and may be significant enough to effect benchmarks. Java has been around long enough, so that I expected better. In fact, I expected UTF8 to be in native code in the JVM. Byte-bashing code is better suited to C/C++.)</p>
<p>If strings were already in UTF8 (or ASCII) format &#8211; as in C/C++ code &#8211; a similar Trie implementation would perform somewhat better, but still use too much memory. Whether a Trie in C++ could more closely approach the performance of a hash table is a bit of an open question.</p>
<p>Not sure why the <a href="http://svn.bannister.us/public/Trie/sources/us/bannister/structures/WideTrie.java">&#8220;wide&#8221; Trie</a> (using one node to index one UTF8 byte) is slightly slower than the <a href="http://svn.bannister.us/public/Trie/sources/us/bannister/structures/TallTrie.java">&#8220;tall&#8221; Trie</a> (using two nodes to index 4-bits each from one UTF8 byte). Expected the &#8220;wide&#8221; version to be faster. Suspect the larger memory use is enough to bust the CPU cache, and on smaller data the &#8220;wide&#8221; variant may be faster. Also saw a lot of jitter in the times &#8211; presumably due to Java GC activity (garbage collection) &#8211; though the relative results were consistent.</p>
<p>Clearly this &#8220;fast, general-purpose&#8221; Trie implementation is not overly useful &#8211; at ~5 times slower than a hash table, and huge use of memory. Alternate forms can be more space-efficient, but are likely to be slower. </p>
<p>As a guess, a Trie is not going to find much use in my code. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/10/05/example-general-purpose-trie-in-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Concurrency and threading is the new thing, again.</title>
		<link>http://bannister.us/weblog/2009/10/02/concurrency-and-threading-is-the-new-thing-again/</link>
		<comments>http://bannister.us/weblog/2009/10/02/concurrency-and-threading-is-the-new-thing-again/#comments</comments>
		<pubDate>Fri, 02 Oct 2009 20:56:49 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1555</guid>
		<description><![CDATA[Tim Bray is writing a series of posts, taking a run at the concurrent programming problem, with a focus on languages. I think Tim is aiming in the right direction, but has his focus set at the wrong distance. There are good reasons to take a run at the problem. Physics is changing what we [...]]]></description>
			<content:encoded><![CDATA[<p>Tim Bray is writing <a href="http://www.tbray.org/ongoing/When/200x/2009/09/27/Concur-dot-next">a series of posts</a>, taking a run at the concurrent programming problem, with a focus on languages. I think Tim is aiming in the right direction, but has his focus set at the wrong distance.</p>
<p>There are good reasons to take a run at the problem. Physics is changing what we can expect from future computers. Starting a few years back, and barring any unexpected shifts in technology, the rate at which a CPU can process a single instruction stream will increase only slowly. The economics of chip fabrication allow us to build a CPU with multiple cores. The physics of power consumption tell us that we can get more computing done per watt with slimmer cores at slightly lower clock rates. All of which argues for fabricating CPU chips with a slowly increasing number of cores, and slowly increasing clock rates.</p>
<p>All of which means that to make full use of present and future CPUs, there has to be a lot of concurrent computing.</p>
<p>Concurrency in programming is tricky, and often got wrong. This is nothing new. My first job out of college (so many years ago) was to work of an <a href="http://en.wikipedia.org/wiki/Ada_(programming_language)">Ada</a> compiler (a computer language with direct support for multi-threaded interaction) on a product (the <a href="http://en.wikipedia.org/wiki/Pascal_MicroEngine">Pascal Microengine</a>) that had thread support built into the CPU&#8217;s microcode. There was then much talk of how to do concurrent programming.</p>
<p>What we learned then and in the time since is that fine-grained multi-threaded concurrent programming is tricky, and very easy to get wrong. For the bulk of programs and programmers, there is very limited need for this sort of concurrency. All things considered, this is probably a good thing.</p>
<p>Tim Bray &#8211; and the folk responding to his post &#8211; are mainly focusing on a programming language for concurrent programs. I suspect this is a mistake. Maybe some new (or newish) programming language will make bug-free concurrent programs easy, but I do not think this is likely. </p>
<p>I think we already have the bulk solution. Web-scale applications (at least those that work well) make use of large numbers of CPUs, with a huge amount of concurrent execution. Web-scale programming is mostly about swarms of small-scale execution single-threaded programs (not necessarily small), well isolated from other threads. Many of the attributes Tim lists are true &#8211; or mostly-true, or should-be-true &#8211; of web scale applications.</p>
<p>Clearly the web-scale application approach does not work for all applications &#8211; though it may work for more than you expect. There are always going to be some applications that need fine-grained threading &#8230; but I suspect this group is very small. For the bulk of programming we want to allow for massive concurrency, but well-isolated and coarse-grained.</p>
<p>Why the focus on programming languages that support fine-grained concurrent programs? What problem &#8211; in the application space &#8211; does that solve? Are more than a tiny fraction of applications in that space?</p>
<p>My answer to Tim&#8217;s question is to point at the concerns of web-scale applications and cloud-computing. The problem does not drive an interest in new programming languages. The answer to large-scale concurrent execution is &#8211; for most applications &#8211; large numbers of single-threaded programs, responding to requests. Tim&#8217;s list of characteristics &#8211; in part &#8211; is useful for that sort of programming. Editing down the list:</p>
<ul>
<li><a href="http://www.tbray.org/ongoing/When/200x/2009/09/30/C-dot-next-laundry-list#p-4">Message passing</a><br />
and <a href="http://www.tbray.org/ongoing/When/200x/2009/09/30/C-dot-next-laundry-list#p-11">Dataflow</a></li>
<li><a href="http://www.tbray.org/ongoing/When/200x/2009/09/30/C-dot-next-laundry-list#p-2">Immutable data</a>,<br />
and <a href="http://www.tbray.org/ongoing/When/200x/2009/09/30/C-dot-next-laundry-list#p-7">Transactional memory</a>,<br />
and <a href="http://www.tbray.org/ongoing/When/200x/2009/09/30/C-dot-next-laundry-list#p-8">Tuple space</a></li>
</ul>
<p>(Have to admit, I am not sure the above grouped notions are distinct, when viewed at this level.)</p>
<p>For the bulk of programmers and applications, the main needed change is finding the simplest possible adaptation to the needs of web-scale programming. Once done, concurrency and threading are solved problem. We do not want or need fine-scale single-name/address space multi-threading &#8211; that way lies madness. We do need well-isolated single threads, in mass numbers, cooperating across web-scale process, machine, and network boundaries.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/10/02/concurrency-and-threading-is-the-new-thing-again/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>When hardware guys write about software</title>
		<link>http://bannister.us/weblog/2009/09/03/when-hardware-guys-write-about-software/</link>
		<comments>http://bannister.us/weblog/2009/09/03/when-hardware-guys-write-about-software/#comments</comments>
		<pubDate>Fri, 04 Sep 2009 02:23:27 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Humor]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1527</guid>
		<description><![CDATA[My father sent a link to an article in a hardware-oriented magazine &#8211; EDN (Electronic Design News) &#8211; about the upcoming Power7 CPU from IBM. This is out of our fields, but interesting. http://www.edn.com/article/CA6686259.html?nid=2435&#038;rid=8150303 I liked the one comment: &#8220;the generally horrible code from the Microsoft world&#8221;. My characteristically reserved response: Yes, it is usually [...]]]></description>
			<content:encoded><![CDATA[<p>My father sent a link to an article in a hardware-oriented magazine &#8211; EDN (Electronic Design News) &#8211; about the upcoming Power7 CPU from IBM.<br />
<blockquote>
This is out of our fields, but interesting.</p>
<p><a href="http://www.edn.com/article/CA6686259.html?nid=2435&#038;rid=8150303">http://www.edn.com/article/CA6686259.html?nid=2435&#038;rid=8150303</a></p>
<p>I liked the one comment: &#8220;the generally horrible code from the Microsoft world&#8221;.
</p></blockquote>
<p>My characteristically reserved response:<br />
<blockquote>
Yes, it is usually cute when hardware guys talk about software.</p>
<p>As I remember, the result came from the large number of branches found in &#8220;the generally horrible code from the Microsoft world that dominated tasks in those days&#8221;. Except this characteristic is common to most modern code, not just in &#8220;Microsoft world&#8221; code. It comes from complex behaviors &#8211; an increasing and increasingly common characteristic in contemporary code. Outside a small set of niche applications (with large number crunching), you <b>are</b> going to have a lot of branches. Branching limits the value of concurrent instruction dispatch, and speculative execution (as we might guess). Results pointed at more than ~3-way dispatch as on the wrong side of the &#8220;knee&#8221; of the curve.</p>
<p>I think the chip design offers a hint as to their expected application domain:</p>
<blockquote><p>Each of the Power7&#8242;s cores has 12 execution units, including two each load/store and fixed-point, four double-precision floating-point, and a decimal floating-point unit. The core can dispatch six instructions per cycle.</p></blockquote>
<p>With <b>four</b> floating-point execution units, my guess is that they are aiming at niche mass number-crunching applications, not general purpose applications. Combined with the large shared-cache, these should do well on problems that allow highly concurrent bulk number crunching. Might see a lot of these to run weather simulations (to predict Global Warming), and some sorts of large engineering design calculations/simulations.</p>
<p>As a general purpose CPU, my guess is that benchmarks (when and if they appear) will be unimpressive.</p>
<p>Add to this the fact that most big new web applications are moving (have moved, in fact) to a no-shared-memory model &#8211; lots of single-board nodes with 1 to 8 CPU sockets, no shared memory between processes, connected via ethernet to large-scale high-performance redundant datastores (specialized database/filesystems).</p>
<p>Which makes the really cool design aspects of the Power7 pretty much useless. This is not the sort of CPU that would ever see large-scale deployment in a web-service datacenter.</p>
<p>Too bad the writer of the article does not have a clue about software, but no surprise from a hardware magazine.</p>
<p><i>Oh wait! I take it all back! This CPU could be terrific at the more compute-intense computer games! Perhaps Microsoft will pick this up for the next XBox? Super high frame rates in MegaBlaster2010! &#8230; except highly concurrent shared memory applications are a real bitch to write, even worse to debug, so better make that MegaBlaster2012, or 2013, or 2015, or &#8230; (hey! they never did ship!?!).</i></p>
<p>&#8230; maybe not. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/09/03/when-hardware-guys-write-about-software/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building things</title>
		<link>http://bannister.us/weblog/2009/08/26/building-things/</link>
		<comments>http://bannister.us/weblog/2009/08/26/building-things/#comments</comments>
		<pubDate>Wed, 26 Aug 2009 22:45:29 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1509</guid>
		<description><![CDATA[Took off this week and last with no specific plans. Might have gone driving (might still), but I made several trips in the last year, so &#8230; (shrug). Guess I have been on a bit of a home improvement kick. Imagining a new bit, and &#8211; much work later &#8211; seeing an entirely satisfactory end [...]]]></description>
			<content:encoded><![CDATA[<p>Took off this week and last with no specific plans. Might have gone driving (might still), but I made several trips in the last year, so &#8230; (shrug). Guess I have been on a bit of a home improvement kick. Imagining a new bit, and &#8211; much work later &#8211; seeing an entirely satisfactory end result, is nice. </p>
<p>Was out in the garage last night, planning the next bit, when my daughter popped out of the house. &#8220;Oh, you&#8217;re thinking again, aren&#8217;t you?&#8221; She was not surprised to find me sitting and staring for an hour, before starting on the next bit.</p>
<p>Seems I use roughly the same approach whether cooking, building things out of wood and metal, or writing software. When cooking I never use recipes, I am always changing <i>something</i>, and usually get good results. When building I never use plans (other than occasional rough sketches), will think through the problem and build the next bit in my mind, before starting manufacture &#8211; and usually get good results. When writing software I do not have much use for design documents or formal processes, but cannot start until I have thought through the problem, and usually get good results.</p>
<p>In each case I can visualize the end result before I start. In each case I am working without plans (written plans or plans from others), yet &#8230; I <b>always</b> have a plan.</p>
<p>As I learned back in school, this pattern of work does not work at all for some of my peers. My upper-division Physics lab class was the first clear example. The lab was &#8211; at base &#8211; one student alone in a room with a pile of equipment, and a set experiment to complete in a couple weeks. The lab had a new instructor when I took the class, and he was not familiar with the equipment. Also, for some reason, none of the equipment worked. So with little or no instructions, I had to figure out what the equipment did, how it <i>should</i> work, and what needed to be fixed.</p>
<p>Getting the experiments to work was both frustrating, and fun!</p>
<p>Midway through the course, the instructor assigned another student to work with me, as this guy was not doing well by himself. I knew the guy, counted him as a bit of a friend, and thought he was at least as good as I at the theory classes. But in the lab, he drove me nuts. I needed silence so I could visualize how things should work. For the parts I did not understand well enough to visualize, I would disassemble and play with the equipment, until I better understood how it worked. </p>
<p>I suppose it looked to the other guy like I did things in no particular order, while ignoring anything he said. After an indeterminate period of seemingly aimless activity, he&#8217;d suddenly hear &#8220;Ah &#8230; I get it!&#8221;, followed by an ordered, logical explanation, and a burst of activity &#8211; after which the equipment would (usually) work.</p>
<p>Bet I drove him a bit nuts, as he did to me.</p>
<p>Also, up to that point in the lab class, I thought I was doing poorly. Most times &#8211; when the instructor dropped by &#8211; the equipment was not working, and I had no coherent explanation (or so I felt) of what to do next. Once the equipment was working, I could &#8211; in short order &#8211; perform the experiment, and move to the next lab.</p>
<p>Bet also my more present co-workers see something similar &#8211; an indeterminate period of seemingly aimless activity, followed suddenly by a burst of activity and plans. Probably drives some folk &#8211; of differing nature &#8211; slightly nuts.</p>
<p>So my daughter caught me planning, again &#8230; <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/08/26/building-things/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Extending the Pearson hash function to larger values</title>
		<link>http://bannister.us/weblog/2009/08/18/extending-the-pearson-hash-function-to-larger-values/</link>
		<comments>http://bannister.us/weblog/2009/08/18/extending-the-pearson-hash-function-to-larger-values/#comments</comments>
		<pubDate>Wed, 19 Aug 2009 01:09:04 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1501</guid>
		<description><![CDATA[My favorite hash function is without doubt the Pearson hash. In my measurements, for my usage, custom hash tables built using a Pearson hash to index into a power-of-two-sized table, have always performed better than the best alternatives. (For not-small tables each table slot is the root of a binary tree.) Certainly this result is [...]]]></description>
			<content:encoded><![CDATA[<p>My favorite hash function is without doubt the <a href="http://en.wikipedia.org/wiki/Pearson_hashing">Pearson hash</a>. In my measurements, for <a href="http://bannister.us/weblog/2005/01/18/a-diversion-into-hash-tables-and-binary-searches/">my usage</a>, custom hash tables built using a Pearson hash to index into a power-of-two-sized table, have always performed better than the best alternatives. (For not-small tables each table slot is the root of a binary tree.) Certainly this result is going to be somewhat specific to the use-case and current CPU architecture, but each time performance was important, my solution &#8211; using binary trees in 256 hash buckets, using the Pearson hash &#8211; won the benchmarks.</p>
<p>The main limitation of the Pearson hash is that it only computes an 8-bit value (i.e. the range 0..255). For some problems a bigger range of values would be good. The means offered in Pearson&#8217;s original paper (as I recall &#8211; threw out the old magazine long ago, and the paper is not on the web) was to run the algorithm more than once to add additional 8-bit chunks to the hash.</p>
<p>Just now I&#8217;m feeling like a bit of an idiot, as it seems the Pearson hash could be easily extended up to 16-bits (or a bit beyond) with a ridiculously small change.</p>
<p>The original Pearson algorithm:
<pre>
static unsigned char T[] = {
    // The values 0..255 shuffled into random order.
};

static inline unsigned hashOf(unsigned h,const char* s) {
    for (int c = *s++; c; c = *s++) {
        h = T[h ^ (255 &#038; c)];
    }
    return h;
}
</pre>
<p>The algorithm modified to generate a 16-bit hash:
<pre>
static unsigned short T[] = {
    // The values 0..65535 shuffled into random order.
};

static inline unsigned hashOf(unsigned h,const char* s) {
    for (int c = *s++; c; c = *s++) {
        h = T[h ^ (255 &#038; c)];
    }
    return h;
}
</pre>
<p>Note that only the type and size of the table <b>T</b> changed! </p>
<p>With the large cache sizes of current CPUs, the 128KB table will fit nicely into cache, and perform very well. On CPUs with smaller caches, you might want to use a smaller table. Also clearly the table size for a 32-bit hash would be huge. You might want some sort of test for degenerate patterns in <b>T[]</b> (though I&#8217;d guess the probability of getting a bad table is very small).</p>
<p>This notion is so simple, I am a bit surprised that no one (so far as I can tell) suggested this approach before.</p>
<p>For large numbers of keys, each bit added to the hash value saves the equivalent of a key comparison. Adding 8-bits to the key saves 8 key comparisons. If the expense added to the hash function is less than the cost of one key comparison (certainly true of string compares), then you are ahead from the first bit added.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/08/18/extending-the-pearson-hash-function-to-larger-values/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Installing Pidgin with support for Microsoft IM</title>
		<link>http://bannister.us/weblog/2009/08/05/installing-pidgin-with-support-for-microsoft-im/</link>
		<comments>http://bannister.us/weblog/2009/08/05/installing-pidgin-with-support-for-microsoft-im/#comments</comments>
		<pubDate>Wed, 05 Aug 2009 17:40:56 +0000</pubDate>
		<dc:creator>Preston L. Bannister</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1495</guid>
		<description><![CDATA[Needed to get IM working from my Ubuntu Linux boxes to my employer&#8217;s Microsoft Office Communicator (2007?) service. Was using the Microsoft Messenger 4.7 client in a VM hosting Windows, but of late this seems to not work well enough for some of my coworkers (since I am often not actively using that VM). I [...]]]></description>
			<content:encoded><![CDATA[<p>Needed to get IM working from my Ubuntu Linux boxes to my employer&#8217;s Microsoft Office Communicator (2007?) service. Was using the Microsoft Messenger 4.7 client in a VM hosting Windows, but of late this seems to not work well enough for some of my coworkers (since I am often not actively using that VM).</p>
<p>I had looked at this over a year ago, but the SIPE support for Pidgin did not support the options needed for the IM service as configured by the company. On checking again, it seemed possible that the current version might work, but I needed the most recent version of Pidgin and the SIPE plugin.</p>
<p>So &#8230; for future reference. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Building Pidgin and the SIPE plugin from sources on Ubuntu</h3>
<ol>
<li>Install the dependencies needed to build from sources.
<pre>
sudo apt-get build-dep pidgin pidgin-sipe
</pre>
</li>
<li>Remove any existing installation.
<pre>
sudo apt-get remove pidgin pidgin-sipe libpurple-dev
</pre>
</li>
<li>Download <a href="http://www.pidgin.im/download/source/">Pidgin sources</a>.</li>
<li>Change to the directory in which you unpacked the Pidgin sources, and build.
<pre>
./configure
make
sudo make install
</pre>
</li>
<li>Download <a href="http://sourceforge.net/projects/sipe/files/">Pidgin SIPE plugin</a>.</li>
<li>Change to the directory in which you unpacked the SIPE plugin sources, and build.
<pre>
./configure
make
sudo make install
</pre>
</li>
<li>Now run Pidgin. It should appear in the usual Gnome menu, or can be run from the command line. Given that figuring out the right options for you company&#8217;s setup can be tricky (it was for me), you might want to do the initial connection setup with debug output from the command line.
<pre>
pidgin -d
</pre>
<p>Not strictly necessary, as the &#8220;Debug Window&#8221; (got to via the &#8220;Help&#8221; menu) offers the same(?) information.</p>
<p>You might also want to (temporarily) disable any other IM accounts you have configured.
</li>
</ol>
<p>Figuring out the right options to use with your company IM service &#8230; you are on your own. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/08/05/installing-pidgin-with-support-for-microsoft-im/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Subversion, CVS, and tags</title>
		<link>http://bannister.us/weblog/2009/06/17/subversion-cvs-and-tags/</link>
		<comments>http://bannister.us/weblog/2009/06/17/subversion-cvs-and-tags/#comments</comments>
		<pubDate>Wed, 17 Jun 2009 21:53:30 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1470</guid>
		<description><![CDATA[In the process of converting a group of programmers to Subversion, I ran across a surprisingly awkward bit. As a regular practice, before generating a build to go to customers, I always carefully review all the changes to the program sources since the last customer release. Using CVS the command line is simple (run from [...]]]></description>
			<content:encoded><![CDATA[<p>In the process of converting a group of programmers to Subversion, I ran across a surprisingly awkward bit.</p>
<p>As a regular practice, before generating a build to go to customers, I always carefully review all the changes to the program sources since the last customer release. Using CVS the command line is simple (run from the root directory for the component):</p>
<pre>cvs diff -t WIDGET_v301</pre>
<p>Reviewing the differences helps both to write up a customer-oriented list of changes, and also helps insure that nothing unintended (say, left over experiments or debug code) makes it into the customer release.</p>
<p>With Subversion under Eclipse the equivalent comparison is easy &#8211; just select the <b>trunk</b> and <b>tags/v301</b> and a compare gets the same result as CVS.</p>
<p>With Subversion on the command line, the equivalent comparison is ridiculously tedious.</p>
<pre>svn diff http://hostname/path/widget/tags/v301</pre>
<p>Um, hello? Subversion knows the <b>http://hostname/path/</b> part. If I followed the conventional layout, Subversion can deduce that the current directory is <b>trunk</b> and from that the path to <b>tags</b>. Yes, I know the repository layout (in terms of branches and tags) under Subversion can be arbitrary (and this is all very cool), but optimizing for the most-common case is always good. </p>
<p>Figuring that I must have missed something, I went digging, and came up with this gem:</p>
<blockquote><p>
<a href="http://gcc.gnu.org/ml/gcc/2005-11/msg00136.html">Branko Äibej &#8211; Re: svn diff branch woprking copy against mainline?</a><br />
This seems to be a common misconception. The important thing to remember here is that there is no separate namespace for labels and branches in SVN, and that the layout of the repository is arbitrary. IOW, the fact that you have branches in /branches is a convention, not something imposed by the SVN server.
</p></blockquote>
<p>The writer is almost exactly wrong. There <b>are</b> a separate namespaces in the Subversion repository. The namespaces are established by convention. The recommended PROJECT/{trunk,tags,branches} can be recognized by default, and any local variant specified by user-set properties in Subversion. This is easy.</p>
<p>I expected Subversion to be a nearly uniform improvement over CVS, not stuck for years on how to cover simple usage.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/06/17/subversion-cvs-and-tags/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows 2008 Server</title>
		<link>http://bannister.us/weblog/2009/03/26/windows-2008-server/</link>
		<comments>http://bannister.us/weblog/2009/03/26/windows-2008-server/#comments</comments>
		<pubDate>Fri, 27 Mar 2009 00:27:48 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1396</guid>
		<description><![CDATA[In testing a new software release, reached the point where testing against Windows 2008 Server is now a reasonable requirement. Until now, I&#8217;d not had any reason to try out Win2008 Server. Since the (upcoming) 2008 R2 is going to be 64-bit only, created a 64-bit VM (under Sun&#8217;s VirtualBox). Note that the default VM [...]]]></description>
			<content:encoded><![CDATA[<p>In testing a new software release, reached the point where testing against Windows 2008 Server is now a reasonable requirement. Until now, I&#8217;d not had any reason to try out Win2008 Server. Since the (upcoming) 2008 R2 is going to be 64-bit only, created a 64-bit VM (under Sun&#8217;s VirtualBox). </p>
<p>Note that the default VM memory size suggested by VirtualBox is 512MB (where 256MB is recommended for 2003 Server) &#8211; and for good reason! At least during the (usual) install and patch phase, it looks like 512MB is not enough the keep the VM away from swapping. Have to note &#8211; while the eye-candy is better installing 2008 Server, a Ubuntu server is a lot less trouble. As a sysadmin installing your third, tenth, or hundredth server, the eye-candy loses all appeal. </p>
<p>This leads to an interesting question. If Windows 2008 Server is run directly on the hardware, current server hardware is likely a quad processor with at least 4GB. On hardware the 2008 Server needs are a complete non-issue. On the other hand, if 2008 Server is run on a VM (and at this point I <i>would</i> expect most Windows Server instances to be VMs), then a 512MB baseline requirement for the VM may be excessive.</p>
<p>This has got to be a pretty problem for Microsoft.</p>
<p>Naturally Microsoft would like to sell the OS installed on the hardware. Lots of money to be got there. At the same time, smart customers are more likely to want something else (less compulsive expense) as the base OS, and push Windows Server instances into VMs. This creates a conflict. If Windows 2008 Server is too fat to run in a VM, that puts a bit more pressure on Windows business customers to either stay with Windows 2003 Server (or earlier), or to move off Windows servers entirely.</p>
<p>So &#8230; Microsoft could squeeze more money out of business customers, at the risk of losing those same customers &#8230; and that loss could prove permanent. Tricky. I would hate to be the guy at Microsoft making that call. </p>
<p>No &#8230; that is not right. I&#8217;d not have any trouble making that call. <i>The customer is always right.</i> I would be looking to keep the customers happy, and making the latest server OS a non-issue. Give customers no incentive to move off Windows Server. (Which is going to conflict with the MS folks still wanting lock-in.)</p>
<p>The install and upgrade UI on 2008 Server are a bit more &#8220;webby&#8221; &#8230; but it seems the Microsoft folk have not yet entirely got the message. While a distinct improvement, the 2008 UI still feels a bit dated. </p>
<p>Right. I&#8217;m describing an MS product as &#8220;dated&#8221;. You have no idea how odd that feels. Windows has gone from the extreme cutting edge (as when I went to a Windows developer conference in 1984), to &#8220;dated&#8221;?! Very, very strange &#8230;.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/03/26/windows-2008-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My Google Android phone</title>
		<link>http://bannister.us/weblog/2009/03/09/my-google-android-phone/</link>
		<comments>http://bannister.us/weblog/2009/03/09/my-google-android-phone/#comments</comments>
		<pubDate>Tue, 10 Mar 2009 05:51:51 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1324</guid>
		<description><![CDATA[A progress report of sorts&#8230;. Back in December I got a Google Android phone &#8211; specifically a T-Mobile G1 phone. I happened to walk into the local T-Mobile store just out of curiosity. They had the phone in-stock and for a good price &#8211; and I was thoroughly disgusted with my Motorola Razor from Verizon. [...]]]></description>
			<content:encoded><![CDATA[<p>A progress report of sorts&#8230;.</p>
<p>Back in December I got a <a href="http://bannister.us/weblog/2008/12/29/gps-in-an-android-phone/">Google Android phone</a> &#8211; specifically a T-Mobile G1 phone. I happened to walk into the local T-Mobile store just out of curiosity. They had the phone in-stock and for a good price &#8211; and I was thoroughly disgusted with my Motorola Razor from Verizon. The fact that this was the first phone where I could freely (almost) install software was a clincher.</p>
<p><a href="http://flickr.com/photos/dreadedhill/3272663630/"><img src="http://farm4.static.flickr.com/3464/3272663630_ea6389614f.jpg?v=0"/></a></p>
<p>Compared to every phone I have used prior, the <b>G1</b> is without doubt a solid improvement. The effect of the unified IM/text/email notifications is distinctly Pavlovian &#8211; my phone blurps, and I check GMail on my computer. At the same time, there are bits about the Android phone that are not ideal. The touch-selection algorithm needs work. I am never certain whether my touch will cause selection, activation, dragging, or &#8230;. I dunno. As a guess, it does what I wanted a bit less than half the time.</p>
<p>The GPS/location feature is very cool &#8211; except when it decides I am somewhere else. Again, an algorithm that needs work.</p>
<p>I have fat-fingered the phone and invoked the wrong actions when answering a call &#8230; less often than with the Razor, but still too often. Again, something that needs work.</p>
<p>Overall, I am distinctly encouraged. More work is needed, but this seems the best work as yet.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/03/09/my-google-android-phone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sound troubles on Ubuntu 8.10</title>
		<link>http://bannister.us/weblog/2009/03/08/sound-troubles-on-ubuntu-810/</link>
		<comments>http://bannister.us/weblog/2009/03/08/sound-troubles-on-ubuntu-810/#comments</comments>
		<pubDate>Sun, 08 Mar 2009 21:02:34 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1313</guid>
		<description><![CDATA[Count this article as a bookmark, so I can find the relevant links, the next time I have a sound problem. I have a recurring problem with sound on Ubuntu 8.10. On my desktop sound almost always stops working after a few hours (or sooner). On my laptop sound is less trouble, but still occasionally [...]]]></description>
			<content:encoded><![CDATA[<p>Count this article as a bookmark, so I can find the relevant links, the next time I have a sound problem.</p>
<p>I have a recurring problem with sound on Ubuntu 8.10. On my desktop sound almost always stops working after a few hours (or sooner). On my laptop sound is less trouble, but still occasionally does stop working.</p>
<p>Also the volume on the my laptop was far too low. Found by opening the volume control applet and running all the sliders up to the max, I could get the volume to a decent level. One problem solved &#8211; after a fashion. Clearly there is an issue if the master volume control is defeated by a non-obvious secondary setting.</p>
<p>Lots of folks are having trouble with sound on Ubuntu, but a fair number are due to configuration issues. Wading through far too many notes, I found something that works:</p>
<blockquote><p>
<a href="http://ubuntuforums.org/showthread.php?t=1028259">Re: no sound on Ubuntu 8,10</a><br />
try to restart alsa
<pre>
sudo /etc/init.d/alsa-utils stop
sudo alsa force-reload
sudo /etc/init.d/alsa-utils start
</pre>
</blockquote>
<p>Yep, that worked. Had to manually kill <b>rhythmbox</b> (which was playing music when sound died), and had to re-start the volume control applet. Bit crude.</p>
<p>Seems the base problems are <a href="http://drowninginbugs.blogspot.com/2009/02/pulseaudio.html">more-or-less well known</a>. Also seems at least one guy came up with <a href="http://ubuntuforums.org/showthread.php?t=205449">instructions for diagnosing audio problems</a> (though more for no-sound-ever problems). The <a href="http://www.pulseaudio.org/">PulseAudio</a> software seems to be at the heart of the problem.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/03/08/sound-troubles-on-ubuntu-810/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Are designers stuck in a rut?</title>
		<link>http://bannister.us/weblog/2009/03/03/are-designers-stuck-in-a-rut/</link>
		<comments>http://bannister.us/weblog/2009/03/03/are-designers-stuck-in-a-rut/#comments</comments>
		<pubDate>Wed, 04 Mar 2009 00:33:41 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1302</guid>
		<description><![CDATA[Looking for an update on CSS-based web design, so took a look at CSS Reboot and css Zen Garden. What you mainly find are layouts tall, skinny, and top-heavy with large headers. User&#8217;s screens &#8211; with growing use of &#8220;widescreen&#8221; panels &#8211; are becoming mainly very wide, but not very tall. As a result, most [...]]]></description>
			<content:encoded><![CDATA[<p>Looking for an update on CSS-based web design, so took a look at <a href='http://www.cssreboot.com/'>CSS Reboot</a> and <a href="http://www.csszengarden.com/">css Zen Garden</a>. What you mainly find are layouts tall, skinny, and top-heavy with large headers. User&#8217;s screens &#8211; with growing use of &#8220;widescreen&#8221; panels &#8211; are becoming mainly very wide, but not very tall. As a result, most of those clever examples fit very poorly on most user&#8217;s screens. On many current screens, about the only thing showing is the designer&#8217;s beautiful and clever graphics (surrounded by acres of empty horizontal space). This may make the web designer happy, but the user will be less happy with the scarce scrap of content visible (if visible at all) at the bottom of the screen, and less happy with the amount of scrolling needed for a viewing a tall skinny column.</p>
<p>When low-end laptops ship with 1280&#215;800 panels, large headers above tall/skinny content seem a <i>very</i> poor design. Designs that suit <a href="http://bannister.us/weblog/2008/09/14/design-for-standard-screen-sizes/">common screen sizes</a> seem a little scarce.</p>
<p>Was hoping for inspiration, but left the above-mentioned CSS example sites feeling <i>very</i> uninspired.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/03/03/are-designers-stuck-in-a-rut/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Guess I&#8217;m a little slow&#8230;</title>
		<link>http://bannister.us/weblog/2009/02/23/guess-im-a-little-slow/</link>
		<comments>http://bannister.us/weblog/2009/02/23/guess-im-a-little-slow/#comments</comments>
		<pubDate>Mon, 23 Feb 2009 09:51:45 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Humor]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1262</guid>
		<description><![CDATA[Self-judgment is always tricky. Some bits took me a long time to figure out. Whenever I go into a new situation, I expect to find someone much smarter. It never seems to work out that way. Often I find folk as-smart, but never (yet) anyone I thought clearly smarter. (Worse, I tend to over-estimate the [...]]]></description>
			<content:encoded><![CDATA[<p>Self-judgment is always tricky. Some bits took me a <b>long</b> time to figure out.</p>
<p>Whenever I go into a new situation, I expect to find someone much smarter. It never seems to work out that way. Often I find folk as-smart, but never (yet) anyone I thought clearly smarter. (Worse, I tend to over-estimate the abilities of folk I know.) </p>
<p>Yet my expectation persists.</p>
<p>I have had some occasional odd interactions with folk in the past, that I did not understand at the time. Easy to forget that other folk have insecurities of their own. Years after one episode, I figured out the most likely and unexpected explanation. </p>
<p>My career-path meant I got heavy exposure to client-server programming very early. I tend to think through the issues fairly throughly (perhaps I have an odd notion of &#8220;fun&#8221;), when presented with an interesting problem. A decade later, I started at a company that had a client-server product. The product had clearly got some of the <a href="http://bannister.us/weblog/2009/02/03/elegant-distributed-applications/">principles</a> I had <a href="http://bannister.us/weblog/2009/02/02/long-term-compatible-protocols/">arrived at</a>, badly wrong. After starting at Quest, and seeing the current-generation product, I heard there was a guy working on the next-generation of that same product. So I went to the guy and outlined all the things I thought he should do for the next generation product. On one point he said something like &#8220;we thought about doing that, but it was too hard&#8221;, for which my answer was &#8220;we did that on my last project, and it worked out very well&#8221;. I think the guy&#8217;s name was Jeremy.</p>
<p>I was just trying to be helpful.</p>
<p>What I did not know at the time was that what I had seen demonstrated was in fact the <i>next-generation</i> product, the result of two years of Jeremy&#8217;s work. So instead of criticizing the old generation to offer advice for the new generation &#8230; it must have sounded to Jeremy like this new guy was tearing apart his best work. The product was called &#8220;Vista&#8221;.</p>
<p>Then things got much worse.</p>
<p>Not many folk are intimately familiar with both Windows and Unix programming. Vista was a Unix-only product. Most folk in that outfit were Unix-only. Since I knew both platforms, I got tasked with porting Vista to Windows. Another guy &#8211; a long-term hand at the company &#8211; was tasked to work with me. The other guy &#8211; John? &#8211; I knew and liked. He was pretty sharp, but had other obligations. After a few weeks he bowed out, claiming that he was only going to slow me down.</p>
<p>I am a sucker for challenging problems &#8211; and always have been. </p>
<p>There were about a half-dozen programmers working on the Vista product. I had to keep up with their changes, while building a port to Windows, against an ever-diverging source-base. After six months of <i>very</i> intense work, I had a dual port. The same source base could be compiled to generate either a Windows or Unix version of the product. To keep up the necessary pace, I had isolated myself from most of the daily workings of the group. Along the way I found some perfectly astonishing bad code. I had assumed that programmers with equivalent levels of experience, but who had spent their entire careers doing only Unix programming, would be far better than I in that domain. Working through the Vista source code, I found out that I was wrong &#8211; and very badly wrong. Since I was under huge time pressure, I took an extremely pragmatic approach. When I found a malfunctioning section of code, I first tried to read through the code. If I was unclear what the code was meant to do, I re-wrote the section of code for clarity. At this point I usually realized the original code contained a number of errors. Code-level errors do not necessarily translate to customer-level errors, and the pressure on my time was enormous, so I made no effort to report my findings.</p>
<p>When I had finished, the group cut-over to using my source-base. They were a bit surprised to find that I had kept current with all their changes. (I had a secret weapon. They were using RCS-based source code control. I used CVS to track their changes.) What was worse was that I had almost-accidentally killed off a large part of their bug list. The code inspection and re-coding for clarity was just to make the port work, but had the side-effect of eliminating a lot of existing bugs. By this time Jeremy had been made manager for the group, and he called me in and yelled(!) at me, while admitting this. I was confused.</p>
<p>What I did not realize &#8211; until years later &#8211; is how this looked from Jeremy&#8217;s point of view. </p>
<p>Imagine a new guy at your company walks into your office, and details all the things wrong with the project you have worked on for the past two years. Imagine he claims to have done something you thought was too hard to do. Now imagine that the new guy is tasked with something you cannot do. You expect him to fail (and I know he did). Instead of failing he delivers a result that not only works, but is much improved over your best work. </p>
<p>Did I mention that Jeremy was made manager for the Vista group, and that I reported to him?</p>
<p>Midway through the project, I realized each client instance of the server in the Unix implementation of the Vista server was started via <b>inetd</b>. Since the Vista server-side incorporated Ghostscript, and the Ghostscript initialization took 20-30 seconds (loading a large amount of font information, mainly), this put a serious crimp in per-client startup time, and a pretty serious load on the server when there was a lot of clients. Since the Ghostscript initialization was the same for all clients, if the Vista server was run as a server, the per-client startup could be reduced to milliseconds, and the total load placed on the server hugely reduced. I went to Jeremy to explain how this could be done. He <i>angrily</i> rejected my suggestion, for no logical reason that I could discern. I was puzzled, but went back to the all-consuming task of completing the port.</p>
<p>Imagine that annoying new-guy offers a suggestion (which he has already implemented and demonstrated) that improves the startup time of your pet project by a few-thousandfold. At this point Jeremy <i>really</i> did not like me, and I had no clue as to why.</p>
<p>Naturally I had taken the same approach with the Windows port, so the per-client startup time was almost instantaneous. </p>
<p>After finishing the Vista port (to the great pleasure of the Marketing folk, who could now sell Vista into the Windows market), I got tasked with another port. I had a conversation with a distinctly grumpy Jeremy, who had to admit that the Vista port worked, and since I was apparently some sort of legend (not to my knowledge), I was tasked to port another product. </p>
<p>The next product was called &#8220;Shareplex&#8221;, and was a database replication product for Oracle databases. </p>
<p>I had used Oracle as a programmer/user a fair amount while at FileNet, so I was pretty familiar with Oracle at a user-level. Knowledge of Oracle at the reversed-engineering level needed for third-party database replication was entirely outside what I knew. Moreover the guy in charge of the group had written a book on Oracle performance tuning, so I felt entirely out-classed on both fronts.</p>
<p>Again, I was one guy trying to keep up with an active development group of about a half-dozen, while performing a port from Unix to Windows. It quickly became plain that I could not perform testing in the same labor-intensive manner as the existing group. (It also struck me as odd that there were no comprehensive test suites for a potentially mission-critical product.) So I came up with a series of Perl scripts to perform a series of automated tests against the database-replication product. The tests created, exercised, and destroyed a series of progressive, then semi-random, series of structures, data, and operations.</p>
<p>At the end of the port &#8211; another six months of very intense work &#8211; I had another dual-port. The same sources could be compiled for either Windows or Unix. The tests I had built proved that the port worked just as well as the original Unix code &#8211; and also proved that the original code did not work. </p>
<p>I was stunned. I had no idea how to proceed from that point.</p>
<p>What I did not realize at the time was that I had given two important group managers reason to hate me. A few weeks later, Charles &#8211; the Shareplex group manager &#8211; escorted me to a meeting, where I was fired.</p>
<p>Much later, I learned that the Shareplex product was withdrawn from the market, and took another 2-3 years of rewrites before it became a marketable product. The conclusions I derived from my tests were right, but I did not, at the time, know what to do with the information.</p>
<p>Without knowing, I had intimidated co-workers, by doing things that they could not. </p>
<p>This morning I made a different connection along the same lines&#8230;.</p>
<p>When I worked at FileNet, the company hired in a new graduate from CalTech. Management treated him as if he was a living legend. Dan was a fairly smart guy, but I kind of doubted his ability to walk on water. Fairly early on, he did a performance analysis in the area in which I was working, and made a couple fairly elementary mistakes (for a first-year Physics student), and circulated some bogus results. I sent out a correction, but did nothing else.</p>
<p>What I did not take into account was his likely insecurity as a guy promoted well past his peers. Any sort of criticism was likely seen as a serious threat.</p>
<p>Fast-forward a bit over a decade. A marketing guy at my then-current employer wants to go talk to the folk at FileNet. I don&#8217;t see a likely gain to the exercise, but went along to support the marketing guy. We end up waiting in the entrance lobby for an oddly long time. Dan walks through the lobby. Dan claims not to know me, with a hint of anger. Odd. To put this in context &#8211; the lobby is not on a natural path between any two points within the building (I used to work there). That Dan would walk through the lobby at the time I was there was &#8230; odd. That Dan would not remember me (we worked together for about seven years), is &#8230; odd. </p>
<p>Since the episode was of no importance, the odd bit stuck, but was otherwise forgotten. In retrospect, if I had indeed made Dan feel threatened, then the combination of oddities add up to a probability. Is it possible I had made Dan feel threatened? Is it possible that we were made to wait until Dan could walk through the lobby? At some point Dan was promoted to CTO. Was the odd delay, odd diversion, and odd lack of recognition &#8230; not odd at all? </p>
<p>This morning&#8217;s amusement &#8211; of no further import. <img src='http://bannister.us/weblog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Now &#8230; I could be wrong about any of the above. On the other hand, even if right, it seems unlikely that anyone involved would confirm anything. Oh well. </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/02/23/guess-im-a-little-slow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to get started?</title>
		<link>http://bannister.us/weblog/2009/02/22/how-to-get-started/</link>
		<comments>http://bannister.us/weblog/2009/02/22/how-to-get-started/#comments</comments>
		<pubDate>Mon, 23 Feb 2009 01:44:43 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1248</guid>
		<description><![CDATA[This is simple programming problem. Tune out if this is not your thing. Started writing a simple cxi (for CGI-like Xml Interchange, perhaps) program as suggested in a prior scripting-oriented article. Pretty straight-forward coding (with some added clever notions that occurred to me later), right up until I got stuck on how to get the [...]]]></description>
			<content:encoded><![CDATA[<p>This is simple programming problem. Tune out if this is not your thing.</p>
<p>Started writing a simple <b>cxi</b> (for CGI-like Xml Interchange, perhaps) program as suggested in a <a href="http://bannister.us/weblog/2009/01/30/scripting-inspired-by-monad-for-unix/">prior scripting-oriented article</a>. Pretty straight-forward coding (with some added clever notions that occurred to me later), right up until I got stuck on how to get the child command started.</p>
<p>Commands to be run by <b>cxi</b> fall into two main groups.
<ul>
<li><i>Generators</i> &#8211; commands that generate output, without reading input.</li>
<li><i>Filters</i> &#8211; commands that read input and generate output.</li>
</ul>
<p>Filters are the easier case.
<ol>
<li>Read environment for HTTP_ACCEPT.</li>
<li>Read the input for headers.</li>
<li>Read the command line for options.</li>
<li>Based on the last found, select the command to run, and set HTTP_ACCEPT for the child process.</li>
</ol>
<p>Note that the input must be scanned for headers <b>before</b> the child process can be started. Generators are tricky. We cannot read the input for headers (since there is no input), as this would block, and the child process would never get started. We could require pre-knowledge to distinguish generators from filters (a sort of database of command metadata), but this strikes me as inelegant. Traditionally in Unix there is no way to distinguish between commands that are filters and generators. Any given command could be either. </p>
<p>Need a simple convention to mark a generator. I&#8217;m inclined to conflate two needs and address with single solution. At the start of a pipeline of <b>cxi</b> commands, you need to establish the desired intermediate data type, as well as indicate that the command is a generator. Since this a usual case, the presence of a command option could indicate both. So a pipeline of <b>cxi</b> commands would look like:</p>
<pre>cxi <b>-t xml</b> ps | cxi foo | cxi bar</pre>
<p>In the less-usual case, where the initial command is a filter, and addition option could override and force filter-behavior:</p>
<pre>cxi -t xml <b>-f</b> ps | cxi foo | cxi bar</pre>
<p>In the less-usual case, where the type is indicated in the environment, then another option to indicate the generator:</p>
<pre>
export HTTP_ACCEPT=text/xml
cxi <b>-g</b> ps | cxi foo | cxi bar
cxi <b>-g</b> ps | cxi foo | cxi bar
</pre>
<p>Can&#8217;t think of anything more elegant, so will go with the above.</p>
<p>As a slightly amusing sidelight &#8211; turns out the GNU <b>getopt</b> is too smart for my purposes, and I will have to use an older &#8220;dumber&#8221; implementation of getopt. The GNU implementation will pick out options anywhere in the command line, and I want it to stop at the first non-option argument. </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/02/22/how-to-get-started/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Just enough &#8220;Security&#8221;</title>
		<link>http://bannister.us/weblog/2009/02/07/just-enough-security/</link>
		<comments>http://bannister.us/weblog/2009/02/07/just-enough-security/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 20:34:01 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1199</guid>
		<description><![CDATA[This is brilliant. Munich&#8217;s Metro Stressful, But It Goes Everywhere &#124; Autopia from Wired.com Riders purchase tickets at self-service kiosk priced by zone &#8212; a ticket covering most of the inner city costs €2.30 ($2.95) &#8212; and before boarding, stamp them with an old-school time clock. It&apos;s possible to score a free ride by &#8220;forgetting&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>This is brilliant.</p>
<blockquote><p>
<a href='http://blog.wired.com/cars/2009/02/stressful-munic.html'>Munich&#8217;s Metro Stressful, But It Goes Everywhere | Autopia from Wired.com</a><br />
Riders purchase tickets at self-service kiosk priced by zone &#8212; a ticket covering most of the inner city costs €2.30 ($2.95) &#8212; and before boarding, stamp them with an old-school time clock. It&apos;s possible to score a free ride by &#8220;forgetting&#8221; to stamp your ticket, but at the risk of getting caught by plainclothes agents making random checks. They’ll slap you with a big fine, and the &#8220;I&apos;m just a confused tourist&#8221; won&apos;t get you anywhere.
</p></blockquote>
<p><a href="http://www.schneier.com/index.html">Bruce Schneier</a> frequently repeats the notion that security should be cost-effective (counting <i>both</i> material and non-material costs). Whomever came up with this approach for the Munich Metro &#8230; the result is brilliant. The investment in infrastructure for fee collection should be less. The Metro can employ fewer folk in less repetitive, more intelligent jobs. (I would bet the plainclothes agents are cross-trained to look for other criminal behaviors.) Peak traffic flows are less likely to be impeded by &#8220;security&#8221; measures.</p>
<p>Incidentally, I do believe that Schneier is having a large positive effect on application of concepts for security, directly or indirectly. Seems more and more often I am running into fragments of his thoughts in other venues. I know enough about the principles to use security effectively in my working domain (which makes me the &#8220;security expert&#8221;, though I would not claim that title). When folk want or need know more I almost always point them to Schneier&#8217;s writings. I suspect there are other folk who also point to Schneier as the standard-bearer for security-related issues. Over time it looks as though the cumulative effect is significant.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/02/07/just-enough-security/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Elegant distributed applications</title>
		<link>http://bannister.us/weblog/2009/02/03/elegant-distributed-applications/</link>
		<comments>http://bannister.us/weblog/2009/02/03/elegant-distributed-applications/#comments</comments>
		<pubDate>Wed, 04 Feb 2009 04:24:46 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1131</guid>
		<description><![CDATA[&#8220;Elegance is the attribute of being unusually effective and simple&#8221; Heard this first applied to theories in Physics. Any not-over-complicated theory that effectively explained the facts was &#8211; as I understood &#8211; considered an &#8220;elegant&#8221; theory. (My original interest &#8211; and college degree &#8211; was in Physics. I wanted to build starships.) Came up, long [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Elegant">&#8220;Elegance is the attribute of being unusually effective and simple&#8221;</a> </p>
<p>Heard this first applied to theories in Physics. Any not-over-complicated theory that effectively explained the facts was &#8211; as I understood &#8211; considered an &#8220;elegant&#8221; theory. (My original interest &#8211; and college degree &#8211; was in Physics. I wanted to build starships.)  </p>
<p>Came up, long ago, with rules for &#8220;elegant&#8221; distributed applications. The first significant distributed application I spent time with was the early FileNet system. This was well over twenty years ago. Strong interest in scaling to large (by the standards of the time) deployments, forced careful thought about performance. Pretty much everything I have worked on before and since has had a network in the middle, so I&#8217;ve had time and reason to think about the subject. Changing technology does not change the inherent nature of distributed systems, so the relevant set of notions will pretty much <i>always</i> apply. </p>
<p>(<i>What continues to surprise me is that individuals and outfits still get these same bits</i> <b>wrong</b>!) </p>
<p>In the interest of hitting the <i>usual</i> points once&#8230;.</p>
<p>When building distributed applications, there are some basic principles you should always keep in mind. </p>
<dl>
<dt>Minimize the amount of data crossing the network.</dt>
<dd>The capacity of the network is always limited. Capacity is large in the usual development setup, when both server and client are on the same segment. In real use the available capacity is almost always less, and sometimes <i>much</i> less.</dd>
<dt>Minimize the number of round-trips across the network.</dt>
<dd>Networks always have <i>latency</i>. This has nothing to do with the speed of a network (in terms of bits/second). I have written about this <a href="http://bannister.us/weblog/2007/03/29/rest-should-not-be-uniform/">before</a>. For an interactive application the ideal is one round-trip per user action.</dd>
<dt>Shift computation from the server to the clients, where practical</dt>
<dd>There are almost always more clients than servers. If you can shift computation to the clients, you will get better overall throughput.</dd>
</dl>
<p>From the above principles, for building web applications you can derive further guidelines.</p>
<dl>
<dt>Code complexity belongs on the server. Code in the client should be simple.</dt>
<dd>Code to be executed on the client must be shipped across the network. The less you have to ship across the network, the better. If you have an application that calls for code-complexity on the client, you want to ship the code once, and should be looking at solutions like <a href="http://en.wikipedia.org/wiki/Java_Web_Start">Java WebStart</a>. Otherwise you want to ship as little code as possible across the network. This fits perfectly with the use of compact scripts in the client using Javascript.</p>
<p>The fact that Javascript is interpreted on each and every load (and not compiled) serves only to reinforce this guideline.</dd>
<dt>Large iterations belong mainly on the server, not the client.</dt>
<dd>Compiled code is usually much more efficient than interpreted code. The server can (or should) use compiled code. In the case of web applications, the client code is interpreted code.</dd>
<dt>Large data belongs on the server, not the client.</dt>
<dd>The (sometimes) narrow network channel, and the relative efficiency of compiled code &#8211; both argue for keeping large data on the server.</dd>
<dt>Use the strengths of the web browser.</dt>
<dd>Native code is faster than interpreted code. The web browser is smart, and incorporates a large set of behaviors in native code. Use of built-in behaviors can mean smaller Javascript and faster execution.</dd>
</dl>
<p>In the present, Javascript offers an elegant solution to the need for a scripting language in both client and server. Made the mistake(?) of responding to a <a href="http://alarmingdevelopment.org/?p=191">recent post</a>. Seems that each time something like this comes up, we have an almost fixed set of notions coming back. After a few iterations &#8211; just not very interesting. </p>
<p>Some quotes, with names omitted to protect the guilty.</p>
<blockquote><p>Arrays have no semantics. They are not first-class collections. Do not use them in any public API, regardless of the language you use. Wrap arrays with a public type that exposes semantics.</p></blockquote>
<p>The semantics of arrays as collections and iterations are simple and perfectly suited for small scripts. For the most part, you do not need anything more. The domain for scripting is small, concise, and hideously flexible code. More elaborate solutions might make sense in large server-side code. Client-side script or structures shipped between client and server should only be as elaborate as is needed &#8211; and no more.</p>
<blockquote><p>You’re right in the sense that JS is “good enough” for most of basic usages, but almost useless for writing bigger software. It’s the reason why there’s been recently a lot of higher level languages that generates JS code. Either Java (GWT) or haXe (http://haxe.org)</p></blockquote>
<p>You should not be writing large code in Javascript. You must use Javascript in the client (the web browser), but in-browser script should <b>not</b> be large. You should consider Javascript as &#8220;glue&#8221; code on the server-side &#8211; small code, few iterations, with huge flexibility &#8211; to allow special-case customization without re-coding. Javascript (as with ELisp in Emacs and AutoLisp in AutoCAD) is a scripting language. You do not want to write the bulk of a large application in Javascript. Large code is a non-goal for a scripting language.</p>
<blockquote><p><a href="http://pinderkent.phumblog.com/post/2009/01/i_still_dislike_javascript_and_likely_always_will_it_has_some_pretty_fundamental_flaws">I still dislike JavaScript, and likely always will. It has some pretty fundamental flaws.</a></p></blockquote>
<p>Javascript evolved almost as a hack (if not quite). Early versions were less capable. Early examples were uninspired (or worse). The present iteration preserves past mistakes. Ignore the mistakes, and use the good parts. The good parts are &#8230; very good, as suits a scripting language.</p>
<blockquote><p>
<a href="">JavaScript has no place on the server.</a>
</p></blockquote>
<p>Quite the opposite &#8211; as the scripting language known to the largest group, and fated to be well-known over a long period &#8211; Javascript can serve exceptionally in the role that scripting languages have long met in large, successful applications. Not for large use, but with a valued place. Often when faced with the need to adapt a large application to a specific customer/site needs, there are always cases when a simple list of options is not enough (and rarely-used options serve only to make the application obscure to all customers). There is always a part of the problem space best met by a scripting language deeply integrated with your application. </p>
<p>Historically we have always had a zoo of suitable scripting languages, with insufficient reason to choose between them. In an odd way, the rise of Javascript in the web browser does us a favor, as Javascript is sufficient, and now most widely known of all scripting languages. There are times when a single logical solution is best for all involved.</p>
<p><b>Javascript is good enough.</b></p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/02/03/elegant-distributed-applications/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Long-term compatible protocols</title>
		<link>http://bannister.us/weblog/2009/02/02/long-term-compatible-protocols/</link>
		<comments>http://bannister.us/weblog/2009/02/02/long-term-compatible-protocols/#comments</comments>
		<pubDate>Tue, 03 Feb 2009 06:02:03 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1150</guid>
		<description><![CDATA[Prompted by this article: Lightweight versioning for lightweight protocols Yes, this is a many-times-solved problem &#8211; though not always well solved. Seems like most everything I&#8217;ve done in the last twenty-odd years (or more) has had a network in the middle. From that experience, a few guidelines&#8230; Guidelines for network protocols used in distributed applications [...]]]></description>
			<content:encoded><![CDATA[<p>Prompted by this article: <a href="http://ocaml.janestreet.com/?q=node/41">Lightweight versioning for lightweight protocols</a></p>
<p>Yes, this is a many-times-solved problem &#8211; though not always well solved. Seems like most everything I&#8217;ve done in the last twenty-odd years (or more) has had a network in the middle. From that experience, a few guidelines&#8230;</p>
<h3>Guidelines for network protocols used in distributed applications</h3>
<ol>
<li><b>Design for differing versions on the client and server.</b>
<p>In some limited cases you can keep the client and server versions in sync. For the most part &#8211; over time and over growing deployments &#8211; you cannot. Assume that the client version will often differ from the server (in either direction).
</li>
<li><b>Convert at the edges.</b>
<p>The types inside your application may change many times (for good reason). The structures that cross the network should change slowly (if at all). In your application, convert from internal to protocol structures at the edge.
</li>
<li><b>Version your protocol.</b>
<p>This could be as simple as a single version number offered by the client to the server, and the server to the client, at the beginning of the conversation. You could do finer-grained versioning, but this seems rarely needed.
</li>
<li><b>Do not be over-specific in your protocol types.</b>
<p>When passing a number across the net, use a general number type. You application might internally use a small number type (8-bit or 16-bit), and you may think that is all you will ever need. Some of those numbers will grow. At the edge, when ingesting incoming data, check for values outside the range you can handle.</p>
<p>The same argument applies to strings, arrays, and the like.
</li>
<li><b>Design for upwards and downwards compatibility.</b>
<p>When the client asks a question of the server, older servers may not understand the question, or return a smaller answer. When the client is older, the server may have a larger answer, or the question may be smaller. Most of the time, changes are incremental, and both server and client can handle the difference without explicit reference to the protocol version. Sooner or later someone will goof, and you will need to add a special case for a specific version.
</li>
</ol>
<p>This all might sound complex or difficult &#8211; but is simpler than the alternative. </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/02/02/long-term-compatible-protocols/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Scripting inspired by Monad &#8211; for Unix</title>
		<link>http://bannister.us/weblog/2009/01/30/scripting-inspired-by-monad-for-unix/</link>
		<comments>http://bannister.us/weblog/2009/01/30/scripting-inspired-by-monad-for-unix/#comments</comments>
		<pubDate>Sat, 31 Jan 2009 01:47:57 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1121</guid>
		<description><![CDATA[Scripting on Windows has always been pretty lame compared to Unix. The usual command.exe or cmd.exe shells on Windows are pretty pathetic compared to the Bourne Shell (which was released back in 1977!). Lacking a good shell, and with a population of GUI developers less familiar with the command line, Microsoft never really got a [...]]]></description>
			<content:encoded><![CDATA[<p>Scripting on Windows has always been pretty lame compared to Unix. The usual <b>command.exe</b> or <b>cmd.exe</b> shells on Windows are pretty pathetic compared to the <a href="http://en.wikipedia.org/wiki/Bourne_shell">Bourne Shell</a> (which was released back in 1977!). Lacking a good shell, and with a population of GUI developers less familiar with the command line, Microsoft never really got a clue. The Cygnus ports (and the like) of GNU command line utilities and <a href="http://en.wikipedia.org/wiki/Bash">bash</a> likely lessened the demand somewhat.</p>
<p>A few years back a group at Microsoft came up with <a href="http://weblogs.asp.net/jnadal/archive/2003/10/29/34413.aspx">Monad</a> &#8230; which was a pretty cool idea, if a little too fat. Took years before they eventually shipped (renamed as <a href="http://en.wikipedia.org/wiki/Windows_PowerShell">Powershell</a>).</p>
<p>Unix has tended to be about simple ideas with a lot of mileage. While the notion of building something like Monad on Unix is pretty interesting (I&#8217;d use Javascript via <a href="">Rhino</a> on the JVM), what I really wanted was a simpler notion that better &#8220;fit&#8221; the Unix-tools mindset. </p>
<p>One of the really cool bits about Monad was the ability to stream structured data &#8211; typically XML. Unix tools typically work on character streams &#8211; and only that. The character streams could quite naturally include structured data &#8230; but there seemed to be something missing.</p>
<p>Turns out there is a simple/elegant solution that could be retrofit to existing shells and tools, and fits very well within the usual Unix-ish way of doing things. The notion is so simple, it is a bit funny. This makes a round-trip of sorts&#8230;.</p>
<p>The first generation of web servers were almost entirely running on Unix. The first common-model for dynamic content was <a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a>, which was simply (and logically) a slightly warmed-over of Unix shell scripting. Running a shell script for each incoming web request was not especially efficient, so we got a whole zoo of alternatives (ASP, JSP, mod_perl, etc.) but the programming model is still based on CGI &#8211; which in turn is derived from Unix shell scripting.</p>
<p>When a web browser makes a request of a web server, the request includes a &#8220;Content-Type&#8221; header to indicate the <a href="http://en.wikipedia.org/wiki/MIME">MIME</a> type of the request data, and a set of &#8220;Accept&#8221; headers to indicate acceptable data-types for the response.</p>
<p>When a shell script is used for CGI, the HTTP headers turn into environment variables &#8211; for example:</p>
<pre>
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_LANGUAGE=en-us,en;q=0.5
HTTP_ACCEPT_ENCODING=gzip,deflate
HTTP_ACCEPT_CHARSET=ISO-8859-1,utf-8;q=0.7,*;q=0.7
</pre>
<p>Unix tools that got used for CGI applications (Perl, Python, PHP, etc.) were all taught to ingest HTTP requests and generate HTTP responses. The code exists and is generally quite mature.</p>
<p>How could we pipe structured data between processes, automatically negotiating the data-type where possible?<br />
<b>In <i>exactly</i> same way a web browser negotiates the content-type with the web server!</b> </p>
<p>There are a relatively small number of missing bits needed to make shell run scripts/tools in a similar fashion to CGI.</p>
<ol>
<li>The shell needs to read HTTP response headers from the output of a prior process, and translate the headers into environment variables, before invoking the next process in a pipeline.</li>
<li>Some convention so tools know whether to generate a plain character stream, or structured data. The presence of the HTTP_ACCEPT environment variable is probably sufficient.</li>
<li>&#8230; er, maybe that&#8217;s all.</li>
</ol>
<p>There are many ways we could enable this behavior, but it could be as simple as:</p>
<pre><b>HTTP_ACCEPT=text/xml</b> ps -e | fold | spindle | whatever</pre>
<p>&#8230; to invoke use of XML in a single pipeline, or:</p>
<pre>
export <b>HTTP_ACCEPT=text/xml</b>
ps -e | fold | spindle | whatever
</pre>
<p>&#8230; to invoke use of XML over an entire script.</p>
<p>Could use a shim without changing the shell. Something like:</p>
<pre>HTTP_ACCEPT=text/xml ps -e | <b>cgi</b> perl a.pl | <b>cgi</b> php -f b.php</pre>
<p>An exercise for another day&#8230;.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/01/30/scripting-inspired-by-monad-for-unix/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RSS-Atom</title>
		<link>http://bannister.us/weblog/2009/01/26/rss-atom/</link>
		<comments>http://bannister.us/weblog/2009/01/26/rss-atom/#comments</comments>
		<pubDate>Mon, 26 Jan 2009 20:35:50 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1112</guid>
		<description><![CDATA[A small proposal &#8211; the Atom Publishing Protocol should be named and referred to as &#8220;RSS-Atom&#8221;. Why? Because when you subscribe to a feed from a website, you are often offered a list of choices. RSS-0.92 RSS-2.0 Atom This is bad user interface design. The end user &#8211; who has no deep notion of what [...]]]></description>
			<content:encoded><![CDATA[<p>A small proposal &#8211; the <a href="http://en.wikipedia.org/wiki/Atom_(standard)">Atom Publishing Protocol</a> should be named and referred to as &#8220;RSS-Atom&#8221;.</p>
<p>Why? Because when you subscribe to a feed from a website, you are often offered a list of choices.</p>
<pre>
RSS-0.92
RSS-2.0
Atom
</pre>
<p>This is bad user interface design. The end user &#8211; who has no deep notion of what these things are and how they differ &#8211; has no means of knowing which to pick. A well-designed user interface will hide the redundant choices, and select the most appropriate (probably Atom, going forward). Over the entire universe of applications, we can pretty much count on the fact that not all user interfaces are well-designed. If we can help the end user, we should.</p>
<p>The naming used should result in the list of choices:</p>
<pre>
RSS-0.92
RSS-2.0
RSS-Atom
</pre>
<p>Now even with indifferently-done user interfaces the user is given a sufficient clue. Clearly the choices are all of the same &#8220;kind&#8221;. Sorted alphabetically &#8220;RSS-Atom&#8221; will appear after any &#8220;RSS-<i>version-number</i>, which is a clue to the user that Atom is a better choice.</p>
<p>A small aid to hundreds of millions (soon billions?) of users is a big deal.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2009/01/26/rss-atom/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Performance parsing CSV data</title>
		<link>http://bannister.us/weblog/2008/12/21/performance-parsing-csv-data/</link>
		<comments>http://bannister.us/weblog/2008/12/21/performance-parsing-csv-data/#comments</comments>
		<pubDate>Mon, 22 Dec 2008 01:34:09 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1041</guid>
		<description><![CDATA[Not sure how exactly, but I ran across an article that claimed parsing of CSV data was necessarily CPU-bound. I was pretty sure that with reasonably efficient code, there was no reason this had to be true. Still, proof is better than opinion, so I took the feed-readers code from a prior exercise, and adapted [...]]]></description>
			<content:encoded><![CDATA[<p>Not sure how exactly, but I ran across an <a href="http://www.daniel-lemire.com/blog/archives/2008/12/08/parsing-text-files-is-cpu-bound/">article</a> that claimed parsing of <a href="http://en.wikipedia.org/wiki/Comma-separated_values">CSV data</a> was necessarily CPU-bound. I was pretty sure that with reasonably efficient code, there was no reason this had to be true. Still, proof is better than opinion, so I took the <a href="http://svn.bannister.us/public/wide-finder-1/">feed-readers</a> code from a <a href="http://bannister.us/weblog/2008/06/13/wrapping-up-wide-finder-2/">prior exercise</a>, and adapted the code to parse CSV files.</p>
<p>You can grab the sources for the test <a href="http://svn.bannister.us/public/CSV-parser-1/trunk">CSV-parser-1</a> program from Subversion. The test program does a full CSV parse as described in <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a> (including handling quoted fields with embedded line breaks) and a bit more, but does nothing with the parsed data.</p>
<p>Results from test runs &#8211; on my HP laptop (Intel T9300 Core 2 Duo CPU @ 2.5GHz with 4GB memory):</p>
<pre>
preston@mercury:~/workspace/CSV-parser-1$ time Release/CSV-parser-1 -n 0 in/1g.txt
TIME Sun Dec 21 16:45:43 2008
Scanning: in/1g.txt
Done with: in/1g.txt
TIME Sun Dec 21 16:45:47 2008
Elapsed (ms): 4249, total (MB): 981
Scanned <b>230 MB/s</b>

real	0m4.254s
user	0m3.500s
sys	0m0.656s
</pre>
<p>The above is for a ~1GB file, fully cached in memory (do repeated test runs until the times stabilize). </p>
<pre>
preston@mercury:~/workspace/CSV-parser-1$ time Release/CSV-parser-1 -n 0 in/4g.txt
TIME Sun Dec 21 16:49:31 2008
Scanning: in/4g.txt
Done with: in/4g.txt
TIME Sun Dec 21 16:51:02 2008
Elapsed (ms): 91449, total (MB): 3944
Scanned <b>43 MB/s</b>

real	1m31.455s
user	0m41.271s
sys	0m10.549s
</pre>
<p>The above is for a ~4GB file, <b>not</b> cached in memory. The result is very clear &#8211; an efficient CSV file parser can ingest data <b>much</b> faster than the data can be read off ordinary disks (a bit over five times faster). Even a fast RAID would be hard-pressed to deliver data faster than it could be parsed.</p>
<p>Of course, in &#8220;real&#8221; applications, any processing performed on the parsed CSV data will likely dominate the runtime. Application-specific processing could easily saturate more the one CPU. The problem partitions into most-efficient read-and-parse of CSV data from disk (which is what this example does), and distribution of application-specific processing across multiple CPUs (which this example can do in the same manner as <b>feed-workers</b> &#8230; and which may or may not suit your application).</p>
<p><i>Insert the usual caveats here. The example program has seen only basic testing. There were other applications (minimally) active. The C++ code was written for reuse, and has not been run through a profiler. You could tweak the code to get slightly better performance, but probably not any large improvements.</i></p>
<p>The same test run on a desktop (slower CPUs, faster disk):</p>
<pre>
preston@brutus:~/workspace/CSV-parser-1$ time Release/CSV-parser-1 -n 0 in/1g.txt
TIME Sun Dec 21 17:21:19 2008
Scanning: in/1g.txt
Done with: in/1g.txt
TIME Sun Dec 21 17:21:27 2008
Elapsed (ms): 7530, total (MB): 981
Scanned <b>130 MB/s</b>

real	0m7.535s
user	0m6.136s
sys	0m1.388s
</pre>
<p>The above times are for a file cached in memory.</p>
<pre>
preston@brutus:~/workspace/CSV-parser-1$ time Release/CSV-parser-1 -n 0 in/4g.txt
TIME Sun Dec 21 17:23:31 2008
Scanning: in/4g.txt
Done with: in/4g.txt
TIME Sun Dec 21 17:25:00 2008
Elapsed (ms): 89020, total (MB): 3944
Scanned <b>44 MB/s</b>

real	1m29.084s
user	0m26.994s
sys	0m7.004s
</pre>
<p>The above times are for a file <b>not</b> cached in memory. The results are entirely consistent with the first set of runs.</p>
<p>Clearly, an efficient CSV file parser can process data faster than a single disk can deliver. The SSD&#8217;s (solid-state disks) currently on the market seem to manage sustained read rates in the range of 40-100MB/s, so a single-process parser should be able to fully saturate the disk.</p>
<p>If you are doing large-scale processing of CSV data, your most-efficient approach is most likely to use a single (efficient!) reader-parser thread, and then roughly as many application-specific processing threads (or processes) as you have CPUs.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2008/12/21/performance-parsing-csv-data/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Microsoft Office Project Server</title>
		<link>http://bannister.us/weblog/2008/11/15/microsoft-office-project-server/</link>
		<comments>http://bannister.us/weblog/2008/11/15/microsoft-office-project-server/#comments</comments>
		<pubDate>Sat, 15 Nov 2008 16:46:51 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=1009</guid>
		<description><![CDATA[Some folks at my employer have chosen to deploy Microsoft Office Project Server. I have used Microsoft Project, on and (mostly) off over a couple decades. Each time I&#8217;ve come back to using Microsoft Project, the experience is less than satisfactory. I seem to detect an anti-pattern. Those folks that are hopelessly invested in the [...]]]></description>
			<content:encoded><![CDATA[<p>Some folks at my employer have chosen to deploy Microsoft Office Project Server. </p>
<p>I have used Microsoft Project, on and (mostly) off over a couple decades. Each time I&#8217;ve come back to using Microsoft Project, the experience is less than satisfactory. I seem to detect an anti-pattern. Those folks that are hopelessly invested in the use of Microsoft Project offer a weak defense of the product. Pretty much everyone else thinks the product a poor choice. The single-copy price has always been somewhat high, so relatively few folk have tried to use MS Project.</p>
<p>In the last encounter with project planning software (several years back), I became convinced that a web-based solution was the <i>right</i> solution to the for this sort of problem. Schedules are inherently a shared thing, and passing file copies around via email was a poor substitute. A few years later Microsoft came up with a web-based variant of Project, but by then I was too busy (fortunately) to mess around with project-planning software. (Some other poor guy had inherited the job of making and updating the MS Project schedules &#8211; a vast and singularly non-productive time sink.)</p>
<p>Still &#8230; there was a chance Microsoft could have done good things since the last time I had used Project. Turning out a web-based application is very different from turning out a desktop application. With the forced change there is the opportunity to re-think the design, and perhaps to make things better.</p>
<p>Nope.</p>
<p>The web-based version of Project is unbelievably lame. I could make a list &#8230; but it would be too long. (Hey, this is my personal weblog, not a paid-for review.) The static screenshots look nice, but as a <i>usable</i> web application, this is not. For the design aspects, the words that come to mind are &#8220;completely clueless&#8221;. (Again, I am puzzled that Microsoft &#8211; with all their vast resources &#8211; could do anything so badly. Though from my own experience at other companies, I might have a pretty good idea why this can happen.)</p>
<p>Did a bit of searching to see what others were saying about the product, and mainly found folk selling training or consulting &#8211; a sign of a pain-in-the-ass to use product. I&#8217;m guessing that anyone who has a choice and a bit of intelligence uses something else.</p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2008/11/15/microsoft-office-project-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paper size and user interface design</title>
		<link>http://bannister.us/weblog/2008/09/19/paper-size-and-user-interface-design/</link>
		<comments>http://bannister.us/weblog/2008/09/19/paper-size-and-user-interface-design/#comments</comments>
		<pubDate>Fri, 19 Sep 2008 19:12:08 +0000</pubDate>
		<dc:creator>Preston</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://bannister.us/weblog/?p=973</guid>
		<description><![CDATA[In a prior post I tried to make clear why today&#8217;s common idioms in user interface design are horribly inefficient on today&#8217;s screen sizes. Thinking about this a bit later, I realized there is something I did not mention &#8211; the single common basis for why the old trade-offs once made sense, are now complete [...]]]></description>
			<content:encoded><![CDATA[<p>In a <a href="http://bannister.us/weblog/2008/09/14/design-for-standard-screen-sizes/">prior post</a> I tried to make clear why today&#8217;s common idioms in user interface design are horribly inefficient on today&#8217;s screen sizes. Thinking about this a bit later, I realized there is something I did not mention &#8211; the single common basis for why the old trade-offs once made sense, are now complete non-sense, and has to do with the size of a piece of paper.</p>
<p>Most of the information we present through software (content, not decorations) is organized to present well on an ordinary letter-sized piece of paper. Once the width of a screen became wider than letter-sized paper, we no longer needed to be severely constrained in our use of horizontal space. We crossed a threshold. Now even the lowest-end screens (at 1280&#215;800) are much wider than paper.</p>
<p>Why should an ancient medium like paper have anything to do with modern user interface design? Common paper sizes are &#8211; in effect &#8211; the result of a centuries-long experiment in user interface design. Paper can easily be fabricated in practically any size and shape. The paper sizes we use are the result of human choice, not a limitation somehow imposed by the manufacturing process. The range of paper sizes we most commonly use today are the result of generations of humans choosing what works best. </p>
<p>If you compare screen size to paper size, the old trade-offs make a lot more sense. There <i>was</i> for a long time &#8211; as long as 800&#215;600 (or smaller) screens were significant &#8211; a very strong motivation to place overhead elements on the top and bottom of the screen, and <i>not</i> on the sides. For those small screens the cases where content was wider than the screen were not rare. For anything text-like, usage that requires horizontal scrolling is horribly inefficient (for the human, not for the computer). Any design element that took away from horizontal space for content was going to force an ever larger fraction of usage into horizontal scrolling. Put simply, screens were effectively <b>narrower than paper</b> and best design required preserving as much horizontal space as possible for content.</p>
<p>The old trade-offs did <i>in the past</i> make a lot of sense. </p>
<p>A low-end 1280&#215;800 14-inch laptop screen is about 11.9 inches wide and 7.4 inches tall. Given the content area on letter-sized is about 7.5 inches wide and 10 inches tall, the screen about 2.6 inches too short and 4.4 inches too wide. Hold a piece of paper up against the screen, and the problem is pretty clear.</p>
<p>Have to admit: when &#8220;widescreen&#8221; panels first started to appear on laptops, I was hoping this was an aberration, not a trend. Unless you spend all your time watching movies and playing video games, most of the content you look at is text-like, and &#8220;widescreen&#8221; is not efficient for this sort of content. By now it seems very clear that we are going to be stuck with this form-factor for a very long time, and must adjust our design-habits to match.</p>
<p>There is a good deep/underlying basis that justified the old design idioms on old screens. That same basis on today&#8217;s &#8220;widescreen&#8221; panels makes those old idioms horribly wrong. </p>
]]></content:encoded>
			<wfw:commentRss>http://bannister.us/weblog/2008/09/19/paper-size-and-user-interface-design/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
