I’m Old, Part LXXXII: Making Friends

At Atalasoft, we frequently tapped into local colleges for filling paid internships and we ended up hiring a fair percentage of those students once they had finished their degree. One of our first interns was Sean McKenna who came to us from UMass Amherst. At the time he was brought on, the company was barely single digits and crammed into an office in an old mill building in Northampton.

One day, we were having a conversation about beer pong and other drinking games. I discovered from Sean that at UMass, they called it ‘Beirut’ instead of beer pong. Sean and Dave Cilley started getting into a friendly argument over the point of drinking games in general and beer pong specifically. At one point, Sean said, “Beirut isn’t about drinking; it’s about making friends.” Dave immediate retorted deadpan, “I don’t need friends.” He immediately put the argument to bed. All of us doubled over in laughter except Dave who walked away.

This event entered into our collective lore and would come up every year or so.

PDF Redaction

As of January, 2019, lawyers for Paul Manafort released a “redacted” PDF that allowed one to easily read the redacted text. I will not speculate as to how this could have happened beyond the chestnut, “Never ascribe to malice that which can adequately be explained by incompetence”. Instead, I will go into detail as to how difficult a problem redaction is to solve properly in PDF beyond pilot error.

To begin with, let’s start with the elements of content of PDF. PDF was designed to represent all that could be represented on a printed page. There are three basic elements that can appear in a PDF: paths, bit-mapped images, and text. Paths can be either Bezier curves or rectangles (and in fact, rectangles are shorthand for paths). Paths can be stroked, filled, both, or used as a mask to clip out other elements.

These elements are represented in a postfix program within the file. One technique to display a page is to execute the program and collect all the elements into a display list which can then be rendered onto a screen on into print.

The problem here is that each of those three elements can contain something that is visually text. To be able to perfectly redact a PDF, there are two approaches. The first is to turn the page into just an image and paint over the text to be redacted and create a new document with only the image on the page. This a cheesy approach and it works, but there is a cost. Any of the text that was actual text is no longer text and can’t be selected or indexed. Furthermore, the final document is likely to be substantially larger than the original.

To do this in a non-cheesy way you need to handle each of the elements. Why? Because any of the three elements could render as text that is readable to a human. For example, an image could contain text (or a face). A set of paths could be a logo or the shapes of letters. To handle text, you need to be able to iterate over all the text in a page and create an equivalent program on the page that no longer includes any text within the area to be redacted. This is a tricky problem on its own, especially when text elements span regions to be redacted. Next, for images you need to be able to decode all the possible image formats with the PDF: JPG, LZW, CCITT, RLE, JPEG2000, and JBIG. The latter two are non-trivial to decode. Then replace the image with a new one with the areas painted over. Finally, there are path items. Ideally, you would remove any paths that intersect redaction areas, but that gets tricky because changing the paths that are only partially occluded is a very hard problem to do well.

In addition to these basic elements, there are others including composite objects, layers, annotations – any of which can be manipulated to appear as text or other information that should be redacted.

When I was working at Atalasoft on PDF tools, I considered the task of redaction and chose to pass on it. It lost us a sale or two, but I would rather lose a sale than get blamed for an incorrectly redacted document.

The commercial version of Adobe Acrobat has redaction tools built in and these do the job quite well. Best to depend on that for the time being.

I’m Old, Part LXXXI: Notes

When I worked for Newfire in that late 90’s, there were a number of things we did that were clearly designed to make the company more enticing for purchase. We had continuous integration, source code control, QA, marketing, etc.

In addition, there was the issue of intellectual property. We were implementing some really interesting things in terms of 3D code and a bunch of it was novel. We had patent attorneys come in and interview us and we applied for a stack of patents on our technology. In addition, my boss Marty gave every engineer a notebook with two very special criteria: they were bound and the pages were numbered. This would make it clear if the notebooks had been altered.

If we came up with any good ideas, we were encouraged to write them down and with the date and if they were particularly good, we had to put the book in front of another engineer who signed and dated the entry as a witness. Although it is possible to forge the books, this was supposed to provide a modicum of protected against intellectual property issues.

My book had a bunch of inventions in it that I really enjoyed. One was a process to turn a polygon into a minimal set of triangles, the goal being to turn text into meshes or indexed face sets. Another was a programming language built on finite-state automata for defining object behaviors in games. It was designed to be easy to read, interoperate with our game engine, and to be trivial to JIT compile.

Years later, at Atalasoft, I carried this over. I ordered a stack of notebooks and whenever we brought in a new engineer, I game them a notebook with the same instructions I was given.

What I think I really liked about the process was seeing how each engineer used the notebooks. It was an interesting reflection on their working styles. Some were planners and very much filled their notebooks with everything. Some were journalers and just wrote down a few bullet points for the day. Some were save game slots: wrote just enough to make it easier to pick up the next day. Some didn’t really use the notebooks at all. I was totally OK with all of those approaches.

Swift Splatting

Splatting in a programming language is the ability to take a function that is variadic (can have an unlimited number of parameters) and treat it like a function that takes an array.

C has a fairly dangerous way of doing this that involves mucking around with a pointer to the last parameter. It’s cute and it works and it’s also decidedly not type-safe.

C# has a way of doing this too, which is to use the params keyword, which allows the last argument to be zero or more arguments of one type. For example:

public int HowManyArgsDidIGet(params int[] args) {
        return args.Length;

This will take any number of integers and returns how many were passed in. The syntax, which requires the type to be an array, reveals the implementation: all the parameters will get folded into a single array. C# has the limitation that you can have up to precisely one params argument and it has to be the last argument. One nice thing is that you can also call this method with an array of int. This is called splatting.

Swift also has variadic arguments and they are implemented under the hood identically. One difference between swift and C# is that swift allows you to have as many variadic arguments as you’d like by using the argument label syntax.

The equivalent swift to the C# is:

public func howManyArgsDidIGet(args: Int...) -> Int {
    return args.count // args is an array

The problem that arises is that swift does not allow splatting. This is probably because I can also write this function:

public func howManyArgsDidIGet(args: [Int]) -> Int {
    return args.count

Which even though they are ostensibly the same function have different signatures.

The problem that arises is when you are writing classic OOP code:

open class NotImportant {
    public init () {
    open func howManyArgsDidIGet(args: Int...) -> Int {
        return args.count // args is an array
public class DoesntWork : NotImportant {
    open override func howManyArgsDidIGet(args: Int...) -> {
        print ("calling super...")
        return super.howManyArgsDidIGet(args: args) // Cannot convert value of type [Int] to expected argument type 'Int'

This is a known problem in swift and one that has had a number of suggestions for possible implementations, but it still leaves you high and dry if you need to override a variadic method. Or does it? I’m an old time C programmer so I have absolutely no problems casting something to the type that I know it really is. Swift lets you do that too.

public class DoesWork : NotImportant { open override func howManyArgsDidIGet(args: Int…) -> { print (“calling super…”) let clos = unsafeBitCast(super.howManyArgsDidIGet, to: (([Int])->Int).self) return clos(args) } }

What’s going on here? We recognize that even though the signature of the original method is (Int...)->Int, it is 100% equivalent to ([Int])->Int, so I take the method I want to call and cast it as a closure of the type that I want and call it. Ta-da! splatting.

The problem that this doesn’t solve is variadic subscripts, which are also legal in swift. This issue here is that neither the getter nor the setter are accessible from swift as a closure, so this kind of casting doesn’t work.

If you don’t like this kind of casting, just don’t ever use variadic parameters in swift methods that can be overridden. And never under any circumstances use variadic subscripts in overridable methods.

Is Swift a Floor Wax or a Dessert Topping?

I’m going to go into some of the details of how Swift does method dispatch. It’s going to involve some assembly language, but don’t panic – I’ll talk you through it.

Let’s start off with plain swift code:

public class Foo {
    public func doSomething() {
        print("not interesting")
let x = Foo()

What we’re interested in is how x.doSomething gets invoked. What you look for in an assembly language is an instruction that changes the control flow to some other place with the intention of coming back and picking up where you left off. In many processors it’s something like “jsr” (Jump to SubRoutine) or “bsr” (Branch to Subroutine) or in the case of X64, “call”. This instruction usually takes one argument, which is the address of the routine you want to go to. If doSomething were a top level function, not a method, this would be straight forward since the compiler knows the name of the routine it wants to call. For example:

call  _someGlobalFunc

With that in mind, here is the code that gets generated for the last two lines:

call       __T010unitHelper3FooCMa
mov        r13, rax
call       __T010unitHelper3FooCACycfC
mov        qword [__T010unitHelper1xAA3FooCvp], rax
mov        rax, qword [__T010unitHelper1xAA3FooCvp]
mov        rsi, qword [rax]
mov        r13, rax
call       qword [rsi+0x50]

So what’s going on here? The first call to the lovely name ‘__T010unitHelper3FooCMa’ is a call to get the type metadata for the type Foo. This is more or less the ISA value for the type. Every constructor takes one of these (and in anything but generic types it gets completely ignored). The result of that ends up in rax, which gets stored in a global variable named x (__T010unitHelper1xAA3FooCvp). This represents the let statement. Next we copy x (__T010unitHelper1xAA3FooCvp) into rax and read the first pointer value from the instance (the ISA) into rsi. Then we move rax into r13. This step is because in the swift ABI, all instance methods are called with their instance value in r13. This was different in versions of swift earlier than 4.0, but that’s where we are now. Finally, there’s the call instruction – but instead of a global address, it’s using [rsi+0x50]. That means the we’re looking at an address that is 0x50 bytes offset from rsi (the ISA pointer, remember?) and reading an address from there to jump to.
Why? Well, the function that’s getting called can be overridden in a subclass and unless we’re sure that x is really a Foo and not a subclass of Foo, we can’t just call Foo.doSomething directly. Instead, we look up the address we need in a table that’s in the ISA pointer. This is called vtable dispatch and is what pretty much every OOP language uses because it’s reasonably efficient.

Where things get interesting is if you declare the type to be @objc, which means that swift should obey ObjC calling conventions:

import Foundation
public class Bar : NSObject {
    @objc public func doIt() { }
let b = Bar()

This is more or less the same code, but now we’re looking at an ObjC type. The generated code looks like this:

call       __T010unitHelper3BarCMa
mov        r13, rax
call       __T010unitHelper3BarCACycfC
lea        rsi, qword [_swift_isaMask]
mov        qword [__T010unitHelper1bAA3BarCvp], rax
mov        rax, qword [__T010unitHelper1bAA3BarCvp]
mov        r13, qword [rax]
and        r13, qword [rsi]
mov        qword [rbp+var_20], r13
mov        r13, rax
mov        rax, qword [rbp+var_20]
call       qword [rax+0x50]

Again, we start with a call to get the class metadata and we move it to r13 and then call the constructor, __T010unitHelper3BarCACycfC. Then we get the address of a global variable named _swift_isaMask and hold that in rsi. It gets used in a bit. Just remember that. Next we store the instance which is in rax in a global variable named __T010unitHelper1bAA3BarCvp, and because this is not optimized code, we read it right back into rax. Next step is reading the ISA pointer from the instance and putting that in r13 and then we mask off some of the bits. This is different. There are some bits in the ISA pointer that get set aside to be used to identify this ISA as really ObjC or swift. If we treat the ISA pointer as a pointer without the mask, we’ll read from the wrong location. It turns out that r13 and rax need to get swapped. That happens through a local variable named [rbp+var_20]. This is abysmal code, by the way. There are other better ways of doing the same thing, but again, this is not optimized so I’m not surprised. Finally, we call doIt by looking it up in a vtable. Wait. What? This is weird because we declared that this was @objc – how come swift isn’t using ObjC’s name dispatch? Does it even have it?
The answer to the latter question is yes. If we poke around in the binaries, we find this routine:

                     -[_TtC10unitHelper3Bar doIt]:
push       rbp
mov        rbp, rsp
push       r13
sub        rsp, 0x18
mov        qword [rbp+var_10], rdi
mov        qword [rbp+var_18], rsi
call       imp___stubs__objc_retain
mov        r13, qword [rbp+var_10]
mov        qword [rbp+var_20], rax
call       __T010unitHelper3BarC4doItyyF
mov        rdi, qword [rbp+var_10]
call       imp___stubs__objc_release
add        rsp, 0x18
pop        r13
pop        rbp

The instance comes in in rdi and gets stored in local var_10. rsi is stored in var_18, but is never used again. Then we call objc_retain on the instance (this is automatic reference counting at work). Then the instance gets read back into r13 and we store rax (the return from retain) into var20. Then we call __T010unitHelper3BarC4doItyyF, which is the mangled name for doIt. So here is the ObjC implementation which is just an adapter to call into the swift implementation. Tada.

So it is possible to call Bar from ObjC and it ends up going right into the existing Swift code.

There is code that can handle dispatch by name and dispatch by vtable.
We can conclude that Swift is both a floor wax and a dessert topping.

I’m Old, Part LXXX: Revisiting Very Old Code

Archive.org has been working to collect old software and emulators to digitally preserve our early roots in computing. In particular, they’ve been working to preserve Apple II software, some of which has had very creative DRM. One way to preserve software with DRM that depended on unformatted data on disks was to create a means of modelling that. This is the WOZ file format. They’ve been recently posting preserved disks under the title “woz-a-day” and yesterday was apparently my day!

I donated a disk of mine to the archive which has the original version that I tried to sell as a game. The story of how it came to be is in the description. Today, what I’m going to do is take apart one bit blitter, a routine to move pixels around the screen, and show you how it works. I wrote this blitter when I was 14, so this is going to be a good solid dose of personal WTF.

Here’s the code as it exists on that original disk (I’ll take it apart piece by piece after).

    LDA #$11
    ADC $300
    STA $302
    LDY #$00
    STY $304
    LDX $300
L1  LDA $6000,X
    STA $01
    LDA $6100,X
    STA $00
    LDY $304
    STY $304
    LDY $301
    STA ($00),Y
    LDY $304
    STY $304
    CPX $302
    BNE L1
    .HS 0063141C3E7F6B6B7F5D633E1414367722

What does this do? From a high level, it takes coordinates from locations $300 and $301 and uses those to draw a shape on the screen. $300 is the Y coordinate and runs from 0-181. $301 is the X coordinate and is runs from 0-39. The actual X value on the screen is X * 7, this is because this draws on byte boundaries.

Here’s my initial reaction to this: holy crap, what was I thinking? Yes, it will work but there is no clipping at the bottom and this routine is wasteful as hell. It’s a crash waiting to happen.

OK – let’s take it apart piece by piece:

    LDA #$11
    ADC $300
    STA $302
    LDA #$00
    STY $304

The shape is 17 pixels high (that’s the $11). So what I’m doing is adding $11 to the Y start coordinate and storing it in $302. That CLC clears the carry flag because addition on the 6502 always includes a carry. If you don’t clear it explicitly, you get an off by one error. What I’m doing here is setting an end condition for when to stop drawing. This routing will always draw $11 lines. $304 is the index into the table of data for the shape.

   LDX $300
L1 LDA $6000,X
   STA $01
   LDA $6100,X
   STA $00

This is setting up a pointer for the destination on the screen. I initialize the X register to the initial Y coordinate then lookup the address of the start of that scanline in two tables at $6000 and $6100. These get stored in locations $01 and $00 which are in the “zero page” (the bottom 256 bytes), which is special on the 6502 for a couple of reasons. The one I’m taking advantage of here is that if you want to dereference a pointer without self-modifying code, you need to use 0 page memory.

   LDY $304
   STY $304
   LDY $301
   STA ($00),Y

I’m doing a lot of register juggling here. The current shape index is in $304. We read a byte of shape, reload the X coordinate (x 7) from $301, then write the byte to the screen. Remember that $00 and $01 point to the first byte of the scanline. The STA ($00),Y instruction takes that address, adds Y to it, then stores A at the location.

   LDY $304
   STY $304

This reloads the shape index into Y and stores it back into $304. This is not great code. I should have done that back after I read the shape and I wouldn’t have needed the redundant load or done something more creative (dun-dun-DUNH! foreshadowing!)

   CPX $302
   BNE L1

This adds 1 to X and compares it to the largest Y coordinate. If it’s not equal, we go back and repeat.


So how bad is this code? Well, not too bad, but it could be better. The main loop takes 52 cycles and run to completion, the main loop will take roughly 882 cycles or .8 milliseconds. Let’s see if we can do better.

    LDA #$11
    STA $02
    STA L2+1
    LDX $300
    LDY $301
L1  LDA $6000,X
    STA $00
    LDA $6100,X
    STA $01
    STA $(00),Y
    INC L2+1
    DEC $02
    BNE L1

A couple changes. Notably, I’m now using self modifying code:

   STA L2+1

This takes the low byte of the address of the shape and stores it into the LDA SHAPE instruction. This has the effect of restoring the code to what it was when it was assembled. Rather doing register juggling, this code does an INC L2+1, which changes the instruction to look at the next byte. As long as the shape is less than 256 bytes high (it is) and as long as the shape doesn’t cross a page boundary (it doesn’t), this is totally cool. Disgusting, but cool.

The main loop takes 39 cycles, 663 cycles total, or .64 ms, or 80% of the time of the previous code. There’s still no clipping, but I would have put that in the set up and change the value stored at $02 depending.

Now let’s look at the shape data:


There are 17 bytes of data for the shape. The Apple II displayed 7 bits out of every byte as pixels. The 0th bit was the far left in every group of 7. I lovingly translated that in PhotoShop and made this:

The process of redrawing this from the data took me 5 minutes, I think. When I was 14, I would have drawn this on graph paper and converted it by hand into base 16 using a look up table I had. Later on I wrote a rudimentary paint program that automated this process somewhat.

It’s missing the 0 on the top, which is a one pixel high bar of nothing. So, this shape was being used for drawing a creature that only went top to bottom in one pixel increments, erasing what it left behind as it went. My guess is that this is the creature that shows up every now and again in the attract mode of the game.

I’m Old, Part LXXIX: Taking Care of Coworkers

If you are working in a small office, you will quickly understand how important you all are to each other. When I was at Atalasoft, I often interviewed candidates and part of my routine included time set aside for a candidate to ask me questions. Often, a candidate would ask me what I liked about Atalasoft. Very often, I answered that I felt like on any given day, I felt that I could make the office a better place in a measurable way.

We had a wonderful office manager, Christina. She was a delight to have in the office because she did a lot to keep everything running smoothly and her laugh was sunshine on a rainy day. At one point, Christina was expecting a baby, which is always a wonderful thing. There was a problem, though. Our office was fairly sizeable and Christina’s desk was almost as far as possible from the bathroom in the office as you could be. She often would get to the bathroom and find that it was occupied, leaving her to walk on to the restroom on the floor, even further.

I remember a lunchtime conversation with Christina about her problem with some of the other engineers and we all discussed possible solutions. Many of the more junior engineers were talking web cams pointing at the door and so on. All of them could work, but they were either Baroque, expensive, intrusive or all three.

I talked to Christina and asked if I could be floated about $75 budget to solve her problem. I bought an Arduino, and ethernet shield, a magnetic security switch, some cable and a plastic enclosure. I hooked up the security switch to the bathroom door and ran it into the server room next door. I got a static IP address for the Arduino (they don’t like DHCP) from Jason, and we hooked it into the network. The Arduino ran a teeny tiny webserver that when pinged would return a web page with either “door : open” or “door : closed” for the content. Simple.

Now Christina could ping this web server before she got up to go to the bathroom.

Of course, this wasn’t the end. There is always room for improvement. David T complained about the clunky interface. I invited him to make it better. He wrote a tool tray icon which polled the server periodically and displayed an icon of either an open door or a closed door. Nice. We dubbed the device the AtalaToidy 2000 and we put a label on the enclosure as such.

The point in this is that you and your coworkers are a team. To be a functional team, you need to support each other. To support each other, look for things that you can do to make things easier or better for each other. It can be little things like birthday celebrations or a kind word in a public meeting. It can be bigger things like helping a peer solve a customer’s problem or answering a question for sales. It can be environmental, like putting up Mario Brothers decals or creating the AtalaToidy 2000.  Certainly, what comes around goes around. If you make things better for your coworkers, they will certainly make things better for you.

A Nice Little C# Gem

One of the built-in operators that I like in C# is nameof(). Given an identifier, nameof returns a string which contains the identifier. Typically, it gets used for argument checking:

public void BeExcellent(int input)
    if (input < 0) {
        throw new ArgumentOutOfRangeException (nameof (input));

But there’s more that you can do with nameof(). I saw some code yesterday that looked like this:

public static const string Operator = "Operator";
public static const string Constant = "Constant";
public static const string Expression = "Expression";

Using nameof(), we change it to this:

public static const string Operator = nameof (Operator);
public static const string Constant = nameof (Constant);
public static const string Expression = nameof (Expression);

This nice thing about this is that typos are consistent – if you make a typo in the symbol name, it will be in the string instead of the possibility of there being a typo only in the string or only in the symbol. Also if you need to refactor and change the name of a symbol, you can do everything in one shot with your refactoring tools as it should be.

What surprised me about this is from the way that compilers are built. When you have binding expression of the form type symbol = expr, usually the symbol is hidden from the the expression. It’s a convenience that prevents bad code of referencing an unbound symbol, but in this case it does work and it makes sense for it to work since we don’t care about the value of symbol, just its name.

I’m Old, Part LXXVIII: Code is Data

My second programming language was 6502 assembly. It’s a ttruly inspired and awful processor. It has a tiny number of registers and all of them (save the program counter) are 8 bit. There are a bunch of addressing modes that really aren’t all that useful except in corner cases. There are only two math operations: addition and subtraction. If you want anything else, you’re on your own, branches were only relative for -127 bytes to 128 bytes. The stack was 256 bytes and fixed, so screw you recursion.

If you wanted to move data more than 256 bytes from one location to another, there was really only one proscribed method to do that and that was using an addressing mode called indexed indirect. You had to give up 2 precious bytes in the coveted 0-page (addresses 0-255) which served as a pointer and then you could index off that pointer using only the Y register. After you moved 255 bytes, you could increment one of the 0 page addresses to move onto the next block of 255 bytes.

The problem with this is that this particular addressing mode is slower than most instructions – it takes 5 cycles. If you’re moving memory, that’s 10 cycles for a read and a write and 4 bytes of 0 page used. Whereas regular indexed addressing takes 4 cycles, 8 for a read and write, but like I said, it’s limited to 256 bytes at a time.

Or is it.

For example, if I want to read from location $4000 and write to location $2000, indexed on X, I can write the following code:

0300: BD 00 40 LDA $4000,X
0303: 9D 00 20 STA $2000,X

The thing to realize here is that those three numbers after the 0300 are the values that represent the instruction. It’s data. It’s code. It turns out that if that code is RAM, we can change it on the fly. That 40 in the first line at address 302? Increment it and now you’re addressing memory at location $4100 and on. This is called self-modifying code and if you were coding for the Apple ][, it was de rigeur. Why? Because, like I said, the addressing modes available were not so great and the Apple ][ only ran at a paltry 1.023 MHz and if you were writing a game, it was important to shave cycles where you could and this instruction was 20% faster than the proscribed addressing modes.

It became so natural to me that most of the code that I wrote ended up being self-modifying in some way or another.

Flash forward a few years and I’m in college taking a computer architecture class. The professor was present the Manchester Mark I computer. As presented, the machine had 7 instructions – far fewer than the generous 56 in the 6502. The professor talked about how John von Neumann wrote self-modifying code with a an air of awe and respect. Wait. What? It’s supposed to be hard? No – it’s a tool. A horrible, horrible tool which is what you use when you don’t have.

One of the things that we’ve learned as the field has advanced is that we’re really terrible at managing side-effects in code, so consider the issues of side effects in code modifying code. More than once I had code that ran out of control stomping all over memory because I botched an address in self-modifying code.

Altering 6502 on the fly is one thing. It’s really a small case of code writing code, which is a time-honored tradition. When I was taking a class in automata theory, we had an assignment to write code to generate finite state machines from a description language. The assignment was written to push us to using Scheme for the implementation and the rule was every state had to be a separate program. Since I had enough of Scheme at this point, I decided that I would do this in C. Each state in the state machine read its input from its args in main, did a switch on it and depending on the state transition would pick the next program to execute and set up the right fork/exec pair to make that happen.

So my solution to the assignment read the automata description and wrote a separate C program for every state and compiled them into specific names that each of the other programs knew. Code writing code.

And oddly enough, I’m my current work project involves reading a program, tearing it apart into pieces and then writing code to interact with it in two different programming languages. And my unit tests are typically code that writes more code to test the output of the code written by code.

It’s turtles all the way down.

I’m Old, Part LXXVII: Easter Eggs

While working on Acrobat, there was a tradition of putting in Easter eggs into the about box. For example, in early internal releases on the Mac, if you brought up the Acrobat about box and did an option-click on the contents, it would put up a picture of Homer Simpson play a sound file of Homer saying “D’oh!”.

When I was working on Search for the mac, I did the something similar. When you brought up the about box for the search plug-in and typed ‘homer’, it replaced the contents of the about box with a picture of Homer and played a very long sound file of Homer saying, “I am so smart. I am so smart. I am so smart. S-M-R-T. I mean S-M-A-R-T.”. QA enjoyed this, as I heard it playing more than once.

When it came time for release, we were told that the existing Easter eggs had to go. Fine. I had plans. Evil plans. First, I put in code that if you option-clicked with the about box up would do a slide show of small icons the entire search team with their names. I felt it was important to put this in because so many people worked on the product. When my picture came up, I made my name flash in red and yellow (a la Defender). I removed the picture of Homer and the sound file which were both huge, and replaced them with an implementation of Break Out, but only if you typed in my last name in lower case. Because I cared, the implementation worked in black and white or color and if you moved the window to span multiple monitors of different bit depths, it still looked great. And here’s the kicker: I got all of that to work in under 2K. The beauty of this approach was that the old Easter egg took something like 100K. Removing it and replacing it with 2K was completely invisible as far as the final output was concerned.

Wait a minute – Steve, didn’t you violate the directive from management? They told you to remove the Easter eggs, didn’t they? Why yes. This is true. I did remove the Easter egg. The directive didn’t say “…and don’t add more.” So as far as I’m concerned, I followed the directive. If you’re looking, this Easter egg is in Acrobat Search in version 2, 3, and 4.