Fading memory

May 25th, 2008

The Tyan K8WE motherboard in hinge has always seemed to have flaky DIMM slots. I seemed to have to wiggle the DIMMs just right in order for the machine to recognize all of the memory. A recent graphics card upgrade nudged things and I was unable to nudge them back into an agreeable position. The board had about a month left on its three year warranty; so, back to Tyan it went.

So I am without my primary development machine for at least a week. It’s hard to believe I’ve had this thing for nearly three years. Last year I upgraded the CPUs to Opteron 285s from the 242s I originally installed; but still it doesn’t seem that old. Its predecessor was a dual Pentium 3 machine that lasted me five years

When hinge is put back together, I think I’ll go ahead and release OpenVRML 0.17.6. I still haven’t fixed that damn JPEG bug; but I think there have been enough less consequential changes to warrant a release.

Another change I’d hoped to work into a point release in the 0.17 series is the use of D-Bus to replace the existing IPC mechanism used in openvrml-xembed. Aside from being generally cleaner and nicer than the existing hacky IPC solution, I’m thinking that expressing the IPC interface in D-Bus should lead to an improved code organization that will in turn make it easier to convert the existing multiprocess approach into a multithread approach for Mac OS X and Windows. This is becoming more pertinent as it seems there may be folks interested in contributing code to a Windows plug-in.

An RSS parser in PHP for SourceForge feeds

January 26th, 2008

I wrote this for parsing RSS feeds associated with a SourceForge project. It should be reasonably capable for that purpose; though I do not expect it to be generally robust for handling arbitrary feeds. Reed suggested that this might be generally useful; however, I don’t want to put it anywhere that I might feel compelled to maintain it. So this seems like a good spot.

//
// Copyright 2008  Braden McDaniel
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; if not, see .
//

class RSS2Image
{
    var $url = "";
    var $title = "";
    var $link = "";
    var $width = "";
    var $height = "";
}

class RSS2Item
{
    var $title = "";
    var $link = "";
    var $description = "";
    var $author = "";
    var $category = "";
    var $comments = "";
    var $enclosure = "";
    var $guid = "";
    var $pub_date = "";
    var $source = "";
}

class RSS2Channel
{
    var $title;
    var $link;
    var $description;
    var $copyright;
    var $last_build_date;
    var $generator;
    var $image;
    var $items;

    function RSS2Channel()
    {
        $this->title = "";
        $this->link = "";
        $this->description = "";
        $this->copyright = "";
        $this->last_build_date = "";
        $this->generator = "";
        $this->image = null;
        $this->items = array();
    }
}

class RSS2Parser
{
    var $parser;
    var $channel;

    var $in_channel, $in_image, $in_item;
    var $current_element;

    function RSS2Parser()
    {
        $this->in_channel = false;
        $this->in_image = false;
        $this->in_item = false;
        $this->parser = xml_parser_create();
        xml_set_object($this->parser, $this);
        xml_set_element_handler($this->parser,
                                "start_element",
                                "end_element");
        xml_set_character_data_handler($this->parser,
                                       "character_data");
    }

    function parse($data)
    {
        xml_parse($this->parser, $data);
        return $this->channel;
    }

    function start_element($parser, $name, $attribs)
    {
        $name = strtolower($name);
        if ($name == "channel") {
            $this->in_channel = true;
            $this->channel = new RSS2Channel();
            return true;
        }

        if ($this->in_channel) {
            if ($name == "image") {
                $this->in_image = true;
                $this->channel->image = new RSS2Image();
                return true;
            } elseif ($name == "item") {
                $this->in_item = true;
                $this->channel->items[] = new RSS2Item();
                return true;
            }
        }

        if ($this->in_image) {
            $this->current_element = &$this->channel->image->$name;
        } elseif ($this->in_item) {
            $this->current_element = &end($this->channel->items)->$name;
        }
        return true;
    }

    function end_element($parser, $name)
    {
        $name = strtolower($name);
        if ($name == “channel”) {
            $this->in_channel = false;
        } elseif ($name == “image”) {
            $this->in_image = false;
        } elseif ($name == “item”) {
            $this->in_item = false;
        }
        unset($this->current_element);
        return true;
    }

    function character_data($parser, $data)
    {
        if (isset($this->current_element)) {
            $this->current_element .= $data;
        }
        return true;
    }
}

Exception-safe management of JNI local references

January 21st, 2008

…or, solving the problem of how to delete local references when the execution context isn’t clear.

