[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Interesting poke idiom: handling sparse tables
From: |
Jose E. Marchesi |
Subject: |
Interesting poke idiom: handling sparse tables |
Date: |
Sat, 28 Jan 2023 02:14:24 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) |
Hi people!
During tonight call, our friend hdzki came with an interesting use case.
He is poking at some binary structures that are like sparse tables whose
entries are distributed in the file in an arbitrary way.
Each sparse table is characterized by an array of consecutive non-NULL
pointers. Each pointer points to an entry in the table. The table
entries can be anywhere in the IO space, and are not necessarily
consecutive, nor be in order.
Example of a table with three entries:
IO space
| |
+-->| item2 |
| | |
| : ... :
| | |
| +----------------+ <------- table
| | ptr1 |------+
+---| ptr2 | |
| ptr3 |---+ |
+----------------+ | |
| | | |
| | | |
: ... : | |
| | | |
| item3 |<- + |
| | |
: ... : |
| | |
| item1 |<-----+
| |
: ... :
How to poke at this?
First, we define the things themselves. This can be any structure, but
this will serve for this example:
type Thing =
struct
{
byte control;
string data;
};
Then, the pointers, which are 32-bit long, in bytes, and not NULL:
type Pointer =
struct
{
offset<uint<32>,B> ptr : ptr != 0#B;
};
Then the sparse table itself:
type Sparse_Table =
struct
{
Pointer[] ptrs;
computed Thing[] items;
method get_items = Thing[]:
{
var items = Thing[]();
for (p in ptrs)
apush (items, Thing @ p.ptr);
return items;
}
method _print = void:
{
printf "%v", items;
}
};
Interesting things to note:
- The array of pointers will cover every consecutive 32-bit pointer
until the first NULL pointer, exclusive. This is achieved by having
the constraint in the Pointer struct. Remember that unbounded array
types are mapped until either EOF is encountered or a constraint
fails.
- The computed field `items' constructs a _non-mapped_ array that
contains mapped Things at the right place. This is an interesting
example where poke's ability of mixing mapped and non-mapped values
transparently really pays back.
- The pretty-printer helps to visualize the items stored in the sparse
table. However, we follow the poke convention of enclosing
pretty-printed values between #<...> so the casual user of the
definitions above won't be induced to thing that a sparse table is an
array: it is not.
Let's see this in action. First, we open an all-zeroed memory IO space
as our play ground:
(poke) .mem foo
Then, lets plant a few Things around at some memorable offsets and the
table of consecutive pointers at offset 0:
(poke) Thing @ 100#B = Thing { control = 1UB, data = "one" }
(poke) Thing @ 200#B = Thing { control = 2UB, data = "two" }
(poke) Thing @ 300#B = Thing { control = 3UB, data = "three" }
(poke) Pointer[] @ 0#B = [Pointer { ptr = 100#B }, Pointer { ptr = 200#B },
Pointer { ptr = 300#B }]
And finally let's map our sparse table:
(poke) var table = Sparse_Table @ 0#B
(poke) table
#<[Thing {control=1UB,data="one"},Thing {control=2UB,data="two"},Thing
{control=3UB,data="three"}]>
At this point, we can access the items comfortably by using the computed
field. Since the array elements are mapped, they will always show
actual contents, and modifying them modifies the underlying bytes in the
IO space:
(poke) table.items[1].data = "quux"
(poke) Thing @ 200#B
Thing {
control=2UB,
data="quux"
}
Disabling the pretty-printer we see the crude reality of what a
Sparse_Table structure really is, a sequence of pointers:
(poke) .set pretty-print no
(poke) table
Sparse_Table {
ptrs=[Pointer {
ptr=100U#B
},Pointer {
ptr=200U#B
},Pointer {
ptr=300U#B
}]
Salud!
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Interesting poke idiom: handling sparse tables,
Jose E. Marchesi <=