Nodes reach each other through a pager, never directly. Today you build the smallest pager - one that hands out page ids and reads and writes whole page buffers - so the tree can allocate and revisit pages without knowing whether they live in RAM or a file.
Build an in-memory pager whose AllocPage hands out increasing ids and whose ReadPage returns what WritePage stored.
Every access to a node will go through a pager: allocate a fresh page, read
a page by id, write a page by id, and (later) free one. Putting this behind an
interface is the pivot the whole project turns on - the core chapters use a
simple in-memory pager, and the on-disk chapter swaps in a file-backed one with
no change to the tree. Because the tree only ever speaks AllocPage / ReadPage
/ WritePage, moving to disk is a pager swap, not a rewrite.
The in-memory pager is deliberately dull: keep a slice of page buffers, let
AllocPage return the next index and grow the slice, and let ReadPage /
WritePage index into it. Ids start at 0 and increase. That id-to-slot mapping is
exactly the id-to-offset mapping a file pager will use later, just with a slice
standing in for the file.
type Pager interface {AllocPage() PageIDReadPage(id PageID) []byteWritePage(id PageID, buf []byte)FreePage(id PageID) // used later}// in-memory impl: a slice of page buffers, next id = lentype memPager struct { pages [][]byte }