Skip to content
This repository was archived by the owner on Nov 4, 2025. It is now read-only.

Commit 253cfb6

Browse files
author
Sergey Kanzhelev
committed
addressed PRfeedback and more
1 parent 8b5f9a5 commit 253cfb6

File tree

5 files changed

+127
-60
lines changed

5 files changed

+127
-60
lines changed

index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<section id='abstract' data-include="spec/01-abstract.md" data-include-format='markdown'></section>
5555
<section id='sotd' data-include="spec/02-sotd.md" data-include-format='markdown'></section>
5656

57-
<section data-include="spec/20-BINARY_FORMAT.md" data-include-format='markdown'></section>
57+
<section data-include="spec/20-binary-format.md" data-include-format='markdown'></section>
58+
<section data-include="spec/31-parsing-algoritm.md" data-include-format='markdown' class="informative"></section>
5859
</body>
5960
</html>

spec/20-BINARY_FORMAT.md renamed to spec/20-binary-format.md

Lines changed: 14 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -49,48 +49,6 @@ If padding of the field is required (`traceparent` needs to be serialized into
4949
the bigger buffer) - any number of bytes can be appended to the end of the
5050
serialized value.
5151

52-
## De-serialization of `traceparent`
53-
54-
Let's assume the algorithm takes a buffer and can set and shift cursor in the
55-
buffer as well as validate whether the end of the buffer was reached or will be
56-
reached after reading the given number of bytes. De-serialization of
57-
`traceparent` should be done in the following sequence:
58-
59-
1. If buffer is empty - return invalid status `BUFFER_EMPTY`. Set a cursor to
60-
the first byte.
61-
2. Read the `version` byte at the cursor position. Shift cursor to `1` byte.
62-
3. If all three fields (`trace-id`, `parent-id`, `trace-flags`) already read -
63-
return them with the status `OK` if `version` is `0` or status
64-
`DOWNGRADED_TO_ZERO` otherwise.
65-
4. If at the end of the buffer return invalid status `TRACEPARENT_INCOMPLETE`.
66-
Otherwise read the field identifier byte at the cursor position. Field
67-
identifier should be read as unsigned byte assuming big-endian bits order.
68-
1. If `0` - check that remaining buffer size is more or equal to `16` bytes.
69-
If shorter - return invalid status `TRACE_ID_TOO_SHORT`. Otherwise read
70-
the next `16` bytes for `trace-id` and shift cursor to the end of those
71-
`16` bytes. Go to step `3`. If `trace-id` is represented as a byte array
72-
- first byte should be set into the first element of that array. See
73-
comment in serialization section.
74-
2. If `1` - check that remaining buffer size is more or equal to `8` bytes.
75-
If shorter - return invalid status `PARENT_ID_TOO_SHORT`. Otherwise read
76-
the next `8` bytes for `parent-id` and shift cursor to the end of those
77-
`8` bytes. Go to step `3`.
78-
3. If `2` - check the remaining size of the buffer. If at the end of the
79-
buffer - return invalid status. Otherwise - read the `trace-flags`
80-
byte. Least significant bit will represent `recorded` value. Go to step
81-
`3`.
82-
4. In case of any other value - if `version` read at step `2` is `0` -
83-
return invalid status `INVALID_FIELD_ID`. If `version` has any other
84-
value - `INCOMPATIBLE_VERSION`.
85-
86-
_Note_, that invalid status names are given for readability and not part of the
87-
specification.
88-
89-
_Note_, that parsing should not treat any additional bytes in the end of the
90-
buffer as an invalid status. Those fields can be added for padding purposes.
91-
Optionally implementation can check that the buffer is longer than `29` bytes as
92-
a very first step if this check is not expensive.
93-
9452
## `traceparent` example
9553