JNI includes notions of “local” and “global” references. Loosely speaking, local references correspond to those that are local to the scope of a function; and global references correspond to those that persist outside the scope of a function. When Java calls a native method, it provides the native code with at stack frame where local references can be stored. The native code can then proceed to make calls to JNI functions which, in general, return local references. These local references are automatically stored in the stack frame; and the Java runtime takes care of cleaning them up when it pops the stack frame upon leaving the native method implementation.

So far, so good. But not all JNI function calls occur in response to Java calling a native method implementation. In fact, if you’re starting up the VM via JNI, you probably end up calling JNI functions that return local references just the same. Only in this case, the Java runtime hasn’t provided you with a stack frame that it will magically clean up once your code is done executing. Instead, you’ll need to delete the local references explicitly.

Thus we have two very different ways local references must be handled, depending on the context of the JNI function calls. And the inevitable problem: in a utility function which might be called from either context, how should intermediate local references be handled? Consider a function that creates a java.net.URL instance:

jobject create_url(JNIEnv & env, const char * const url)
{
    const jstring url_string = env.NewStringUTF(url);
    if (!url_string) {
        env.ExceptionClear();
        throw std::runtime_error("failed to construct string for URL");
    }

    const jclass url_class = env.FindClass("java/net/URL");
    if (!url_class) {
        env.ExceptionClear();
        throw std::runtime_error("could not find java.net.URL class");
    }

    const jmethodID ctor_id =
        env.GetMethodID(url_class, "", "(Ljava/lang/String;)V");
    if (!ctor_id) {
        env.ExceptionClear();
        throw std::runtime_error("failed to get "
                                 "java.net.URL.URL(java.lang.String) "
                                 "constructor");
    }

    const jobject url_obj =
        env.NewObject(url_class, ctor_id, url_string);
    if (!url_obj) {
        env.ExceptionClear();
        throw std::runtime_error("could not create java.net.URL "
                                 "instance");
    }

    return url_obj;
}

The above code will work just fine when it is called from a native method implementation. But if it is called outside that context, it will leak the local references corresponding to url_string and url_class. (We can assume the caller has responsibility for the local reference corresponding to url_obj in both cases.)

So, let’s toss in the code to delete the local references. We need to be exception-safe, so let’s use ScopeGuard:

jobject create_url(JNIEnv & env, const char * const url)
{
    const jstring url_string = env.NewStringUTF(url);
    if (!url_string) {
        env.ExceptionClear();
        throw std::runtime_error("failed to construct string for URL");
    }
    scope_guard url_string_guard =
        make_obj_guard(env, &JNIEnv::DeleteLocalRef, url_string);

    const jclass url_class = env.FindClass("java/net/URL");
    if (!url_class) {
        env.ExceptionClear();
        throw std::runtime_error("could not find java.net.URL class");
    }
    scope_guard url_class_guard =
        make_obj_guard(env, &JNIEnv::DeleteLocalRef, url_class);

    const jmethodID ctor_id =
        env.GetMethodID(url_class, "", "(Ljava/lang/String;)V");
    if (!ctor_id) {
        env.ExceptionClear();
        throw std::runtime_error("failed to get "
                                 "java.net.URL.URL(java.lang.String) "
                                 "constructor");
    }

    const jobject url_obj =
        env.NewObject(url_class, ctor_id, url_string);
    if (!url_obj) {
        env.ExceptionClear();
        throw std::runtime_error("could not create java.net.URL "
                                 "instance");
    }

    return url_obj;
}

There. Now we can call the function outside a native method implementation. But in making that work, we’ve made the function unusable from within a native method implementation. Clearly we need to make the calls to JNIEnv::DeleteLocalRef conditional; and we’ll have to let the caller tell us what context the function is being called in.

jobject create_url(JNIEnv & env,
                    const char * const url,
                    const bool delete_local_refs)
{
    const jstring url_string = env.NewStringUTF(url);
    if (!url_string) {
        env.ExceptionClear();
        throw std::runtime_error("failed to construct string for URL");
    }
    scope_guard url_string_guard =
        make_obj_guard(env, &JNIEnv::DeleteLocalRef, url_string);
    if (!delete_local_refs) { url_string_guard.dismiss(); }

    const jclass url_class = env.FindClass("java/net/URL");
    if (!url_class) {
        env.ExceptionClear();
        throw std::runtime_error("could not find java.net.URL class");
    }
    scope_guard url_class_guard =
        make_obj_guard(env, &JNIEnv::DeleteLocalRef, url_class);
    if (!delete_local_refs) { url_class_guard.dismiss(); }

    const jmethodID ctor_id =
        env.GetMethodID(url_class, "", "(Ljava/lang/String;)V");
    if (!ctor_id) {
        env.ExceptionClear();
        throw std::runtime_error("failed to get "
                                 "java.net.URL.URL(java.lang.String) "
                                 "constructor");
    }

    const jobject url_obj =
        env.NewObject(url_class, ctor_id, url_string);
    if (!url_obj) {
        env.ExceptionClear();
        throw std::runtime_error("could not create java.net.URL "
                                 "instance");
    }

    return url_obj;
}

