Skip to content

MapLibre Tile Specification v1

This document specifies the byte layout of an MLT v1 layer. The data model it assumes, tiles, extents, layers, features, columns and streams, is described in the Overview.

Only implemented features are specified here. Complex and nested property types, logical types, linear referencing, vertex-scoped m-values and separate tileset metadata were designed for v1 but never implemented. They are being redesigned for MLT v2.

Everything below is normative. All integers are VarInt-encoded unless stated otherwise. VarInt is Protobuf's base 128 varint, least significant group first. Signed varints are ZigZag-encoded before being written.

Tile Layout

A tile is a concatenation of tagged layer records. There is no tile header. Layers are encoded independently and can be concatenated without re-encoding.

tile   := layer*
layer  := [varint size] [u8 tag] [u8 body[size - 1]]
  • size counts the tag byte plus the body, so the next layer starts size bytes after the size varint.
  • tag names the format the body is written in. 0x01 is the format specified on this page.
  • A decoder MUST skip a layer whose tag it does not recognize, using size to find the next one.

A decoder MUST NOT assume layer names are unique or ordered.

Layer Body

A v1 layer body is a header, then every column's metadata, then every column's data:

body := [string name]
        [varint extent]
        [varint column_count]
        column_meta * column_count
        column_data * column_count

Strings are a VarInt byte length followed by that many UTF-8 bytes:

---
config:
  class:
    hideEmptyMembersBox: true
title: StringsSchema
---
classDiagram
direction TB
    class String {
      +VarInt length
      +u8 bytes[length] %% encoding is always UTF-8
    }

name MUST NOT be empty.

extent defines the coordinate space size for the tile's geometry and MUST NOT be zero. Geometry coordinates are signed integers in vector-tile grid coordinates, typically near the 0..=extent range but not restricted to it. Values MAY be negative or exceed extent for geometry that crosses tile boundaries. Encoders MAY default to 4096 when a user does not specify the extent.

Metadata and data are in two separate sections, both in column_count order.

A layer MUST contain exactly one geometry column and at most one id column.

Column Metadata

Each column's metadata is a single type byte, optionally followed by a name and, for shared dictionaries, its children:

column_meta := [u8 column_type]
               [string name]                              -- unless the type is Id or Geometry
               [varint child_count] [child_meta*]         -- SharedDict only
child_meta  := [u8 column_type] [string name]

Id and Geometry columns are named implicitly by their type and carry no name field.

Column Types

The low bit of the type byte is the nullability flag: an odd code is the nullable variant of the even code below it. A nullable column writes a Present stream before its data.

Code Type Nullable variant Description
0 Id 1 (OptId) Feature id, up to 32 bits
2 LongId 3 (OptLongId) Feature id, up to 64 bits
4 Geometry - The layer's geometry column, never nullable
10 Bool 11
12 Int8 13
14 UInt8 15
16 Int32 17
18 UInt32 19
20 Int64 21
22 UInt64 23
24 Float 25 IEEE 754 binary32
26 Double 27 IEEE 754 binary64
28 String 29 UTF-8
30 SharedDict - A string dictionary and the columns that index into it

Codes 5-9 and 31 are unassigned. A decoder MUST reject a column whose type byte is unassigned.

Column Data

What follows in the data section depends on the column's type. Every [stream] below is one stream, laid out as described under Streams.

Column type Data layout
Id, LongId [data stream]
Geometry [varint stream_count] [types stream] [stream * (stream_count - 1)]
Scalar (BoolDouble) [data stream]
String [varint stream_count] [stream * stream_count]
SharedDict see Shared Dictionary Columns

A nullable column prefixes its data with a Present stream, which is counted in stream_count where one is present.

Streams

A logical column is separated into several physical streams (sub-columns), inspired by the ORC file format. These streams are stored contiguously. A stream is a sequence of values of a known length in a continuous memory chunk, all sharing the same type.

Each stream is a header followed by its payload:

stream := [u8 stream_type]        category (bits 7-4) | subtype (bits 3-0)
          [u8 encoding]           logical1 (bits 7-5) | logical2 (bits 4-2) | physical (bits 1-0)
          [varint num_values]
          [varint byte_length]
          [varint runs]           RLE streams only, except Present streams
          [varint num_rle_values] RLE streams only, except Present streams
          [varint bits]           Morton streams only
          [varint shift]          Morton streams only
          [u8 payload[byte_length]]

num_values is the number of values the stream holds after decoding. byte_length is the payload size and allows a decoder to skip the stream.

A Present stream is always boolean and derives its RLE parameters from num_values.

Stream Types

The stream_type byte names what role the stream plays. The high nibble is the category and the low nibble is a category-specific subtype.

Category Code Subtypes
Present 0x0 -
Data 0x1 0 None, 1 Single dictionary, 2 Shared dictionary, 3 Vertex, 4 Morton, 5 FSST
Offset 0x2 0 Vertex, 1 Index, 2 String, 3 Key
Length 0x3 0 VarBinary, 1 Geometries, 2 Parts, 3 Rings, 4 Triangles, 5 Symbol, 6 Dictionary

What each category holds:

  • Present: enables efficient encoding of sparse columns by indicating value presence via a bit flag. Omitted when the column is not nullable.
  • Data: the actual column data - boolean, int, float or string values, dictionary entries, or geometry coordinates. For fixed-size data types this is the only required stream besides the optional Present stream.
  • Length: the number of elements for variable-sized data types like strings or rings.
  • Offset: offsets into a data stream when using dictionary encoding, for strings or vertices.

Encoding Byte

The encoding byte gives the encoding of the payload. The two logical fields form one combination. Any combination not listed below is invalid.

