A jump table is an array of function pointers or even machine instructions, each of which actually performs the jump. The latter is commonly used in assembly language and can be produced by compilers, but the former is something you’re likely to write yourself, often to help simplify some code.
C
Consider the following example in C:
int add(int x, int y) { return x + y; }
int sub(int x, int y) { return x - y; }
int div(int x, int y) { return x / y; }
int mul(int x, int y) { return x * y; }
switch (op) {
case OP_ADD: result = add(x, y); break;
case OP_SUB: result = sub(x, y); break;
case OP_DIV: result = div(x, y); break;
case OP_MUL: result = mul(x, y); break;
}
Now compare to a jump table:
typedef int (*operation_fn)(int, int);
operation_fn operations[] = { add, sub, div, mul };
result = operations[op](x, y);
ssuming of course that OP_ADD is 0, OP_SUB is 1 etc.
Imagine the difference this can make when you have perhaps 40 different operations rather than 4. Now instead of a big switch statement, you only have to maintain your array, which is easier since there’s less boiler plate code around it. An even bigger benefit presents itself when you want to be able to change the function associated with a command at runtime. With a jump table, this is as simple as:
operations[OP_MUL] = matrix_mul;
C#
In C# we use delegates in place of function pointers, but the idea is much the same:
delegate int OperationDelegate(int x, int y);
OperationDelegate[] operations = new OperationDelegate[] { Add, Sub, Div, Mul };
result = operations[op](x, y);
The idea is that a command of some form can be used as an index into an array of function pointers or delegates or jumps to the function which handles that command. You can also use a hash table to associate a function pointer with an arbitrary integer or even any other value of any other type. For example, in C#:
Dictionary<string, OperationDelegate> operations = new Dictionary<string, OperationDelegate>() {
{ "add", Add },
{ "sub", Sub },
{ "div", Div },
{ "mul", Mul } };
string op = "mul";
result = operations[op](x, y);
Real Example
I’ll give you a real example of a jump table. The Linux kernel has something called the sys_call_table, which you can find in [1] sys_call_table.c. It looks like this:
#define __SYSCALL(nr, call) [nr] = (call),
void *sys_call_table[__NR_syscalls] = {
#include <asm/unistd.h>
};
You can see that it’s using an include file to populate the table. The contents of unistd.h are [2] here, and you can see that it then just includes [3] this one.
You can see that it contains lots of lines like this:
#define __NR_io_setup 0 __SYSCALL(__NR_io_setup, sys_io_setup) #define __NR_io_destroy 1 __SYSCALL(__NR_io_destroy, sys_io_destroy) #define __NR_io_submit 2 __SYSCALL(__NR_io_submit, sys_io_submit) #define __NR_io_cancel 3 __SYSCALL(__NR_io_cancel, sys_io_cancel) #define __NR_io_getevents 4 __SYSCALL(__NR_io_getevents, sys_io_getevents)
So this all expands to:
void *sys_call_table[__NR_syscalls] = {
[__NR_io_setup] = sys_io_setup,
[__NR_io_destroy] = sys_io_destroy,
[__NR_io_submit] = sys_io_submit,
[__NR_io_cancel] = sys_io_cancel,
[__NR_io_getevents] = sys_io_getevents,
};
And of course, those __NR_ defines are numbers, the indices of the jump table. So when you make a system call on Linux, you kernel looks up the system call function from the system call number something like this:
... = sys_call_table[__NR_something](...);
0 Comments.