Well, there we are. It’s exception-safe and the caller can tell it the Right Thing to do. But…It sure does Suck.

  • It relies on the caller telling it to do the right thing; which means it’s pretty easy to use wrong.
  • We wind up having to check delete_local_refs at each point where we might (or might not) need to call JNIEnv::DeleteLocalRefs. Our need for exception-safety prevents us from consolidating this logic near the end of the function. Consequently, the code is interspersed with yet more error handling logic that gets in the way of understanding the function’s primary logic when reading the code.

So, is there a better way? Fortunately, yes.

JNI provides functions that allow us to create (and destroy) stack frames for local references, JNIEnv::PushLocalFrame and JNIEnv::PopLocalFrame. So, instead of relying on a stack frame that may or may not be there depending on the calling context, we can just create our own, regardless.

jobject create_url(JNIEnv & env, const char * const url)
{
    if (env.PushLocalFrame(3) < 0) { return 0; }
    scope_guard local_frame_guard =
        make_obj_guard(env, &JNIEnv::PopLocalFrame, 0);

    const jstring url_string = env.NewStringUTF(url);
    if (!url_string) {
        env.ExceptionClear();
        throw std::runtime_error("failed to construct string for URL");
    }

    const jclass url_class = env.FindClass("java/net/URL");
    if (!url_class) {
        env.ExceptionClear();
        throw std::runtime_error("could not find java.net.URL class");
    }

    const jmethodID ctor_id =
        env.GetMethodID(url_class, "", "(Ljava/lang/String;)V");
    if (!ctor_id) {
        env.ExceptionClear();
        throw std::runtime_error("failed to get "
                                 "java.net.URL.URL(java.lang.String) "
                                 "constructor");
    }

    const jobject url_obj =
        env.NewObject(url_class, ctor_id, url_string);
    if (!url_obj) {
        env.ExceptionClear();
        throw std::runtime_error("could not create java.net.URL "
                                 "instance");
    }

    return url_obj;
}

Once again, we use ScopeGuard; only now we’re using it to call JNIEnv::PopLocalFrame. Note that we’ve provided space for three local references in our frame, corresponding to url_string, url_class, and url_obj.

But the above code is horribly broken: when we return from the function, the local reference associated with url_obj gets cleaned up with all the others! So, how do we get this reference out of our local frame and return it, unbound, to the caller?

The solution is a temporary global reference. We can convert url_obj to a global reference for the remaining duration of the local frame; and then convert it back to a local reference before returning to the caller. Once more, ScopeGuard is our friend:

jobject create_url(JNIEnv & env, const char * const url)
{
    using boost::ref;

    //
    // We can safely run DeleteGlobalRef in the scope guard because
    // calling DeleteGlobalRef with 0 is a no-op.
    //
    jobject result_global_ref = 0;
    scope_guard result_global_ref_guard =
        make_obj_guard(env,
                       &JNIEnv::DeleteGlobalRef,
                       ref(result_global_ref));
    {
        if (env.PushLocalFrame(3) < 0) { return 0; }
        scope_guard local_frame_guard =
            make_obj_guard(env, &JNIEnv::PopLocalFrame, 0);

        const jstring url_string = env.NewStringUTF(url);
        if (!url_string) {
            env.ExceptionClear();
            throw std::runtime_error("failed to construct string for URL");
        }

        const jclass url_class = env.FindClass("java/net/URL");
        if (!url_class) {
            env.ExceptionClear();
            throw std::runtime_error("could not find java.net.URL class");
        }

        const jmethodID ctor_id =
            env.GetMethodID(url_class, "", "(Ljava/lang/String;)V");
        if (!ctor_id) {
            env.ExceptionClear();
            throw std::runtime_error("failed to get "
                                     "java.net.URL.URL(java.lang.String) "
                                     "constructor");
        }

        const jobject url_obj =
            env.NewObject(url_class, ctor_id, url_string);
        if (!url_obj) {
            env.ExceptionClear();
            throw std::runtime_error("could not create java.net.URL "
                                     "instance");
        }

        //
        // Create a global reference so that the new object will outlive
        // the local frame.
        //
        result_global_ref = env.NewGlobalRef(url_obj);
        if (!result_global_ref) { return 0; }
    }

    //
    // NewLocalRef does not throw any Java exceptions.
    //
    const jobject result = env.NewLocalRef(result_global_ref);
    if (!result) { throw std::bad_alloc(); }

    return result;
}

Ta da. We introduce an additional block scope for the duration of our local stack frame. We propagate the reference to the URL class instance out of the frame’s lifetime by creating an additional global reference. Once the frame is destroyed, this global reference is all that’s left. We then create a new local reference to return to the caller, and let a scope guard take care of cleaning up the global reference.

We’ve got Spirit

December 15th, 2007

I have spent a large chunk of my free time over the last several months replacing OpenVRML’s ANTLR parsers with ones using Spirit. This was my first attempt to do something nontrivial with Spirit. So this comment from the author of Spirit absolutely made my day.

New Autoconf OpenGL macros

November 30th, 2007

After nearly a year since pushing this project to Google’s project hosting, I’ve finally made a release tarball of my Autoconf macros for OpenGL. This was motivated mostly by significant changes that were precipitated by requirements for building on Mac OS X 10.5.

10.5 is a bit of a mixed bag relative to its predecessor as far as building with OpenGL is concerned. The good: they finally fixed the longstanding bug with the GLU tesselator callback function type being interpreted incorrectly. The bad: linking with X11 now requires some extremely goofy linker flags.

One nice thing, though, is that I was finally able to get rid of the annoying ‑‑with‑apple‑opengl‑framework option.

1997 called…

November 11th, 2007

I have begun to wonder just what it would take to create an ActiveX control for OpenVRML.

It has been about a decade since I touched COM. At the time, I had just (barely) learned C++. And I was struck then that just about every Good Practice for C++ I was learning about was being flagrantly violated in COM.

I quickly lost an affinity for Windows programming for that and many other reasons. I’ve done development primarily on POSIXy platforms; but much of that has been sufficiently portable to Windows that I’ve maintained a superficial knowledge of Windows development. But I’ve done enough development and I have a sufficiently deep understanding of C++ that something like COM just isn’t intimidating anymore. Which is not to suggest that I relish picking it up; but, rather, my annoyance at it is overcome by both a desire to show something working and a morbid curiosity about Just How Bad It Is.

My first thought was to apply the “modern” technique for building COM components, ATL. ATL is a template library; though the Wikipedia entry that (as of this writing) claims that ATL was patterned after the C++ STL is quite far off the mark. Aside from being written in C++ and using templates, there isn’t much similarity between the two. For the most part, ATL seems to be a bunch of CRTP mixin class templates. I can see how I would value the legwork it saves me if I had 10 or 20 or 100 COM components to write. But as I have just one, the obfuscation implicit in ATL usage seems unlikely to pay off.

And it’s just as well. Apparently Microsoft no longer includes ATL with the Windows, née Platform, SDK. So if I want this thing to build with the Express version of the compiler, no ATL.

It’s a bit too early to say if this effort will actually pay off in terms of a usable OpenVRML ActiveX control. It could happen. It could also be the case that I become so put off with the API that I never consider doing this again.

Nokia N75

September 9th, 2007

I’ve used Motorola mobile phones since I’ve had a mobile phone, starting with the V60i back in 2002 (as a customer of the original AT&T Wireless), followed with a V551 (with Cingular), which was in turn replaced with a RAZR V3 (upon its untimely demise).

The latter two phones had nearly identical software; but I was (and still am) a sucker for the RAZR’s form factor and construction.

  • I keep my phone in my pocket, so I like thin.
  • Antenna nubs are annoying. They’re pretty much a thing of the past on GSM phones; but when the RAZR was introduced, most clamshell phones had them.
  • There’s something satisfying about a cool-to-the-touch metal case.
  • The RAZR’s construction is solid. The hinge mechanism has a nice stiff feel to it with very little play. Mine has held up well through a couple of years of use.

The RAZR is not without its flaws, though. My chief annoyance was the weak phone book. This weakness is by no means unique to the RAZR. Most Motorola phones share a lot of the same software; and the phone book appears basically unchanged on models as recent as the KRZR. The number one, what-the-hell-were-they-thinking problem with the this phone book has to be: the single name field; i.e., no means of distinguishing between first and last names. So if you want to sort your phone book by last names, you need to put the last name first in each field. And then when you go to use synchronization software, you’re generally screwed because it doesn’t know about the scheme you’ve superimposed. A lot could be forgiven if this one problem were fixed. But there is plenty more wrong:

  • Rather than let you attach multiple phone numbers to a single entry, the Motorola phone book instead makes you create multiple entries with the same name, then gives you the option to visually merge the identically-named entries and treat one as primary. While this mostly works, it can at times be cumbersome to use.
  • That single name field is, on the RAZR at least, way too short.
  • The phone book accommodates storing phone number and e-mail addresses—nothing else.

So while I have preferred the Motorola clamshell phones, my wife has preferred Nokia candybar format phones. No question about it, Nokia’s UI is consistently superior—particularly its phone book. But there’s something about the ergonomics of opening a clamshell phone to answer it that I’ve always found attractive. And until relatively recently, Nokia’s suite of clamshell phones available in the US was quite weak.

That’s changed lately, which brings me to the N75. This phone uses a more recent version of the S60 software driving Gina’s 6682, so I was reasonably sure I’d be satisfied with the phone on this account. Indeed, the phone sports an extremely capable address book. It knows about first names and last names. It also knows about entries for which there should just be a company name. It can even store physical addresses—a feature I’ve been wishing for in a phone for quite some time.

The N75 has fared so-so in reviews. The major strikes against it seem to be that it is a battery hog and that it takes merely so-so pictures. My demands of a phone-bound camera are quite low; and coming from the RAZR’s VGA camera, even a mediocre 2 megapixel camera is a significant upgrade. I haven’t tried the camera much; but picture quality issues aside, I am quite pleased with the traditional camera-style ergonomics for the camera controls. Camera lenses on some phones seem to be designed to be obscured by a finger; that’s no problem here, even with my large paws. (The nice large keypad on this phone is also very accommodating of large fingers.)

As for the battery issue, I seem to be getting about two days on a charge. That may improve in the future, once I’m opening the phone to edit the address book less.

The construction of this phone feels reasonably solid; but it’s not up to the level of the RAZR in this regard. Nokia could learn a thing or two from Motorola with regard to hinge construction. Mind you, the N75 certainly isn’t bad in this regard. But the opening mechanism isn’t even as satisfying as my old V551; and it’s really nowhere near the nice feel of the RAZR. Apart from the hinge area, the body of the N75 is a matte black plastic. It seems likely to hide scratches reasonably well. But it doesn’t have the nice cool-to-the-touch feel of the RAZR’s metal case. Closed, the phone has almost the same footprint as the RAZR; but it’s about 30% thicker. While not terribly thick, the N75 isn’t really a “thin” phone by modern standards.

All in all, I’m pretty satisfied with the N75.

Consolation prize

August 6th, 2007

I discovered Consolas a couple of weeks ago; and I’ve been using it heavily since then. It renders nicely on the Mac (where it has supplanted Andale Mono) as well as with Xft (where it has supplanted DejaVu Sans Mono).

Something about Consolas makes Emacs behave just a little bit goofy: for some reason windows sometimes stop scrolling in response to console output.

A snake in a blender

July 22nd, 2007

I didn’t want to like Python. I’m still not sure that I do. But I have to admit that it is starting to grow on me. I’m beginning to get used to its goofy ideas about whitespace. And its double-underscore-emblazoned special identifiers are starting to hurt my eyes less.

I have been learning to use Blender. I can’t count the number of times in the past I had started the application, messed around for a few minutes, and then closed it in frustration. But a few days ago, I got it to do something I wanted.

Cool.

So I’ve continued to play around with it. And, slowly, I think Blender is managing to bend my will to its own.

Of course, it wasn’t too long before I needed to use the VRML exporter. And I found that it didn’t play nicely with meshes to which the “Bevel Center” script had been applied. So, a few hours of staring at the export script (in Python, like a significant chunk of Blender’s functionality) and asking around on #blendercoders later, I had a fix.

And now I know some Python.

Shedding ANTLR

June 18th, 2007

My word… was it really all the way back in 2001 that I first latched onto the idea of replacing OpenVRML’s ANTLR-based parser with one written using Spirit?

I guess I shouldn’t be so surprised; I was immediately taken with Spirit upon discovering it. Though back in 2001 Spirit still had a good deal of growing up to do; it’s come a long way since then. And given that Joel de Guzman and his cohorts are hard at work on Spirit 2, I get the idea that it will yet go a good deal farther. But I digress.