Combination logical1 (bits 7-5) logical2 (bits 4-2) Meaning
None 000 000 Values as they are
Delta 001 000 ZigZag deltas between consecutive values
DeltaRle 001 011 Delta, then RLE
ComponentwiseDelta 010 000 Deltas per coordinate component, for vertex streams
Rle 011 000 Run-length encoded
Morton 100 000 Morton (Z-order) codes, for vertex streams
MortonDelta 100 001 Deltas between Morton codes
MortonRle 100 011 RLE over Morton codes

The physical field (bits 1-0) says how the resulting integers are laid out in bytes:

Code Physical
00 None - fixed-width little-endian words
01 SIMD-FastPFOR, 256-value big-endian blocks
10 VarInt

RLE streams store all run lengths first, then all values, and carry runs and num_rle_values in the header.

The algorithms themselves are specified in Encoding Definitions.

Property Columns

ID Column

An id column is not mandatory. If included, it should be UInt64 or narrower (UInt32 if possible) for MVT compatibility. A narrower type enables the use of efficient encodings like SIMD-FastPFOR.

Scalar Columns

Boolean, integer and floating-point columns are a Present stream when nullable, then one data stream. Boolean data streams are bit-packed, one bit per present value; float and double data streams hold fixed-width IEEE 754 words.

String Columns

A string column declares how many streams follow, and the number of streams determines its layout:

Streams Layout Stream order
2 Plain Length/VarBinary, Data/None
3 Dictionary Length/Dictionary, Offset/String, Data/Single
4 FSST Length/Symbol, Data/FSST, Length/Dictionary, Data/Single
5 FSST dictionary Length/Symbol, Data/FSST, Length/Dictionary, Data/Single, Offset/String

A nullable column's Present stream precedes these and is counted in stream_count.

In the plain layout the Length stream holds the byte length of each present value and the Data stream holds their UTF-8 bytes back to back. In a dictionary layout the Offset/String stream holds one dictionary index per present value, and the Length and Data streams describe the distinct values. FSST layouts compress the value bytes with a symbol table; see FSST.

The offset stream comes last in the 5-stream layout and before the data stream in the 3-stream layout. An encoder MAY also use the 5-stream layout for an undeduplicated FSST corpus, writing the identity [0, 1, 2, …] as its offsets.

Shared Dictionary Columns

Several string columns, such as name:en, name:de and name:fr, can share a single dictionary.

shared_dict := [varint stream_count]
               [dictionary streams]     2 (plain) or 4 (FSST), as in the table above
               child * child_count      child_count comes from the column metadata
child       := [varint stream_count]
               [present stream]         only when the child's type is nullable
               [offset stream]          one dictionary index per present value

stream_count on the column counts every stream that follows: the dictionary streams, one offset stream per child, and one present stream per nullable child. The dictionary streams end at the Data/Single or Data/Shared stream. The children begin after it.

Note

Some tiles in the wild were written with stream_count one too high, by an encoder bug since fixed. Decoders SHOULD accept expected + 1 as well so those files still parse.

Each child's own stream_count is 1, plus 1 when it is nullable.

Geometry Column

The geometry column uses a Structure of Arrays (SoA) layout (data-oriented design). The x, y coordinates are stored interleaved in a VertexBuffer for efficient CPU processing and direct copying to GPU buffers.

The geometry information is separated into different streams, partly inspired by the geoarrow specification. This separation enables better compression optimization and faster processing. Pre-tessellated polygon meshes can also be stored directly to avoid runtime triangulation.

A geometry column can consist of the following streams:

Stream Name Data Type Encoding Mandatory
GeometryType Byte Integer
NumGeometries UInt32 Integer
NumParts UInt32 Integer
NumRings UInt32 Integer
NumTriangles UInt32 Integer
IndexBuffer UInt32 Integer
VertexOffsets UInt32 Integer
VertexBuffer Int32 or Vertex[] Plain, Dictionary, Morton

Depending on the geometry type, the following streams are used in addition to GeometryType:

  • Point: VertexBuffer
  • LineString: NumParts, VertexBuffer
  • Polygon: NumParts (Polygon), NumRings (LinearRing), VertexBuffer
  • MultiPoint: NumGeometries, VertexBuffer
  • MultiLineString: NumGeometries, NumParts (LineString), VertexBuffer
  • MultiPolygon: NumGeometries, NumParts (Polygon), NumRings (LinearRing), VertexBuffer

When LineString and Polygon types are mixed in the same column, LineString vertex counts are stored in the NumRings stream (see Length Stream Encoding Rules below).

An additional VertexOffsets stream is present when using Dictionary or Morton-Dictionary encoding. If geometries (mainly polygons) are pre-tessellated for direct GPU use, NumTriangles and IndexBuffer streams must be provided.

Geometry Types

Six geometry types are supported, encoded as unsigned integers:

Value Type Description
0 Point Single coordinate
1 LineString Sequence of coordinates forming a line
2 Polygon Closed rings (exterior + optional interior holes)
3 MultiPoint Collection of points
4 MultiLineString Collection of line strings
5 MultiPolygon Collection of polygons

Binary Structure

A geometry column is stored as a sequence of streams, prefixed by a stream count:

graph LR
    subgraph "Stream Count"
        SC[varint]
    end

    subgraph "Meta Stream"
        MS[geometry types]
    end

    subgraph "Topology Streams"
        TS[lengths/offsets]
    end

    subgraph "Vertex Stream"
        VS[coordinates]
    end

    SC --> MS --> TS --> VS

The streams listed above map to the following physical and logical stream types:

Physical Type Logical Type Specification Name Content
DATA NONE GeometryType Geometry type per feature (see Geometry Types)
LENGTH GEOMETRIES NumGeometries Number of sub-geometries in Multi* types
LENGTH PARTS NumParts Number of rings per polygon or lines per multi-linestring
LENGTH RINGS NumRings Number of vertices per ring or per linestring segment
LENGTH TRIANGLES NumTriangles Number of triangles per polygon (tessellated)
OFFSET INDEX IndexBuffer Triangle vertex indices (tessellated)
OFFSET VERTEX VertexOffsets Indices into vertex dictionary
DATA VERTEX VertexBuffer Vertex coordinates (x, y pairs)
DATA MORTON VertexBuffer Morton-encoded vertex coordinates

Topology Encoding

MLT uses a length-based encoding for geometry topology rather than explicit drawing commands. This enables efficient random access to individual features.

Conceptual Hierarchy

graph TD
    subgraph "Feature Table"
        GT["Geometry Types Stream<br/>[Point, Polygon, MultiPolygon, ...]"]
    end

    subgraph "Level 1: Geometries"
        GL["Geometries Length Stream<br/>sub-geometry counts<br/>for Multi* types"]
    end

    subgraph "Level 2: Parts"
        PL["Parts Length Stream<br/>ring counts per polygon"]
    end

    subgraph "Level 3: Rings"
        RL["Rings Length Stream<br/>vertex counts per ring"]
    end

    subgraph "Vertex Data"
        VB["Vertex Buffer<br/>[x₀,y₀, x₁,y₁, ...]"]
    end

    GT --> GL --> PL --> RL --> VB

Which Streams Are Present

The streams included depend on the geometry types in the column:

Geometry Type Geometries Parts Rings
Point - - -
MultiPoint - -
LineString - ✓* -
MultiLineString ✓* -
Polygon -
MultiPolygon

*LineString and MultiLineString parts are stored in the Rings stream when Polygons are also present in the same column.

Length Stream Encoding Rules

Length values are stored only for geometry types that need them. The key insight is that simple types have an implicit count of 1, while Multi* types store their sub-geometry counts explicitly.

The encoding uses a type threshold to determine which geometries need explicit lengths:

Stream Threshold Types Needing Explicit Length
geometry_offsets Polygon MultiPoint, MultiLineString, MultiPolygon
part_offsets (Rings stream present) LineString Polygon, MultiPoint, MultiLineString, MultiPolygon
part_offsets (no Rings stream) Point LineString

Rule: If a geometry type's value is greater than the threshold type's value (see Geometry Types table), store its length explicitly. Otherwise, the length is implicitly 1.

Example: A column with [Point, MultiPolygon, Polygon] geometry types:

Encoded geometries lengths: [3]     // Only MultiPolygon needs explicit count
                                    // (MultiPolygon=5 > Polygon=2)

Decoding to offsets:
  Point:        implicit 1  -> offset 0->1   (Point=0, not greater than Polygon=2)
  MultiPolygon: explicit 3  -> offset 1->4   (MultiPolygon=5 > Polygon=2, read from stream)
  Polygon:      implicit 1  -> offset 4->5   (Polygon=2, not greater than Polygon=2)

Result: geometry_offsets = [0, 1, 4, 5]

Vertex Encoding

Componentwise Delta Encoding

Vertices are stored as interleaved (x, y) coordinate pairs using componentwise delta encoding:

graph LR
    subgraph "Original Coordinates"
        C["(100,200), (105,210), (102,215)"]
    end

    subgraph "Delta per Component"
        D["ΔX: 100, 5, -3<br/>ΔY: 200, 10, 5"]
    end

    subgraph "Zigzag Encoded"
        Z["X: 200, 10, 5<br/>Y: 400, 20, 10"]
    end

    subgraph "Interleaved Output"
        O["200, 400, 10, 20, 5, 10"]
    end

    C --> D --> Z --> O

Encoding steps:

  1. Track previous X and previous Y separately (both start at 0)
  2. For each vertex, compute delta_x = x - prev_x and delta_y = y - prev_y
  3. Apply zigzag encoding: zigzag(n) = (n << 1) ^ (n >> 31) (maps negatives to positives)
  4. Output as varint: [zigzag(Δx₀), zigzag(Δy₀), zigzag(Δx₁), zigzag(Δy₁), ...]

Decoding steps:

  1. Read varints in pairs
  2. Apply zigzag decoding: n = (zigzag >> 1) ^ -(zigzag & 1)
  3. Accumulate: x = prev_x + delta_x, y = prev_y + delta_y

Dictionary Encoding (Optional)

When vertices repeat frequently, a dictionary encoding may be used:

graph TD
    subgraph "With Dictionary"
        VO["Vertex Offsets<br/>[0, 1, 2, 1, 0, 2]"]
        VD["Vertex Dictionary<br/>[(0,0), (10,10), (20,20)]"]
    end

    subgraph "Resolved Vertices"
        RV["(0,0), (10,10), (20,20), (10,10), (0,0), (20,20)"]
    end

    VO --> RV
    VD --> RV

The vertex dictionary is sorted by Hilbert curve index for spatial locality. The OFFSET/VERTEX stream contains indices into this dictionary.

Morton Encoding (Optional)

For spatial optimization, vertices can be Morton-encoded (Z-order curve):

Coordinate (5, 3):
  X bits: 1 0 1
  Y bits: 0 1 1
  Interleaved: 01 10 11 = 011011₂ = 27

Stored as: Morton code + metadata (numBits, coordinateShift)

Morton encoding enables efficient spatial clustering and range queries.

Integer Stream Encoding

Topology streams (lengths, offsets) use adaptive encoding selected for minimal size. All integer encodings use variable-length quantity (varint) as the base encoding:

Logical Encoding Format Best For
None Plain varints Random values
Delta Delta + zigzag + varint Monotonic sequences
RLE Run-length encoded Repeated values
DeltaRLE Delta + RLE Constant increments

RLE Format

