Designing the Macros

Since you already have a rough idea what code your macros will need to generate, the next step, according to the process for writing a macro I outlined in Chapter 8, is to switch perspectives and think about what a call to the macro should look like. Since the goal is to be able to write something as compressed as the pseudocode in the ID3 specification, you can start there. The header of an ID3 tag is specified like this:

  1. ID3/file identifier "ID3"
  2. ID3 version $02 00
  3. ID3 flags %xx000000
  4. ID3 size 4 * %0xxxxxxx

In the notation of the specification, this means the “file identifier” slot of an ID3 tag is the string “ID3” in ISO-8859-1 encoding. The version consists of two bytes, the first of which—for this version of the specification—has the value 2 and the second of which—again for this version of the specification—is 0. The flags slot is eight bits, of which all but the first two are 0, and the size consists of four bytes, each of which has a 0 in the most significant bit.

Some information isn’t captured by this pseudocode. For instance, exactly how the four bytes that encode the size are to be interpreted is described in a few lines of prose. Likewise, the spec describes in prose how the frame and subsequent padding is stored after this header. But most of what you need to know to be able to write code to read and write an ID3 tag is specified by this pseudocode. Thus, you ought to be able to write an s-expression version of this pseudocode and have it expanded into the class and function definitions you’d otherwise have to write by hand—something, perhaps, like this:

  1. (define-binary-class id3-tag
  2. ((file-identifier (iso-8859-1-string :length 3))
  3. (major-version u1)
  4. (revision u1)
  5. (flags u1)
  6. (size id3-tag-size)
  7. (frames (id3-frames :tag-size size))))

The basic idea is that this form defines a class id3-tag similar to the way you could with **DEFCLASS**, but instead of specifying things such as :initarg and :accessors, each slot specification consists of the name of the slot—file-identifier, major-version, and so on—and information about how that slot is represented on disk. Since this is just a bit of fantasizing, you don’t have to worry about exactly how the macro define-binary-class will know what to do with expressions such as (iso-8859-1-string :length 3), u1, id3-tag-size, and (id3-frames :tag-size size); as long as each expression contains the information necessary to know how to read and write a particular data encoding, you should be okay.