9654
``` js
@@ -105,7 +63,7 @@ This corresponds to:
10563
- `trace-id` is
10664
`{75, 249, 47, 53, 119, 179, 77, 166, 163, 206, 146, 157, 0, 14, 71, 54}` or
10765
`4bf9273577b34da6a3ce929d000e4736`.
108-
- `span-id` is `{52, 240, 103, 170, 11, 169, 2, 183}` or `34f067aa0ba902b7`.
66+
- `parent-id` is `{52, 240, 103, 170, 11, 169, 2, 183}` or `34f067aa0ba902b7`.
10967
- `trace-flags` is `1` with the meaning `recorded` is true.
11068

11169
## `tracestate` binary format
@@ -128,4 +86,16 @@ value-len = 1BYTE ; length of the value string
12886
Zero length key (`key-len == 0`) indicates the end of the `tracestate`. So when
12987
`tracestate` should be serialized into the buffer that is longer than it
13088
requires - `{ 0, 0 }` (field id `0` and key-len `0`) will indicate the end of
131-
the `tracestate`.
89+
the `tracestate`.
90+
91+
## `tracestate` example
92+
93+
``` js
94+
{ 0, 3, 102, 111, 111, 16, 51, 52, 102, 48, 54, 55, 97, 97, 48, 98, 97, 57, 48, 50, 98, 55,
95+
0, 3, 98, 97, 114, 4, 48, 46, 50, 53, }
96+
97+
```
98+
99+
This corresponds to 2 tracestate entries:
100+
101+
`foo=34f067aa0ba902b7,bar=0.25`

spec/21-BINARY_FORMAT_RATIONALE.md

Lines changed: 0 additions & 15 deletions
This file was deleted.

spec/21-binary-format-rationale.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Rationale for decision on binary format
2+
3+
Binary format is similar to proto encoding without any reference on
4+
protobuf project. It uses field identifiers in bytes in front of field
5+
values.
6+
7+
## Field identifiers
8+
9+
Protocol uses field identifiers for fields like `trace-id`, `parent-id`,
10+
`trace-flags` and tracestate entries. The purpose of the field
11+
identifiers is two-fold. First, allow to remove existing fields or add
12+
new ones going forward. Second, provides an additional layer of
13+
validation of the format.
14+
15+
## How can we add new fields
16+
17+
If we follow the rules that we always append the new ids at the end of the
18+
buffer we can add up to 127. After that we can either use varint encoding or
19+
just reserve 255 as a continuation byte. Assumption at the moment is
20+
that specification will never get to this point.
21+
22+
## Why custom binary protocol
23+
24+
We didn't find non-proprietary wide used binary protocol that can be
25+
used in this specification.

spec/31-parsing-algoritm.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# De-serialization algorithms
2+
3+
This is non-normative section that describe de-serialization algorithm
4+
that may be used to parse `traceparent` and `tracestate` field values.
5+
6+
## De-serialization of `traceparent`
7+
8+
Let's assume the algorithm takes a buffer - bytes array - and can set
9+
and shift cursor in the buffer as well as validate whether the end of
10+
the buffer was reached or will be reached after reading the given number
11+
of bytes. This algorithm can work on stream of bytes. De-serialization
12+
of `traceparent` MAY be done in the following sequence:
13+
14+
1. If buffer is empty - RETURN invalid status `BUFFER_EMPTY`. Set a cursor to
15+
the first byte.
16+
2. Read the `version` byte at the cursor position. Shift cursor to `1` byte.
17+
3. If at the end of the buffer RETURN invalid status `TRACEPARENT_INCOMPLETE`.
18+
4. **Parse `trace-id`**. Read the field identifier byte at the cursor
19+
position. If NOT `0` - go to step `8. Report invalid field`.
20+
Otherwise - check that remaining buffer size is more or equal to `16`
21+
bytes. If shorter - RETURN invalid status `TRACE_ID_TOO_SHORT`.
22+
Otherwise read the next `16` bytes for `trace-id` and shift cursor to
23+
the end of those `16` bytes.
24+
5. **Parse `trace-id`**. Read the field identifier byte at the cursor
25+
position. If NOT `1` - go to step `8. Report invalid field`.
26+
Otherwise - check that remaining buffer size is more or equal to `8`
27+
bytes. If shorter - RETURN invalid status `PARENT_ID_TOO_SHORT`.
28+
Otherwise read the next `8` bytes for `parent-id` and shift cursor
29+
to the end of those `8` bytes.
30+
6. **Parse `trace-id`**. Read the field identifier byte at the cursor
31+
position. If NOT `2` - go to step `8. Report invalid field`.
32+
Otherwise - check the remaining size of the buffer. If at the end of
33+
the buffer - RETURN invalid status. Otherwise - read the
34+
`trace-flags` byte. Least significant bit will represent `recorded`
35+
value.
36+
7. RETURN status `OK` if `version` is `0` or status `DOWNGRADED_TO_ZERO`
37+
otherwise.
38+
8. **Report invalid field**. If `version` is `0` RETURN invalid status
39+
`INVALID_FIELD_ID`. If `version` has any other value -
40+
`INCOMPATIBLE_VERSION`
41+
42+
_Note_, that invalid status names are given for readability and not part of the
43+
specification.
44+
45+
_Note_, that parsing should not treat any additional bytes in the end of the
46+
buffer as an invalid status. Those fields can be added for padding purposes.
47+
Optionally implementation can check that the buffer is longer than `29` bytes as
48+
a very first step if this check is not expensive.
49+
50+
## De-serialization of `tracestate`
51+
52+
Let's assume the algorithm takes a buffer - bytes array - and can set
53+
and shift cursor in the buffer as well as validate whether the end of
54+
the buffer was reached or will be reached after reading the given number
55+
of bytes. Algorithm also uses `version` value parsed from `traceparent`.
56+
If `version` was not given - value `0` SHOULD be used. This algorithm
57+
can work on stream of bytes. De-serialization of `tracestate` MAY be
58+
done in the following sequence:
59+
60+
1. If at the end of the buffer - RETURN status `OK`. Otherwise set a
61+
cursor to the first byte.
62+
2. **Parse `list-member` field identifier**. Read the field identifier
63+
byte at the cursor position and shift cursor to `1` byte. If NOT `0`
64+
and `version` is `0` RETURN invalid status `INVALID_FIELD_ID`. If NOT
65+
`0` and `version` has any other value - `INCOMPATIBLE_VERSION`.
66+
3. **Parse key**.
67+
1. If at the end of the buffer - RETURN status `OK`. This situation
68+
indicates that `tracestate` value was padded with `0`.
69+
2. Read the `key-len` byte. Shift cursor to `1` byte. If the value of
70+
`key-len` is `0` - RETURN status `OK`. This situation indicates an
71+
explicit end of a key.
72+
3. Check that buffer has `key-len` more bytes. If not - RETURN
73+
`KEY_TOO_SHORT`.
74+
4. Read `key-len` bytes as `key`. Shift cursor to `key-len` bytes.
75+
4. **Parse value**.
76+
1. If at the end of the buffer - RETURN status `INCOMPLETE_LIST_MEMBER`.
77+
2. Read the `value-len` byte. Shift cursor to `1` byte. If the value of
78+
`value-len` is `0` - add `list-member` with the `key` and empty
79+
`value` to the `tracestate` list. RETURN status `OK`.
80+
3. Check that buffer has `value-len` more bytes. If not - RETURN
81+
`VALUE_TOO_SHORT`.
82+
4. Read `value-len` bytes as `value`. Shift cursor to `value-len`
83+
bytes.
84+
5. Add `list-member` with the `key` and `value` to the `tracestate`
85+
list.
86+
5. Go to step `2. Parse list-member field identifier`.

0 commit comments

Comments
 (0)