Build a B-tree storage engine (SQLite-style) (11 scenes)
Scene 02 · The file is a strip of pages
The DB file is a strip of identical fixed-size pages — every read/write is one whole page because the disk and OS work in pages.
Previously

We need an ordered structure. Before we can build it, we have to know what the storage substrate looks like — and it's not a sea of bytes.

Scene 02
The file is a strip of pages
Diagram
**users.db** is rendered as a horizontal strip of identical rectangles labeled **page 1**, **page 2**, …, **page N** (each 4096 bytes by default). Page 1 carries the **file header** flag. A second lane below shows the OS's own **4 KB blocks**; the two lanes line up at 4 KB and visibly mis-align at 1 KB or 64 KB. A **read-head** lands on a page; one click = one logical disk I/O.
FILEusers.dbpage_size = 4 KBread-headHDRp11p22p33p44p55p66p77p88p99p1010p1111p1212p1313p1414p1515p1616OS BLOCKS · 4 KB each · ALIGNED · 1 page = 1 OS block4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KB4 KBI/O EVENTclick a page to fire one I/OIO COUNT0FILE SIZE (this layout)64 KBBANDWIDTH PER ROW LOOKUP1× (4 KB)an empty strip of identical 4 KB pages — every read or write moves one whole page
The file isn't a soup of bytes — it's a strip of identical pages. Watch the read-head sweep across them; each landing is one disk I/O. Then an INSERT picks exactly ONE page as rowid 42's home.
Implementation
Pager.read_one_row
fetch the WHOLE page; slice the row out in RAM
1def read_one_row(file, page_size, page_no, row_offset):
2 # disk addresses are page numbers, not byte offsets
3 offset = (page_no - 1) * page_size
4 page_buf = read(file, offset, page_size) # one I/O = one page
5
6 # row extraction happens entirely in RAM
7 row = extract_row_from_page_buffer(page_buf, row_offset)
8 return row
Kernel.effective_io
bytes the kernel actually moves for one page read
1def effective_io(row_size, page_size, os_block=4096):
2 if page_size < os_block:
3 # sub-block page: kernel still drags a whole 4 KB block,
4 # SQLite throws the rest away
5 sub = os_block // page_size
6 return os_block # waste = (sub - 1) * page_size per read
7
8 if page_size == os_block:
9 # aligned: one page = one block = one disk transfer
10 return os_block
11
12 # supra-block page: one logical read spans many blocks
13 blocks = page_size // os_block
14 return blocks * os_block # bandwidth = blocks * 4 KB per row