Run-length encoding stores runs followed by values:

Input:  [5, 5, 5, 3, 3, 3, 3]
Runs:   [3, 4]        // 3 fives, 4 threes
Values: [5, 3]
Output: [3, 4, 5, 3]  // runs concatenated with values

Stream metadata includes runs and num_rle_values counts for decoding.

Tessellation Data (Optional)

Pre-tessellated polygons include additional streams for direct GPU rendering:

graph TD
    subgraph "Tessellated Polygon Data"
        TRI["Triangles Stream<br/>(LENGTH/TRIANGLES)"]
        IDX["Index Buffer<br/>(OFFSET/INDEX)"]
        VTX["Vertices<br/>polygon vertices"]
    end

    TRI --> |"how many"| GPU[GPU Rendering]
    IDX --> |"which vertices"| GPU
    VTX --> |"coordinates"| GPU

Example - A square polygon tessellated into 2 triangles:

Vertices: [(0,0), (100,0), (100,100), (0,100)]
Triangles: [2]                    // This polygon has 2 triangles
Index buffer: [0, 1, 2, 0, 2, 3]  // Triangle 1: v0,v1,v2; Triangle 2: v0,v2,v3

Decoding Examples

Point

Streams:
  META: types = [0]          // Point
  DATA/VERTEX: [200, 400]    // zigzag-encoded deltas

Decode:
  zigzag(200) = 100, zigzag(400) = 200
  Result: Point(100, 200)

LineString

Streams:
  META: types = [1]                 // LineString
  LENGTH/PARTS: [3]                 // 3 vertices
  DATA/VERTEX: [0,0, 200,0, 0,200]  // encoded deltas

Decode:
  part_offsets = [0, 3]                   // from lengths
  vertices = [(0,0), (100,0), (100,100)]  // delta-decoded
  Result: LineString with 3 vertices

Polygon with Hole

Streams:
  META: types = [2]          // Polygon
  LENGTH/PARTS: [2]          // 2 rings (exterior + 1 hole)
  LENGTH/RINGS: [4, 4]       // 4 vertices each ring
  DATA/VERTEX: [...]         // 8 vertices total

Decode:
  part_offsets = [0, 2]      // 1 polygon with 2 rings
  ring_offsets = [0, 4, 8]   // ring boundaries
  Result: Polygon with exterior ring (4 verts) and hole (4 verts)

MultiPolygon

Streams:
  META: types = [5]          // MultiPolygon
  LENGTH/GEOMETRIES: [2]     // 2 polygons
  LENGTH/PARTS: [1, 1]       // 1 ring each
  LENGTH/RINGS: [4, 4]       // 4 vertices each
  DATA/VERTEX: [...]         // 8 vertices total

Decode:
  geometry_offsets = [0, 2]  // spans indices 0-1 in parts
  part_offsets = [0, 1, 2]   // each polygon has 1 ring
  ring_offsets = [0, 4, 8]   // vertex boundaries
  Result: MultiPolygon with 2 simple polygons

Mixed Geometry Columns

A single column can contain mixed geometry types. The decoding logic handles this by:

  1. Reading geometry types to determine which length streams to expect
  2. Iterating through features, applying type-specific offset rules
  3. Using implicit length=1 for simple types (Point, LineString, Polygon) and reading explicit lengths for Multi* types

Example: [Point, LineString, Polygon] in one column:

Streams:
  META: types = [0, 1, 2]
  LENGTH/PARTS: [1]          // Polygon: 1 ring
  LENGTH/RINGS: [3, 4]       // LineString: 3 verts, Polygon ring: 4 verts
  DATA/VERTEX: [...]         // 1 + 3 + 4 = 8 vertices

Decode:
  Point: vertices[0]
  LineString: vertices[1..4] // from ring_offsets (Polygon present)
  Polygon: vertices[4..8]    // from ring_offsets

The integer and vertex encodings referenced above are specified in detail in the Encoding Definitions document.

Choosing Encodings

MLT uses various lightweight compression schemes for space-efficient storage and fast decoding. Encodings can be recursively cascaded (hybrid encodings) to a certain degree. For example, integer columns resulting from dictionary encoding can be further compressed using integer encoding schemes.

The following encoding pool was selected based on analysis of compression ratio and decoding speed on test datasets like OpenMapTiles and Bing Maps tilesets.

Data Type Logical Level Technique Physical Level Technique
Boolean Boolean RLE
Integer Plain, RLE, Delta, Delta-RLE SIMD-FastPFOR, Varint
Float Plain, RLE
String Plain, Dictionary, FSST, FSST Dictionary
Geometry Plain, Dictionary, Morton-Dictionary

SIMD-FastPFOR is generally preferred over Varint encoding due to its smaller output and faster decoding speed. Varint encoding is included mainly for compatibility and simplicity, and it can be more efficient when combined with heavyweight compression like GZip.

A brute-force search for the best encoding scheme is too costly. Instead, we recommend the selection strategy from the BTRBlocks paper:

  • Calculate data metrics to exclude unsuitable encodings early (e.g., exclude RLE if the average run length is less than 2).
  • Use a sampling-based algorithm: randomly select parts of the data totaling ~1% of the full dataset and apply the candidate encodings from step 1. Choose the scheme that produces the smallest output.

Choosing the right column to sort features by can also significantly reduce the size of a layer, and is crucial for leveraging the columnar layout fully. Exhaustively testing every possible sorting order for every column in every layer is computationally expensive, so the same sampling heuristic applies.

Examples

Every fixture under test/synthetic/0x01/ has an annotated hexdump and the .json a decoder MUST produce from it. The annotations are generated from the tiles.

The same annotation can be produced for any tile with the mlt CLI:

mlt hexdump path/to/tile.mlt

