Data Pages
Data Pages
A Chunk stores its Pages consecutively after the Chunk Header, without padding between them. data_size counts all Page Headers and stored bodies together. An empty aligned Value Chunk has data_size = 0 and no Pages.
Page Header
A non-empty Page has the following format:
Page :=
UVarInt uncompressed_size
UVarInt compressed_size
Statistics statistics? # determined by the Chunk marker
byte stored_data[compressed_size]uncompressed_size is the size of the Page payload after decryption and decompression. compressed_size is the size of the Page payload stored in the file. The two values are equal when compression_type = UNCOMPRESSED and no length-expanding encryption is used. For Page AEAD, compressed_size includes the encrypted body framing and authentication overhead, even when compression is disabled. See Encryption.
The low six bits of the Chunk marker determine whether Page Statistics are present:
| Chunk marker low bits | Page count | Page Statistics |
|---|---|---|
0x01 | More than one | Present in every non-empty Page Header |
0x05 | Zero or one | Absent |
The Statistics layout is defined in Metadata.
If a Page starts at P, let h be the serialized size of its two length fields and optional Statistics. The stored payload occupies the half-open interval [P + h, P + h + compressed_size) and the next Page, if any, starts at P + h + compressed_size. That end position must not exceed the enclosing Chunk boundary.
After decryption and decompression, the payload must contain exactly uncompressed_size bytes. A decoder rejects a payload whose produced size differs, even if the compression library reports successful termination.
Page Encoding
When writing a non-empty Page, the data passes through these stages:
logical values
-> encode time and/or values
-> assemble uncompressed page payload
-> compression
-> optional encryption
-> stored_dataReading applies the inverse order: decrypt, decompress, then decode values.
Encoding and compression are independent. The encoding converts typed values into an uncompressed byte stream; compression treats that stream as bytes. Page boundaries reset both the value encoder/decoder and the compressor/decompressor, so no state is carried from one Page to the next.
Non-aligned Page
The uncompressed payload of a non-aligned Page is:
NonAlignedPagePayload :=
UVarInt time_stream_size
byte encoded_time_stream[time_stream_size]
byte encoded_value_stream[remaining bytes]A non-aligned Page stores both time and values. It does not use a separate Time Page followed by a Value Page. The complete time stream is stored first, followed by the complete value stream; timestamp, value pairs are not interleaved point by point. The two streams contain the same number of elements and pair by position to form (timestamp, value) points.
For an uncompressed payload of size U and a serialized time_stream_size field of v bytes, the value stream length is:
value_stream_size = U - v - time_stream_sizeThe length must be non-negative. Both streams are bounded by the Page payload and must decode to the same number of points, with valid endings for their respective codecs.
A non-aligned Chunk Header records only the value stream's encoding_type. See Configurations for the time-stream encoding.
Aligned Time Page
The uncompressed payload of an aligned Time Page consists entirely of the encoded time stream:
AlignedTimePagePayload := byte encoded_time_stream[remaining bytes]The encoding_type in the Time Chunk Header identifies the time encoding.
The number of decoded timestamps is the logical position count of this Page. That count controls the bitmap width and null reconstruction of every corresponding aligned Value Page.
Aligned Value Page
The uncompressed payload of a non-empty aligned Value Page is:
AlignedValuePagePayload :=
int32 position_count
byte present_bitmap[ceil(position_count / 8)]
byte encoded_non_null_values[remaining bytes]Within one aligned set, Value Page number i corresponds to Time Page number i. See Nulls for the null bitmap and the all-null empty Page representation.
position_count must equal the decoded timestamp count of the corresponding Time Page. The bitmap has exactly ceil(position_count / 8) bytes; unused low-order bits in its last byte do not describe positions. The value decoder is invoked only for set bits and must produce exactly the number of set bits.
Page Selection and Skipping
Page Statistics can rule out a Page before its body is read. For example, a Page whose time range misses the query range can be skipped. See Statistics for which value summaries are available.
A one-Page Chunk omits Page Statistics, so use its Chunk or series Statistics from the footer. If the available summary cannot rule out a match, read the Page and check its values.
Empty Aligned Value Page
An aligned value column can be null for every position in a Time Page. This case is encoded as a single unsigned-varint zero:
EmptyAlignedValuePage := UVarInt(0) # uncompressed_sizeThere is no compressed_size, Page Statistics, bitmap, or stored payload after that zero. The position count is inherited from the corresponding Time Page. This representation is valid only where the aligned Page sequence supplies that external position count.