It’s the middle of 2007 and I’ve gotten serious about writing a VRML parser for OpenVRML using Spirit. Why now? Well, ANTLR certainly hasn’t gotten to be any less of an annoying dependency over the years. But another factor is that a new major version of ANTLR has been released (3.0). I think my efforts are better spent moving away from ANTLR entirely than on upgrading to the new version (which I understand to include nontrivial changes to the grammar format).

Now, I don’t want to come across as disliking ANTLR. It’s a really nice tool. In fact, it’s the nicest parser generator I’ve ever used. Functionality is very discoverable and I found, for the most part, the general behavior of ANTLR parsers to be very intuitive. But it has its downsides:

  • It’s a code generator, and thus it has the caveats that go with any code generation.
  • It’s a Java program, and thus it has the caveats that go with any Java program.
  • Even though it can generate code for a number of languages that are Not Java, the Not Java language backends are maintained by persons other than the primary author of ANTLR; thus, these languages wind up being second class citizens. (For example, ANTLR 3.0 has been released without C++ support since the maintainer of the C++ backend for ANTLR 2.x didn’t have time to port it to the new version.)

I actually started in earnest on this project at the end of last summer. I made a good deal of headway, getting as far as developing a good understanding of how to use Spirit’s stored_rule to create a grammar with productions that get modified as part of the parse. This solved the somewhat tricky issue of parsing node field values. But then I got side-tracked with getting the stand-alone viewer (openvrml-player) working reliably; that took much, much longer than I’d anticipated. But now I’ve picked up pretty much where I left off. As of this writing, I can parse nodes, EXTERNPROTOs, and PROTOs, except for IS mappings. I still have to do ROUTEs; though they will be pretty easy now that I’ve got DEF working.

As with the ANTLR-based parser, I’m doing a good deal of semantic checking; this parser will be just as aggressive about rejecting code that’s Not Quite Right as OpenVRML’s current parser. But unlike OpenVRML’s current parser, I’m using very little of OpenVRML’s runtime machinery to accomplish this checking. The idea is to make this parser much more reusable than OpenVRML’s current parser. The current parser isn’t really exposed; users can read the file into the runtime and then inspect the node tree created for the runtime. It turns out, though, that a good deal of OpenVRML’s users (and prospective users) don’t care one bit about a VRML/X3D runtime—they just want to read a VRML or X3D file and do something with the data. So, the new parser will have

  • Pluggable semantic actions
  • Minimal dependencies on the rest of libopenvrml—ideally, linking with libopenvrml won’t be required at all for someone just using the parser

This will all be possible through the magic of Spirit and C++ templates.

iRiver S10

May 30th, 2007

After years of sitting on the sidelines, I’ve taken the plunge and gotten a digital audio player.

iPods don’t interest me. While I appreciate their slick interface, I’m not the least bit interested in iTunes (or other online music services). And the fact that they don’t play Ogg is a deal-breaker for me. I use a Linux desktop (GNOME) and Ogg is easy for me to use, high quality, and completely unencumbered.

The requirement for Ogg narrows the field quite a bit. Narrowing it further, I’m interested in a flash-based device. So for some weeks I’ve been considering the Cowon D2 or the iRiver Clix. But ultimately the iRiver S10 won me over. The multimedia capabilities of the D2 and Clix don’t interest me; and the S10’s form factor is quite attractive.

I received my S10 yesterday. The functionality and quality are top-notch. And Nautilus makes it easy for me to convert selections from my library of FLACs to Oggs on the S10.

I am very happy with it. But here are the few things I don’t like:

  • Unless you’re using a playlist, files play in the order of their FAT entries. I’d have preferred/expected alphanumeric order. The included software includes the facility to reorder the entries (and thus play order) however you like; however…
  • The included software only works on Windows. I realize that providing software that works on Linux is probably expecting too much; but I was expecting a Mac version. This software isn’t absolutely vital; but the FAT entry reordering is very useful and the software is the most convenient means I’ve found of creating playlists. (Though so far I haven’t found myself messing with playlists.)
  • The included software seems not to work at all unless it’s run by a user with Administrator privileges. Lame. Really lame.
  • No case was included; nor does anyone appear to make one. Hopefully that will change. An armband would be especially handy.
  • The “D-click” navigation is a pleasure to use; but it means that the player can’t really be operated while stored somewhere that’s at all a tight fit—like, for instance, pockets in jeans. If there is some way to lock the interface, I haven’t discovered it yet.

Feeding (and silencing) the beast

May 2nd, 2007

My primary development box, hinge, has seen a bit of an overhaul lately. Most significantly, I snagged some Opteron 285s that were refurbished units (i.e., system pulls) from HP servers for $325 each. These replaced the 242s that I originally built hinge with. Twice the cores and a full 1 GHz faster—quite an improvement.

