snaproute Go BGP Code Dive (3)

snaproute Go BGP Code Dive (3)

This week, I want to do a little more housekeeping before we get into actually asking questions of the bgp code. First there is the little matter of an editor. I use two different editors most of the time, Notepad++ and Atom.

  • Notepad++ is a lightweight, general purpose text editor that I use for a lot of different things, from writing things in XML, HTML, CSS, Python Javascript, C, and just about anything else. This is an open source project hosted on the Notepad++ web site with the code hosted at github.
  • Atom is a more GUI oriented “programmer’s editor.” This is a more full featured editor, going beyond basic syntax highlighting into projects, plugins that pull diffs in side by side windows, and the like. I don’t really have a build environment set up right now, so I don’t know how it would interact with compiled code, but I assume it would probably have a lot of the tricks I’m used to, like being able to trace calls through the code, etc. Atom is available here.

I haven’t actually chosen one or the other—I tend to use both pretty interchangeably, so you’re likely to see screen shots from both/either as I move through various bits of the code. There is a second bit of “housekeeping,” I wanted to point out up front how project files are usually structured. This can be a little confusing to folks who haven’t worked on large projects, so this might be helpful.

Code is built from front, or top, of the file to the end, or the back, of the file. To understand, assume I’m going to build a small program that either adds or multiplies two numbers, based on the operator in the arguments. Suppose I want to be able to extend it later, and I don’t like switch statements, so I decide to implement it as three different functions, like this—

int operate(int num1, int num2, int operator) {
if operator == 1 return add(num1, num2);
if operator == 2 return multiply(num1, num2);
}

int add(int num1, int num2) {
return num1 + num2;
}

int multiply(int num1, int num2) {
return num1 * num2;
}

I’ve not dealt with overflows, floating points, and all the rest here—this is just to illustrate a simple principle (I’d normally #define the operators, or build an array and call straight through based on a pointer to the actual operator function—but this isn’t elegant, this is simple). If I actually tried to compile this bit of code, I’d get an error on the line—

if operator == 1 return add(num1, num2);

—telling me “add” isn’t defined. There are two ways I can solve this. The first would be to declare the function someplace. Normally I’d do this in a file that’s included in other files, but with something this simple I might just put a function declaration at the top of the file and leave it at that. The problem with either of these methods is that I must remember to change the declaration if I happen to change the function itself. For instance, if I modify “add” to accept floating point numbers, I need to not only change the function, but the declaration of the function, as well.

Instead of declaring things in this way, what is normally done is to build “helper” and “basic” functions first, then to call them in more complex functions later on in the same file. If you’re going to break up a single project into multiple files, of course, you still have to make certain you declare things you’ve included in one file, and want to use in another. But in many cases, an entire module is only going to have one or two functions that can be called from other files—the majority of the functions are going to be “hidden,” in that they’re only used in the single file in which they’re defined and coded up.

I know that’s a big chunk of text there, but to give an example, the simplest thing here is to do this:

int add(int num1, int num2) {
return num1 + num2;
}

int multiply(int num1, int num2) {
return num1 * num2;
}

int operate(int num1, int num2, int operator) {
if operator == 1 return add(num1, num2);
if operator == 2 return multiply(num1, num2);
}

Now by the time operate is compiled, the functions it relies on have already been compiled—the compiler knows where to find them, and what they actually do. To complete the example, this makes perfect sense if the only function in this file I ever intend to call from anyplace else is operate. I can declare operate in a file that’s included in other files, and just leave add and multiply as local declarations, only usable from within the file operate is defined.

As a practical matter, this means the big chunks of code are going to be at the bottom of any given file. If you’re looking, for instance, for how BGP processes a particular packet in the BGP code, you’re going to find the answer at the bottom of the file containing the function that processes that particular packet. To understand how the function operates, you’re going to need to “trace up” the file, examining each of the “helper functions.”

But when you’re reading code, you need to start at the bottom of the file, not the top.