LEB128 Varint Decoder
This unsigned LEB128 decoder turns a variable-length byte sequence into its exact integer value.
Run — free
Paste hexadecimal bytes such as E5 8E 26, or select decimal notation when your source uses ordinary byte numbers. The decoder follows the continuation bit in every byte, validates that the sequence ends correctly, and returns the normalized bytes alongside the decimal result. It is useful when inspecting WebAssembly binaries, Protocol Buffers payloads, DWARF debugging data, compact file formats, or any protocol that stores unsigned integers seven bits at a time.
How unsigned LEB128 stores an integer
LEB128 is a little-endian, base-128 representation for integers. Each byte contributes seven payload bits. The most significant bit is reserved as a continuation flag: when that bit is one, another byte belongs to the same integer; when it is zero, the value ends. Because the least significant group arrives first, the payload from the first byte occupies bit positions zero through six, the next payload occupies positions seven through thirteen, and so on. For example, E5 8E 26 has payload groups 65, 0E, and 26 after the continuation flags are removed. Combining those groups at successive seven-bit offsets produces 624485. Small values need fewer bytes, while larger values expand without wasting leading zero bytes. This capability decodes the unsigned form only, so it does not apply sign extension and never interprets the top payload bit as a negative sign. The result is returned as a decimal string, preserving exact values even when they exceed JavaScript's safe integer range.
Enter and validate a byte sequence
Paste one complete encoded integer into the text field. In the default hexadecimal mode, write one- or two-digit hexadecimal byte tokens separated by spaces, commas, colons, or hyphens; E5 8E 26 and 0xE5,0x8E,0x26 are equivalent. A 0x prefix always means hexadecimal. Choose decimal mode when an inspection tool displays bytes as numbers from 0 through 255, such as 229 142 38. The decoder rejects malformed tokens, values outside the byte range, empty input, and sequences longer than the published 128-byte limit. It also checks structural correctness. Every byte with its high bit set promises that another byte follows, so a sequence ending in 80 is incomplete. Conversely, once a byte with a clear high bit appears, the value is finished; any later token is rejected instead of being silently ignored or treated as another integer. These rules make input mistakes visible and prevent a plausible-looking result from a truncated or concatenated sequence.
Use the decoded result during binary debugging
The response includes the exact decimal value, the number of bytes consumed, the parsed byte array, and a normalized uppercase hexadecimal representation. Together, those fields make the result easy to compare with a hex dump or carry into a test fixture. WebAssembly uses unsigned LEB128 for many indices, lengths, and integer immediates. DWARF uses it for compact unsigned attributes, while Protocol Buffers uses a closely related base-128 varint layout. In each case, begin at the byte identified by the surrounding format, collect bytes through the first one whose continuation bit is clear, and decode only that slice. If the returned value looks wrong, confirm the starting offset and whether the enclosing field is actually unsigned. Signed LEB128 and Protocol Buffers zigzag encoding require an additional signed interpretation that this decoder intentionally does not perform. For automated checks, call the same deterministic operation through the API for $0.002; identical text and format inputs always produce identical output.
What you can do with it
Inspect a WebAssembly binary
Decode an index, section length, or unsigned immediate copied from a WebAssembly hex dump.
Debug a Protocol Buffers varint
Recover the raw unsigned integer represented by a base-128 varint before applying any field-specific interpretation.
Read DWARF debugging data
Translate a ULEB128 attribute or opcode operand into an exact decimal value while checking its termination byte.
FAQ
What does the decoder cost?
Each API request costs $0.002. The browser version runs locally and is free to use.
Does this decode signed LEB128 values?
No. It decodes unsigned LEB128 only and does not apply signed extension.
Why is the decoded value returned as a string?
A decimal string preserves integers larger than the safe exact range of a JavaScript number.
Can I paste bytes with 0x prefixes?
Yes. Prefixing a token with 0x always selects hexadecimal notation for that byte.
Why does an ending byte such as 80 fail?
Its continuation bit is set, so the encoding promises another byte that was not provided.
Is a Protocol Buffers varint always the final application value?
Not necessarily. Signed protobuf fields may apply zigzag or two's-complement interpretation after the raw varint is decoded.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/dev/leb128-decode \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"E5 8E 26"}'const res = await fetch("https://api.kit.forhosting.com/dev/leb128-decode", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "E5 8E 26"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/leb128-decode",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "E5 8E 26"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/leb128-decode", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"E5 8E 26"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"E5 8E 26"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/leb128-decode", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "E5 8E 26"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.leb128_decode",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_items | 128 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |