Thursday, May 28, 2020

Subtraction is not a Comparison Operation

I'm looking at you, Java experts

As a professional Java programmer, I often find myself looking at Java blogs, tutorials and news sites. One example that crops up again, and again, is an implementation of the Comparable<T> interface, or a comparator lambda that uses subtraction instead of comparison. Consider the following toy class:

package example.bad;

public class Value implements Comparable<Value> {


    private final int value;

    public Value(int value) {
        this.value = value;
    }

    @Override
    public int compareTo(Value o) {
        return this.value - o.value;
    }

    public int getValue() {
        return value;
    }
}


Even prominent, respected Java experts are guilty of this. It looks perfectly plausible, and it even works if you only call it with values in the range [-1,073,741,824, +1,073,741,824]. But the hallmark of a correct implementation is not plausibility, or "happy-path" success. The hallmark of a correct implementation is that it works with all valid inputs, not some valid inputs. Let's add some tests. If the code works, it just works, right?

package example.bad;

import org.junit.Test;

import static org.junit.Assert.assertTrue;

public class ValueTest {
    Value max = new Value(Integer.MAX_VALUE);
    Value min = new Value(Integer.MIN_VALUE);
    Value zero = new Value(0);
    Value one = new Value(1);
    Value minusOne = new Value(-1);

    @Test
    public void happyTest() {
        assertTrue(one.compareTo(zero) > 0);
        assertTrue(zero.compareTo(one) < 0);
        assertTrue(one.compareTo(one) == 0);
    }

    @Test
    public void realTest1() {
        assertTrue(max.getValue()+" < "+minusOne.getValue(), max.compareTo(minusOne) > 0);
        // AssertionError: 2147483647 < -1
    }

    @Test
    public void realTest2() {
        assertTrue(min.getValue()+" > "+one.getValue(), min.compareTo(one) < 0);
        // AssertionError: -2147483648 > 1
    }

    @Test
    public void realTest3() {
        assertTrue(max.getValue()+" < "+min.getValue(), max.compareTo(min) > 0);
        // AssertionError: 2147483647 < -2147483648
    }

    @Test
    public void realTest4() {
        assertTrue(min.getValue()+" > "+max.getValue(), min.compareTo(max) < 0);
        // AssertionError: -2147483648 > 2147483647
    }
}


Looks like it just doesn't work.

And before you object that "well my inputs aren't anywhere near Integer.MAX_VALUE or Integer.MIN_VALUE", remember that bad assumptions kill. Your method might never produce a bogus result... but someone else who reads it might assume that's how you implement a comparison. They might go on to use subtraction to implement comparison in a financial program, or worse yet, an embedded pacemaker, or worse yet, an airplane flight control module, or... you think of the worst possible example you can, and it could end up there.

It's inexcusable, and it needs to stop.

What makes this "sample code" not just dangerous but also incredibly lazy is that Integer.compare(a, b), Long.compare(a, b), etc. have been in the standard library since Java 7. You will never need to implement them yourself. So stop writing sample code that does. Before you kill someone.

Finally, allow me leave you with the easy, and also completely correct, way to write an integer comparison.

@Override
public int compareTo(Value o) {
    return Integer.compare(this.value, o.value);
}

Try substituting it for the bogus one and rerunning the tests!

Saturday, December 17, 2011

In which I fight bcdedit.exe for a week

I'm fixing a friend's HP Pavilion dv6928us laptop, which got infected with one of those fake system fix malwares. The malware pretended to find all kinds of horrible problems which were about to cause IMMINENT SYSTEM FAILURE and also by the way marked all of the files in her user directory hidden, making it seem like ALL HER FILES WERE GONE OH NO. Tried removing it with half a dozen different tools, no go.

So after doing some attrib -H C:\Users /S /D, backing everything up to an external drive, reformatting C:, restoring Vista from the HP Recovery partition, and installing Microsoft Security Essentials, everything should have been hunky-dory.

Except there was a little chunk of rootkit stuck somewhere in the Master Boot Record or BOOTMGR or something, and MSE kept detecting it and failing to remove it. So I did what anyone* would do and attempted surgery via LiveCD. I backed up the Master Boot Record and the Partition Boot Records, used sfdisk -d to make a text version of the partition table, dd to zero out the whole part of the disk before the start of the first partition, sfdisk to restore the partition layout, and BootICE to reinstall the Vista MBR. Problem solved! Or... not. Somehow, doing that screwed up the Boot Configuration Database so that the laptop would boot Vista but not boot the HP Recovery partition. Well, until that point I didn't know that the BCD existed so I hadn't made a backup of it before reformatting. This then set me off on about a week (I had a cold for much of it so a lot of that week was spent sleeping) of painful wrestling with bcdedit.exe and related tools, absolutely none of which resulted in a working recovery partition.

Then while poking around in the HP-specific areas of the disk, on the off-chance that there was a program named "FIX HP RECOVERY PARTITION BCD ENTRY" (yeah right) I found, against all odds [sfx: angelic chorus] C:\WINDOWS\SMINST\HPRM-BCDFix.cmd.bak.

Well, that was easy.

Of course, before running it I opened it up to see what it did, which seemed to be exactly what it said on the label. I made a copy without the .bak extension and ran it from an Administrator console.

And it worked.

TIL that the answer I'm searching for far and wide is sometimes right under my nose.

*Anyone insane.

Monday, January 03, 2011

Thanks, Netflix!

So I signed up for a Netflix instant streaming free trial under the mistaken impression that I would be able to use it to watch movies. Turns out they require Silverlight, which is of course the work of Satan. After doing some searching to find out if there's a way to stream to a PC without Silverlight (there isn't) and whether it's possible to contact technical support without making a phone call (it isn't) I decided to cancel my account. Time elapsed: 42 minutes.

Now here's the interesting part: on the why did you cancel, oh god, why? page there are the obvious questions, but midway down there's something that kind of surprised me. They want to know where I'm going to be watching movies and TV shows now that I'm cut off from their life-giving nectar, which is a reasonable question. However the first two multiple-choice options are Bootleg DVDs and Peer-to-peer / bit torrent sites such as Torrentz.com, Demonoid.com, Tvlinks.com, ThePirateBay.org, etc.

Wow, Netflix is giving out juicy tips on where to illegally acquire copyrighted material! I'm really glad they have such a positive attitude towards P2P that they're basically advertising it to ex-customers! A+++++ WOULD CANCEL SERVICE AGAIN.

Wednesday, August 22, 2007

The Instant Backreference Effect

While browsing news sites, I frequently refer to Wikipedia when I encounter a reference to some medical procedure or person of note or anything at all that I'm ignorant of. Lately, I've noticed an interesting trend: By the time I get to the Wikipedia article for the topic I'm curious about, it already has a link to the site I was just reading.

The specific incident of this effect that made me take notice of it was the Kaz II Ghost Yacht incident on April 18, 2007. It was all over the news for being what the Mary Celeste wasn't: A ship found completely abandoned with the the table still set for dinner. When I opened up the article on the Mary Celeste, I found that a section about the Kaz II had already been written only two days after the incident occurred.

I felt a little bit like the character from Harvey who tried to read the encyclopedia entry on Pookas only to find that the titular invisible rabbit had inserted the text, ...and how are you today, Mr. Wilson?

Tuesday, October 17, 2006

What's Wrong with Tivoization

This post originally was going to be a comment on A fight against evil or a fight for attention? but it got kinda long.

The primary purpose of the GPL is not to make better software, or to make software available for private tinkering. The purpose of the GPL is to keep software Free with a capital F. While Tivo's attempt to maintain their business model may or may not be ethical, their attempt to hamstring the Freedoms that the GPL provides is definitely not.

When Tivo does it, people say "Well, don't buy a Tivo". But what will you do when it becomes standard practice for hardware to only allow signed, approved software? What good does the Freedom to Run modified software do you if your hardware refuses to run it?

Petreley asks, "What does the GPL have to do with hardware? Hopefully, nothing.

In fact, I am suspicious of the motives of anyone who wants to modify the GPL such that it forces vendors to redesign their appliances to conform to the Free Software Foundation's ideas of how such appliances should work."

Have you even read the GPLv3 draft? The DRM sections require distributors of GPLed code to provide any and all encryption keys required to make the software work. That's a far cry from "forcing vendors to redesign their appliances." In fact, the GPLv3 says nothing about how manufacturers should design their hardware, it merely says that they must provide the tools to make modified GPLed software run.

He goes on to say, "Why is the FSF cramming issues into the GPL that are arguably unrelated to the original intent of the GPL?"

I suppose if the FSF were to do such a thing, that would be a problem. But the Freedom to Run is Freedom 0 in the Free Software Definition, and preserving those Freedoms is precisely and exactly the original intent of the GPL.

Fighting DRM is not a sidenote to preserving user's Freedoms, it's a central issue, and as such its inclusion in the GPLv3 is entirely appropriate. People don't seem to get that the Free Software Foundation's purpose is to maximise the Freedom that users have with their Software. Like the name says, maybe? Commercial software and hardware companies' business models are wholly irrelevant to that purpose. Would you compromise Freedom of Speech to allow a company to make an extra buck? No? Then why should the FSF compromise Freedom of Software?