One point with one id: the tile envelope, the column metadata section and the data section.

00000000                                                                     | layer[0] (30 B)
00000000  1d                                                .                |   size: 29 (varint) - tag + body
00000001  01                                                .                |   tag: 0x01 -> Tag01
00000002  06 6c 61 79 65 72 31                              .layer1          |   name: "layer1"
00000009  50                                                P                |   extent: 80
0000000a  02                                                .                |   column_count: 2
0000000b                                                                     |   schema (2 B)
0000000b                                                                     |     column[0] (1 B)
0000000b  00                                                .                |       type: 0x00 Id
                                                                             |         └ bits 7-1 = 0000000 -> base type = Id
                                                                             |         └ bit 0 = 0 -> not optional: each feature has a non-NULL value
0000000c                                                                     |     column[1] (1 B)
0000000c  04                                                .                |       type: 0x04 Geometry
                                                                             |         └ bits 7-1 = 0000010 -> base type = Geometry
                                                                             |         └ bit 0 = 0 -> not optional: each feature has a non-NULL value
0000000d                                                                     |   column data (17 B)
0000000d                                                                     |     column[0] Id (5 B)
0000000d                                                                     |       id (5 B)
0000000d                                                                     |         header (4 B)
0000000d  10                                                .                |           stream_type: 0x10 Data(None)
                                                                             |             └ bits 7-4 = 0001 -> category = Data
                                                                             |             └ bits 3-0 = 0000 -> subtype = None
0000000e  02                                                .                |           encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
0000000f  01                                                .                |           num_values: 1
00000010  01                                                .                |           byte_length: 1
00000011  64                                                d                |         data [Data(None) Int(None)/VarInt, 1 values, 1 B]
                                                                             |           decoded: [100]
00000012                                                                     |     column[1] Geometry (12 B)
00000012  02                                                .                |       stream_count: 2
00000013                                                                     |       meta (5 B)
00000013                                                                     |         header (4 B)
00000013  30                                                0                |           stream_type: 0x30 Length(VarBinary)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0000 -> subtype = VarBinary
00000014  02                                                .                |           encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
00000015  01                                                .                |           num_values: 1
00000016  01                                                .                |           byte_length: 1
00000017  00                                                .                |         data [Length(VarBinary) Int(None)/VarInt, 1 values, 1 B]
                                                                             |           decoded: [0]
00000018                                                                     |       stream[0] (6 B)
00000018                                                                     |         header (4 B)
00000018  13                                                .                |           stream_type: 0x13 Data(Vertex)
                                                                             |             └ bits 7-4 = 0001 -> category = Data
                                                                             |             └ bits 3-0 = 0011 -> subtype = Vertex
00000019  42                                                B                |           encoding: 0x42 logical=Vertex(ComponentwiseDelta) physical=VarInt
                                                                             |             └ bits 7-5 = 010 -> logical1 = ComponentwiseDelta
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
0000001a  02                                                .                |           num_values: 2
0000001b  02                                                .                |           byte_length: 2
0000001c  1a 54                                             .T               |         data [Data(Vertex) Vertex(ComponentwiseDelta)/VarInt, 2 values, 2 B]
                                                                             |           decoded: [13, 42]

Part and ring length streams.

00000000                                                                     | layer[0] (47 B)
00000000  2e                                                .                |   size: 46 (varint) - tag + body
00000001  01                                                .                |   tag: 0x01 -> Tag01
00000002  06 6c 61 79 65 72 31                              .layer1          |   name: "layer1"
00000009  50                                                P                |   extent: 80
0000000a  01                                                .                |   column_count: 1
0000000b                                                                     |   schema (1 B)
0000000b                                                                     |     column[0] (1 B)
0000000b  04                                                .                |       type: 0x04 Geometry
                                                                             |         └ bits 7-1 = 0000010 -> base type = Geometry
                                                                             |         └ bit 0 = 0 -> not optional: each feature has a non-NULL value
0000000c                                                                     |   column data (35 B)
0000000c                                                                     |     column[0] Geometry (35 B)
0000000c  04                                                .                |       stream_count: 4
0000000d                                                                     |       meta (5 B)
0000000d                                                                     |         header (4 B)
0000000d  30                                                0                |           stream_type: 0x30 Length(VarBinary)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0000 -> subtype = VarBinary
0000000e  02                                                .                |           encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
0000000f  01                                                .                |           num_values: 1
00000010  01                                                .                |           byte_length: 1
00000011  02                                                .                |         data [Length(VarBinary) Int(None)/VarInt, 1 values, 1 B]
                                                                             |           decoded: [2]
00000012                                                                     |       stream[0] (5 B)
00000012                                                                     |         header (4 B)
00000012  32                                                2                |           stream_type: 0x32 Length(Parts)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0010 -> subtype = Parts
00000013  02                                                .                |           encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
00000014  01                                                .                |           num_values: 1
00000015  01                                                .                |           byte_length: 1
00000016  02                                                .                |         data [Length(Parts) Int(None)/VarInt, 1 values, 1 B]
                                                                             |           decoded: [2]
00000017                                                                     |       stream[1] (8 B)
00000017                                                                     |         header (6 B)
00000017  33                                                3                |           stream_type: 0x33 Length(Rings)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0011 -> subtype = Rings
00000018  62                                                b                |           encoding: 0x62 logical=Int(Rle(Split { runs: 1, num_rle_values: 2 })) physical=VarInt
                                                                             |             └ bits 7-5 = 011 -> logical1 = Rle
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
00000019  02                                                .                |           num_values: 2
0000001a  02                                                .                |           byte_length: 2
0000001b  01                                                .                |           runs: 1
0000001c  02                                                .                |           num_rle_values: 2
0000001d  02 03                                             ..               |         data [Length(Rings) Int(Rle(Split { runs: 1, num_rle_values: 2 }))/VarInt, 2 values, 2 B]
                                                                             |           decoded: [3, 3]
