Next: Setters and Getters, Up: Writing Pickles [Contents][Index]
Very often the structure of the data encoded in binary is not very intelligible. This is because it is usual for binary formats to be designed with goals in mind other than being readable by humans: compactness, detail etc.
In our pickle we of course want to provide access to the very finer detail of the data structures. However, we also want for the user to be able to peruse the data visually, and only look at the fine detail on demand.
Consider for example an ID3V1 tag data from some MP3 file. This is
the result of mapping a ID3V1_Tag
:
ID3V1_Tag { id=[0x54UB,0x41UB,0x47UB], title=[0x30UB,0x31UB,0x20UB,0x2dUB,0x20UB,...], artist=[0x4aUB,0x6fUB,0x61UB,0x71UB,0x75UB,...], album=[0x4dUB,0x65UB,0x6eUB,0x74UB,0x69UB,...], year=[0x20UB,0x20UB,0x20UB,0x20UB], data=struct { extended=struct { comment=[0x20UB,0x20UB,0x20UB,0x20UB,0x20UB,...], zero=0x0UB, track=0x1UB } }, genre=0xffUB }
Not very revealing. Fortunately, poke supports pretty printers. If a
struct type has a method called _print
, it will be used by poke
as a pretty printer if the pretty-print option is set:
(poke) .set pretty-print yes
By convention, the output of pretty-printers should always start with
#<
and end with >
. The convention makes it explicit for
the user that everything she sees between #<
and >
is
pretty-printed, and do not necessarily reflect the physical
structure of the data. Also some information may be missing. In
order to get an exact and complete description of the data, the user
should .set pretty-print no
and evaluate the value again at
the prompt.
For example, in the following BPF instructions it is obvious at first sight that the shown register values are pretty-printed:
BPF_Insn = { ... regs=BPF_Regs { src=#<%r3>, dst=#<%r0> } ... }
If the pretty-printed representation spans for more than one line,
please place the opening #<
in its own line, then the lines
with the data, and finally >
in its own line, starting at
column 0.
Example of the MP3 tag above, this time pretty-printed:
#< genre: 255 title: 01 - Eclipse De Mar artist: Joaquin Sabina album: Mentiras Piadosas year: comment: track: 1 >
Let’s say we are writing a pretty-printer method for a struct type that has an optional field. Like for example:
type Packet = struct { byte magic : magic in [MAGIC1,MAGIC2]; byte n; byte[n] payload; if (magic == MAGIC2) PacketTrailer trailer; }
In this case, the struct value will have a trailer
conditionally, which has to be tackled on the pretty-printer
somehow.
An approach that often works good is to replicate the logic in the optional field condition expression, like this:
struct { byte magic : magic in [MAGIC1,MAGIC2]; [...] PacketTrailer trailer if packet_magic2_p (magic); method _print = void: { [...] if (magic == MAGIC2) pretty_print_trailer; } }
This works well in this simple example. In case the expression is big and complicated, we can avoid rewriting the same expression by encapsulating the logic in a function:
fun packet_magic2_p = int: { return magic == MAGIC2; } type Packet = struct { byte magic : magic in [MAGIC1,MAGIC2]; [...] if (packet_magic2_p (magic)) PacketTrailer trailer; method _print = void: { [...] if (packet_magic2_p (magic)) pretty_print_trailer; } }
However, this may feel weird, as the internal logic of the type
somehow leaks its natural boundary into the external function
packet_magic2_p
.
An alternative is to use the following idiom, that checks whether the field actually exists in the struct:
type Packet = struct { byte magic : magic in [MAGIC1,MAGIC2]; [...] if (packet_magic2_p (magic)) PacketTrailer trailer; method _print = void: { [...] try pretty_print_trailer; catch if E_elem {} } }
This approach also works with unions:
type ID3V1_Tag = struct { [...] union { /* ID3v1.1 */ struct { char[28] comment; byte zero = 0; byte track : track != 0; } extended; /* ID3v1 */ char[30] comment; } data; [...] method _print = void: { [...] try print " comment: " + catos (data.comment) + "\n"; catch if E_elem { print " comment: " + catos (data.extended.comment) + "\n"; printf " track: %u8d", data.extended.track; } } }
Next: Setters and Getters, Up: Writing Pickles [Contents][Index]