<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7844549485270153160</id><updated>2012-02-16T00:16:08.709-08:00</updated><category term='win32'/><category term='linux'/><category term='apache'/><category term='autoconf'/><category term='job'/><category term='sound'/><category term='dns'/><category term='python'/><category term='bug'/><category term='kernel'/><category term='dts'/><category term='debian'/><category term='gcc'/><category term='non-bug'/><category term='fix'/><category term='freebsd'/><category term='tv'/><category term='systemd'/><category term='sip'/><category term='benchmark'/><category term='conference'/><category term='hardware'/><category term='rant'/><title type='text'>My Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>32</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-6658492198163198152</id><published>2011-11-25T08:32:00.000-08:00</published><updated>2012-01-25T04:39:02.115-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='job'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Namespace issues in Apache</title><content type='html'>Some time ago I was given a task to write a Python WSGI script that handles all (really all!) URLs on a given Apache-based virtual host. Doesn't that sound easy? Here is the "obvious" part of the Apache configuration file for that virtual host:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;WSGIScriptAlias / /path/to/script.wsgi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Except that it is slightly wrong. The problem is that the script really should handle all URLS, even "evil" ones, without any omissions. In the above form, it doesn't.&lt;br /&gt;&lt;br /&gt;First, if the URL contains a percent-encoded slash (e.g., as in http://example.org/foo%2fbar), Apache gives a 404 error by default, without even calling the script. Solution:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;AllowEncodedSlashes On&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Wait, there is more! Regular aliases have higher priority than our WSGIScriptAlias, and that there are some default aliases usually set by distributions. Due to that, http://example.org/icons/folder.gif will map to a static file, not to the WSGI script. There is &lt;a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=44181"&gt;a wishlist bug in Apache&lt;/a&gt; that one cannot easily remove aliases from the namespace.&lt;br /&gt;&lt;br /&gt;The solution (or, more precisely, a bad hack) that I found is to use mod_rewrite. By adding some prefix to all URLs (and, of course, dealing with it in the script), one makes sure that the existing aliases are not hit:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;AllowEncodedSlashes On&lt;br /&gt;&lt;br /&gt;RewriteEngine On&lt;br /&gt;RewriteRule ^/(.*)$ /dummyprefix/$1 [PT]&lt;br /&gt;&lt;br /&gt;WSGIScriptAlias /dummyprefix /path/to/script.wsgi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Still, this doesn't work (gives a 404 error instead of calling the script) on URLs that include some bad characters such as newlines, e.g. http://example.org/foo%0abar . Removing the dollar sign from the pattern fixes the 404 error, calls the script, but it then receives a truncated PATH_INFO in the environment. Still, SCRIPT_URI is correct, so this may be enough. In fact, if the script doesn't care about PATH_INFO, even this works:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;AllowEncodedSlashes On&lt;br /&gt;&lt;br /&gt;RewriteEngine On&lt;br /&gt;RewriteRule ^/ /dummyprefix [PT]&lt;br /&gt;&lt;br /&gt;WSGIScriptAlias /dummyprefix /path/to/script.wsgi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The real cause of the issue with the original RewriteRule is that the "." metacharacter doesn't really match all characters. Indeed, it doesn't match a newline. So, in order to match the full URL, including the evil encoded newline in the middle, one has to write an explicit range covering all possible characters:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;AllowEncodedSlashes On&lt;br /&gt;&lt;br /&gt;RewriteEngine On&lt;br /&gt;RewriteRule ^/([\x00-\xff]*)$ /dummyprefix/$1 [PT]&lt;br /&gt;&lt;br /&gt;WSGIScriptAlias /dummyprefix /path/to/script.wsgi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whoops. Now a typical sysadmin probably won't understand the need (or will forget the reason) behind such a complex configuration for a seemingly simple issue of passing all URLs to a single script. And I am still not 100% sure that all valid URLs are really handled by the script. Maybe I should have started with a different web server, the one that doesn't have a polluted namespace in virtual hosts by default.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update&lt;/b&gt;: In the comments, the following was suggested:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;AllowEncodedSlashes On&lt;br /&gt;WSGIHandlerScript wsgi-handler /path/to/handler.wsgi&lt;br /&gt;SetHandler wsgi-handler&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here handler.wsgi would be a typical WSGI script, except that it provides the "handle_request" callable object instead of "application".&lt;br /&gt;&lt;br /&gt;This solution looks elegant and simple, but, if PHP is also installed on the same server, it is wrong (thus proving the complexity of the problem and the fragility of Apache URL namespace). It misses URLs that end in .php (even though the document root s empty). So, back to the ugly solution based on mod_rewrite.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-6658492198163198152?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/6658492198163198152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=6658492198163198152' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/6658492198163198152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/6658492198163198152'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2011/11/namespace-issues-in-apache.html' title='Namespace issues in Apache'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-5914509391832486795</id><published>2011-09-27T00:25:00.000-07:00</published><updated>2011-09-27T00:25:16.633-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='autoconf'/><title type='text'>Autoconf and defaults</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Autoconf is based on a set of macros. these macros are put by developers into the configure.ac file, and the "autoconf" program generates a proper configure script from that. The configure script commonly accepts options such as --enable-foo and --with-foo. It is important that they behave in a sensible way.&lt;br /&gt;&lt;br /&gt;The --with-foo argument is processed with the AC_ARG_WITH macro. It is &lt;a href="http://www.gnu.org/s/hello/manual/autoconf/External-Software.html"&gt;documented&lt;/a&gt; as follows:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;AC_ARG_WITH&lt;/b&gt; (&lt;var&gt;package, help-string, &lt;/var&gt;&lt;span class="roman"&gt;[&lt;/span&gt;&lt;var&gt;action-if-given&lt;/var&gt;&lt;span class="roman"&gt;]&lt;/span&gt;&lt;var&gt;, &lt;/var&gt;&lt;span class="roman"&gt;[&lt;/span&gt;&lt;var&gt;action-if-not-given&lt;/var&gt;&lt;span class="roman"&gt;]&lt;/span&gt;)&lt;var&gt;&lt;a href="http://www.blogger.com/blogger.g?blogID=7844549485270153160" name="index-AC_005fARG_005fWITH-1864"&gt;&lt;/a&gt;&lt;/var&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;var&gt;&lt;a href="http://www.blogger.com/blogger.g?blogID=7844549485270153160" name="index-AC_005fARG_005fWITH-1864"&gt;&lt;/a&gt;&lt;/var&gt;If the user gave &lt;samp&gt;&lt;span class="command"&gt;configure&lt;/span&gt;&lt;/samp&gt; the option &lt;samp&gt;&lt;span class="option"&gt;--with-&lt;/span&gt;&lt;var&gt;package&lt;/var&gt;&lt;/samp&gt;or &lt;samp&gt;&lt;span class="option"&gt;--without-&lt;/span&gt;&lt;var&gt;package&lt;/var&gt;&lt;/samp&gt;, run shell commands&lt;var&gt;action-if-given&lt;/var&gt;.  If neither option was given, run shell commands&lt;var&gt;action-if-not-given&lt;/var&gt;.  The name &lt;var&gt;package&lt;/var&gt; indicates anothersoftware package that this program should work with.  It should consistonly of alphanumeric characters, dashes, plus signs, and dots.&lt;/blockquote&gt;&lt;blockquote&gt;The option's argument is available to the shell commands&lt;var&gt;action-if-given&lt;/var&gt; in the shell variable &lt;code&gt;withval&lt;/code&gt;, which isactually just the value of the shell variable named&lt;code&gt;with_&lt;/code&gt;&lt;var&gt;package&lt;/var&gt;, with any non-alphanumeric characters in&lt;var&gt;package&lt;/var&gt; changed into ‘&lt;samp&gt;&lt;span class="samp"&gt;_&lt;/span&gt;&lt;/samp&gt;’.  You may use that variable instead,if you wish. &lt;/blockquote&gt;&amp;nbsp;Let's see what's wrong with this piece of configure.ac, found in lxdm-0.4.1:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;AC_ARG_WITH(pam,AC_HELP_STRING([--without-pam],[build without pam]),&lt;br /&gt;[],[AC_CHECK_LIB([pam], [pam_open_session])])&lt;/pre&gt;&lt;br /&gt;This does nothing if the --without-pam option is not given, and checks for the PAM library if the option is given. Looks right? No!&lt;br /&gt;&lt;br /&gt;This also does nothing if the --with-pam option is given, thus, resulting in a build without PAM support!&lt;br /&gt;&lt;br /&gt;Note that the documentation for AC_ARG_WITH even provides examples how to use the macro properly. Use them as a reference, and have the following checklist:&lt;br /&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;Use the &lt;code&gt;$withval&lt;/code&gt; or &lt;code&gt;$with_&lt;/code&gt;&lt;var&gt;package&lt;/var&gt; variable.&lt;/li&gt;&lt;li&gt;Test it for values such as "yes", "no", "auto" and explicit path. &lt;/li&gt;&lt;li&gt;Check the presence of the package unless it is explicitly disabled or the support is experimental.&lt;/li&gt;&lt;li&gt;Fail if the external package is requested but not available.&lt;/li&gt;&lt;/ul&gt;Yes, this becomes verbose, but it is necessary for correctness.&lt;br /&gt;&lt;br /&gt;The first point also applies to the AC_ARG_ENABLE macro:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;AC_ARG_ENABLE(debug,         AS_HELP_STRING([--enable-debug],&lt;br /&gt;                                  [Enable debugging (default: disabled)]),&lt;br /&gt;                                [DEBUGGING=&lt;b&gt;$enableval&lt;/b&gt;], [DEBUGGING=no])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-5914509391832486795?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/5914509391832486795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=5914509391832486795' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/5914509391832486795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/5914509391832486795'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2011/09/autoconf-and-defaults.html' title='Autoconf and defaults'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-2035059791465316803</id><published>2011-09-24T06:13:00.000-07:00</published><updated>2011-09-24T06:13:08.270-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sound'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='dts'/><title type='text'>I wrote a DTS encoder</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Let me announce a piece of software that I have published a week ago: &lt;a href="http://aepatrakov.narod.ru/dcaenc/"&gt;dcaenc&lt;/a&gt;, an open-source DTS encoder. The package contains sources for a shared library, a command-line tool and an ALSA plugin.&lt;br /&gt;&lt;br /&gt;DTS is one of the compressed formats that allow transfer of multichannel (e.g., 5.1) audio over SPDIF connections. The other common format is AC3. The SPDIF standard does not define a method for passing more than two channels of uncompressed PCM audio, so compression has to be used. Both AC3 and DTS are also used in DVD sound tracks.&lt;br /&gt;&lt;br /&gt;Open-source decoders for both AC3 and DTS already exist: &lt;a href="http://liba52.sourceforge.net/"&gt;liba52&lt;/a&gt; and &lt;a href="http://www.videolan.org/developers/libdca.html"&gt;libdca&lt;/a&gt; (side note: please don't use libdca, it is a security risk, there are some files that crash it or are decoded improperly). &lt;a href="http://ffmpeg.org/"&gt;FFmpeg&lt;/a&gt; can also decode these formats. However, useful open-source encoders existed only for AC3: one in FFmpeg, and the other one (&lt;a href="http://aften.sourceforge.net/"&gt;aften&lt;/a&gt;) based on it. The DTS "encoder" in FFmpeg was ported by someone else from my old proof-of-concept code that served as a tool to understand the DTS subband transform. It could only encode stereo PCM files into a valid DTS bitstream of the same bitrate, which is useless for any practical purpose. Now dcaenc provides a useful encoder that accepts multichannel sound and encodes it to the bitrate specified by the command line parameter.&lt;br /&gt;&lt;br /&gt;As already mentioned, there are the following use cases for my encoder:&lt;br /&gt;&lt;ul style="text-align: left;"&gt;&lt;li&gt;On-the-fly encoding of multichannel PCM audio produced by arbitrary ALSA applications (e.g. games) for transmission via SPDIF&lt;/li&gt;&lt;li&gt;Creation of DVD soundtracks and DTS CDs.&lt;/li&gt;&lt;/ul&gt;Some people ask me why I didn't integrate my encoder into FFmpeg instead of releasing it as a standalone package. Indeed, there are faster implementations of the basic DSP building blocks in FFmpeg, and the criticism that I reinvented a lot of wheels is valid. Integration with FFmpeg is indeed a desired long-term goal.&lt;br /&gt;&lt;br /&gt;There are still several reasons why I decided not to integrate right from the beginning. First, I don't think that my work is in the necessary shape for integration yet. E.g., in FFmpeg, floating-point codecs are preferred, while my library currently uses fixed-point (I thought it would be beneficial for porting to the ARM architecture). Second and the most important reason: when the encoder is standalone, users can get it immediately and use it, without the hassle of replacing the distribution-provided FFmpeg package and potentially breaking distribution-provided software such as VLC that depends on it. Third, if I know that I wrote all the code myself, I can sell LGPL exceptions.&lt;br /&gt;&lt;br /&gt;While dcaenc already produces "transparent" output at 1411 or 1536 kilobits per second, there is still room for quality improvement at lower bitrates. This is because the library does not yet use all compression possibilities offered by the DTS standard. I am going to implement at least linear prediction (incorrectly called ADPCM in the specification) and channel coupling in the future versions. Stay tuned!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-2035059791465316803?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/2035059791465316803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=2035059791465316803' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/2035059791465316803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/2035059791465316803'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2011/09/i-wrote-dts-encoder.html' title='I wrote a DTS encoder'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-7309971794750624528</id><published>2011-08-05T14:25:00.000-07:00</published><updated>2011-08-05T14:25:38.683-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>I am going to Desktop Summit 2011</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" height="110" src="http://4.bp.blogspot.com/-tqlzqKSacQY/TjxeH8xKJyI/AAAAAAAAAfs/l1hr8ugADqA/s400/DS2011banner.png" width="333" /&gt;&lt;/div&gt;&lt;br /&gt;I am going to &lt;a href="https://desktopsummit.org/"&gt;Desktop Summit&lt;/a&gt;. Actually, I have already arrived in Berlin.&lt;br /&gt;&lt;br /&gt;At the summit, I will mostly follow the Platform track (Rm2002) and then try to participate in BoFs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-7309971794750624528?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/7309971794750624528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=7309971794750624528' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7309971794750624528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7309971794750624528'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2011/08/i-am-going-to-desktop-summit-2011.html' title='I am going to Desktop Summit 2011'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-tqlzqKSacQY/TjxeH8xKJyI/AAAAAAAAAfs/l1hr8ugADqA/s72-c/DS2011banner.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-3100730509100122111</id><published>2011-03-12T14:10:00.000-08:00</published><updated>2011-03-12T14:10:21.625-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><title type='text'>The case of a non-raised exception</title><content type='html'>As you probably know, python uses exceptions for error handling. It is considered a good style to avoid adding error-handling code in the form of conditional statements. Instead, one should rely on the fact that an appropriate exception is raised once an error condition is detected, and caught when it can be dealt with.&lt;br /&gt;&lt;br /&gt;So, let'a assume that you are given a task of downloading a file given the URL and the file name on disk, using Python. You may want to write the following code and hope that you don't have to add any error-handling because (as you think) all errors that can happen are either network errors or file write errors, and those two types of errors already raise exceptions for you.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#!/usr/bin/python&lt;br /&gt;&lt;br /&gt;import urllib2&lt;br /&gt;import sys&lt;br /&gt;import socket&lt;br /&gt;&lt;br /&gt;def download(url, fname):&lt;br /&gt;    net = urllib2.urlopen(url)&lt;br /&gt;    f = open(fname, "wb")&lt;br /&gt;    &lt;br /&gt;    while True:&lt;br /&gt;        data = net.read(4096)&lt;br /&gt;        if not data:&lt;br /&gt;            break&lt;br /&gt;        f.write(data)&lt;br /&gt;    &lt;br /&gt;    net.close()&lt;br /&gt;    f.close()&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    if len(sys.argv) != 3:&lt;br /&gt;        print "Usage: download.py URL filename"&lt;br /&gt;    &lt;br /&gt;    url = sys.argv[1]&lt;br /&gt;    fname = sys.argv[2]&lt;br /&gt;    &lt;br /&gt;    socket.setdefaulttimeout(30)&lt;br /&gt;    &lt;br /&gt;    download(url, fname)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Indeed, this code downloads existing files via HTTP just fine. Also, it provides sensible tracebacks for non-existing hosts, 404 errors, full-disk situations, and socket timeouts. So, it looks like the result of calling the download() fnction is either a successfully downloaded file, or an exception that the other part of the application will likely be able to deal with.&lt;br /&gt;&lt;br /&gt;But actually, it only looks like this. Consider a situation when the HTTP server closes the connection gracefully at the TCP level, but prematurely. You can test this by starting your own Apache web server, putting a large file there, and calling "apache2ctl restart" while the client is downloading the file. Result: an incompletely downloaded file, and no exceptions.&lt;br /&gt;&lt;br /&gt;I don't know if it should be considered a bug in urllib2 or in the example download() function above. In fact, urllib2 could have noticed the mismatch of the total number of bytes before the EOF and the value in the Content-Length HTTP header.&lt;br /&gt;&lt;br /&gt;Here is a version of the download() function that detects incomplete downloads based on the Content-Length header:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;def download(url, fname):&lt;br /&gt;    net = urllib2.urlopen(url)&lt;br /&gt;    contentlen = net.info().get("Content-Length", "")&lt;br /&gt;    f = open(fname, "wb")&lt;br /&gt;    datalen = 0&lt;br /&gt;    &lt;br /&gt;    while True:&lt;br /&gt;        data = net.read(4096)&lt;br /&gt;        if not data:&lt;br /&gt;            break&lt;br /&gt;        f.write(data)&lt;br /&gt; datalen += len(data)&lt;br /&gt;    &lt;br /&gt;    net.close()&lt;br /&gt;    f.close()&lt;br /&gt;&lt;br /&gt;    try:&lt;br /&gt;        contentlen = int(contentlen)&lt;br /&gt;    except ValueError:&lt;br /&gt;        contentlen = None&lt;br /&gt;&lt;br /&gt;    if contentlen is not None and contentlen != datalen:&lt;br /&gt;        raise urllib2.URLError("Incomplete download")&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-3100730509100122111?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/3100730509100122111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=3100730509100122111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3100730509100122111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3100730509100122111'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2011/03/case-of-non-raised-exception.html' title='The case of a non-raised exception'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-7644003129159041798</id><published>2011-01-04T10:44:00.000-08:00</published><updated>2011-01-04T10:44:31.341-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='systemd'/><category scheme='http://www.blogger.com/atom/ns#' term='non-bug'/><title type='text'>Writing systemd service files</title><content type='html'>Today I tried to convert a Gentoo &lt;a href="http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/net-misc/vde/files/vde.init?view=markup"&gt;initscript&lt;/a&gt; for &lt;a href="http://vde.sourceforge.net/"&gt;VDE&lt;/a&gt; to a systemd service file. This post documents the required steps. VDE has been chosen as an example because it is a simple daemon (commonly used for communication between several instances of &lt;a href="http://wiki.qemu.org/Main_Page"&gt;QEMU&lt;/a&gt; and the host) that illustrates the matter well.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Background&lt;/h4&gt;&lt;br /&gt;For those who don't know, &lt;a href="http://www.freedesktop.org/wiki/Software/systemd"&gt;systemd&lt;/a&gt; is a next-generation replacement for /sbin/init written by &lt;span class="value"&gt;Lennart Poettering&lt;/span&gt;. While systemd supports traditional initscripts, service files are its native form of configuration. Service files have a structure similar to that of desktop files or Windows ini files. Their syntax is well documented in the &lt;a href="http://0pointer.de/public/systemd-man/"&gt;manual pages&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Initscripts typically contain boilerplate code that checks whether the daemon is already running or figures out which process to kill when the daemon has to be stopped. Tools like start-stop-daemon help, but systemd reduces the syntax overhead to the minimum. This simplification occurs because service files specify &lt;i&gt;what&lt;/i&gt; should be done, not &lt;i&gt;how&lt;/i&gt; it should be done. I.e., unlike initscripts, they follow the declarative style and are not programs.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Simple services&lt;/h4&gt;&lt;br /&gt;Here is the simplest possible service file that starts VDE with options that work for me on my computer. Save it as /etc/systemd/system/vde.service:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[Unit]&lt;br /&gt;Description=Virtual Distributed Ethernet&lt;br /&gt;&lt;br /&gt;[Service]&lt;br /&gt;ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \&lt;br /&gt; --dirmode 0750 --group qemu&lt;br /&gt;&lt;br /&gt;[Install]&lt;br /&gt;WantedBy=multi-user.target&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note the difference from a traditional init script: for simplicity, vde_switch is started in such a way that it doesn't become a daemon. In fact, it is possible to start real daemons that fork, you just have to tell systemd about that:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[Unit]&lt;br /&gt;Description=Virtual Distributed Ethernet&lt;br /&gt;&lt;br /&gt;[Service]&lt;br /&gt;Type=forking&lt;br /&gt;# The PID file is optional, but recommended in the manpage&lt;br /&gt;# "so that systemd can identify the main process of the daemon"&lt;br /&gt;PIDFile=/var/run/vde.pid&lt;br /&gt;ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \&lt;br /&gt; --dirmode 0750 --group qemu \&lt;br /&gt; --daemon --pidfile /var/run/vde.pid&lt;br /&gt;&lt;br /&gt;[Install]&lt;br /&gt;WantedBy=multi-user.target&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The difference is in the way the dependencies are handled. If some other services depend on vde.service, then, in the first example, systemd will be able to run them as soon as it starts vde_switch. In the second example, systemd will wait until vde_switch forks. The difference matters, because vde_switch creates its control socket after starting, but before forking. So, in the first example, there is some chance that systemd will start something that tries to connect to the socket before vde_switch creates it.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Automatic restarts&lt;/h4&gt;&lt;br /&gt;Let's also add the proper dependency on the system logger and tell systemd to restart vde_switch if it crashes due to an uncaught signal (although it never happened to me):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[Unit]&lt;br /&gt;Description=Virtual Distributed Ethernet&lt;br /&gt;After=syslog.target&lt;br /&gt;&lt;br /&gt;[Service]&lt;br /&gt;Type=forking&lt;br /&gt;PIDFile=/var/run/vde.pid&lt;br /&gt;ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \&lt;br /&gt; --dirmode 0750 --group qemu \&lt;br /&gt; --daemon --pidfile /var/run/vde.pid&lt;br /&gt;Restart=on-abort&lt;br /&gt;&lt;br /&gt;[Install]&lt;br /&gt;WantedBy=multi-user.target&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now try to start and crash the daemon:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;home ~ # &lt;b&gt;systemctl start vde.service&lt;/b&gt;&lt;br /&gt;home ~ # &lt;b&gt;systemctl status vde.service&lt;/b&gt;&lt;br /&gt;vde.service - Virtual Distributed Ethernet&lt;br /&gt;   Loaded: loaded (/etc/systemd/system/vde.service)&lt;br /&gt;   Active: active (running) since Tue, 04 Jan 2011 22:08:10 +0500;&lt;br /&gt;15s ago&lt;br /&gt;  Process: 31434 (/usr/bin/vde_switch --tap tap0...,&lt;br /&gt;code=exited, status=0/SUCCESS)&lt;br /&gt; Main PID: 31435 (vde_switch)&lt;br /&gt;   CGroup: name=systemd:/system/vde.service&lt;br /&gt;    └ 31435 /usr/bin/vde_switch --tap tap0...&lt;br /&gt;home ~ # &lt;b&gt;kill -SEGV 31435&lt;/b&gt;&lt;br /&gt;home ~ # &lt;b&gt;systemctl status vde.service&lt;/b&gt;&lt;br /&gt;vde.service - Virtual Distributed Ethernet&lt;br /&gt;   Loaded: loaded (/etc/systemd/system/vde.service)&lt;br /&gt;   Active: failed since Tue, 04 Jan 2011 22:11:27 +0500;&lt;br /&gt;4s ago&lt;br /&gt;  Process: 31503 (/usr/bin/vde_switch --tap tap0...,&lt;br /&gt;code=exited, status=0/SUCCESS)&lt;br /&gt; Main PID: 31504 (code=exited, status=1/FAILURE)&lt;br /&gt;   CGroup: name=systemd:/system/vde.service&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I.e., restarting didn't work. The system log tells us why:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Jan  4 22:11:27 home vde_switch[31504]: Error in pidfile&lt;br /&gt;creation: File exists&lt;/pre&gt;&lt;br /&gt;So, VDE has a bug in its pidfile creation. There are two ways how one can deal with this: either tell systemd to remove the PID file before starting vde_switch, or drop the PID file altogether (because vde_switch has exactly one process, there can be no confusion which process is the main one). Both ways work. Here is how to implement the first alternative:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[Unit]&lt;br /&gt;Description=Virtual Distributed Ethernet&lt;br /&gt;After=syslog.target&lt;br /&gt;&lt;br /&gt;[Service]&lt;br /&gt;Type=forking&lt;br /&gt;PIDFile=/var/run/vde.pid&lt;br /&gt;# Note the -f: don't fail if there is no PID file&lt;br /&gt;ExecStartPre=/bin/rm -f /var/run/vde.pid&lt;br /&gt;ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \&lt;br /&gt; --dirmode 0750 --group qemu \&lt;br /&gt; --daemon --pidfile /var/run/vde.pid&lt;br /&gt;Restart=on-abort&lt;br /&gt;&lt;br /&gt;[Install]&lt;br /&gt;WantedBy=multi-user.target&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And here is the second alternative:&lt;br /&gt;&lt;pre&gt;[Unit]&lt;br /&gt;Description=Virtual Distributed Ethernet&lt;br /&gt;After=syslog.target&lt;br /&gt;&lt;br /&gt;[Service]&lt;br /&gt;Type=forking&lt;br /&gt;ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \&lt;br /&gt; --dirmode 0750 --group qemu \&lt;br /&gt; --daemon&lt;br /&gt;Restart=on-abort&lt;br /&gt;&lt;br /&gt;[Install]&lt;br /&gt;WantedBy=multi-user.target&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Configuration&lt;/h4&gt;&lt;br /&gt;Many initscripts come with configuration files that allow the user to customize how the daemon is started. For example, some users may want to pass the --hub argument to vde_switch, and others may want to enable the management socket. So, in Gentoo, the traditional configuration file for the initscript looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;# load the tun module&lt;br /&gt;VDE_MODPROBE_TUN="yes"&lt;br /&gt;# virtual tap networking device to be used for vde&lt;br /&gt;VDE_TAP="tap0"&lt;br /&gt;# mode and group for the socket&lt;br /&gt;VDE_SOCK_CHMOD="770"&lt;br /&gt;VDE_SOCK_CHOWN=":qemu"&lt;br /&gt;&lt;br /&gt;# This is the actual options string passed to VDE.&lt;br /&gt;# Change this at your own risk.&lt;br /&gt;VDE_OPTS="--tap ${VDE_TAP} -daemon"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Systemd service files typically use the EnvironmentFile key to provide users with a file where they can put their preferences regarding the service.&lt;br /&gt;&lt;br /&gt;Traditional initscripts source their configuration files. Thus, any syntax construction supported by /bin/sh will work in the configuration file. In the example above, we see comments, assignment of values to variables, and reusing the values in later assignments. Systemd uses a different syntax from bash, so please resist the temptation to reuse the same configuration file for the traditional initscript and the service file. Resist even though some service files in the Gentoo systemd overlay do use the same configuration files as the corresponding traditional initscripts -- they are just buggy. Let me explain this in more detail.&lt;br /&gt;&lt;br /&gt;Of course, the configuration file example above is not suitable for systemd because variable interpolation is not supported in systemd environment files. But let's suppose that we want to invent something that is suitable both as a bash script fragment and a systemd environment file, and still allows the user to configure vde_switch according to his wishes.&lt;br /&gt;&lt;br /&gt;Let's focus on the VDE_OPTS variable only, as it is the only thing that matters. Indeed, module loading can be done directly by systemd (man modules-load.d), and the options related to the socket group and octal permissions can be expressed using vde_switch command line options, as illustrated in the examples above. Since the value of the VDE_OPTS variable can contain spaces, we have to quote it if we want bash to be able to understand what we mean:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;VDE_OPTS="--tap tap0 --mode 0660 --dirmode 0750 --group qemu"&lt;/pre&gt;&lt;br /&gt;Without quotes, bash would interpret this as follows: with the variable VDE_OPTS that has value "--tap" in the environment, start the "tap0" process and pass 6 parameters to it. So, quotes are essential here.&lt;br /&gt;&lt;br /&gt;Let's try to use this variable from the service file. We want it to be split by the spaces, so that each part becomes a separate vde_switch parameter. So, according to the systemd.service manpage, we have to use the $VDE_OPTS form, not ${VDE_OPTS}. So here is what we have:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;[Unit]&lt;br /&gt;Description=Virtual Distributed Ethernet&lt;br /&gt;After=syslog.target&lt;br /&gt;&lt;br /&gt;[Service]&lt;br /&gt;Type=forking&lt;br /&gt;EnvironmentFile=/etc/conf.d/vde2&lt;br /&gt;ExecStart=/usr/bin/vde_switch --daemon $VDE_OPTS&lt;br /&gt;Restart=on-abort&lt;br /&gt;&lt;br /&gt;[Install]&lt;br /&gt;WantedBy=multi-user.target&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Result: no "tap0" interface and wrong permissions on the control socket. This happened because quotes play a special role in systemd environment files, different from their role in bash scripts. For systemd, they mean that the spaces inside them should not be treated as argument separators. So, all the arguments in $VDE_OPTS were passed to vde_switch as one long argument. No surprise that it didn't work.&lt;br /&gt;&lt;br /&gt;In fact, the service file is correct. Its configuration just can't be made compatible with bash, because it is in a different language. The service works with the following configuration file (alas, incompatible with bash):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;VDE_OPTS=--tap tap0 --mode 0660 --dirmode 0750 --group qemu&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Conclusion&lt;/h4&gt;&lt;br /&gt;Let's hope that all of the above will get you started writing your own systemd service files. Since there are many initscripts in Gentoo still not converted, the project needs your help.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Thanks&lt;/h4&gt;&lt;br /&gt;The following people from #systemd IRC channel on freenode provided me with valuable support: MK_FG, zdzichuBG, miti1.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-7644003129159041798?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/7644003129159041798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=7644003129159041798' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7644003129159041798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7644003129159041798'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2011/01/writing-systemd-service-files.html' title='Writing systemd service files'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-3146031750238746743</id><published>2010-11-12T21:35:00.000-08:00</published><updated>2010-11-12T21:35:51.159-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><category scheme='http://www.blogger.com/atom/ns#' term='job'/><title type='text'>Minimizing server downtime</title><content type='html'>Suppose that you are responsible for a typical LAMP server running Debian. The goal is to have the needed security updates and minimize downtime. Unfortunately, due to installation of security updates, services need to be restarted, and sometimes (after kernel updates) even a reboot is required. Here are some tips that will help you shrink the time of service unavailability.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Upgrade your key services (apache, php and mysql) separately from the rest of software. Reason: if you just run "apt-get dist-upgrade" to upgrade everything, then your MySQL server will be stopped at the beginning of the whole upgrade and started only at the end, after unpacking all other packages. So, run "apt-get install mysql-server" before "apt-get dist-upgrade" to minimize the delay.&lt;/li&gt;&lt;li&gt;If you know that kexec works on your hardware, use it: apt-get install kexec-tools. This way, you will avoid the long time (sometimes more than a minute) while your server shows useless BIOS screens. Instead of rebooting to the BIOS, the old kernel will load the new kernel directly and pass control to it, thus avoiding the delay.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-3146031750238746743?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/3146031750238746743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=3146031750238746743' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3146031750238746743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3146031750238746743'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2010/11/minimizing-server-downtime.html' title='Minimizing server downtime'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-4756553046519799306</id><published>2010-10-23T20:20:00.000-07:00</published><updated>2010-10-23T20:20:23.271-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><title type='text'>DNS servers in resolv.conf</title><content type='html'>Yesterday I found a problem with my &lt;a href="http://www.linode.com/"&gt;Linode&lt;/a&gt; VPS. When running "aptitude update", it complained about not being able to resolve the name for security.debian.org (a server from where Debian distributes security updates).&lt;br /&gt;&lt;br /&gt;Here is the /etc/resolv.conf file from that VPS, generated by dhclient:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;domain members.linode.com&lt;br /&gt;search members.linode.com&lt;br /&gt;nameserver 109.74.194.20&lt;br /&gt;nameserver 109.74.192.20&lt;br /&gt;nameserver 109.74.193.20&lt;/pre&gt;&lt;br /&gt;It looks like the 109.74.194.20 server is overloaded and returns a SERVFAIL ("server failure - The name server was unable to process this query due to a problem with the name server") answer from time to time. Unfortunately, Debian has a &lt;a href="http://bugs.debian.org/535504"&gt;bug&lt;/a&gt; in its version of libc that prevents the resolver from trying the other (working) servers if the first one returns an answer indicating a temporary failure.&lt;br /&gt;&lt;br /&gt;The bug does not exist in glibc-2.12.1 on my Gentoo box at home. However, if the server does not respond at all, the Gentoo resolver spends 15 seconds before trying the next one, which is also not nice.&lt;br /&gt;&lt;br /&gt;To work around the bug, you can run your own DNS server on 127.0.0.1 that forwards all queries to the official servers, and put 127.0.0.1 as the only nameserver in /etc/resolv.conf.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-4756553046519799306?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/4756553046519799306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=4756553046519799306' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4756553046519799306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4756553046519799306'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2010/10/dns-servers-in-resolvconf.html' title='DNS servers in resolv.conf'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-176114957678888629</id><published>2010-08-05T21:54:00.000-07:00</published><updated>2010-08-05T21:54:52.227-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='non-bug'/><title type='text'>Floating-point fields in MySQL</title><content type='html'>It is a well-known fact that floating-point values should not be compared for equality, due to rounding problems. MySQL puts an interesting twist to it, as demonstrated on the &lt;a href="http://www.linux.org.ru/forum/general/5191936"&gt;linux.org.ru forum&lt;/a&gt;. To see the strange effect, run the following SQL statements:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;mysql&amp;gt; &lt;b&gt;create table test (field float);&lt;/b&gt;&lt;br /&gt;mysql&amp;gt; &lt;b&gt;insert into test(field) values(0.3);&lt;/b&gt;&lt;br /&gt;mysql&amp;gt; &lt;b&gt;select * from test;&lt;/b&gt;&lt;br /&gt;+-------+&lt;br /&gt;| field |&lt;br /&gt;+-------+&lt;br /&gt;|   0.3 |&lt;br /&gt;+-------+&lt;br /&gt;1 row in set (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; &lt;b&gt;select * from test where field = 0.3;&lt;/b&gt;&lt;br /&gt;&lt;span style="color: red;"&gt;Empty set&lt;/span&gt; (0.00 sec)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I.e., it looks like MySQL successfully stores the value, but cannot find it later.&lt;br /&gt;&lt;br /&gt;To see why it happens, recall that 0.3 cannot be represented exactly in binary form. It is a recurring binary fraction 0b0.0100110011... that has to be truncated somewhere when stored by a computer. When stored as a float data type in the table, 24 significant binary digits are retained. However, when 0.3 is mentioned directly in the query, it is treated as a double-precision number, and 53 significant bits are kept. The truncation thus occurs in different places, and the resulting numbers are just different:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;mysql&amp;gt; &lt;b&gt;select cast(field as decimal(15,15)) from test;&lt;/b&gt;&lt;br /&gt;+-------------------------------+&lt;br /&gt;| cast(field as decimal(15,15)) |&lt;br /&gt;+-------------------------------+&lt;br /&gt;|             0.300000011920929 |&lt;br /&gt;+-------------------------------+&lt;br /&gt;1 row in set (0.00 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; &lt;b&gt;select cast(0.3 as decimal(15,15));&lt;/b&gt;&lt;br /&gt;+-----------------------------+&lt;br /&gt;| cast(0.3 as decimal(15,15)) |&lt;br /&gt;+-----------------------------+&lt;br /&gt;|           0.300000000000000 |&lt;br /&gt;+-----------------------------+&lt;br /&gt;1 row in set (0.00 sec)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;MySQL documentation contains more information on &lt;a href="http://dev.mysql.com/doc/refman/5.1/en/problems-with-float.html"&gt;problems with floating-point values&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-176114957678888629?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/176114957678888629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=176114957678888629' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/176114957678888629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/176114957678888629'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2010/08/floating-point-fields-in-mysql.html' title='Floating-point fields in MySQL'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-8701566673255824928</id><published>2010-05-29T07:29:00.000-07:00</published><updated>2010-05-29T07:29:41.342-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='benchmark'/><title type='text'>When linear search is faster than binary</title><content type='html'>It is often stated that a binary search algorithm performs better than linear search. Indeed, let's assume that the goal is to find the index of the largest integer in the sorted zero-based array A that is less than or equal to the given integer X. For simplicity, let's also assume that the size of array is N = 2&lt;sup&gt;K&lt;/sup&gt;, that for all possible inputs A[0] &amp;lt;= X and that one can also put a sentinel value after the end, so that X &amp;lt; A[N] for all possible inputs X. E.g., if it is known that X always fits in 16 bits, one can put 65536 as the sentinel.&lt;br /&gt;&lt;br /&gt;The assumptions above are true for the entropy decoder found in the Monkey's Audio codec. In that case, N = 64, X is unsigned and always fits in 16 bits, A[0] = 0, A[64] = 65536.&lt;br /&gt;&lt;br /&gt;This is linear search:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;A[N] = sentinel;&lt;br /&gt;index = 0;&lt;br /&gt;while (A[index + 1] &amp;lt;= X)&lt;br /&gt;    ++index;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is binary search:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;int bit = N &amp;gt;&amp;gt; 1;  /* N is a power of two */&lt;br /&gt;index = 0;&lt;br /&gt;while (bit != 0) {&lt;br /&gt;    if (A[index + bit] &amp;lt;= X)&lt;br /&gt;        index += bit;&lt;br /&gt;    bit &amp;gt;&amp;gt;= 1;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If the size of array to be searched is N, then the average case complexity is commonly stated to be O(log(N)) for binary search and O(N) for linear search. So, for the Monkey's Audio case presented above, one would expect, on average, 32 loop iterations for the linear search algorithm and 6 iterations for the binary search.&lt;br /&gt;&lt;br /&gt;However, there is one key fact that makes the estimate above invalid. Such simple averaging is valid only if each result appears with the same probability. For Monkey's Audio entropy decoder, this is not the case.&lt;br /&gt;&lt;br /&gt;In fact, out of the 64 possible results, "0" appears with 31% probability, and the total probability of the first 6 results is 87%. So, it is quite natural that linear search is faster than binary in this case, because it often stops very early.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-8701566673255824928?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/8701566673255824928/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=8701566673255824928' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/8701566673255824928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/8701566673255824928'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2010/05/when-linear-search-is-faster-than.html' title='When linear search is faster than binary'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-5878021044635524805</id><published>2010-05-10T01:56:00.000-07:00</published><updated>2010-05-10T07:01:26.547-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><title type='text'>Converting audio samples</title><content type='html'>Different audio applications process audio in different sample formats. Some use 16-bit samples internally, others use 32-bit or floating point samples. In either case, there is a need to convert the sample format for the purposes of export and playback. Sounds like a simple task? No, a rich field for disagreements and bugs, even without taking dithering into account.&lt;br /&gt;&lt;br /&gt;E.g., when converting from floating-point samples to 16 bits, there is no universal agreement on the scale. Some (e.g., &lt;a href="http://www.jackaudio.org/"&gt;JACK&lt;/a&gt;) think that 1.0 should convert to 32767 and -1.0 to -32767. Within that model, there are two opinions how to get -32768 (the most negative integer representable in 16 bits): "-1.00003" or "you can't". The other viewpoint, implemented, e.g., in &lt;a href="http://www.alsa-project.org/"&gt;ALSA-lib&lt;/a&gt;, is that -1.0 should map to -32768, 0.99997 should give 32767 and 1.0 should clip. There is also a third possibility, namely, to use different scale factors for negative and positive sample values, so that 0.0 maps to 0, 1.0 maps to 32767 and -1.0 maps to -32768.&lt;br /&gt;&lt;br /&gt;And now some code. If one adopts the 32767.0 scale factor (as in JACK) and declares the -32768 sample value as being unreachable, one can use the following C function for conversion:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;int16_t to_int16(float f)&lt;br /&gt;{&lt;br /&gt;    if (f &gt; 1.0) f = 1.0;&lt;br /&gt;    if (f &lt; -1.0) f = -1.0;&lt;br /&gt;    return (int16_t)(f * 0x7fff);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;As expected, to_int16(1.0) == 32767 and to_int16(-1.0) == -32767. However, the following quite similar function that is supposed to convert floating-point samples to 32-bit signed ones &lt;a href="http://jira.atheme.org/browse/AUD-191"&gt;doesn't actually work properly&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;int32_t to_int32(float f)&lt;br /&gt;{&lt;br /&gt;    if (f &gt; 1.0) f = 1.0;&lt;br /&gt;    if (f &lt; -1.0) f = -1.0;&lt;br /&gt;    return (int32_t)(f * 0x7fffffff);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Indeed, to_int32(1.0) returns the minimum, not the maximum possible 32-bit integer. That happens because, unlike 0x7fff, one cannot represent 0x7fffffff exactly as a floating-point number. So the nearest representable one is substituted, and that's 0x80000000. So the overflow happens, and the function returns a wrong result.&lt;br /&gt;&lt;br /&gt;One fix would be to cast 0x7fffffff to a double-precision floating point number. Another (but not 100% equivalent) option is to change the scale factor so that it is close enough, but doesn't cause this overflow, i.e. to 0x7fffff00.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-5878021044635524805?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/5878021044635524805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=5878021044635524805' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/5878021044635524805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/5878021044635524805'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2010/05/converting-audio-samples.html' title='Converting audio samples'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-997300831579493280</id><published>2010-05-10T01:15:00.000-07:00</published><updated>2010-05-10T01:54:15.829-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='job'/><title type='text'>New job</title><content type='html'>This blog didn't receive any updates for quite a while. That's both because I was too busy at Yandex and because there were no interesting bugs to blog about. I no longer work at Yandex. There was no single big reason for this decision, just a lot of small issues that accumulated and that don't apply to anyone else.&lt;br /&gt;&lt;br /&gt;Now I work for a different company (won't name it here, it is nether SEO nor Google) as a senior developer of a web service. The most pleasant result of the job change is that now I feel more like a developer: write mostly C code, not bash scripts, and can even make architectural decisions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-997300831579493280?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/997300831579493280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=997300831579493280' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/997300831579493280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/997300831579493280'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2010/05/new-job.html' title='New job'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-4608276350875220137</id><published>2009-09-06T01:23:00.000-07:00</published><updated>2009-09-06T03:43:21.950-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='job'/><category scheme='http://www.blogger.com/atom/ns#' term='benchmark'/><title type='text'>Matching multiple strings</title><content type='html'>Sometimes, it is necessary to check efficiently which of the many input strings contain one of the few pre-defined substrings.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;The simplest approach&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;This task, of course, can be solved inefficiently by brute force, i.e. by passing each substring to strstr(), as demonstrated in the following benchmark that tries to search for known names of linux games in the same string (that actually doesn't match) 100000 times:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/* compile as follows: gcc -o benchmark1 benchmark1.c */&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include &amp;lt;sys/time.h&amp;gt;&lt;br /&gt;&lt;br /&gt;const char* words[] = { "actioncube", "alien arena", "astromenace",&lt;br /&gt;"armagetron", "assault cube", "atomorun2008", "battle for wesnoth",&lt;br /&gt;"blob wars", "breakout", "briquolo", "bzflag", "celetania",&lt;br /&gt;"conquest", "enigma", "freeciv", "freecol", "freeorion",&lt;br /&gt;"freetrain", "glest", "hedgewars", "heretic", "hexen",&lt;br /&gt;"jardinanis", "lordsawar", "lxdream", "maniadrive", "neverball",&lt;br /&gt;"nexuiz", "oolite", "openarena", "opencity", "openmw",&lt;br /&gt;"open quartz", "openttd", "paintball 2", "planeshift", "postal 2",&lt;br /&gt;"sacred", "scorched 3d", "smokin' guns", "supertux", "supertuxkart",&lt;br /&gt;"teeworlds", "tremulous", "urban terror", "vegastrike", "warsow",&lt;br /&gt;"wormux", "x-moto" };&lt;br /&gt;&lt;br /&gt;const char* text = "gnome uses gconf to store all of its configuration";&lt;br /&gt;&lt;br /&gt;int check(const char* p)&lt;br /&gt;{&lt;br /&gt;    int i;&lt;br /&gt;    for (i = 0; i &amp;lt; sizeof(words)/sizeof(words[0]); i++) {&lt;br /&gt;        if (strstr(p, words[i]) != NULL)&lt;br /&gt;            return 0;&lt;br /&gt;    }&lt;br /&gt;    return -1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt;    struct timeval start, end;&lt;br /&gt;    int usec_spent;&lt;br /&gt;    int i;&lt;br /&gt;    int result;&lt;br /&gt;&lt;br /&gt;    gettimeofday(&amp;amp;start, NULL);&lt;br /&gt;    for (i = 0; i &amp;lt; 100000; i++)&lt;br /&gt;        result = check(text);&lt;br /&gt;&lt;br /&gt;    gettimeofday(&amp;amp;end, NULL);&lt;br /&gt;    if (result == 0)&lt;br /&gt;        printf("The string matched\n");&lt;br /&gt;    else&lt;br /&gt;        printf("The string didn't match\n");&lt;br /&gt;    usec_spent = 1000000 * (end.tv_sec - start.tv_sec);&lt;br /&gt;    usec_spent += (end.tv_usec - start.tv_usec);&lt;br /&gt;    printf("Spent %d ms\n", usec_spent / 1000);&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;On my desktop, this benchmark takes 2925 ms.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Aho-Corasick algorithm&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Special fast algorithms exist for the same task. All of them preprocess the given set of substrings and use the result of such preprocessing when deciding whether the input string matches. One of such ahorithms is the &lt;a href="http://en.wikipedia.org/wiki/Aho-Corasick_algorithm"&gt;Aho-Corasick algorithm&lt;/a&gt;. A free (BSD-licensed) implementation is available for &lt;a href="http://www.komodia.com/index.php?page=AhoCorasick.html"&gt;download&lt;/a&gt;. Unfortunately, this implementation operates on wide-character strings and thus is not directly comparable with the strstr() approach. Also, if one of the substrings matches the very beginning of the input string, one cannot distinguish this situation with CSuffixTrie::SearchAhoCorasik() from non-match by looking only atCSuffixTrie::_DataFound::iFoundPosition. To compensate for this, I have extracted the SuffixTrie.{h,cpp} files from the archive and applied the following edits:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;--- SuffixTrie.h&lt;br /&gt;+++ SuffixTrie.h&lt;br /&gt;@@ -51,7 +51,7 @@&lt;br /&gt; {&lt;br /&gt; public:&lt;br /&gt;     //Our string type&lt;br /&gt;-    typedef std::wstring SearchString;&lt;br /&gt;+    typedef std::string SearchString;&lt;br /&gt; &lt;br /&gt;     //Data returned from our search&lt;br /&gt;     typedef struct _DataFound&lt;br /&gt;@@ -107,7 +107,7 @@&lt;br /&gt;     virtual ~CSuffixTrie();&lt;br /&gt; private:&lt;br /&gt;     //Our char search type&lt;br /&gt;-    typedef wchar_t SearchChar;&lt;br /&gt;+    typedef char SearchChar;&lt;br /&gt; &lt;br /&gt;     //Forward declare the node&lt;br /&gt;     struct _Node;&lt;br /&gt;--- SuffixTrie.cpp    2008-09-24 02:20:46.000000000 +0600&lt;br /&gt;+++ SuffixTrie.cpp    2009-09-06 15:22:11.000000000 +0600&lt;br /&gt;@@ -1,4 +1,3 @@&lt;br /&gt;-#include "stdafx.h"&lt;br /&gt; #include "SuffixTrie.h"&lt;br /&gt; &lt;br /&gt; CSuffixTrie::CSuffixTrie()&lt;br /&gt;@@ -136,7 +135,7 @@&lt;br /&gt; void CSuffixTrie::BuildTreeIndex()&lt;br /&gt; {&lt;br /&gt;     //Build index on the root&lt;br /&gt;-    BuildIndex(L"",&lt;br /&gt;+    BuildIndex("",&lt;br /&gt;                &amp;amp;m_aRoot);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt;@@ -261,7 +260,7 @@&lt;br /&gt; {&lt;br /&gt;     //Our data found&lt;br /&gt;     DataFound aData;&lt;br /&gt;-    aData.iFoundPosition=0;&lt;br /&gt;+    aData.iFoundPosition=-1;&lt;br /&gt; &lt;br /&gt;     //The current string we match&lt;br /&gt;     SearchString sMatchedString;&lt;br /&gt;@@ -295,7 +294,7 @@&lt;br /&gt;                     pNode=&amp;amp;m_aRoot;&lt;br /&gt; &lt;br /&gt;                     //Reset search string&lt;br /&gt;-                    sMatchedString=L"";&lt;br /&gt;+                    sMatchedString="";&lt;br /&gt; &lt;br /&gt;                     //Did we do a switch?&lt;br /&gt;                     if (bSwitch)&lt;br /&gt;@@ -386,7 +385,7 @@&lt;br /&gt;                     pNode=&amp;amp;m_aRoot;&lt;br /&gt; &lt;br /&gt;                     //Reset search string&lt;br /&gt;-                    sMatchedString=L"";&lt;br /&gt;+                    sMatchedString="";&lt;br /&gt; &lt;br /&gt;                     //Did we do a switch?&lt;br /&gt;                     if (bSwitch)&lt;br /&gt;@@ -439,7 +438,7 @@&lt;br /&gt;             iCount-=sMatchedString.length()-1;&lt;br /&gt; &lt;br /&gt;             //Reset the data&lt;br /&gt;-            sMatchedString=L"";&lt;br /&gt;+            sMatchedString="";&lt;br /&gt;         }&lt;br /&gt;     }&lt;br /&gt; &lt;br /&gt;@@ -495,7 +494,7 @@&lt;br /&gt; &lt;br /&gt;     //Start to build the trie&lt;br /&gt;     BuildStrings(aVector,&lt;br /&gt;-                 L"",&lt;br /&gt;+                 "",&lt;br /&gt;                  &amp;amp;m_aRoot);&lt;br /&gt; &lt;br /&gt;     //Done&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is a benchmark:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/* compile as follows: g++ -O2 -o benchmark2 benchmark2.cpp SuffixTrie.cpp */&lt;br /&gt;#include "SuffixTrie.h"&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;string&amp;gt;&lt;br /&gt;#include &amp;lt;sys/time.h&amp;gt;&lt;br /&gt;&lt;br /&gt;const char* words[] = { "actioncube", "alien arena", "astromenace",&lt;br /&gt;"armagetron", "assault cube", "atomorun2008", "battle for wesnoth",&lt;br /&gt;"blob wars", "breakout", "briquolo", "bzflag", "celetania",&lt;br /&gt;"conquest", "enigma", "freeciv", "freecol", "freeorion",&lt;br /&gt;"freetrain", "glest", "hedgewars", "heretic", "hexen",&lt;br /&gt;"jardinanis", "lordsawar", "lxdream", "maniadrive", "neverball",&lt;br /&gt;"nexuiz", "oolite", "openarena", "opencity", "openmw",&lt;br /&gt;"open quartz", "openttd", "paintball 2", "planeshift", "postal 2",&lt;br /&gt;"sacred", "scorched 3d", "smokin' guns", "supertux", "supertuxkart",&lt;br /&gt;"teeworlds", "tremulous", "urban terror", "vegastrike", "warsow",&lt;br /&gt;"wormux", "x-moto" };&lt;br /&gt;&lt;br /&gt;const char* text = "gnome uses gconf to store all of its configuration";&lt;br /&gt;&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt;    struct timeval start, end;&lt;br /&gt;    int usec_spent;&lt;br /&gt;&lt;br /&gt;    CSuffixTrie aTree;&lt;br /&gt;    int i;&lt;br /&gt;    for (i = 0; i &amp;lt; sizeof(words)/sizeof(words[0]); i++)&lt;br /&gt;        aTree.AddString(words[i]);&lt;br /&gt;&lt;br /&gt;    aTree.BuildTreeIndex();&lt;br /&gt;&lt;br /&gt;    std::string stext = text;&lt;br /&gt;    int pos;&lt;br /&gt;&lt;br /&gt;    gettimeofday(&amp;amp;start, NULL);&lt;br /&gt;    for (i = 0; i &amp;lt; 100000; i++) {&lt;br /&gt;        pos = aTree.SearchAhoCorasik(stext).iFoundPosition;&lt;br /&gt;    }&lt;br /&gt;    gettimeofday(&amp;amp;end, NULL);&lt;br /&gt;&lt;br /&gt;    if (pos == -1)&lt;br /&gt;        printf("The string didn't match\n");&lt;br /&gt;    else&lt;br /&gt;        printf("The string matched\n");&lt;br /&gt;&lt;br /&gt;    usec_spent = 1000000 * (end.tv_sec - start.tv_sec);&lt;br /&gt;    usec_spent += (end.tv_usec - start.tv_usec);&lt;br /&gt;    printf("Spent %d ms\n", usec_spent / 1000);&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;On my desktop, this benchmark takes 326 ms. I.e., this is indeed faster than the simple strstr()-based approach.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Regular expressions&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;One may wonder why the Aho-Corasick algorithm is not available in the standard C library. The answer is that a more general mechanism, based on a similar idea, is available: regular expressions. One first constructs a regular expression by escaping and joining the given substrings using "|" as the separator, then compiles this regular expression with regcomp() and uses the result in regexec(). Here is a benchmark:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/* compile as follows: gcc -o benchmark3 benchmark3.c */&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;regex.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include &amp;lt;sys/time.h&amp;gt;&lt;br /&gt;&lt;br /&gt;const char* re = "(actioncube|alien arena|astromenace|armagetron|"&lt;br /&gt;"assault cube|atomorun2008|battle for wesnoth|blob wars|breakout|"&lt;br /&gt;"briquolo|bzflag|celetania|conquest|enigma|freeciv|freecol|"&lt;br /&gt;"freeorion|freetrain|glest|hedgewars|heretic|hexen|jardinanis|"&lt;br /&gt;"lordsawar|lxdream|maniadrive|neverball|nexuiz|oolite|openarena|"&lt;br /&gt;"opencity|openmw|open quartz|openttd|paintball 2|planeshift|"&lt;br /&gt;"postal 2|sacred|scorched 3d|smokin' guns|supertux|supertuxkart|"&lt;br /&gt;"teeworlds|tremulous|urban terror|vegastrike|warsow|wormux|x-moto)";&lt;br /&gt;&lt;br /&gt;const char* text = "gnome uses gconf to store all of its configuration";&lt;br /&gt;&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt;    struct timeval start, end;&lt;br /&gt;    int usec_spent;&lt;br /&gt;    int i;&lt;br /&gt;    regex_t reg;&lt;br /&gt;    int result;&lt;br /&gt;&lt;br /&gt;    result = regcomp(&amp;amp;reg, re, REG_EXTENDED | REG_NOSUB);&lt;br /&gt;    if (result)&lt;br /&gt;        return 1;&lt;br /&gt;&lt;br /&gt;    gettimeofday(&amp;amp;start, NULL);&lt;br /&gt;    for (i = 0; i &amp;lt; 100000; i++)&lt;br /&gt;        result = regexec(&amp;amp;reg, text, 0, NULL, 0);&lt;br /&gt;    gettimeofday(&amp;amp;end, NULL);&lt;br /&gt;    if (result == 0)&lt;br /&gt;        printf("The string matched\n");&lt;br /&gt;    else&lt;br /&gt;        printf("The string didn't match\n");&lt;br /&gt;    usec_spent = 1000000 * (end.tv_sec - start.tv_sec);&lt;br /&gt;    usec_spent += (end.tv_usec - start.tv_usec);&lt;br /&gt;    printf("Spent %d ms\n", usec_spent / 1000);&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;On this linux x86 system with glibc-2.10.1, it takes 312 ms, i.e., as good as the Aho-Corasick implementation referenced above.&lt;br /&gt;&lt;br /&gt;On other platforms, the result may be different, because the system C library uses a different regular expression engine. E.g., the same benchmark, when compiled with &lt;a href="http://laurikari.net/tre/"&gt;TRE&lt;/a&gt; (replace &amp;lt;regex.h&amp;gt; with &amp;lt;tre/regex.h&amp;gt; and add -ltre to the gcc command line), takes 4158 ms, i.e., more than the brute-force approach. Since NetBSD will use TRE in the next release, this unsatisfactory result will likely manifest itself there. So, if you want to give the same performance guarantees on all platforms, don't use regcomp()/regexec().&lt;br /&gt;&lt;br /&gt;The same note about unsatisfactory speed (8884 ms here) applies to &lt;a href="http://www.pcre.org/"&gt;PCRE&lt;/a&gt;. To repeat this test, replace &amp;lt;regex.h&amp;gt; with &amp;lt;pcreposix.h&amp;gt; and add -lpcreposix to gcc command line.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Generated code&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;It is possible to match multiple substrings much faster, if they are known at compile time. The trick is to convert the regular expression to C code with a tool such as &lt;a href="http://re2c.org/"&gt;re2c&lt;/a&gt;. Here is a benchmark of this approach:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;/* compile as follows:&lt;br /&gt;  re2c benchmark4.re &amp;gt;benchmark4.c&lt;br /&gt;  gcc -O1 -o benchmark4 benchmark4.c */&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;sys/time.h&amp;gt;&lt;br /&gt;&lt;br /&gt;int check(const char* p)&lt;br /&gt;{&lt;br /&gt;    const unsigned char* marker;&lt;br /&gt;/*!re2c&lt;br /&gt;    re2c:define:YYCTYPE  = "unsigned char";&lt;br /&gt;    re2c:define:YYCURSOR = p;&lt;br /&gt;    re2c:yyfill:enable = 0;&lt;br /&gt;    re2c:define:YYMARKER = marker;&lt;br /&gt;    re2c:yych:conversion = 1;&lt;br /&gt;    [\001-\377]*( "actioncube" | "alien arena" |&lt;br /&gt;    "astromenace" | "armagetron" |&lt;br /&gt;    "assault cube" | "atomorun2008" |&lt;br /&gt;    "battle for wesnoth" | "blob wars" |&lt;br /&gt;    "breakout" | "briquolo" | "bzflag" |&lt;br /&gt;    "celetania" | "conquest" | "enigma" |&lt;br /&gt;    "freeciv" | "freecol" | "freeorion" |&lt;br /&gt;    "freetrain" | "glest" | "hedgewars" |&lt;br /&gt;    "heretic" | "hexen" | "jardinanis" |&lt;br /&gt;    "lordsawar" | "lxdream" | "maniadrive" |&lt;br /&gt;    "neverball" | "nexuiz" | "oolite" |&lt;br /&gt;    "openarena" | "opencity" | "openmw" |&lt;br /&gt;    "open quartz" | "openttd" | "paintball 2" |&lt;br /&gt;    "planeshift" | "postal 2" | "sacred" |&lt;br /&gt;    "scorched 3d" | "smokin' guns" |&lt;br /&gt;    "supertux" | "supertuxkart" |&lt;br /&gt;    "teeworlds" | "tremulous" |&lt;br /&gt;    "urban terror" | "vegastrike" |&lt;br /&gt;    "warsow" | "wormux" |&lt;br /&gt;    "x-moto" ) { return 0; }&lt;br /&gt;    "" { return 1; }&lt;br /&gt;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;const char* text = "gnome uses gconf to store all of its configuration";&lt;br /&gt;&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt;    struct timeval start, end;&lt;br /&gt;    int usec_spent;&lt;br /&gt;    int i;&lt;br /&gt;    int result;&lt;br /&gt;&lt;br /&gt;    gettimeofday(&amp;amp;start, NULL);&lt;br /&gt;    for (i = 0; i &amp;lt; 100000; i++)&lt;br /&gt;        result = check(text);&lt;br /&gt;    gettimeofday(&amp;amp;end, NULL);&lt;br /&gt;    if (result == 0)&lt;br /&gt;        printf("The string matched\n");&lt;br /&gt;    else&lt;br /&gt;        printf("The string didn't match\n");&lt;br /&gt;    usec_spent = 1000000 * (end.tv_sec - start.tv_sec);&lt;br /&gt;    usec_spent += (end.tv_usec - start.tv_usec);&lt;br /&gt;    printf("Spent %d ms\n", usec_spent / 1000);&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When compiled with -O1 or -O3, this benchmark runs 12 ms here (i.e., almost 250 times faster than the brute-force approach). However, the -O2 optimization option makes the code slower (it runs 16 ms).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-4608276350875220137?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/4608276350875220137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=4608276350875220137' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4608276350875220137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4608276350875220137'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2009/09/matching-multiple-strings.html' title='Matching multiple strings'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-8480177361858537145</id><published>2009-07-25T06:43:00.000-07:00</published><updated>2009-07-25T08:16:18.752-07:00</updated><title type='text'>Dangers of setjmp()/longjmp()</title><content type='html'>Suppose that you want to use an external library (e.g., libpng or libjpeg) that, in terms of error handling, only gives you a choice between an approach based on setjmp()/longjmp(), or aborting the program. Of course, in robust programs, the first method should be used. But there are pitfalls inherently associated with using setjmp(). Let me illustrate one of them using ImageMagick as an example.&lt;br /&gt;&lt;br /&gt;Once ImageMagick determines the dimensions of a PNG image that it reads, it allocates memory to store the pixels. The task is to free it if the image file is in fact broken. But the input image can be so broken that libpng calls longjmp() before telling the dimensions of the image. In this case, ImageMagick allocates nothing and thus has nothing to free. In short, the error path has to free memory if and only if it has been allocated before.&lt;br /&gt;&lt;br /&gt;Sounds simple? Let's look at the code that seems to implement this logic:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;static Image *ReadOnePNGImage(MngInfo *mng_info,&lt;br /&gt;  const ImageInfo *image_info, ExceptionInfo *exception)&lt;br /&gt;{&lt;br /&gt;Image&lt;br /&gt;  *image;&lt;br /&gt;&lt;br /&gt;png_struct&lt;br /&gt;  *ping;&lt;br /&gt;&lt;br /&gt;unsigned char&lt;br /&gt;  *png_pixels;&lt;br /&gt;&lt;br /&gt;/* set up ping, do other things */&lt;br /&gt;&lt;br /&gt;png_pixels=(unsigned char *) NULL;&lt;br /&gt;if (setjmp(ping-&gt;jmpbuf))&lt;br /&gt;  {&lt;br /&gt;    /*&lt;br /&gt;      PNG image is corrupt.&lt;br /&gt;    */&lt;br /&gt;    /* ... */&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;      if (png_pixels != (unsigned char *) NULL)&lt;br /&gt;       png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);&lt;/span&gt;&lt;br /&gt;    /* ... */&lt;br /&gt;    return(GetFirstImageInList(image));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;/* read png dimensions and other information - might call longjmp() */&lt;br /&gt;&lt;br /&gt;if (num_passes &gt; 1)&lt;br /&gt;  png_pixels=(unsigned char *) AcquireQuantumMemory(image-&gt;rows,&lt;br /&gt;    ping_info-&gt;rowbytes*sizeof(*png_pixels));&lt;br /&gt;else&lt;br /&gt;  png_pixels=(unsigned char *) AcquireQuantumMemory(ping_info-&gt;rowbytes,&lt;br /&gt;    sizeof(*png_pixels));&lt;br /&gt;&lt;br /&gt;/* read the scanlines - might call longjmp() */&lt;br /&gt;/* do other useful things */&lt;br /&gt;return (image);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;At first, it seems that the code does exactly what is stated above: the bold statement frees the png_pixels pointer only if the memory has been allocated. But actually it doesn't work: the memory leaks if a broken PNG file is attempted to be processed. The library seems to forget after returning to setjmp() that a non-NULL value has been assigned to png_pixels. If one inserts printf() calls for debugging, they will print a non-NULL value after AcquireQuantumMemory(), but a NULL before the NULL check. So the memory is allocated and then not freed.&lt;br /&gt;&lt;br /&gt;The explanation is that, after longjmp(), according to the ISO C standard (7.13.2.1, "The longjmp function"),&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;All accessible objects have values as of the time longjmp was called, except that the values of objects of automatic storage duration that are local to the function containing the invocation of the corresponding setjmp macro that do not have volatile-qualified type and have been changed between the setjmp invocation and longjmp call are indeterminate.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Indeed, the png_pixels variable is automatic, not volatile, and is changed between the calls to setjmp() and longjmp(), and thus the standard allows it to forget its value. GCC sometimes (but not always!) warns about such cases.&lt;br /&gt;&lt;br /&gt;The simplest fix is usually to declare the variable as volatile. As for ImageMagick, they opted to fix the problem &lt;a href="http://trac.imagemagick.org/changeset/1173/ImageMagick/trunk/coders/png.c?contextlines=12&amp;amp;old=1101&amp;amp;old_path=ImageMagick%2Ftrunk%2Fcoders%2Fpng.c"&gt;in a different way&lt;/a&gt; (but, if I read it correctly - with some dead code, and not for all variables), and version 6.5.4-6 is supposed to have no such memory leak.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-8480177361858537145?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/8480177361858537145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=8480177361858537145' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/8480177361858537145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/8480177361858537145'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2009/07/dangers-of-setjmplongjmp.html' title='Dangers of setjmp()/longjmp()'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-2087908025360136971</id><published>2009-06-28T07:20:00.000-07:00</published><updated>2009-06-28T08:05:01.702-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tv'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>New TV Set</title><content type='html'>Recently I bougth a new TV set - a &lt;a href="http://www.sony.co.uk/product/t32-w-series/kdl-32w5500"&gt;Sony KDL-32W5500&lt;/a&gt; LCD. It replaced the old Soviet-era CRT-based "Akari" TV.&lt;br /&gt;&lt;br /&gt;The new TV set receives both analog (still the norm in Russia) and digital stations. It contains an AVC tuner and thus doesn't need a set-top box or a separate decoder to watch the digital broadcasts (Russia, unlike the rest of the world, uses MPEG-4 Part 10, aka H.264, for them).&lt;br /&gt;&lt;br /&gt;The default color settings are good, unlike those in the old TV, which gave oversaturated colors by default. The new TV set has a 1920x1080 panel, but I found that, for my eyes, 1080i and 720p (from the computer monitor) look nearly the same. And there is not a lot of HD content in Yekaterinburg anyway.&lt;br /&gt;&lt;br /&gt;The default option for aspect ratio, however, is to stretch 4:3 content to 16:9 non-uniformly. The options to preserve the original 4:3 aspect and to stretch the letterboxed content uniformly do exist. I wish it could autodetect the letterbox - this would mean one less button to use.&lt;br /&gt;&lt;br /&gt;While digital broadcasts are displayed perfectly, the analog tuner is not so good as in the old TV. Stations that were displayed with "snow" by the old TV now are not watchable at all due to either flashing color stripes or the "no signal" message.&lt;br /&gt;&lt;br /&gt;As far as the inputs go, the Sony TV has 4 HDMI sockets, one VGA, one USB and one Ethernet connection. Using a DVI -&gt; HDMI cable and a separate analog audio cable, this TV can be connected to my computer's video card and sound card. The intel driver had no problems recognizing it as a 1920x1080@60p panel.&lt;br /&gt;&lt;br /&gt;However, as the primary TV show consumer is my mother, this is still not a very viable solution. I want to work while she watches films. So, I tried using the built-in media playback capabilities of this TV set.&lt;br /&gt;&lt;br /&gt;First, it can play files from USB flash drives. The instruction says it accepts only MPEG-1, but de-facto, it accepted non-interlaced MPEG-2, too. It could not play a DVD rip from the flash drive. So, using this option means transcoding everything - not very good.&lt;br /&gt;&lt;br /&gt;Second, it can act as a UPnP Renderer. The instruction says that the TV accepts MPEG-1, MPEG-2 and AVCHD formats. So, I thought I could set up a media server on my computer. I have tried &lt;a href="http://mediatomb.cc/"&gt;MediaTomb&lt;/a&gt; and &lt;a href="http://sourceforge.net/projects/minidlna/"&gt;MiniDLNA&lt;/a&gt;. MediaTomb did not work at all (the TV displayed the "server is unsupported" message), MiniDLNA worked somewhat. I was able to send MPEG-1 and MPEG-2 files (including the DVD rip that failed to play from the flash drive) to the TV. I could not remux an available H.264 + AC3 Matroska file to MPEG-TS in such a way that the TV understands it. So, for typical torrented video files, this still means transcoding. So, I must conclude that the media playback capabilities of this TV are there only for the marketing department to be able to say that they exist.&lt;br /&gt;&lt;br /&gt;So, I am a bit dissatisfied with this offer from Sony.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-2087908025360136971?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/2087908025360136971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=2087908025360136971' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/2087908025360136971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/2087908025360136971'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2009/06/new-tv-set.html' title='New TV Set'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-9097093191100419737</id><published>2009-03-08T11:10:00.000-07:00</published><updated>2009-03-08T11:13:47.096-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gcc'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><title type='text'>Don't use old dtoa.c</title><content type='html'>A lot of projects use the &lt;a href="http://www.netlib.org/fp/dtoa.c"&gt;dtoa.c&lt;/a&gt; file that bears the following copyright notice:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt; * The author of this software is David M. Gay.&lt;br /&gt; *&lt;br /&gt; * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If your software uses it, please take steps to update this file or stop using it. The reason is very simple: your program will compile just fine, but will work incorrectly when compiled with gcc-4.4.0 (not released yet). Here is a short testcase (save as test.c):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;&lt;br /&gt;double strtod(const char *s00, char **se);&lt;br /&gt;&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt; double result = strtod(argv[1], 0);&lt;br /&gt; printf("%f\n", result);&lt;br /&gt; return 0;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Compile as follows: gcc-4.4 -O2 -Dstrtod=my_strtod -DIEEE_8087=1 -DHonor_FLT_ROUNDS=1 -DTrust_FLT_ROUNDS -DOmit_Private_Memory=1 -DNO_INFNAN_CHECK=1 -DNO_HEX_FP=1 dtoa.c test.c&lt;br /&gt;&lt;br /&gt;The -Dstrtod=my_strtod flag is needed in order to make sure that the version of strtod() from dtoa.c, not from your system C library, is used. -O2 is the default optimization level used by many software projects. The rest of the flags are explained in the dtoa.c file.&lt;br /&gt;&lt;br /&gt;The testcase converts its argument to a double-precision floating-point number, and then prints the result. So, when given a number, this testcase should print it back. let's try:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ ./a.out 1.1&lt;br /&gt;11.000000&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The result is clearly incorrect. The reason (as one can see by appending -Wall to the compiler command line) is that strict aliasing rules are violated in dtoa.c. Indeed, if one adds the -fno-strict-aliasing flag to the compiler command line, the resulting program will behave correctly:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;$ ./a.out 1.1&lt;br /&gt;1.100000&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is the formulation of the aliasing rules from &lt;a href="http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf"&gt;ISO/IEC 9899:TC2&lt;/a&gt;, section 6.5:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;An object shall have its stored value accessed only by an lvalue expression that has one of the following types:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;    &lt;li&gt;a type compatible with the effective type of the object,&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;a qualified version of a type compatible with the effective type of the object,&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;a type that is the signed or unsigned type corresponding to the effective type of the object,&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;a character type.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;dtoa.c does this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;typedef union { double d; ULong L[2]; } U;&lt;br /&gt;&lt;br /&gt;#define word0(x) ((U*)&amp;x)-&amp;gt;L[1]&lt;br /&gt;#define word1(x) ((U*)&amp;x)-&amp;gt;L[0]&lt;br /&gt;#define dval(x) ((U*)&amp;x)-&amp;gt;d&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, it stores doubles, but reads this memory using lvalue expressions of the "ULong" type (and the other way round), contrary to the standard.&lt;br /&gt;&lt;br /&gt;GCC allows such access only if the memory is accessed through a union type. The TC3 revision of the C99 standard also allows type punning through the union in the footnote in the section 6.5.2.3:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning".&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;In dtoa.c, the union is "invented" each time the code wants to reinterpret a double value as two ULongs. So, it doesn't make sense to speak about the member last used to store a value in the union, and the note above doesn't apply. I.e., instead of this code,&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;double a;&lt;br /&gt;word0(a) = L;&lt;br /&gt;word1(a) = 0;&lt;br /&gt;return dval(a);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;this should be used:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;U a;&lt;br /&gt;a.L[0] = L;&lt;br /&gt;a.L[1] = 0;&lt;br /&gt;return a.d;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that there are no pointer casts in the corrected code.&lt;br /&gt;&lt;br /&gt;The topic of strict aliasing rules, with working and non-working examples, is explained in the &lt;a href="http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Optimize-Options.html#index-fstrict_002daliasing-721"&gt;GCC manual&lt;/a&gt;. An updated version of &lt;a href="http://gcc.gnu.org/viewcvs/trunk/libjava/classpath/native/fdlibm/dtoa.c?view=markup"&gt;dtoa.c&lt;/a&gt; and the needed &lt;a href="http://gcc.gnu.org/viewcvs/trunk/libjava/classpath/native/fdlibm/mprec.h?view=markup"&gt;header&lt;/a&gt; are available in the sources of GCC itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-9097093191100419737?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/9097093191100419737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=9097093191100419737' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/9097093191100419737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/9097093191100419737'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2009/03/dont-use-old-dtoac.html' title='Don&apos;t use old dtoa.c'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-4348399495219216667</id><published>2009-01-31T01:43:00.000-08:00</published><updated>2009-01-31T02:39:59.313-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Package dependencies</title><content type='html'>Let's start with this fact: linphone uses the ffmpeg library to encode video. How can various package managers be informed about this fact by maintainers while building the linphone package?&lt;br /&gt;&lt;br /&gt;In Arch Linux, the dependency information is expressed in the PKGBUILD file in a very straightforward way:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;depends=('alsa-lib' 'ffmpeg&gt;=20080715' 'gtk2' 'libexosip2&gt;=3.1.0' 'speex&gt;=1.1.12')&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And this propagates in the same form into the binary package of linphone. But, is it really what is needed to prevent installation of a broken package? No.&lt;br /&gt;&lt;br /&gt;The linphone binary from the package uses the libavcodec.so.51 library, not just "any ffmpeg &gt;=20080715". So the linphone package will break when the ffmpeg package starts providing libavcodec.so.52 (and this already happened in the testing repository). Let's see how other package managers deal with the situation.&lt;br /&gt;&lt;br /&gt;RPM-based distributions have to add this line to the linphone.spec script:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;BuildRequires:  ffmpeg-dev&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, RPM will build the linphone package and examine the resulting binaries. It will notice that the linphone binary uses the libavcodec.so.51 library and will add the dependency on that library (not on some abstract "ffmpeg" package). This way, if the new ffmpeg package no longer includes libavcodec.so.51, RPM will notice that the linphone package now has an unsatisfied dependency, print an error message and refuse to upgrade ffmpeg.&lt;br /&gt;&lt;br /&gt;This is better than the Arch Linux way, but requires some external tool to know that libavcodec.so.51 is actually in the ffmpeg package. Otherwise, how would the "install linphone with all dependencies" request be handled?&lt;br /&gt;&lt;br /&gt;Now let's talk about Debian and its derivatives. They also have a notion of a build-time dependency, and it is expressed in the debian/control file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Build-Depends: ..., libavcodec-dev, ...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When the package is built, debhelper also notices that the resulting linphone binary depends on libavcodec.so.51, and searches the installed packages for this library. As a result, the dependency on the found package is added automatically to the binary package of linphone. This looks similar to the Arch Linux case, but Debian has a policy that requires the shared library version to be mentioned in the package name. I.e., linphone gets a dependency on "libavcodec51" package, while "libavcodec52" is a completely different package that (assuming no bugs) can be installed in parallel with libavcodec51, thus allowing the rebuilds of other packages for the ffmpeg upgrade to take place gradually, without any hurry and without the broken intermediate state.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-4348399495219216667?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/4348399495219216667/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=4348399495219216667' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4348399495219216667'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4348399495219216667'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2009/01/package-dependencies.html' title='Package dependencies'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-4004285625282165876</id><published>2009-01-06T01:04:00.000-08:00</published><updated>2009-01-06T02:10:54.660-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='job'/><title type='text'>Inefficient use of Valgrind</title><content type='html'>This is already stated in the documentation, but let me repeat. Valgrind can detect memory leaks only if the application uses malloc() and free(), or, in C++, operator new() and operator delete().&lt;br /&gt;&lt;br /&gt;This is not always the case. E.g., ImageMagick, when configured with the --enable-embeddable switch, uses mmap() directly in order to allocate memory for its needs. Other problematic cases involve custom memory pools on top of malloc() and free(), as done in Glib.&lt;br /&gt;&lt;br /&gt;So, if you want to find a memory leak with Valgrind, the first thing to do is to turn off all those custom memory allocation schemes. OTOH, if the buggy thing is the allocator itself, this will hide the bug.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-4004285625282165876?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/4004285625282165876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=4004285625282165876' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4004285625282165876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4004285625282165876'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2009/01/inefficient-use-of-valgrind.html' title='Inefficient use of Valgrind'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-367645875440975035</id><published>2008-12-27T04:17:00.000-08:00</published><updated>2008-12-27T05:18:13.465-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Getting more quality from onboard sound</title><content type='html'>Onboard high-definition audio chips are very common now. From the viewpoint of software, they support unbelievable quality of sound reproduction: 24 bits of precision at the 192 kHz sampling rate. The reality is in fact worse, as there is always noise in the analog output that the headphones are connected to.&lt;br /&gt;&lt;br /&gt;I am not talking about the &lt;a href="http://en.wikipedia.org/wiki/Johnson–Nyquist_noise"&gt;thermal noise&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Pink_noise"&gt;1/f noise&lt;/a&gt; that is created intrinsically by every electronic device and sounds like a soft "shshsh...". These kinds of noise are important in radio receivers or tape recorders that have a high-gain amplifier needed to recover a weak incoming signal, but not in computers.&lt;br /&gt;&lt;br /&gt;I am talking about the interference, where the signal in one wire (not intended to be played as sound) propagates into the other wire located nearby or to another circuit connected to the same badly-filtered power supply. The exact sound of such interference depends on what the computer is doing. If the computer is doing something periodically (e.g., drawing a scene on the screen 60 times a second, or filling a sound buffer in JACK 500 times a second), this unwanted sound becomes a tone, and thus becomes easily noticeable. Here are the steps that helped me to reduce it in my desktop computer based on the Intel DG965SS motherboard.&lt;br /&gt;&lt;br /&gt;First, the wire that picks up a lot of interference is the wire that connects the front headphone socket to the motherboard. It is better to avoid using the front socket at all, and plug the headphones (if the cord is long enough) into the green socket on the back of the motherboard. This way, the cord is screened from the noisy components inside the computer by the metallic case.&lt;br /&gt;&lt;br /&gt;Second, it may be a good idea to reduce the time variations of the power consumption of various components in the computer.&lt;br /&gt;&lt;br /&gt;E.g., the processor draws a lot of power when it is busy, but consumes less power when it is idle. When it switches between the busy and idle states periodically, it creates a tone in the power line, and that tone ultimately gets to the sound chip output. So, to suppress the tone, one solution would be to keep the processor always busy. This is what the "idle=poll" Linux kernel parameter does. Note that a busy processor runs hot (in my computer, at 47.0°C), so think twice before using this option. A less dragonian solution would be to pass the "max_cstate=3" parameter to the "processor" module. (A big "thanks" goes to Robert Hancock and Sitsofe Wheeler who suggested these parameters.)&lt;br /&gt;&lt;br /&gt;The other component of the computer that periodically draws a lot of power is the video card. But it is easier to keep it mostly idle, not busy. The most common desktop process that keeps the GPU busy is a compositing window manager (kwin or compiz). So, disable desktop effects, and enjoy better sound quality.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-367645875440975035?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/367645875440975035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=367645875440975035' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/367645875440975035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/367645875440975035'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/12/getting-more-quality-from-onboard-sound.html' title='Getting more quality from onboard sound'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-291802843828522425</id><published>2008-12-13T03:44:00.000-08:00</published><updated>2008-12-13T05:41:01.236-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='job'/><title type='text'>JPEG quality is a meaningless number</title><content type='html'>Suppose that a web designer tells you:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Your task is to make a script that creates images for this web page. The images should be in JPEG format, have such-and-such dimensions, and their quality should be this number of percent.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Ignore the bit about the desired quality, especially if the web designer uses Adobe Photoshop and has tested his page only with images made with Photoshop. The reason is that the same number yields different quality in different applications. Let me demonstrate this using &lt;a href="http://lh5.ggpht.com/_GeeUK7zpESs/SUOwheQwQjI/AAAAAAAAASg/4_Z4SOz7jko/d/parrots-original.jpg"&gt;this original image&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The image has been resized to 400x300 pixels using Photoshop (that contains Adobe's own JPEG encoder) and GIMP (that uses &lt;a href="http://www.ijg.org/"&gt;libjpeg from IJG&lt;/a&gt;, as many web scripts do). Here are the screenshots of the corresponding settings.&lt;br /&gt;&lt;br /&gt;Photoshop:&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;width: 306px; height: 260px;" src="http://3.bp.blogspot.com/_GeeUK7zpESs/SUOjRpY7MnI/AAAAAAAAARc/jz-xjpWE-Wk/s400/photoshop-setup.png" border="0" alt="Photoshop setup" /&gt;&lt;br /&gt;&lt;br /&gt;GIMP:&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;width: 554px; height: 523px;" src="http://4.bp.blogspot.com/_GeeUK7zpESs/SUOoDzPHfSI/AAAAAAAAARk/fsyUOipMnB4/s800/gimp-setup.png" border="0" alt="GIMP setup" /&gt;&lt;br /&gt;&lt;br /&gt;As you see, both programs are set to optimization of Huffman tables, no smoothing, no chroma subsampling, and 60% quality according to their scales. Here are the resulting images.&lt;br /&gt;&lt;br /&gt;Photoshop:&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; width: 400px; height: 300px;" src="http://lh5.ggpht.com/_GeeUK7zpESs/SUOtXwJi78I/AAAAAAAAASQ/UeDtDCemXSQ/s800/parrots-photoshop.jpg" border="0" alt="Parrots, compressed with Adobe Photoshop" /&gt;&lt;br /&gt;&lt;br /&gt;GIMP:&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; width: 400px; height: 300px;" src="http://lh5.ggpht.com/_GeeUK7zpESs/SUOujTvsqrI/AAAAAAAAASY/x3NroSxm_9g/s800/parrots-gimp.jpg" border="0" alt="Parrots, compressed with GIMP" /&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the image created by Adobe Photoshop is almost perfect, while on the image created with GIMP there are visible artifacts in the form of double contours around the parrots in the top row. This is not what the web designer wanted.&lt;br /&gt;&lt;br /&gt;Here are some statistics. The image made with Photoshop takes 46 KB, while the image made with GIMP takes only 26 KB.&lt;br /&gt;&lt;br /&gt;The quantization tables stored in the JPEG files are also different. If you don't know how to interpret numbers in them: the bigger the number is, the more precision is lost. The closer the number is to the right or bottom, to finer horizontal or vertical details it corresponds.&lt;br /&gt;&lt;br /&gt;Photoshop:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;          Luminance                           Chrominance&lt;br /&gt; 6   4   4   6   9  11  12  16       7   7  13  24  26  31  31  31&lt;br /&gt; 4   5   5   6   8  10  12  12       7  12  16  21  31  31  31  31&lt;br /&gt; 4   5   5   6  10  12  14  19      13  16  17  31  31  31  31  31&lt;br /&gt; 6   6   6  11  12  15  19  28      24  21  31  31  31  31  31  31&lt;br /&gt; 9   8  10  12  16  20  27  31      26  31  31  31  31  31  31  31&lt;br /&gt;11  10  12  15  20  27  31  31      31  31  31  31  31  31  31  31&lt;br /&gt;12  12  14  19  27  31  31  31      31  31  31  31  31  31  31  31&lt;br /&gt;16  12  19  28  31  31  31  31      31  31  31  31  31  31  31  31&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;GIMP:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;          Luminance                           Chrominance&lt;br /&gt;13   9   8  13  19  32  41  49      14  14  19  38  79  79  79  79&lt;br /&gt;10  10  11  15  21  46  48  44      14  17  21  53  79  79  79  79&lt;br /&gt;11  10  13  19  32  46  55  45      19  21  45  79  79  79  79  79&lt;br /&gt;11  14  18  23  41  70  64  50      38  53  79  79  79  79  79  79&lt;br /&gt;14  18  30  45  54  87  82  62      79  79  79  79  79  79  79  79&lt;br /&gt;19  28  44  51  65  83  90  74      79  79  79  79  79  79  79  79&lt;br /&gt;39  51  62  70  82  97  96  81      79  79  79  79  79  79  79  79&lt;br /&gt;58  74  76  78  90  80  82  79      79  79  79  79  79  79  79  79&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So indeed, Photoshop saves more details than GIMP at the indicated settings. The meaningless quality percentages match, the actual quality settings don't. In order to achieve in GIMP (and thus in web scripts that use the same libjpeg library from IJG) what the web designer actually meant, one would need to use quality=85% or so.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-291802843828522425?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/291802843828522425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=291802843828522425' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/291802843828522425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/291802843828522425'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/12/jpeg-quality-is-meaningless-number.html' title='JPEG quality is a meaningless number'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_GeeUK7zpESs/SUOjRpY7MnI/AAAAAAAAARc/jz-xjpWE-Wk/s72-c/photoshop-setup.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-2145166482570952344</id><published>2008-12-06T01:59:00.000-08:00</published><updated>2008-12-06T02:16:07.447-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freebsd'/><title type='text'>A way to lose a file</title><content type='html'>The ln command from GNU coreutils warns when the source and the destination of the attempted hardlink are the same file, and does nothing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ echo 123 &gt;testfile&lt;br /&gt;$ ls -l testfile&lt;br /&gt;--rw-r--r--  1 aep  aep  4 Dec  6 15:08 testfile&lt;br /&gt;$ ln testfile testfile &lt;br /&gt;ln: creating hard link `testfile': File exists&lt;br /&gt;$ ln -f testfile testfile &lt;br /&gt;ln: `testfile' and `testfile' are the same file&lt;br /&gt;$ ls -l testfile&lt;br /&gt;--rw-r--r--  1 aep  aep  4 Dec  6 15:08 testfile&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Its counterpart from FreeBSD, however, doesn't warn. Instead, it erases the file.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ echo 123 &gt;testfile&lt;br /&gt;$ ls -l testfile&lt;br /&gt;--rw-r--r--  1 aep  aep  4 Dec  6 15:08 testfile&lt;br /&gt;$ ln testfile testfile&lt;br /&gt;ln: testfile: No such file or directory&lt;br /&gt;$ ls -l testfile&lt;br /&gt;ls: testfile: No such file or directory&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-2145166482570952344?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/2145166482570952344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=2145166482570952344' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/2145166482570952344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/2145166482570952344'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/12/way-to-lose-file.html' title='A way to lose a file'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-8097755963696799039</id><published>2008-12-06T01:51:00.000-08:00</published><updated>2008-12-06T01:58:53.366-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='fix'/><title type='text'>Fixes</title><content type='html'>Some time ago, I mentioned in this blog some bugs that annoyed me. Some of them got fixes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Speaker-test fails with pcsp: &lt;a href="http://git.alsa-project.org/?p=alsa-lib.git;a=commitdiff;h=c0fd854ff1d137a0658d850f43cece48e73ff01a"&gt;fixed in ALSA Git&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Kernel panic due to iwlwifi: fixed in linux-2.6.27.6&lt;/li&gt;&lt;li&gt;Internal compiler error in GCC-4.3 on proprietary code: &lt;a href="http://gcc.gnu.org/viewcvs?view=rev&amp;amp;revision=141111"&gt;fixed in GCC SVN&lt;/a&gt;, the fix will be a part of GCC-4.4.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-8097755963696799039?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/8097755963696799039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=8097755963696799039' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/8097755963696799039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/8097755963696799039'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/12/fixes.html' title='Fixes'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-7416178999228848580</id><published>2008-11-23T05:31:00.000-08:00</published><updated>2008-11-23T06:09:20.939-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><title type='text'>Linux audio sucks</title><content type='html'>because of bugs in drivers, libraries, and applications. Here are some of them. They are quite real, because they interfere with my not-so-unrealistic requirements (play music through headphones connected to the onboard HD audio chip, make some noise with the PC speaker when someone calls me via SIP, and copy sound from my TV tuner to the HD audio chip when I watch TV).&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In a driver. Take, for example, saa7134-alsa (a kernel module that allows recording audio from SAA713X-based TV tuners). It advertises support for the following sample rates: 32 kHz and 48 kHz. What it fails to mention is that it doesn't support recording at 48 kHz except for the case of "original SAA7134 with the MIXER_ADDR_LINE2 capture source". In all other cases (e.g., SAA7133 with the MIXER_ADDR_TVTUNER capture source), it gives out 32 kHz samples, but mislabels them as 48 kHz. Thus, applications such as MPlayer have to be configured to use only the 32 kHz sample rate. Not a big problem if such configuration is possible (i.e.: if not using PulseAudio together with HAL), but still a bug.&lt;/li&gt;&lt;li&gt;In the ALSA library. Today I discovered that the PC speaker at home no longer plays ring tones in SIP clients. It did work before, but I can no longer find a working revision. While testing this sound device with speaker-test, I found &lt;a href="http://mailman.alsa-project.org/pipermail/alsa-devel/2008-November/012838.html"&gt;this floating point exception&lt;/a&gt; inside the ALSA library.&lt;/li&gt;&lt;li&gt;In applications. The most frequent bug is that an application doesn't have a way to use an arbitrary ALSA device, and instead has a drop-down box with pre-defined choices. Such applications often cannot work with Bluetooth headsets (that I don't have), FireWire audio cards (that I don't have either), and through non-default PulseAudio devices (and this has bit me when I tried to use Linphone with PulseAudio - I couldn't configure it to use the PC speaker through PulseAudio). What's even worse is that HAL endorses this faulty "drop-down box" enumeration scheme.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-7416178999228848580?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/7416178999228848580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=7416178999228848580' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7416178999228848580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7416178999228848580'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/11/linux-audio-sucks.html' title='Linux audio sucks'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-5416627771991325107</id><published>2008-11-16T04:01:00.000-08:00</published><updated>2008-11-16T05:11:10.039-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='freebsd'/><category scheme='http://www.blogger.com/atom/ns#' term='job'/><title type='text'>Strange backtrace</title><content type='html'>Some time ago I had to debug a strange crash. It was in a multithreaded program and manifested itself only on FreeBSD i386. The code (with all the needed declarations included, seemingly irrelevant details removed, and everything renamed) looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include &amp;lt;cstdio&amp;gt;&lt;br /&gt;#include &amp;lt;cstring&amp;gt;&lt;br /&gt;#include &amp;lt;cerrno&amp;gt;&lt;br /&gt;#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;#include &amp;lt;exception&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class system_error : public exception&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    system_error() throw() :error_text(strerror(errno)) {}&lt;br /&gt;    virtual const char* what() const throw() { return error_text; }&lt;br /&gt;private:&lt;br /&gt;    const char* error_text;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class strange_thing&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    strange_thing(); // fills in some useful defaults&lt;br /&gt;private:&lt;br /&gt;    // &lt;span style="font-style:italic;"&gt;lots&lt;/span&gt; of implementation details&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class strange_container&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    strange_container();&lt;br /&gt;    ~strange_container()  { if (fd != -1) close(fd); }&lt;br /&gt;    void play_with_strange_thing(const char* filename);&lt;br /&gt;private:&lt;br /&gt;    int fd;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;strange_container::strange_container()&lt;br /&gt;: fd(-1)&lt;br /&gt;{&lt;br /&gt;    play_with_strange_thing("test.file");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void strange_container::play_with_strange_thing(const char* filename)&lt;br /&gt;{&lt;br /&gt;    fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0777);&lt;br /&gt;    if (fd == -1)&lt;br /&gt;        throw system_error();&lt;br /&gt;    strange_thing ss;&lt;br /&gt;    /* here goes some code that uses ss and fd */&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// well, actually it is not the main function, but something buried in a thread&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt;    strange_container c;&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The test file is not created, and the segfault looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[aep@bsd1 ~/crashtest]$ gdb ./a.out&lt;br /&gt;GNU gdb 6.1.1 [FreeBSD]&lt;br /&gt;Copyright 2004 Free Software Foundation, Inc.&lt;br /&gt;GDB is free software, covered by the GNU General Public License, and you are&lt;br /&gt;welcome to change it and/or distribute copies of it under certain conditions.&lt;br /&gt;Type "show copying" to see the conditions.&lt;br /&gt;There is absolutely no warranty for GDB.  Type "show warranty" for details.&lt;br /&gt;This GDB was configured as "i386-marcel-freebsd"...&lt;br /&gt;(gdb) run&lt;br /&gt;Starting program: /usr/home/aep/crashtest/a.out &lt;br /&gt;[New LWP 100043]&lt;br /&gt;[New Thread 0x28301100 (LWP 100043)]&lt;br /&gt;&lt;br /&gt;Program received signal SIGSEGV, Segmentation fault.&lt;br /&gt;[Switching to Thread 0x28301100 (LWP 100043)]&lt;br /&gt;strange_container::play_with_strange_thing (this=0x28306098, &lt;br /&gt;    filename=0x8048c33 "test.file") at crashtest.cpp:57&lt;br /&gt;57  fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At this point, everything looks valid, including the "this" pointer. By all applicable logic, the program just cannot segfault by calling open() with valid parameters. So I started adding debugging printf() statements. The statement just before the call to play_with_strange_thing() worked fine, and none of the statements inside play_with_strange_thing() worked. Moreover, when I added a printf() as the very first line of strange_container::play_with_strange_thing() and ran gdb on the result, it showed this printf() in the backtrace!&lt;br /&gt;&lt;br /&gt;So, I didn't believe my eyes. I thought (wrongly) that printf() and buffering somehow interacts with the segfault, and thus invented a different mechanism to find out whether a certain line of code was reached by the program. Namely, I replaced all my debugging printf() calls in strange_container::play_with_strange_thing() with throwing exceptions, with the intention to remove them one-by-one:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void strange_container::play_with_strange_thing(const char* filename)&lt;br /&gt;{&lt;br /&gt;    throw system_error();  // (1)&lt;br /&gt;    fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0777);&lt;br /&gt;    throw system_error();&lt;br /&gt;    if (fd == -1)&lt;br /&gt;        throw system_error();&lt;br /&gt;    throw system_error(); // (2)&lt;br /&gt;    strange_thing ss;&lt;br /&gt;    throw system_error(); // (3)&lt;br /&gt;    /* here goes some code that uses ss and fd, err... throws system_error() */&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This worked. I knew for sure that point (2) was reached, and point (3) was't. So there is something bad with creation of the strange thing that, however, doesn't cause gdb to complain about its constructor.&lt;br /&gt;&lt;br /&gt;So, I had to take another look into the implementation of the strange_thing class. The issue was actually with the huge size of the object (several megabytes)! So, no wonder that it overflowed the thread stack. You can reproduce the crash on your own system by replacing "lots of implementation details" with "char c[2000000];", implementing the strange_thing default constructor, and running with a low-enough "ulimit -s" setting.&lt;br /&gt;&lt;br /&gt;As the reason of the crash became known, it was an easy matter to fix properly, by not creating huge objects on the stack.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-5416627771991325107?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/5416627771991325107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=5416627771991325107' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/5416627771991325107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/5416627771991325107'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/11/strange-backtrace.html' title='Strange backtrace'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-6292971554197843727</id><published>2008-10-25T03:25:00.000-07:00</published><updated>2008-10-25T04:40:43.601-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gcc'/><title type='text'>Unexpected warning</title><content type='html'>Today I refactored a function in my C program in order to make it more readable and remove special-casing. What came unexpected is that I got a new warning in a function that I didn't touch. While this sounds strange, here is a very simple example that shows how it could happen.&lt;br /&gt;&lt;br /&gt;This is the function that I didn't touch, but that will, at the end, get a warning:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;extern const int db_to_add[32];&lt;br /&gt;&lt;br /&gt;static int add_db(int a, int b)&lt;br /&gt;{&lt;br /&gt; if (a &lt; b) {&lt;br /&gt;  int tmp = a;&lt;br /&gt;  a = b;&lt;br /&gt;  b = tmp;&lt;br /&gt; }&lt;br /&gt; if (a - b &gt;= 32)&lt;br /&gt;  return a;&lt;br /&gt;&lt;br /&gt; return a + db_to_add[a - b];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is a caller, just to make it sure that a static function is used:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int peak_db[32];&lt;br /&gt;static int noise(int band1, int band2, int spectrum1, int spectrum2)&lt;br /&gt;{&lt;br /&gt; /* actually a lot more complex, but the idea is that&lt;br /&gt;    it takes two bands, except for special cases */&lt;br /&gt; int noise1 = peak_db[band1] + spectrum1;&lt;br /&gt; int noise2 = peak_db[band2] + spectrum2;&lt;br /&gt; return add_db(noise1, noise2);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int valid_use(int b, int s)&lt;br /&gt;{&lt;br /&gt; /* a dummy warning-free function&lt;br /&gt;    only for the purpose of this blog post */&lt;br /&gt; return noise(b, b + 1, 0, s);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With all of the above, there is no warning, even when "-O3 -Wall" flags are passed to gcc. Now add this (in the real program, this is the result of value propagation over various branches):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int trigger_warning()&lt;br /&gt;{&lt;br /&gt; return noise(0, 0, 0, -200);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The intention is to attenuate the unneeded term enough so that it doesn't matter, and use the general two-band case. With gcc-4.3.1 -O3 -Wall, the result, referring to the line in add_db() where a is compared with b, is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;warning: assuming signed overflow does not occur when assuming that (X - c) &gt; X is always false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So it looks like gcc inlined everything and attempted to test whether peak_db[0] &lt; peak_db[0] - 200. Because C compilers are allowed to think that signed overflow never occurs, this is always false (as intended). However, tests of this form are often &lt;a href="http://www.derkeiler.com/Mailing-Lists/Full-Disclosure/2007-01/msg00281.html"&gt;incorrectly used as security checks&lt;/a&gt;, that's why the warning.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-6292971554197843727?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/6292971554197843727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=6292971554197843727' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/6292971554197843727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/6292971554197843727'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/10/unexpected-warning.html' title='Unexpected warning'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-7983629899983680122</id><published>2008-10-23T04:48:00.000-07:00</published><updated>2008-10-23T09:01:54.671-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='non-bug'/><title type='text'>Don't believe rrdtool</title><content type='html'>&lt;a href="http://oss.oetiker.ch/rrdtool/"&gt;rrdtool&lt;/a&gt; is the industry standard for plotting time-dependent data (t.g., for monitoring). However, Debian's rrdtool 1.3.1-4 can create misleading plots. Here is how to reproduce.&lt;br /&gt;&lt;br /&gt;First, create a round-robin database that will hold our test data.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rrdtool create testdata.rrd --start 1224453000 --step 1800 \&lt;br /&gt;   DS:testdata:GAUGE:28000:0:U RRA:LAST:0.5:1:1800&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, populate it with numbers:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rrdtool update testdata.rrd 1224453300:1350535&lt;br /&gt;rrdtool update testdata.rrd 1224467700:1350545&lt;br /&gt;rrdtool update testdata.rrd 1224482100:1350554&lt;br /&gt;rrdtool update testdata.rrd 1224496500:1350560&lt;br /&gt;rrdtool update testdata.rrd 1224514800:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224539700:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224557700:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224576000:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224590100:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224604800:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224622500:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224636900:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224651300:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224669300:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224683700:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224698100:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224712500:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224730500:1350562&lt;br /&gt;rrdtool update testdata.rrd 1224744900:1350562&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The number before the semicolon is a UNIX timestamp, and the number after the semicolon is the corresponding value. As you see from the numbers, the value is slightly above 1.35 million and slowly grows in the beginning of the period.&lt;br /&gt;&lt;br /&gt;Let's plot it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rrdtool graph testdata.png -t testdata --start 1224453000 --end 1224757634 \&lt;br /&gt;   DEF:testdata=testdata.rrd:testdata:LAST 'LINE2:testdata#ff0000'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/patrakov/SQBmKaXKPtI/AAAAAAAAAOc/O8iAIBxWbw4/s800/testdata.png" alt="Good result" /&gt;&lt;br /&gt;&lt;br /&gt;Indeed, there is not much change. But let's suppose that we are really interested in the small change that happened over the week.&lt;br /&gt;&lt;br /&gt;Let's use the alternative autoscaling option that, according to the manual page, is designed specifically for such cases:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rrdtool graph testdata-bug.png -t testdata --start 1224453000 --end 1224757634 \&lt;br /&gt;   -A -Y DEF:testdata=testdata.rrd:testdata:LAST 'LINE2:testdata#ff0000'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;img src="http://lh4.ggpht.com/patrakov/SQBmKohptyI/AAAAAAAAAOk/29p6b-TuuJw/s800/testdata-bug.png" alt="bug?!" /&gt;&lt;br /&gt;What? The plot says that the value is 35000M, which is waaaay wrong.&lt;br /&gt;&lt;br /&gt;I don't know yet whether this bug is Debian-spedific, but that's enough for discouraging me from using -A and -Y options.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Update:&lt;/span&gt; not a bug. The label is just cut off from the left. Here is the correct plot:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rrdtool graph testdata-bug.png -t testdata --start 1224453000 --end 1224757634 \&lt;br /&gt;   -A -Y -L 10 DEF:testdata=testdata.rrd:testdata:LAST 'LINE2:testdata#ff0000'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;img src="http://lh6.ggpht.com/patrakov/SQBw6QIcBAI/AAAAAAAAAPE/dtr7jGIL1YU/s800/testdata-non-bug.png" alt="non-bug" /&gt;&lt;br /&gt;&lt;br /&gt;Although I must say that MS Excel handles this case better: it replaces numbers that don't fit with "###". Such placeholder (no number) is better than a wrong (chopped) number.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-7983629899983680122?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/7983629899983680122/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=7983629899983680122' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7983629899983680122'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/7983629899983680122'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/10/dont-believe-debians-rrdtool.html' title='Don&apos;t believe rrdtool'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/patrakov/SQBmKaXKPtI/AAAAAAAAAOc/O8iAIBxWbw4/s72-c/testdata.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-907433565916622038</id><published>2008-10-12T06:48:00.000-07:00</published><updated>2008-10-12T07:40:03.292-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='win32'/><title type='text'>Porting C/C++ code from Unix to Win32</title><content type='html'>At Yandex, some employees use Microsoft Visual Studio on Windows for compiling and debugging their code, some use gcc and gdb on Linux or FreeBSD (via ssh). And thus, portability problems arise. Here is one example.&lt;br /&gt;&lt;br /&gt;A chunk of new code was written on FreeBSD that, essentially, opens a file (or maybe stdin), determines which plugin (i.e., shared library) to load, and passes the file descriptor to a function in the plugin. A natural design for Unix, however, it doesn't work for Windows: the file descriptors opened with open() are valid only in the same module (i.e., DLL or application) that opened them. It appears that one has to use _get_osfhandle() and _open_osfhandle() Win32 functions to overcome this limitation. That's just ugly.&lt;br /&gt;&lt;br /&gt;Just for fun, I also tried to rebuild my own C code (not related to my work at Yandex), on Windows with Microsoft Visual Studio. This, obviously, resulted in problems:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; The Microsoft compiler doesn't have such standard C99 headers as &amp;lt;stdint.h&amp;gt;. I had to download this header from &lt;a href="http://code.google.com/p/msinttypes/"&gt;the msinttypes project&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt; When compiling C files, the compiler doesn't recognize the "inline" keyword and the definition for int64_t from the &amp;lt;stdint.h&amp;gt; header doesn't work. Both issues were fixed with the /TP switch that tells the compiler to treat the code as C++.&lt;/stdint.h&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt; The compiler warns about the POSIX function open() and suggests to use the "standard" _open() function. What standard does it talk about?&lt;/li&gt;&lt;br /&gt;&lt;li&gt; Well, the standard C function for opening files is actually fopen(), so I converted my program to use that. Guess what? A warning saying that fopen() is deprecated and suggesting to use fopen_s() that actually exists only in Microsoft compilers.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The intentions of Microsoft are obvious: to teach people to write programs that will require significant amount of work when porting to other platforms. Thus, if you are a newbie programmer working on Win32, please, switch to a non-Microsoft compiler &lt;span style="font-weight: bold;"&gt;now&lt;/span&gt;, before you acquire any bad habits.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-907433565916622038?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/907433565916622038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=907433565916622038' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/907433565916622038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/907433565916622038'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/10/porting-cc-code-from-unix-to-win32.html' title='Porting C/C++ code from Unix to Win32'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-6855896073919359949</id><published>2008-09-27T06:37:00.000-07:00</published><updated>2008-09-27T06:50:22.415-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><category scheme='http://www.blogger.com/atom/ns#' term='kernel'/><title type='text'>Kernel panic</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_GeeUK7zpESs/SN43jFlUhMI/AAAAAAAAAOU/2tdkej8z6MA/s1600-h/panic.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_GeeUK7zpESs/SN43jFlUhMI/AAAAAAAAAOU/2tdkej8z6MA/s400/panic.jpg" border="0" alt="kernel panic" id="BLOGGER_PHOTO_ID_5250695291494237378" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When I moved to another room in the office, my laptop started getting this kernel panic. So far, this means that I can't use the wireless network. I can work around this by either blacklisting the iwl4965 module or by turning the wireless card off with the hardware rfkill switch.&lt;br /&gt;&lt;br /&gt;Maybe related: this laptop gets "ACPI: EC: non-query interrupt received, switching to interrupt mode" during boot.&lt;br /&gt;&lt;br /&gt;I will report this panic to both the debian bug tracker and the linux-wireless list, let's see how they resolve it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-6855896073919359949?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/6855896073919359949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=6855896073919359949' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/6855896073919359949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/6855896073919359949'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/09/kernel-panic.html' title='Kernel panic'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_GeeUK7zpESs/SN43jFlUhMI/AAAAAAAAAOU/2tdkej8z6MA/s72-c/panic.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-4760113910181439680</id><published>2008-09-20T03:52:00.000-07:00</published><updated>2008-09-20T03:56:43.068-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><title type='text'>Wrong macro</title><content type='html'>Here is how you shouldn't test for old compilers:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;#if _MSC_VER &lt; 1400&lt;br /&gt;    /* some workaround */&lt;br /&gt;#endif&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Reason: the workaround will also be applied to non-Microsoft compilers, e.g., gcc. Here is a more correct version:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;#if defined(_MSC_VER) &amp;&amp; _MSC_VER &lt; 1400&lt;br /&gt;    /* some workaround */&lt;br /&gt;#endif&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-4760113910181439680?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/4760113910181439680/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=4760113910181439680' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4760113910181439680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4760113910181439680'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/09/wrong-macro.html' title='Wrong macro'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-3601932371019526711</id><published>2008-09-18T08:30:00.000-07:00</published><updated>2008-09-18T09:00:36.078-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sip'/><category scheme='http://www.blogger.com/atom/ns#' term='job'/><title type='text'>Recording SIP conversations</title><content type='html'>My chief prefers to use SIP (voice conversations) instead of Jabber (text messaging). The problem is that he sometimes overloads me with a lot of information, and later I forget 90%. This is not a problem with Jabber, as there are logs. However, I don't know any SIP client that supports recording of conversations out of the box.&lt;br /&gt;&lt;br /&gt;Here is my solution. You need alsa-lib 1.0.17 or later (i.e., if you are using Debian Lenny, due to the freeze, you'll have to fetch libasound2 and libasound2-dev from experimental). Create the subdirectory named "voice" in your home directory. Add these lines to $HOME/.asoundrc:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;pcm.voice {&lt;br /&gt;        type asym&lt;br /&gt;        playback.pcm {&lt;br /&gt;                type file&lt;br /&gt;                file "/home/user/voice/p"&lt;br /&gt;                slave.pcm "default"&lt;br /&gt;                format "wav"&lt;br /&gt;                truncate 0&lt;br /&gt;        }&lt;br /&gt;        capture.pcm {&lt;br /&gt;                type file&lt;br /&gt;                file "/home/user/voice/c"&lt;br /&gt;                slave.pcm "default"&lt;br /&gt;                format "wav"&lt;br /&gt;                truncate 0&lt;br /&gt;        }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, configure your SIP client (I use twinkle) as follows. ALSA device for rings: "default", ALSA speaker: "voice", ALSA microphone: "voice", Play back ring tone when the network doesn't: disable. As the result, ALSA library will write the following files into the voice directory: p, c, p.0001, c.0001, p.0002, c.0002, and so on (the number is incremented for each new call). All these files are in wav format, "p" files correspond to the remote side, and "c" files contain what you spoke into the microphone. It appears that it is impossible to record both parties of the conversation into one file using only built-in features that come with ALSA.&lt;br /&gt;&lt;br /&gt;To play back the conversation, do:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;aplay p.0002 &amp; aplay c.0002&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will cause two aplay processes to be started in parallel, with their outputs mixed. Or, you can make one mixed wav file with sox:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sox -m c.0002 p.0002 -f wav talk0002.wav&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-3601932371019526711?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/3601932371019526711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=3601932371019526711' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3601932371019526711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3601932371019526711'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/09/recording-sip-conversations.html' title='Recording SIP conversations'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-3361237521871405218</id><published>2008-09-17T08:53:00.000-07:00</published><updated>2008-09-17T09:38:51.087-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gcc'/><category scheme='http://www.blogger.com/atom/ns#' term='bug'/><title type='text'>gcc-4.3 troubles</title><content type='html'>After obtaining access to the CVS server at Yandex, I tried to build some internal utilities for daily use (e.g., for examining database contents) on my work laptop. The first attempt failed, because Debian Lenny comes with gcc-4.3.1, and the official build machines (that I was in fact supposed to use instead of the laptop) still use gcc-4.2.x.&lt;br /&gt;&lt;br /&gt;First, some of the "known good" snapshots of external projects that are used by the to-be-built utilities have gcc-4.3 related issues. Fortunately, Debian's bug and package database was very helpful, and I was able to extract the needed patches.&lt;br /&gt;&lt;br /&gt;Second, gcc-4.3 added some new warnings, and the build system is set up to use -Werror by default. While some of the new warnings are helpful, others are not. Try, e.g., compiling this simple function with g++ -Wconversion -c test.cpp:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;typedef unsigned char uint8_t;&lt;br /&gt;void grow(uint8_t &amp;amp; u)&lt;br /&gt;{&lt;br /&gt;        u *= 7;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Result:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;test.cpp: In function 'void grow(uint8_t&amp;amp;)':&lt;br /&gt;test.cpp:4: warning: conversion to 'unsigned char' from 'int' may alter its value&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I.e., almost every in-place mathematical operation with shorts and chars results in warnings! With -Werror, this means errors in many files. Obviously, this isn't worth fixing, so I removed -Wconversion from the compiler flags in my checkout. Similar bugs &lt;a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37004"&gt;are&lt;/a&gt; &lt;a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35852"&gt;already&lt;/a&gt; &lt;a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34389"&gt;recorded&lt;/a&gt; in GCC bugzilla.&lt;br /&gt;&lt;br /&gt;Third, even after fixing fixable warnings and removing -Wconversion, I hit an internal compiler error. Since I found no similar reports (the error manifested itself on proprietary code, after all), I reduced the testcase and &lt;a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=37553"&gt;submitted it&lt;/a&gt; to GCC bugzilla.&lt;br /&gt;&lt;br /&gt;After that, I gave up. I posted my changes to the internal code review list (just so that they don't get lost), switched the compiler to gcc-4.2 (it is available in Debian side-by-side with 4.3), and compiled what I needed without further problems.&lt;br /&gt;&lt;br /&gt;BTW, Debian still uses gcc-4.1.2 for compiling their kernels. It would be interesting to know why.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-3361237521871405218?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/3361237521871405218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=3361237521871405218' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3361237521871405218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/3361237521871405218'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/09/gcc-43-troubles.html' title='gcc-4.3 troubles'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7844549485270153160.post-4782074832774435806</id><published>2008-09-16T09:44:00.001-07:00</published><updated>2008-09-16T10:29:10.392-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='job'/><title type='text'>New job!</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_GeeUK7zpESs/SM_rLk_lfmI/AAAAAAAAAN0/we0MmJKUqM0/s1600-h/P1010097.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_GeeUK7zpESs/SM_rLk_lfmI/AAAAAAAAAN0/we0MmJKUqM0/s400/P1010097.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5246670675051380322" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_GeeUK7zpESs/SM_o6WiM-TI/AAAAAAAAANo/mw5rYJOVFgM/s1600-h/OfficeBuildingAtNight.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_GeeUK7zpESs/SM_o6WiM-TI/AAAAAAAAANo/mw5rYJOVFgM/s400/OfficeBuildingAtNight.jpg" border="0" alt="Office building at night" id="BLOGGER_PHOTO_ID_5246668180089010482" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Since September 15, 2008, I work at &lt;a href="http://www.yandex.ru/"&gt;Yandex&lt;/a&gt;, as a developer in the &lt;a href="http://images.yandex.ru/"&gt;Image search&lt;/a&gt; department. I am busy with bash scripts, C++ and even XML. My work computer is a Fujitsu Siemens S laptop, and I installed Debian Lenny there. One strange thing about this job is that I have never seen my chief: he is in Moscow, and all conversations with him are via Jabber and SIP.&lt;br /&gt;&lt;br /&gt;The office in Yekaterinburg is clean, there are nice flowers in front of the building, and my colleagues seem to enjoy working there. Our windows (on the fifth floor) remain lit even at 10 PM.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7844549485270153160-4782074832774435806?l=patrakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://patrakov.blogspot.com/feeds/4782074832774435806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7844549485270153160&amp;postID=4782074832774435806' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4782074832774435806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7844549485270153160/posts/default/4782074832774435806'/><link rel='alternate' type='text/html' href='http://patrakov.blogspot.com/2008/09/new-job.html' title='New job!'/><author><name>Alexander Patrakov</name><uri>http://www.blogger.com/profile/15370096336423115833</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_GeeUK7zpESs/SNEn3Nwf5ZI/AAAAAAAAAN8/BjNmKYwu4O8/S220/photo-75x75.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_GeeUK7zpESs/SM_rLk_lfmI/AAAAAAAAAN0/we0MmJKUqM0/s72-c/P1010097.JPG' height='72' width='72'/><thr:total>0</thr:total></entry></feed>