0000001f                                                                     |       stream[2] (16 B)
0000001f                                                                     |         header (4 B)
0000001f  13                                                .                |           stream_type: 0x13 Data(Vertex)
                                                                             |             └ bits 7-4 = 0001 -> category = Data
                                                                             |             └ bits 3-0 = 0011 -> subtype = Vertex
00000020  42                                                B                |           encoding: 0x42 logical=Vertex(ComponentwiseDelta) physical=VarInt
                                                                             |             └ bits 7-5 = 010 -> logical1 = ComponentwiseDelta
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
00000021  0c                                                .                |           num_values: 12
00000022  0c                                                .                |           byte_length: 12
00000023  16 68 78 28 13 63 08 58 3b 13 28 27               .hx(.c.X;.('     |         data [Data(Vertex) Vertex(ComponentwiseDelta)/VarInt, 12 values, 12 B]
                                                                             |           decoded: [11, 52, 71, 72, 61, 22, 65, 66, 35, 56, 55, 36]

A plain string column: its stream_count, then its length and data streams.

00000000                                                                     | layer[0] (234 B)
00000000  e8 01                                             ..               |   size: 232 (varint) - tag + body
00000002  01                                                .                |   tag: 0x01 -> Tag01
00000003  06 6c 61 79 65 72 31                              .layer1          |   name: "layer1"
0000000a  50                                                P                |   extent: 80
0000000b  02                                                .                |   column_count: 2
0000000c                                                                     |   schema (6 B)
0000000c                                                                     |     column[0] (1 B)
0000000c  04                                                .                |       type: 0x04 Geometry
                                                                             |         └ bits 7-1 = 0000010 -> base type = Geometry
                                                                             |         └ bit 0 = 0 -> not optional: each feature has a non-NULL value
0000000d                                                                     |     column[1] (5 B)
0000000d  1d                                                .                |       type: 0x1D OptStr
                                                                             |         └ bits 7-1 = 0001110 -> base type = OptStr
                                                                             |         └ bit 0 = 1 -> optional: a Present stream precedes the data
0000000e  03 76 61 6c                                       .val             |       name: "val"
00000012                                                                     |   column data (216 B)
00000012                                                                     |     column[0] Geometry (25 B)
00000012  02                                                .                |       stream_count: 2
00000013                                                                     |       meta (8 B)
00000013                                                                     |         header (6 B)
00000013  30                                                0                |           stream_type: 0x30 Length(VarBinary)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0000 -> subtype = VarBinary
00000014  62                                                b                |           encoding: 0x62 logical=Int(Rle(Split { runs: 1, num_rle_values: 6 })) physical=VarInt
                                                                             |             └ bits 7-5 = 011 -> logical1 = Rle
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
00000015  02                                                .                |           num_values: 2
00000016  02                                                .                |           byte_length: 2
00000017  01                                                .                |           runs: 1
00000018  06                                                .                |           num_rle_values: 6
00000019  06 00                                             ..               |         data [Length(VarBinary) Int(Rle(Split { runs: 1, num_rle_values: 6 }))/VarInt, 2 values, 2 B]
                                                                             |           decoded: [0, 0, 0, 0, 0, 0]
0000001b                                                                     |       stream[0] (16 B)
0000001b                                                                     |         header (4 B)
0000001b  13                                                .                |           stream_type: 0x13 Data(Vertex)
                                                                             |             └ bits 7-4 = 0001 -> category = Data
                                                                             |             └ bits 3-0 = 0011 -> subtype = Vertex
0000001c  42                                                B                |           encoding: 0x42 logical=Vertex(ComponentwiseDelta) physical=VarInt
                                                                             |             └ bits 7-5 = 010 -> logical1 = ComponentwiseDelta
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
0000001d  0c                                                .                |           num_values: 12
0000001e  0c                                                .                |           byte_length: 12
0000001f  16 68 78 28 13 63 08 58 3b 13 28 27               .hx(.c.X;.('     |         data [Data(Vertex) Vertex(ComponentwiseDelta)/VarInt, 12 values, 12 B]
                                                                             |           decoded: [11, 52, 71, 72, 61, 22, 65, 66, 35, 56, 55, 36]
0000002b                                                                     |     column[1] OptStr "val" (191 B)
0000002b  03                                                .                |       stream_count: 3
0000002c                                                                     |       present (6 B)
0000002c                                                                     |         header (4 B)
0000002c  00                                                .                |           stream_type: 0x00 Present
                                                                             |             └ bits 7-4 = 0000 -> category = Present
                                                                             |             └ bits 3-0 = 0000 -> subtype = -
0000002d  60                                                `                |           encoding: 0x60 logical=Bool(ByteRle(Split { runs: 1, num_rle_values: 2 })) physical=None
                                                                             |             └ bits 7-5 = 011 -> logical1 = Rle
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 00 -> physical = None
0000002e  06                                                .                |           num_values: 6
0000002f  02                                                .                |           byte_length: 2
00000030  ff 3f                                             .?               |         data [Present Bool(ByteRle(Split { runs: 1, num_rle_values: 2 }))/None, 6 values, 2 B]
                                                                             |           decoded: 6 present-bits: 111111
00000032                                                                     |       stream[0] (10 B)
00000032                                                                     |         header (4 B)
00000032  30                                                0                |           stream_type: 0x30 Length(VarBinary)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0000 -> subtype = VarBinary
00000033  02                                                .                |           encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
00000034  06                                                .                |           num_values: 6
00000035  06                                                .                |           byte_length: 6
00000036  1f 1e 1d 17 19 1f                                 ......           |         data [Length(VarBinary) Int(None)/VarInt, 6 values, 6 B]
                                                                             |           decoded: [31, 30, 29, 23, 25, 31]
0000003c                                                                     |       stream[1] (174 B)
0000003c                                                                     |         header (5 B)
0000003c  10                                                .                |           stream_type: 0x10 Data(None)
                                                                             |             └ bits 7-4 = 0001 -> category = Data
                                                                             |             └ bits 3-0 = 0000 -> subtype = None
0000003d  00                                                .                |           encoding: 0x00 logical=Int(None) physical=None
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 00 -> physical = None
0000003e  06                                                .                |           num_values: 6
0000003f  a9 01                                             ..               |           byte_length: 169
00000041  72 65 73 69 64 65 6e 74 69 61 6c 5f 7a 6f 6e 65   residential_zone |         data [Data(None) Int(None)/None, 6 values, 169 B]
00000051  5f 6e 6f 72 74 68 5f 73 65 63 74 6f 72 5f 31 63   _north_sector_1c |
00000061  6f 6d 6d 65 72 63 69 61 6c 5f 7a 6f 6e 65 5f 73   ommercial_zone_s |
00000071  6f 75 74 68 5f 73 65 63 74 6f 72 5f 32 69 6e 64   outh_sector_2ind |
00000081  75 73 74 72 69 61 6c 5f 7a 6f 6e 65 5f 65 61 73   ustrial_zone_eas |
00000091  74 5f 73 65 63 74 6f 72 5f 33 70 61 72 6b 5f 7a   t_sector_3park_z |
000000a1  6f 6e 65 5f 77 65 73 74 5f 73 65 63 74 6f 72 5f   one_west_sector_ |
000000b1  34 77 61 74 65 72 5f 7a 6f 6e 65 5f 6e 6f 72 74   4water_zone_nort |
000000c1  68 5f 73 65 63 74 6f 72 5f 35 72 65 73 69 64 65   h_sector_5reside |
000000d1  6e 74 69 61 6c 5f 7a 6f 6e 65 5f 73 6f 75 74 68   ntial_zone_south |
000000e1  5f 73 65 63 74 6f 72 5f 36                        _sector_6        |
                                                                             |           decoded: utf-8 "residential_zone_north_sector_1commercial_zone_south_sector_2industrial_zone_east_sector_3park_zone_west_sector_4water_zone_north_sector_5residential_zone_south_sector_6"

A dictionary and the child columns that index into it, each with its own presence and offset stream.

00000000                                                                     | layer[0] (104 B)
00000000  67                                                g                |   size: 103 (varint) - tag + body
00000001  01                                                .                |   tag: 0x01 -> Tag01
00000002  06 6c 61 79 65 72 31                              .layer1          |   name: "layer1"
00000009  50                                                P                |   extent: 80
0000000a  02                                                .                |   column_count: 2
0000000b                                                                     |   schema (17 B)
0000000b                                                                     |     column[0] (1 B)
0000000b  04                                                .                |       type: 0x04 Geometry
                                                                             |         └ bits 7-1 = 0000010 -> base type = Geometry
                                                                             |         └ bit 0 = 0 -> not optional: each feature has a non-NULL value
0000000c                                                                     |     column[1] (16 B)
0000000c  1e                                                .                |       type: 0x1E SharedDict
                                                                             |         └ bits 7-1 = 0001111 -> base type = SharedDict
                                                                             |         └ bit 0 = 0 -> not optional: each feature has a non-NULL value
0000000d  05 6e 61 6d 65 3a                                 .name:           |       name: "name:"
00000013  02                                                .                |       child_count: 2
00000014                                                                     |       column[0] (4 B)
00000014  1d                                                .                |         type: 0x1D OptStr
                                                                             |           └ bits 7-1 = 0001110 -> base type = OptStr
                                                                             |           └ bit 0 = 1 -> optional: a Present stream precedes the data
00000015  02 64 65                                          .de              |         name: "de"
00000018                                                                     |       column[1] (4 B)
00000018  1d                                                .                |         type: 0x1D OptStr
                                                                             |           └ bits 7-1 = 0001110 -> base type = OptStr
                                                                             |           └ bit 0 = 1 -> optional: a Present stream precedes the data
00000019  02 65 6e                                          .en              |         name: "en"
0000001c                                                                     |   column data (76 B)
0000001c                                                                     |     column[0] Geometry (12 B)
0000001c  02                                                .                |       stream_count: 2
0000001d                                                                     |       meta (5 B)
0000001d                                                                     |         header (4 B)
0000001d  30                                                0                |           stream_type: 0x30 Length(VarBinary)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0000 -> subtype = VarBinary
0000001e  02                                                .                |           encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
0000001f  01                                                .                |           num_values: 1
00000020  01                                                .                |           byte_length: 1
00000021  00                                                .                |         data [Length(VarBinary) Int(None)/VarInt, 1 values, 1 B]
                                                                             |           decoded: [0]
00000022                                                                     |       stream[0] (6 B)
00000022                                                                     |         header (4 B)
00000022  13                                                .                |           stream_type: 0x13 Data(Vertex)
                                                                             |             └ bits 7-4 = 0001 -> category = Data
                                                                             |             └ bits 3-0 = 0011 -> subtype = Vertex
00000023  42                                                B                |           encoding: 0x42 logical=Vertex(ComponentwiseDelta) physical=VarInt
                                                                             |             └ bits 7-5 = 010 -> logical1 = ComponentwiseDelta
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
00000024  02                                                .                |           num_values: 2
00000025  02                                                .                |           byte_length: 2
00000026  1a 54                                             .T               |         data [Data(Vertex) Vertex(ComponentwiseDelta)/VarInt, 2 values, 2 B]
                                                                             |           decoded: [13, 42]
00000028                                                                     |     column[1] SharedDict "name:" (64 B)
00000028  06                                                .                |       stream_count: 6
00000029                                                                     |       dict_stream[0] (5 B)
00000029                                                                     |         header (4 B)
00000029  36                                                6                |           stream_type: 0x36 Length(Dictionary)
                                                                             |             └ bits 7-4 = 0011 -> category = Length
                                                                             |             └ bits 3-0 = 0110 -> subtype = Dictionary
0000002a  02                                                .                |           encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 10 -> physical = VarInt
0000002b  01                                                .                |           num_values: 1
0000002c  01                                                .                |           byte_length: 1
0000002d  1e                                                .                |         data [Length(Dictionary) Int(None)/VarInt, 1 values, 1 B]
                                                                             |           decoded: [30]
0000002e                                                                     |       dict_stream[1] (34 B)
0000002e                                                                     |         header (4 B)
0000002e  12                                                .                |           stream_type: 0x12 Data(Shared)
                                                                             |             └ bits 7-4 = 0001 -> category = Data
                                                                             |             └ bits 3-0 = 0010 -> subtype = Shared
0000002f  00                                                .                |           encoding: 0x00 logical=Int(None) physical=None
                                                                             |             └ bits 7-5 = 000 -> logical1 = None
                                                                             |             └ bits 4-2 = 000 -> logical2 = None
                                                                             |             └ bits 1-0 = 00 -> physical = None
00000030  01                                                .                |           num_values: 1
00000031  1e                                                .                |           byte_length: 30
00000032  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41   AAAAAAAAAAAAAAAA |         data [Data(Shared) Int(None)/None, 1 values, 30 B]
00000042  41 41 41 41 41 41 41 41 41 41 41 41 41 41         AAAAAAAAAAAAAA   |
                                                                             |           decoded: utf-8 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
00000050                                                                     |       child[0] OptStr (12 B)
00000050  02                                                .                |         stream_count: 2
00000051                                                                     |         present (6 B)
00000051                                                                     |           header (4 B)
00000051  00                                                .                |             stream_type: 0x00 Present
                                                                             |               └ bits 7-4 = 0000 -> category = Present
                                                                             |               └ bits 3-0 = 0000 -> subtype = -
00000052  60                                                `                |             encoding: 0x60 logical=Bool(ByteRle(Split { runs: 1, num_rle_values: 2 })) physical=None
                                                                             |               └ bits 7-5 = 011 -> logical1 = Rle
                                                                             |               └ bits 4-2 = 000 -> logical2 = None
                                                                             |               └ bits 1-0 = 00 -> physical = None
00000053  01                                                .                |             num_values: 1
00000054  02                                                .                |             byte_length: 2
00000055  ff 01                                             ..               |           data [Present Bool(ByteRle(Split { runs: 1, num_rle_values: 2 }))/None, 1 values, 2 B]
                                                                             |             decoded: 1 present-bits: 1
00000057                                                                     |         data (5 B)
00000057                                                                     |           header (4 B)
00000057  22                                                "                |             stream_type: 0x22 Offset(String)
                                                                             |               └ bits 7-4 = 0010 -> category = Offset
                                                                             |               └ bits 3-0 = 0010 -> subtype = String
00000058  02                                                .                |             encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |               └ bits 7-5 = 000 -> logical1 = None
                                                                             |               └ bits 4-2 = 000 -> logical2 = None
                                                                             |               └ bits 1-0 = 10 -> physical = VarInt
00000059  01                                                .                |             num_values: 1
0000005a  01                                                .                |             byte_length: 1
0000005b  00                                                .                |           data [Offset(String) Int(None)/VarInt, 1 values, 1 B]
                                                                             |             decoded: [0]
0000005c                                                                     |       child[1] OptStr (12 B)
0000005c  02                                                .                |         stream_count: 2
0000005d                                                                     |         present (6 B)
0000005d                                                                     |           header (4 B)
0000005d  00                                                .                |             stream_type: 0x00 Present
                                                                             |               └ bits 7-4 = 0000 -> category = Present
                                                                             |               └ bits 3-0 = 0000 -> subtype = -
0000005e  60                                                `                |             encoding: 0x60 logical=Bool(ByteRle(Split { runs: 1, num_rle_values: 2 })) physical=None
                                                                             |               └ bits 7-5 = 011 -> logical1 = Rle
                                                                             |               └ bits 4-2 = 000 -> logical2 = None
                                                                             |               └ bits 1-0 = 00 -> physical = None
0000005f  01                                                .                |             num_values: 1
00000060  02                                                .                |             byte_length: 2
00000061  ff 01                                             ..               |           data [Present Bool(ByteRle(Split { runs: 1, num_rle_values: 2 }))/None, 1 values, 2 B]
                                                                             |             decoded: 1 present-bits: 1
00000063                                                                     |         data (5 B)
00000063                                                                     |           header (4 B)
00000063  22                                                "                |             stream_type: 0x22 Offset(String)
                                                                             |               └ bits 7-4 = 0010 -> category = Offset
                                                                             |               └ bits 3-0 = 0010 -> subtype = String
00000064  02                                                .                |             encoding: 0x02 logical=Int(None) physical=VarInt
                                                                             |               └ bits 7-5 = 000 -> logical1 = None
                                                                             |               └ bits 4-2 = 000 -> logical2 = None
                                                                             |               └ bits 1-0 = 10 -> physical = VarInt
00000065  01                                                .                |             num_values: 1
00000066  01                                                .                |             byte_length: 1
00000067  00                                                .                |           data [Offset(String) Int(None)/VarInt, 1 values, 1 B]
                                                                             |             decoded: [0]