• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TEN-framework / ten-framework / 20742721841

06 Jan 2026 08:33AM UTC coverage: 58.016% (+0.3%) from 57.746%
20742721841

Pull #1945

github

web-flow
Merge b35d34af5 into 1771357ae
Pull Request #1945: feat: support log with fields

320 of 542 new or added lines in 17 files covered. (59.04%)

7 existing lines in 2 files now uncovered.

54966 of 94743 relevant lines covered (58.02%)

749944.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.0
/core/src/ten_rust/src/value_buffer/mod.rs
1
//
2
// Copyright © 2025 Agora
3
// This file is part of TEN Framework, an open source project.
4
// Licensed under the Apache License, Version 2.0, with certain conditions.
5
// Refer to the "LICENSE" file in the root directory for more information.
6
//
7

8
use std::collections::HashMap;
9

10
use anyhow::{anyhow, Result};
11

12
// Buffer protocol constants - must match C layer
13
const VALUE_BUFFER_MAGIC: u16 = 0x010E;
14
const VALUE_BUFFER_VERSION: u8 = 1;
15
const VALUE_BUFFER_HEADER_SIZE: usize = 8;
16

17
// Buffer type constants - must match TEN_VALUE_BUFFER_TYPE in C
18
#[repr(u8)]
19
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20
enum BufferType {
21
    Invalid = 0,
22
    Bool = 1,
23
    Int8 = 2,
24
    Int16 = 3,
25
    Int32 = 4,
26
    Int64 = 5,
27
    Uint8 = 6,
28
    Uint16 = 7,
29
    Uint32 = 8,
30
    Uint64 = 9,
31
    Float32 = 10,
32
    Float64 = 11,
33
    String = 12,
34
    Buf = 13,
35
    Array = 14,
36
    Object = 15,
37
    Ptr = 16,
38
    JsonString = 17,
39
}
40

41
impl BufferType {
42
    fn from_u8(value: u8) -> Result<Self> {
45✔
43
        match value {
45✔
NEW
44
            0 => Ok(BufferType::Invalid),
×
45
            1 => Ok(BufferType::Bool),
3✔
NEW
46
            2 => Ok(BufferType::Int8),
×
NEW
47
            3 => Ok(BufferType::Int16),
×
48
            4 => Ok(BufferType::Int32),
4✔
49
            5 => Ok(BufferType::Int64),
7✔
NEW
50
            6 => Ok(BufferType::Uint8),
×
NEW
51
            7 => Ok(BufferType::Uint16),
×
NEW
52
            8 => Ok(BufferType::Uint32),
×
53
            9 => Ok(BufferType::Uint64),
3✔
NEW
54
            10 => Ok(BufferType::Float32),
×
55
            11 => Ok(BufferType::Float64),
1✔
56
            12 => Ok(BufferType::String),
15✔
NEW
57
            13 => Ok(BufferType::Buf),
×
58
            14 => Ok(BufferType::Array),
2✔
59
            15 => Ok(BufferType::Object),
10✔
NEW
60
            16 => Ok(BufferType::Ptr),
×
NEW
61
            17 => Ok(BufferType::JsonString),
×
NEW
62
            _ => Err(anyhow!("Unknown buffer type: {}", value)),
×
63
        }
64
    }
45✔
65
}
66

67
/// Represents a deserialized value from the buffer
68
#[derive(Debug, Clone)]
69
pub enum Value {
70
    Bool(bool),
71
    Int8(i8),
72
    Int16(i16),
73
    Int32(i32),
74
    Int64(i64),
75
    Uint8(u8),
76
    Uint16(u16),
77
    Uint32(u32),
78
    Uint64(u64),
79
    Float32(f32),
80
    Float64(f64),
81
    String(String),
82
    Buf(Vec<u8>),
83
    Array(Vec<Value>),
84
    Object(HashMap<String, Value>),
85
    JsonString(String),
86
}
87

88
impl Value {
89
    /// Check if this value is an Object type
NEW
90
    pub fn is_object(&self) -> bool {
×
NEW
91
        matches!(self, Value::Object(_))
×
NEW
92
    }
×
93

94
    /// Get the object if this is an Object type
NEW
95
    pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
×
NEW
96
        match self {
×
NEW
97
            Value::Object(obj) => Some(obj),
×
NEW
98
            _ => None,
×
99
        }
NEW
100
    }
×
101

