Lesson 1 of 10

Bytes have positions. Meaning comes from interpretation.

Estimated reading
~4 min
Exercises / exploration
Additional time; not yet estimated
Activity type
Model · Python snippets

Small Python models illustrate rules. Reading them is enough; they are not performance measurements.

Available here: complete text, examples and worked answers.

A network message, an image, and a model weight file all become bytes. That does not make their contents interchangeable. To interpret a sequence, you need rules: where a value starts, how many bytes it occupies, and what those bytes mean. This is the first step toward understanding parsers, memory layout, and data movement.

You will decode a small field, calculate an element's address, and explain why a correct value can become nonsense when two components disagree about representation. You need basic programming, not prior hardware knowledge.

From a bit to a field

A bit has two possible values, 0 and 1. In the systems covered here, a byte contains eight bits, giving 256 possible patterns. An unsigned byte can represent the integers 0 through 255. A larger integer needs multiple bytes; a text character is interpreted through an encoding and does not necessarily occupy one byte.

Suppose a message begins with two bytes, hexadecimal 01 02. Hexadecimal is a compact notation using sixteen symbols: 0–9 followed by a–f. Two hex digits describe one byte. Our bytes have numeric values 1 and 2.

Which integer do they represent? There is no answer until we specify byte order:

InterpretationCalculationResult
Big-endian: most significant byte first1 × 256 + 2258
Little-endian: least significant byte first1 + 2 × 256513

Neither representation changes the bytes. The interpretation changes. A protocol can choose an order independent of the CPU on which the parser runs.

Decode deliberately

Python · model, not a measurement · Run downloads Pyodide from jsDelivr

Tab indents. Press Escape, then Tab to leave the editor.

H asks for an unsigned two-byte value under these standard-size formats. > selects big-endian and < little-endian. unpack returns a tuple, so [0] selects the one decoded field. Specifying a format is safer than assuming the host's native layout matches a file or protocol. [1]

Expected output is 0102 258 513. Change the two byte values in the standalone snippet, predict both interpretations, and compare with the output. Optional local reproduction uses Python 3.12, with no packages or network required.

An address identifies a location

For a contiguous array with base address 1,000 and four-byte elements, element index i starts at 1000 + 4*i. The first three starts are 1,000, 1,004, and 1,008. “Contiguous” means the representations are placed next to one another without gaps between these array elements.

This formula assumes a particular representation. A Python list of integers is not a packed array of four-byte integers. Using 4 * len(values) as its actual memory consumption would silently substitute a different data structure for the one you have.

IndexStart address in our layoutOccupied byte positions
01,0001,000–1,003
11,0041,004–1,007
21,0081,008–1,011

We will distinguish virtual addresses from physical memory later. For now, the layout calculation only needs a consistent address space and element size.

Challenge: parse a length safely

A message uses a two-byte big-endian length followed by that many payload bytes. You receive 00 03 61 62. Is the message complete? Would interpreting the header as little-endian fix it?

Hint Count the payload separately from its header.
Worked answer The header declares three payload bytes, but only two follow. The message is incomplete. Little-endian would declare 768 bytes and still violate the specified protocol. A parser must follow the agreed format, bound the accepted length, and handle incomplete input rather than reinterpret it until something seems convenient.

Transfer question: what is element 7's start address in the example array? It is 1,028, not 1,007. The index counts elements; the address arithmetic counts bytes. This distinction will explain why a stride can skip useful neighboring data.

Next, follow how long data remains valid. Correctly locating bytes is useful only while the storage still represents the intended object.

Where did we lose you?

Tell us which explanation needs another step or which result surprised you. We'll see which lesson this is about.

Sign in to send feedback