Skip to content

Commit a77318f

Browse files
committed
Extract TLV tag bytes into named constants
1 parent 67f7fe5 commit a77318f

3 files changed

Lines changed: 62 additions & 31 deletions

File tree

docs/rust-abi-spec.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,15 @@ Notes:
300300
- The decoder on the host side rejects unknown tags with
301301
`io::ErrorKind::InvalidData`.
302302

303+
The canonical host-side tag byte values are defined as named constants in
304+
the `tags` module in `runtime/wasm/src/rust_abi/types.rs` (`tags::NULL`,
305+
`tags::STRING`, `tags::INT`, `tags::INT8`, `tags::BIG_INT`,
306+
`tags::BIG_DECIMAL`, `tags::BOOL`, `tags::BYTES`, `tags::ADDRESS`,
307+
`tags::ARRAY`). The `ValueTag` enum discriminants are derived from those
308+
constants, so there is exactly one place in the host codebase where the
309+
on-wire bytes are defined. Any edit to those constants is, by definition,
310+
a breaking ABI change (see section 8.1).
311+
303312
The reference implementation lives in
304313
`runtime/wasm/src/rust_abi/entity.rs`.
305314

runtime/wasm/src/rust_abi/entity.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! Works with `HashMap<String, Value>` for deserialization since graph-node's
55
//! `Entity` type requires schema context for construction.
66
7-
use super::types::{FromRustWasm, ToRustWasm, ValueTag};
7+
use super::types::{tags, FromRustWasm, ToRustWasm, ValueTag};
88
use graph::data::store::scalar::Bytes;
99
use graph::data::store::{Entity, Value};
1010
use graph::prelude::*;
@@ -92,46 +92,46 @@ impl ToRustWasm for Value {
9292
fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<()> {
9393
match self {
9494
Value::Null => {
95-
writer.write_all(&[ValueTag::Null as u8])?;
95+
writer.write_all(&[tags::NULL])?;
9696
}
9797
Value::String(s) => {
98-
writer.write_all(&[ValueTag::String as u8])?;
98+
writer.write_all(&[tags::STRING])?;
9999
s.as_str().write_to(writer)?;
100100
}
101101
Value::Int(n) => {
102-
writer.write_all(&[ValueTag::Int as u8])?;
102+
writer.write_all(&[tags::INT])?;
103103
n.write_to(writer)?;
104104
}
105105
Value::Int8(n) => {
106-
writer.write_all(&[ValueTag::Int8 as u8])?;
106+
writer.write_all(&[tags::INT8])?;
107107
n.write_to(writer)?;
108108
}
109109
Value::BigInt(n) => {
110-
writer.write_all(&[ValueTag::BigInt as u8])?;
110+
writer.write_all(&[tags::BIG_INT])?;
111111
n.write_to(writer)?;
112112
}
113113
Value::BigDecimal(n) => {
114-
writer.write_all(&[ValueTag::BigDecimal as u8])?;
114+
writer.write_all(&[tags::BIG_DECIMAL])?;
115115
n.write_to(writer)?;
116116
}
117117
Value::Bool(b) => {
118-
writer.write_all(&[ValueTag::Bool as u8])?;
118+
writer.write_all(&[tags::BOOL])?;
119119
b.write_to(writer)?;
120120
}
121121
Value::Bytes(b) => {
122-
writer.write_all(&[ValueTag::Bytes as u8])?;
122+
writer.write_all(&[tags::BYTES])?;
123123
b.as_slice().to_vec().write_to(writer)?;
124124
}
125125
Value::List(arr) => {
126-
writer.write_all(&[ValueTag::Array as u8])?;
126+
writer.write_all(&[tags::ARRAY])?;
127127
writer.write_all(&(arr.len() as u32).to_le_bytes())?;
128128
for v in arr {
129129
v.write_to(writer)?;
130130
}
131131
}
132132
Value::Timestamp(ts) => {
133133
// Serialize timestamp as BigInt (microseconds since epoch)
134-
writer.write_all(&[ValueTag::BigInt as u8])?;
134+
writer.write_all(&[tags::BIG_INT])?;
135135
BigInt::from(ts.as_microseconds_since_epoch()).write_to(writer)?;
136136
}
137137
}

runtime/wasm/src/rust_abi/types.rs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,57 @@ use graph::prelude::*;
77
use std::io::{self, Read, Write};
88
use std::str::FromStr;
99

10+
/// Canonical TLV value tag bytes.
11+
///
12+
/// These are the single source of truth for the tag byte of each `Value`
13+
/// variant on the wire. They match the table in
14+
/// `docs/rust-abi-spec.md` section 4.6 one-for-one. Changing any value in
15+
/// this module is a breaking ABI change and requires an `apiVersion` bump.
16+
pub mod tags {
17+
pub const NULL: u8 = 0x00;
18+
pub const STRING: u8 = 0x01;
19+
pub const INT: u8 = 0x02;
20+
pub const INT8: u8 = 0x03;
21+
pub const BIG_INT: u8 = 0x04;
22+
pub const BIG_DECIMAL: u8 = 0x05;
23+
pub const BOOL: u8 = 0x06;
24+
pub const BYTES: u8 = 0x07;
25+
pub const ADDRESS: u8 = 0x08;
26+
pub const ARRAY: u8 = 0x09;
27+
}
28+
1029
/// Value type tags for TLV serialization.
30+
///
31+
/// Discriminants are pulled from the [`tags`] module so there is a single
32+
/// authoritative definition of each on-wire byte.
1133
#[repr(u8)]
1234
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1335
pub enum ValueTag {
14-
Null = 0x00,
15-
String = 0x01,
16-
Int = 0x02,
17-
Int8 = 0x03,
18-
BigInt = 0x04,
19-
BigDecimal = 0x05,
20-
Bool = 0x06,
21-
Bytes = 0x07,
22-
Address = 0x08,
23-
Array = 0x09,
36+
Null = tags::NULL,
37+
String = tags::STRING,
38+
Int = tags::INT,
39+
Int8 = tags::INT8,
40+
BigInt = tags::BIG_INT,
41+
BigDecimal = tags::BIG_DECIMAL,
42+
Bool = tags::BOOL,
43+
Bytes = tags::BYTES,
44+
Address = tags::ADDRESS,
45+
Array = tags::ARRAY,
2446
}
2547

2648
impl ValueTag {
2749
pub fn from_u8(v: u8) -> Option<Self> {
2850
match v {
29-
0x00 => Some(Self::Null),
30-
0x01 => Some(Self::String),
31-
0x02 => Some(Self::Int),
32-
0x03 => Some(Self::Int8),
33-
0x04 => Some(Self::BigInt),
34-
0x05 => Some(Self::BigDecimal),
35-
0x06 => Some(Self::Bool),
36-
0x07 => Some(Self::Bytes),
37-
0x08 => Some(Self::Address),
38-
0x09 => Some(Self::Array),
51+
tags::NULL => Some(Self::Null),
52+
tags::STRING => Some(Self::String),
53+
tags::INT => Some(Self::Int),
54+
tags::INT8 => Some(Self::Int8),
55+
tags::BIG_INT => Some(Self::BigInt),
56+
tags::BIG_DECIMAL => Some(Self::BigDecimal),
57+
tags::BOOL => Some(Self::Bool),
58+
tags::BYTES => Some(Self::Bytes),
59+
tags::ADDRESS => Some(Self::Address),
60+
tags::ARRAY => Some(Self::Array),
3961
_ => None,
4062
}
4163
}

0 commit comments

Comments
 (0)