102
    /// Convert this Value to a serde_json::Value
103
    pub fn to_json(&self) -> serde_json::Value {
35✔
104
        match self {
35✔
105
            Value::Bool(x) => serde_json::Value::Bool(*x),
2✔
NEW
106
            Value::Int8(x) => serde_json::Value::Number((*x as i64).into()),
×
NEW
107
            Value::Int16(x) => serde_json::Value::Number((*x as i64).into()),
×
108
            Value::Int32(x) => serde_json::Value::Number((*x as i64).into()),
4✔
109
            Value::Int64(x) => serde_json::Value::Number((*x).into()),
2✔
NEW
110
            Value::Uint8(x) => serde_json::Value::Number((*x as u64).into()),
×
NEW
111
            Value::Uint16(x) => serde_json::Value::Number((*x as u64).into()),
×
NEW
112
            Value::Uint32(x) => serde_json::Value::Number((*x as u64).into()),
×
113
            Value::Uint64(x) => serde_json::Value::Number((*x).into()),
3✔
NEW
114
            Value::Float32(x) => serde_json::Number::from_f64(*x as f64)
×
NEW
115
                .map_or(serde_json::Value::Null, serde_json::Value::Number),
×
116
            Value::Float64(x) => serde_json::Number::from_f64(*x)
1✔
117
                .map_or(serde_json::Value::Null, serde_json::Value::Number),
1✔
118
            Value::String(s) => serde_json::Value::String(s.clone()),
13✔
NEW
119
            Value::Buf(b) => serde_json::Value::Array(
×
NEW
120
                b.iter().map(|x| serde_json::Value::Number((*x as u64).into())).collect(),
×
121
            ),
122
            Value::Array(arr) => {
1✔
123
                serde_json::Value::Array(arr.iter().map(|v| v.to_json()).collect())
5✔
124
            }
125
            Value::Object(obj) => {
9✔
126
                let mut map = serde_json::Map::with_capacity(obj.len());
9✔
127
                for (k, v) in obj.iter() {
22✔
128
                    map.insert(k.clone(), v.to_json());
22✔
129
                }
22✔
130
                serde_json::Value::Object(map)
9✔
131
            }
NEW
132
            Value::JsonString(s) => serde_json::from_str::<serde_json::Value>(s)
×
NEW
133
                .unwrap_or_else(|_| serde_json::Value::String(s.clone())),
×
134
        }
135
    }
35✔
136
}
137

138
#[derive(Debug)]
139
#[allow(dead_code)]
140
struct BufferHeader {
141
    magic: u16,
142
    version: u8,
143
    type_id: BufferType,
144
    size: u32,
145
}
146

147
impl BufferHeader {
148
    fn parse(buffer: &[u8]) -> Result<Self> {
13✔
149
        if buffer.len() < VALUE_BUFFER_HEADER_SIZE {
13✔
NEW
150
            return Err(anyhow!("Buffer too small to contain header"));
×
151
        }
13✔
152

153
        let magic = u16::from_le_bytes([buffer[0], buffer[1]]);
13✔
154
        let version = buffer[2];
13✔
155
        let type_id = BufferType::from_u8(buffer[3])?;
13✔
156
        let size = u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]);
13✔
157

158
        if magic != VALUE_BUFFER_MAGIC {
13✔
NEW
159
            return Err(anyhow!(
×
NEW
160
                "Invalid buffer magic number: expected 0x{:04X}, got 0x{:04X}",
×
NEW
161
                VALUE_BUFFER_MAGIC,
×
NEW
162
                magic
×
NEW
163
            ));
×
164
        }
13✔
165

166
        if version != VALUE_BUFFER_VERSION {
13✔
NEW
167
            return Err(anyhow!(
×
NEW
168
                "Unsupported buffer protocol version: expected {}, got {}",
×
NEW
169
                VALUE_BUFFER_VERSION,
×
NEW
170
                version
×
NEW
171
            ));
×
172
        }
13✔
173

174
        if buffer.len() < VALUE_BUFFER_HEADER_SIZE + size as usize {
13✔
NEW
175
            return Err(anyhow!(
×
NEW
176
                "Buffer size doesn't match header specification: expected {}, got {}",
×
NEW
177
                VALUE_BUFFER_HEADER_SIZE + size as usize,
×
NEW
178
                buffer.len()
×
NEW
179
            ));
×
180
        }
13✔
181

182
        Ok(BufferHeader {
13✔
183
            magic,
13✔
184
            version,
13✔
185
            type_id,
13✔
186
            size,
13✔
187
        })
13✔
188
    }
13✔
189
}
190

191
/// Deserialize content from buffer based on type
192
fn deserialize_content(buffer: &[u8], pos: &mut usize, buffer_type: BufferType) -> Result<Value> {
45✔
193
    match buffer_type {
45✔
NEW
194
        BufferType::Invalid => Err(anyhow!("Cannot deserialize invalid type")),
×
195

196
        BufferType::Bool => {
197
            if *pos >= buffer.len() {
3✔
NEW
198
                return Err(anyhow!("Buffer too small for bool value"));
×
199
            }
3✔
200
            let val = buffer[*pos] != 0;
3✔
201
            *pos += 1;
3✔
202
            Ok(Value::Bool(val))
3✔
203
        }
204

205
        BufferType::Int8 => {
NEW
206
            if *pos >= buffer.len() {
×
NEW
207
                return Err(anyhow!("Buffer too small for int8 value"));
×
NEW
208
            }
×
NEW
209
            let val = buffer[*pos] as i8;
×
NEW
210
            *pos += 1;
×
NEW
211
            Ok(Value::Int8(val))
×
212
        }
213

214
        BufferType::Int16 => {
NEW
215
            if *pos + 2 > buffer.len() {
×
NEW
216
                return Err(anyhow!("Buffer too small for int16 value"));
×
NEW
217
            }
×
NEW
218
            let val = i16::from_le_bytes([buffer[*pos], buffer[*pos + 1]]);
×
NEW
219
            *pos += 2;
×
NEW
220
            Ok(Value::Int16(val))
×
221
        }
222

223
        BufferType::Int32 => {
224
            if *pos + 4 > buffer.len() {
4✔
NEW
225
                return Err(anyhow!("Buffer too small for int32 value"));
×
226
            }
4✔
227
            let val = i32::from_le_bytes([
4✔
228
                buffer[*pos],
4✔
229
                buffer[*pos + 1],
4✔
230
                buffer[*pos + 2],
4✔
231
                buffer[*pos + 3],
4✔
232
            ]);
4✔
233
            *pos += 4;
4✔
234
            Ok(Value::Int32(val))
4✔
235
        }
236

237
        BufferType::Int64 => {
238
            if *pos + 8 > buffer.len() {
7✔
NEW
239
                return Err(anyhow!("Buffer too small for int64 value"));
×
240
            }
7✔
241
            let val = i64::from_le_bytes([
7✔
242
                buffer[*pos],
7✔
243
                buffer[*pos + 1],
7✔
244
                buffer[*pos + 2],
7✔
245
                buffer[*pos + 3],
7✔
246
                buffer[*pos + 4],
7✔
247
                buffer[*pos + 5],
7✔
248
                buffer[*pos + 6],
7✔
249
                buffer[*pos + 7],
7✔
250
            ]);
7✔
251
            *pos += 8;
7✔
252
            Ok(Value::Int64(val))
7✔
253
        }
254

255
        BufferType::Uint8 => {
NEW
256
            if *pos >= buffer.len() {
×
NEW
257
                return Err(anyhow!("Buffer too small for uint8 value"));
×
NEW
258
            }
×
NEW
259
            let val = buffer[*pos];
×
NEW
260
            *pos += 1;
×
NEW
261
            Ok(Value::Uint8(val))
×
262
        }
263

264
        BufferType::Uint16 => {
NEW
265
            if *pos + 2 > buffer.len() {
×
NEW
266
                return Err(anyhow!("Buffer too small for uint16 value"));
×
NEW
267
            }
×
NEW
268
            let val = u16::from_le_bytes([buffer[*pos], buffer[*pos + 1]]);
×
NEW
269
            *pos += 2;
×
NEW
270
            Ok(Value::Uint16(val))
×
271
        }
272

273
        BufferType::Uint32 => {
NEW
274
            if *pos + 4 > buffer.len() {
×
NEW
275
                return Err(anyhow!("Buffer too small for uint32 value"));
×
NEW
276
            }
×
NEW
277
            let val = u32::from_le_bytes([
×
NEW
278
                buffer[*pos],
×
NEW
279
                buffer[*pos + 1],
×
NEW
280
                buffer[*pos + 2],
×
NEW
281
                buffer[*pos + 3],
×
NEW
282
            ]);
×
NEW
283
            *pos += 4;
×
NEW
284
            Ok(Value::Uint32(val))
×
285
        }
286

287
        BufferType::Uint64 => {
288
            if *pos + 8 > buffer.len() {
3✔
NEW
289
                return Err(anyhow!("Buffer too small for uint64 value"));
×
290
            }
3✔
291
            let val = u64::from_le_bytes([
3✔
292
                buffer[*pos],
3✔
293
                buffer[*pos + 1],
3✔
294
                buffer[*pos + 2],
3✔
295
                buffer[*pos + 3],
3✔
296
                buffer[*pos + 4],
3✔
297
                buffer[*pos + 5],
3✔
298
                buffer[*pos + 6],
3✔
299
                buffer[*pos + 7],
3✔
300
            ]);
3✔
301
            *pos += 8;
3✔
302
            Ok(Value::Uint64(val))
3✔
303
        }
304

305
        BufferType::Float32 => {
NEW
306
            if *pos + 4 > buffer.len() {
×
NEW
307
                return Err(anyhow!("Buffer too small for float32 value"));
×
NEW
308
            }
×
NEW
309
            let bytes = [buffer[*pos], buffer[*pos + 1], buffer[*pos + 2], buffer[*pos + 3]];
×
NEW
310
            let val = f32::from_le_bytes(bytes);
×
NEW
311
            *pos += 4;
×
NEW
312
            Ok(Value::Float32(val))
×
313
        }
314

315
        BufferType::Float64 => {
316
            if *pos + 8 > buffer.len() {
1✔
NEW
317
                return Err(anyhow!("Buffer too small for float64 value"));
×
318
            }
1✔
319
            let bytes = [
1✔
320
                buffer[*pos],
1✔
321
                buffer[*pos + 1],
1✔
322
                buffer[*pos + 2],
1✔
323
                buffer[*pos + 3],
1✔
324
                buffer[*pos + 4],
1✔
325
                buffer[*pos + 5],
1✔
326
                buffer[*pos + 6],
1✔
327
                buffer[*pos + 7],
1✔
328
            ];
1✔
329
            let val = f64::from_le_bytes(bytes);
1✔
330
            *pos += 8;
1✔
331
            Ok(Value::Float64(val))
1✔
332
        }
333

334
        BufferType::String | BufferType::JsonString => {
335
            if *pos + 4 > buffer.len() {
15✔
NEW
336
                return Err(anyhow!("Buffer too small for string length"));
×
337
            }
15✔
338
            let str_len = u32::from_le_bytes([
15✔
339
                buffer[*pos],
15✔
340
                buffer[*pos + 1],
15✔
341
                buffer[*pos + 2],
15✔
342
                buffer[*pos + 3],
15✔
343
            ]) as usize;
15✔
344
            *pos += 4;
15✔
345

346
            let data = if str_len == 0 {
15✔
NEW
347
                String::new()
×
348
            } else {
349
                if *pos + str_len > buffer.len() {
15✔
NEW
350
                    return Err(anyhow!("Buffer too small for string data"));
×
351
                }
15✔
352
                let string_bytes = &buffer[*pos..*pos + str_len];
15✔
353
                let data = String::from_utf8(string_bytes.to_vec())
15✔
354
                    .map_err(|e| anyhow!("Invalid UTF-8: {}", e))?;
15✔
355
                *pos += str_len;
15✔
356
                data
15✔
357
            };
358

359
            if buffer_type == BufferType::String {
15✔
360
                Ok(Value::String(data))
15✔
361
            } else {
NEW
362
                Ok(Value::JsonString(data))
×
363
            }
364
        }
365

366
        BufferType::Buf => {
NEW
367
            if *pos + 4 > buffer.len() {
×
NEW
368
                return Err(anyhow!("Buffer too small for buf length"));
×
NEW
369
            }
×
NEW
370
            let buf_len = u32::from_le_bytes([
×
NEW
371
                buffer[*pos],
×
NEW
372
                buffer[*pos + 1],
×
NEW
373
                buffer[*pos + 2],
×
NEW
374
                buffer[*pos + 3],
×
NEW
375
            ]) as usize;
×
NEW
376
            *pos += 4;
×
377

NEW
378
            let data = if buf_len == 0 {
×
NEW
379
                Vec::new()
×
380
            } else {
NEW
381
                if *pos + buf_len > buffer.len() {
×
NEW
382
                    return Err(anyhow!("Buffer too small for buf data"));
×
NEW
383
                }
×
NEW
384
                let data = buffer[*pos..*pos + buf_len].to_vec();
×
NEW
385
                *pos += buf_len;
×
NEW
386
                data
×
387
            };
388

NEW
389
            Ok(Value::Buf(data))
×
390
        }
391

392
        BufferType::Array => {
393
            if *pos + 4 > buffer.len() {
2✔
NEW
394
                return Err(anyhow!("Buffer too small for array length"));
×
395
            }
2✔
396
            let array_len = u32::from_le_bytes([
2✔
397
                buffer[*pos],
2✔
398
                buffer[*pos + 1],
2✔
399
                buffer[*pos + 2],
2✔
400
                buffer[*pos + 3],
2✔
401
            ]) as usize;
2✔
402
            *pos += 4;
2✔
403

404
            let mut array_data = Vec::with_capacity(array_len);
2✔
405
            for _ in 0..array_len {
2✔
406
                if *pos >= buffer.len() {
8✔
NEW
407
                    return Err(anyhow!("Buffer too small for array item type"));
×
408
                }
8✔
409
                let item_type = BufferType::from_u8(buffer[*pos])?;
8✔
410
                *pos += 1;
8✔
411

412
                let item = deserialize_content(buffer, pos, item_type)?;
8✔
413
                array_data.push(item);
8✔
414
            }
415

416
            Ok(Value::Array(array_data))
2✔
417
        }
418

419
        BufferType::Object => {
420
            if *pos + 4 > buffer.len() {
10✔
NEW
421
                return Err(anyhow!("Buffer too small for object size"));
×
422
            }
10✔
423
            let obj_size = u32::from_le_bytes([
10✔
424
                buffer[*pos],
10✔
425
                buffer[*pos + 1],
10✔
426
                buffer[*pos + 2],
10✔
427
                buffer[*pos + 3],
10✔
428
            ]) as usize;
10✔
429
            *pos += 4;
10✔
430

431
            let mut obj_data = HashMap::with_capacity(obj_size);
10✔
432
            for _ in 0..obj_size {
10✔
433
                // Read key
434
                if *pos + 4 > buffer.len() {
24✔
NEW
435
                    return Err(anyhow!("Buffer too small for object key length"));
×
436
                }
24✔
437
                let key_len = u32::from_le_bytes([
24✔
438
                    buffer[*pos],
24✔
439
                    buffer[*pos + 1],
24✔
440
                    buffer[*pos + 2],
24✔
441
                    buffer[*pos + 3],
24✔
442
                ]) as usize;
24✔
443
                *pos += 4;
24✔
444

445
                if *pos + key_len > buffer.len() {
24✔
NEW
446
                    return Err(anyhow!("Buffer too small for object key data"));
×
447
                }
24✔
448
                let key_bytes = &buffer[*pos..*pos + key_len];
24✔
449
                let key = String::from_utf8(key_bytes.to_vec())
24✔
450
                    .map_err(|e| anyhow!("Invalid UTF-8 in key: {}", e))?;
24✔
451
                *pos += key_len;
24✔
452

453
                // Read value
454
                if *pos >= buffer.len() {
24✔
NEW
455
                    return Err(anyhow!("Buffer too small for object value type"));
×
456
                }
24✔
457
                let val_type = BufferType::from_u8(buffer[*pos])?;
24✔
458
                *pos += 1;
24✔
459

460
                let val = deserialize_content(buffer, pos, val_type)?;
24✔
461
                obj_data.insert(key, val);
24✔
462
            }
463

464
            Ok(Value::Object(obj_data))
10✔
465
        }
466

NEW
467
        BufferType::Ptr => Err(anyhow!("Ptr type is not supported for deserialization")),
×
468
    }
469
}
45✔
470

471
/// Deserialize a Value from buffer
472
pub fn deserialize_from_buffer(buffer: &[u8]) -> Result<Value> {
13✔
473
    let header = BufferHeader::parse(buffer)?;
13✔
474

475
    let mut pos = VALUE_BUFFER_HEADER_SIZE;
13✔
476
    let value = deserialize_content(buffer, &mut pos, header.type_id)?;
13✔
477

478
    Ok(value)
13✔
479
}
13✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc