Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Binary radix routing: choose the longest matching prefix

Last updated: 5 Oct 20269 min read
tutorial
IntermediateBy AITrove Editorial

A binary radix routing trie reads a fixed-width key from its most significant bit. Each node has zero and one child links and may hold a gateway for the prefix ending at that depth. A default route is the root's gateway. Lookup walks the key bits and remembers the most recently seen gateway; when a child is absent, that remembered value is the longest matching prefix. The example rejects prefixes with nonzero host bits, preventing two numeric encodings of the same route from being treated as separate entries. It is an uncompressed binary trie, so a route can allocate one node per prefix bit. This route-selection contract differs from exact key membership or maximum-XOR queries over stored integers.

Operational case

The service table has a fallback route, a four-bit north route, an eight-bit depot route, and a twelve-bit pump route. Service ID A947 in hexadecimal follows the pump prefix and returns pump. A983 follows the depot prefix but diverges before the pump-specific branch, so it returns depot. A key outside the north range returns fallback. Replacing a route at the same prefix changes that node's gateway without removing descendants; more-specific routes still win. A missing default route would return None when no stored prefix matches. The model uses sixteen-bit synthetic service identifiers, not address parsing or network-interface configuration.

Working Python program

python
class RouteNode:
    def __init__(self):
        self.children = [None, None]
        self.gateway = None


class ServiceRouteTable:
    def __init__(self, width=16):
        if width < 1:
            raise ValueError("width must be positive")
        self.width = width
        self.root = RouteNode()

    def add(self, prefix, length, gateway):
        if not 0 <= length <= self.width or not 0 <= prefix < 1 << self.width:
            raise ValueError("invalid prefix or length")
        if length < self.width and prefix & ((1 << (self.width - length)) - 1):
            raise ValueError("prefix has nonzero host bits")
        node = self.root
        for offset in range(length):
            bit = (prefix >> (self.width - offset - 1)) & 1
            if node.children[bit] is None:
                node.children[bit] = RouteNode()
            node = node.children[bit]
        node.gateway = gateway

    def lookup(self, service_id):
        if not 0 <= service_id < 1 << self.width:
            raise ValueError("service ID outside configured width")
        node = self.root
        best = node.gateway
        for offset in range(self.width):
            bit = (service_id >> (self.width - offset - 1)) & 1
            node = node.children[bit]
            if node is None:
                break
            if node.gateway is not None:
                best = node.gateway
        return best


routes = ServiceRouteTable()
routes.add(0, 0, "fallback")
routes.add(0xA000, 4, "north")
routes.add(0xA900, 8, "depot")
routes.add(0xA940, 12, "pump")
print("pump=", routes.lookup(0xA947), " depot=", routes.lookup(0xA983),
      " default=", routes.lookup(0x3901), sep="")

Output

Output
pump=pump depot=depot default=fallback

Time, space, and tradeoff

For width W, insertion visits L bits for prefix length L and costs O(L) time, allocating at most L nodes. Lookup visits at most W nodes and costs O(W) time with O(1) extra space. Across R routes, a naive upper bound is O(RW) nodes, although shared prefixes reduce the actual count. Neither operation scans all routes. This program does not delete routes, compress one-child paths, or support concurrent updates. Fixed width is part of its contract: accepting out-of-range keys without validation could silently alias distinct integers after truncation.

Common Mistakes

  • Do not return the first matching route when a deeper prefix may override it.
  • Do not accept a prefix with nonzero bits outside its declared length.
  • Do not confuse a longest-prefix route with exact membership in a bitwise trie.
  • Do not silently truncate an identifier wider than the configured key width.

Connected lessons

Compare lookup and update behavior with Bitmap hash tries: copy paths for immutable alert maps, Ternary search trees: branch by character and continue prefixes, Extendible hashing: split buckets through a shared directory, then run the index audit and contract quiz.

X-fast trie: predecessor and successor in a fixed integer universe adds a related structure with a different operation boundary.

Reduced ordered decision diagrams: share identical rule branches adds a related structure with a different operation boundary.

Patricia binary routing: compress chains without losing prefix matches examines a related structure with a different operation boundary.

data structures
range-query-structures
Storage details