Variable-length quantity


A variable-length quantity is a universal code that uses an arbitrary number of binary octets to represent an arbitrarily large integer. A VLQ is essentially a base-128 representation of an unsigned integer with the addition of the eighth bit to mark continuation of bytes. See the example below.

Applications and history

Base-128 compression is known by many namesVB, VByte, Varint, VInt, EncInt etc.
A variable-length quantity was defined for use in the standard MIDI file format to save additional space for a resource constrained system, and is also used in the later Extensible Music Format.
Base-128 is also used in ASN.1 BER encoding to encode tag numbers and Object Identifiers. It is also used in the WAP environment, where it is called variable length unsigned integer or uintvar. The DWARF debugging format defines a variant called LEB128, where the least significant group of 7 bits are encoded in the first byte and the most significant bits are in the last byte. Google Protocol Buffers use a similar format to have compact representation of integer values, as does Oracle Portable Object Format and the Microsoft.NET Framework "7-bit encoded int" in the BinaryReader and BinaryWriter classes.
It is also used extensively in web browsers for source mapping – which contain a lot of integer line & column number mappings – to keep the size of the map to a minimum.
Variable width integers in LLVM use a similar principle. The encoding chunks are little-endian and need not be 8 bits in size. The LLVM documentation describes a field that uses 4-bit chunk, with each chunk consisting of 1 bit continuation and 3 bits payload.

General structure

The encoding assumes an octet where the most significant bit, also commonly known as the sign bit, is reserved to indicate whether another VLQ octet follows.
If A is 0, then this is the last VLQ octet of the integer. If A is 1, then another VLQ octet follows.
B is a 7-bit number and n is the position of the VLQ octet where B0 is the least significant. The VLQ octets are arranged most significant first in a stream.

Variants

The general VLQ encoding is simple, but in basic form is only defined for unsigned integers, and is somewhat redundant, since prepending 0x80 octets corresponds to zero padding. There are various signed number representations to handle negative numbers, and techniques to remove the redundancy.

Group Varint Encoding

Google developed Group Varint Encoding after observing that traditional VLQ encoding incurs many CPU branches during decompression. GVE uses a single byte as a header for 4 variable-length uint32 values. The header byte has 4 2-bit numbers representing the storage length of each of the following 4 uint32s. Such a layout eliminates the need to check and remove VLQ continuation bits. Data bytes can be copied directly to their destination. This layout reduces CPU branches, making GVE faster than VLQ on modern pipelined CPUs.
PrefixVarint is a similar design but with a uint64 maximum. It is said to have "been invented multiple times independently". It is possible to be changed into a chained version with infinitely many continuations.

Signed Numbers

Sign bit

Negative numbers can be handled using a sign bit, which only needs to be present in the first octet.
In the data format for Unreal Packages used by the Unreal Engine, a variable length quantity scheme called Compact Indices is used. The only difference in this encoding is that the first VLQ has the sixth bit reserved to indicate whether the encoded integer is positive or negative. Any consecutive VLQ octet follows the general structure.
If A is 0, then this is the last VLQ octet of the integer. If A is 1, then another VLQ octet follows.
If B is 0, then the VLQ represents a positive integer. If B is 1, then the VLQ represents a negative number.
C is number chunk being encoded and n is the position of the VLQ octet where C0 is the least significant. The VLQ octets are arranged most significant first in a stream.

Zigzag encoding

An alternative way to encode negative numbers is to use the least-significant bit for sign. This is notably done for Google Protocol Buffers, and is known as a zigzag encoding for signed integers. One can encode the numbers so that encoded 0 corresponds to 0, 1 to −1, 10 to 1, 11 to −2, 100 to 2, etc.: counting up alternates between nonnegative and negative, whence the name "zigzag encoding". Concretely, transform the integer as ^ for fixed k-bit integers.

Two's complement

LEB128 uses two's complement to represent signed numbers. In this scheme of representation, n bits encodes a range from -2n to 2n-1, and all negative numbers start with a 1 in the most significant bit. In Signed LEB128, the input is sign extended so that its length is a multiple of 7 bits. From there the encoding proceeds as usual.
In LEB128, the stream is arranged least significant first.

Removing redundancy

With the VLQ encoding described above, any number that can be encoded with N octets can also be encoded with more than N octets simply by prepending additional 0x80 octets as zero-padding. For example, the decimal number 358 can be encoded as the 2-octet VLQ 0x8266 or the number 0358 can be encoded as 3-octet VLQ 0x808266 or 00358 as the 4-octet VLQ 0x80808266 and so forth.
However, the VLQ format used in Git removes this prepending redundancy and extends the representable range of shorter VLQs by adding an offset to VLQs of 2 or more octets in such a way that the lowest possible value for such an -octet VLQ becomes exactly one more than the maximum possible value for an N-octet VLQ. In particular, since a 1-octet VLQ can store a maximum value of 127, the minimum 2-octet VLQ is assigned the value 128 instead of 0. Conversely, the maximum value of such a 2-octet VLQ is 16511 instead of just 16383. Similarly, the minimum 3-octet VLQ has a value of 16512 instead of zero, which means that the maximum 3-octet VLQ is 2113663 instead of just 2097151.
In this way, there is one and only one encoding of each integer, making this a base-128 bijective numeration.

Examples

Here is a worked out example for the decimal number 137:
Another way to look at this is to represent the value in base-128, and then set the MSB of all but the last base-128 digit to 1.
The Standard MIDI File format specification gives more examples:
Integer Integer Integer Variable-length quantity Variable-length quantity
00x00000000
00000000 00000000 00000000 00000000
0x00
 00000000
1270x0000007F
00000000 00000000 00000000 01111111
0x7F
 01111111
1280x00000080
00000000 00000000 00000000 10000000
0x81 0x00
 10000001 00000000
81920x00002000
00000000 00000000 00100000 00000000
0xC0 0x00
 11000000 00000000
163830x00003FFF
00000000 00000000 00111111 11111111
0xFF 0x7F
 11111111 01111111
163840x00004000
00000000 00000000 01000000 00000000
0x81 0x80 0x00
 10000001 10000000 00000000
20971510x001FFFFF
00000000 00011111 11111111 11111111
0xFF 0xFF 0x7F
 11111111 11111111 01111111
20971520x00200000
00000000 00100000 00000000 00000000
0x81 0x80 0x80 0x00
10000001 10000000 10000000 00000000
1342177280x08000000
00001000 00000000 00000000 00000000
0xC0 0x80 0x80 0x00
11000000 10000000 10000000 00000000
2684354550x0FFFFFFF
00001111 11111111 11111111 11111111
0xFF 0xFF 0xFF 0x7F
11111111 11111111 11111111 01111111