hinge is housed in a Lian-Li PC-V1200 case. I got the “Plus” version, which includes a blower fan. These blowers seem prone to get noisy after several months. I replaced the blower the first time this happened. Bad fans do happen. But recently the replacement got pretty loud, too. So, figuring these blowers are all destined to make noise, I’ve decided to see how the box fares without it. It does have two 120 mm intake fans; I figure it shouldn’t be that big of a deal for exhaust to be passive.

Unfortunately, I was faced with the realization that the machine was still pretty damn noisy. The primary culprit was the power supply—an Enermax EG651P-VE. The fans on the Cooler Master Vortex TX heat sinks I installed on the new CPUs were contributing to the problem as well. Another thing I really disliked about the Vortex TX heat sinks is that installing them required completely removing the motherboard—they don’t work with the stock mounting bracket for K8 CPUs.

I decided I’d listened to this thing long enough. I’ve now replaced the heat sinks with Hyper TXes; they’re quieter and they use the stock mounting bracket. And the power supply has been replaced with a PC Power & Cooling Silencer 610. Problem solved; this machine is now very quiet. But now the other machines in the room sound loud.

Get it straight

January 8th, 2007

Rounding out the weekend, an Extrusion bug. While at first glance it seems OpenVRML just isn’t normalizing a vector where it should, the larger problem is that the algorithm for identifying linear spines is broken. Sigh.

This is hard

January 8th, 2007

I spent most of the weekend laying tile in my basement. It amounts to Real Work. One 50 lb bag of thinset mix seems to stretch for about 16 of these 18″ tiles (give or take depending on how much I have to use to get a tile set level with respect to adjacent tiles). I went through two bags this weekend. I am perhaps 25% done with the job.

I am impressed with just how hard this tile is. At one point I lost control of a tile and it hit the floor right on the corner of the tile. It made a “bong” sound as the tile flexed in response to the impact where it took a chip out of the concrete slab floor. The tile? Nary a mark.

We’ve been remodeling our basement for a while now. When we moved into the house over two years ago, doing this was Part Of The Plan. The basement looked like it hadn’t been changed since the house was built in 1980. Sculpted brownish green carpet. Dark gray vinyl tile. Darkly stained wood paneling. And the best part? A large conglomeration of nailed-together lumber that seemed intended to be a bar. A disco dungeon of sorts.

But past the unfortunate decor, the basement is spacious and has a fireplace. It could become very comfortable living space. But salvaging it has not been an easy task. The bar was evicted. The wood paneling was replaced with drywall. A closet and built-in shelving were torn out. The sliding glass door to walk out to the back yard has been replaced. The carpet and vinyl tile were dispensed with. The walls and ceiling have been repainted. Even the fireplace doors have been upgraded.

And, of course, there were the unanticipated problems. A leak in the basement was sealed. (”So that’s why it smells like wet dog down here when we get a hard rain.”) The lopsided stairs were righted and their failing supports replaced.

So, at long last, I feel like we’ve reached the last leg of this project. But at this rate, I’ve got at least three more weekends of laying tile left to go. And I hurt.

OpenVRML fall recap

January 5th, 2007

Since going a season without posting here, I feel like I ought to provide some resolution to some of the things I mentioned here earlier in 2006.

Fall was pretty good for OpenVRML. The 0.16 series (which saw its first release back in August) yielded 0.16.3 by the end of the year—which seems to be a pretty usable release. Even the Mozilla plug-in seems to be basically usable, caveat a really annoying bug in the JPEG decoder. Also, I’ve started packaging OpenVRML for Fedora Extras.

Lately I’ve been spending most of my OpenVRML development time working on a stand-alone player. Like the Mozilla plug-in, it is simply a host to the openvrml-gtkplug out-of-process component. But it exercises openvrml-gtkplug in different ways—which accounts for most of my interest in writing the stand-alone player.

uri_grammar hosted in Google svn

January 5th, 2007

After discovering svnsync I’ve migrated uri_grammar to Google’s Subversion hosting. I’ll soon do the same for my Autoconf macros for OpenGL.

Resolve to suck less

January 4th, 2007

So it’s 2007 already.

I have finally repaired the style sheets for this journal such that the stuff that’s supposed to be on the right column actually shows up there. Good grief; I’ve probably forgotten more about CSS than many competent professional Web designers know. I wish I were exaggerating. But the fact is that there was a time in the late nineties that I had a level of expertise with the technical details of CSS shared by a relative few on the planet. But that was back when CSS was a nascent technology. Now it’s bigger, more complicated, and commonplace. And the CSS minutia that was in my brain has been evicted by other minutia. Probably mostly C++.

The style sheets for endoframe.com were written back when I could sling CSS with the best of them. They’re somewhat elaborate. They’re all based on a suite of style sheets that are designed to normalize rendering of HTML 4.0. The problem is, I’ve forgotten a lot about how they work and what they do. But in the persistent absence of any pressing desire to sling a lot more CSS, I guess I’ll just be relearning this from time to time as stuff breaks. So let me not touch this again for a while.

But now that the appearance of this page is less embarassing, I’ll hopefully be more inclined to update it.

Home networking

August 1st, 2006

Some months ago I cobbled together old parts in a new case for a box I’d intended to serve primarily as a nameserver. Last weekend I finally started to play around with getting BIND set up. And I’m pleased to say that, with some help from Tim Seifert’s page on the subject and some correspondence with its author, I have a nameserver.

I also installed a new printer recently after the network interface on my old one appeared to fail. I was pleasantly surprised to see the new printer’s Web server show up under a “Local Sites” category in Epiphany’s bookmarks. Some investigation into this feature led to Avahi; and, so, I now have front (the nameserver box) publishing service descriptors for my wireless router and bridge. Ross Burton has an entry that covers most of how to get this set up; lathiat has an entry that covers the rest. A couple of notes from my experience:

  • Entries in /etc/avahi/hosts must be fully qualified. There’s a comment in the file to this effect; but I managed to glance past it at first.
  • Entries in /etc/avahi/hosts must actually resolve. Perhaps that seems obvious; but on account of the fact that avahi needs an IP address along with the host name in this file, I thought perhaps I could get away without it.

Testing Google code hosting

July 30th, 2006

I have created a project for uri_grammar on Google’s new code hosting service, largely as a means of testing the waters. Though right now the only thing of interest that’s there is another link to the tarball. While Google provides Subversion hosting, it doesn’t look like there’s currently a way to import the contents of an existing repository into it. I won’t be populating the Subversion repository there until I can do that.

The site sports the type of clean, uncluttered interface one would expect from a Google property. And it’s quite snappy, too. It’s a pleasant departure in these respects from SourceForge. But while Google hosting executes very well the things it provides, there is a lot missing compared to SourceForge. Significantly, Web hosting and release hosting are absent. It will be very interesting to see where this goes, though.

Taking exception

February 12th, 2006

The commonly accepted wisdom in C++ these days is that exception specifications aren’t worth bothering with. While it’s certainly the case that they could be substantially improved to provide more utility, I’m not so dismissive of what they do offer: an assertion that a function fails only in the ways it advertises that it can. In this respect, the exception specification is much like a specialized assert. And like assert, they are valuable as a debugging aid.

If an unexpected exception occurs in a codebase that has made thorough and appropriate use of exception specifications, the exception isn’t likely to propagate far before it results in the program being terminated. Without exception specifications, the exception would propagate all the way up to main before terminating the program. The difference is in the call stack you get from a debugger when termination happens: the nearer termination is to the point at which the unexpected exception was thrown, the easier it will be to find where and why that happened.

If the exception propagates all the way back to main, the call stack doesn’t give you much useful information. You can hope that the exception itself is sufficiently specific as to give you a clue about where it came from; beyond that, your best hope is a debugger that can be configured to break automatically when an exception is thrown.

The other service provided by exception specifications is simply documentation. One might reasonably suggest that this service is better provided by actual documentation; but that is only true to the extent that such documentation is complete and accurate. The fact is that assessment of the exceptions that can be thrown by a function requires a rather thorough inspection of that function an all the ones it calls. And when any of this code changes, the documentation needs to be checked to ensure it remains accurate. Unfortunately, that is all too easy to overlook—even with embedded documentation schemes such as that supported by Doxygen. There is a far more compelling interest in updating something that is likely to crash your program if it is wrong. And an accurate exception specification makes documentation that much easier.

However, as Sutter points out, exception specifications are likely to impact performance negatively. Like the validation done in an assert, the exception specification imposes additional run-time checking. assert, of course, is a macro—and that means that by twiddling a preprocessor symbol, the checking it does can be made to go away completely. And there’s no reason the same solution can’t be employed to toggle the presence of exception specifications: rather than use throw directly when writing an exception specification, use a macro that can be redefined to make the whole business go away when that’s desirable (e.g., optimized builds).