1mod helpers;
7mod packed;
8mod varint;
9
10use std::cell::{Cell, RefCell};
11use std::collections::HashMap;
12use std::sync::Arc;
13
14use prost_reflect::{Cardinality, ExtensionDescriptor, FieldDescriptor, Kind, MessageDescriptor};
15
16use crate::helpers::{
17 decode_double, decode_fixed32, decode_fixed64, decode_float, decode_sfixed32, decode_sfixed64,
18};
19use crate::helpers::{
20 parse_varint, parse_wiretag, WiretagResult, WT_END_GROUP, WT_I32, WT_I64, WT_LEN,
21 WT_START_GROUP, WT_VARINT,
22};
23use crate::schema::ParsedSchema;
24use crate::serialize::common::{
25 format_double_protoc, format_fixed32_protoc, format_fixed64_protoc, format_float_protoc,
26 format_sfixed32_protoc, format_sfixed64_protoc, format_wire_fixed32_protoc,
27 format_wire_fixed64_protoc,
28};
29
30use helpers::{
31 render_group_field, render_invalid, render_invalid_tag_type, render_len_field, render_scalar,
32 render_truncated_bytes, ScalarCtx,
33};
34use varint::{decode_varint_typed, render_varint_field, VarintKind};
35
36const PROTOTEXT_MAGIC: &[u8] = b"#@ prototext:";
38
39pub(super) enum FieldOrExt {
44 Field(FieldDescriptor),
45 Ext(ExtensionDescriptor),
46}
47
48impl FieldOrExt {
49 pub(super) fn kind(&self) -> Kind {
50 match self {
51 FieldOrExt::Field(f) => f.kind(),
52 FieldOrExt::Ext(e) => e.kind(),
53 }
54 }
55
56 pub(super) fn cardinality(&self) -> Cardinality {
57 match self {
58 FieldOrExt::Field(f) => f.cardinality(),
59 FieldOrExt::Ext(e) => e.cardinality(),
60 }
61 }
62
63 pub(super) fn is_group(&self) -> bool {
65 match self {
66 FieldOrExt::Field(f) => f.is_group(),
67 FieldOrExt::Ext(_) => false,
68 }
69 }
70
71 pub(super) fn is_packed(&self) -> bool {
72 match self {
73 FieldOrExt::Field(f) => f.is_packed(),
74 FieldOrExt::Ext(_) => false,
75 }
76 }
77
78 #[cfg(feature = "prost-bug-workaround")]
86 pub(super) fn raw_packed_option(&self) -> Option<bool> {
87 let proto = match self {
88 FieldOrExt::Field(f) => f.field_descriptor_proto(),
89 FieldOrExt::Ext(e) => e.field_descriptor_proto(),
90 };
91 proto.options.as_ref().and_then(|o| o.packed)
92 }
93
94 #[cfg(feature = "prost-bug-workaround")]
95 pub(super) fn parent_file_syntax(&self) -> prost_reflect::Syntax {
96 match self {
97 FieldOrExt::Field(f) => f.parent_file().syntax(),
98 FieldOrExt::Ext(e) => e.parent_file().syntax(),
99 }
100 }
101
102 pub(super) fn display_name(&self) -> String {
107 match self {
108 FieldOrExt::Field(f) => f.name().to_owned(),
109 FieldOrExt::Ext(e) => format!("[{}]", e.full_name()),
110 }
111 }
112
113 #[allow(dead_code)]
118 pub(super) fn as_field(&self) -> Option<&FieldDescriptor> {
119 match self {
120 FieldOrExt::Field(f) => Some(f),
121 FieldOrExt::Ext(_) => None,
122 }
123 }
124}
125
126thread_local! {
134 pub(super) static CBL_START: Cell<usize> = const { Cell::new(0) };
135 pub(super) static ANNOTATIONS: Cell<bool> = const { Cell::new(false) };
137 pub(super) static INDENT_SIZE: Cell<usize> = const { Cell::new(2) };
138 pub(super) static LEVEL: Cell<usize> = const { Cell::new(0) };
140 pub(super) static EXPAND_ANY: Cell<bool> = const { Cell::new(true) };
142 pub static EXTRA_HEADER: RefCell<String> = const { RefCell::new(String::new()) };
144}
145
146pub(super) struct LevelGuard;
149
150impl Drop for LevelGuard {
151 fn drop(&mut self) {
152 LEVEL.with(|l| l.set(l.get() - 1));
153 }
154}
155
156pub(super) fn enter_level() -> LevelGuard {
157 LEVEL.with(|l| l.set(l.get() + 1));
158 LevelGuard
159}
160
161pub fn is_prototext_text(data: &[u8]) -> bool {
163 data.starts_with(PROTOTEXT_MAGIC)
164}
165
166pub fn decode_and_render(
177 buf: &[u8],
178 schema: Option<&ParsedSchema>,
179 annotations: bool,
180 indent_size: usize,
181 expand_any: bool,
182) -> Vec<u8> {
183 let capacity = buf.len() * 8;
184 let mut out = Vec::with_capacity(capacity);
185
186 if annotations {
190 out.extend_from_slice(b"#@ prototext: protoc\n");
191 }
192 EXTRA_HEADER.with(|h| {
193 let h = h.borrow();
194 if !h.is_empty() {
195 out.extend_from_slice(h.as_bytes());
196 }
197 });
198 CBL_START.with(|c| c.set(out.len()));
202 ANNOTATIONS.with(|c| c.set(annotations));
203 INDENT_SIZE.with(|c| c.set(indent_size));
204 LEVEL.with(|c| c.set(0));
205 EXPAND_ANY.with(|c| c.set(expand_any));
206
207 let all_descriptors: Option<HashMap<String, Arc<MessageDescriptor>>> =
210 schema.map(build_descriptor_map);
211 let all_schemas = all_descriptors.as_ref();
212
213 let root_desc: Option<MessageDescriptor> = schema.and_then(|s| s.root_descriptor());
214
215 render_message(buf, 0, None, root_desc.as_ref(), all_schemas, &mut out);
216
217 #[cfg(debug_assertions)]
219 {
220 let actual = out.len();
221 if actual < capacity {
222 eprintln!(
223 "[render_text] truncate: input_len={} capacity={} actual={} ratio={:.2}x",
224 buf.len(),
225 capacity,
226 actual,
227 actual as f64 / buf.len().max(1) as f64
228 );
229 }
230 }
231
232 out
233}
234
235fn build_descriptor_map(schema: &ParsedSchema) -> HashMap<String, Arc<MessageDescriptor>> {
237 schema
238 .pool()
239 .all_messages()
240 .map(|msg| (msg.full_name().to_string(), Arc::new(msg)))
241 .collect()
242}
243
244pub(super) fn render_message(
253 buf: &[u8],
254 start: usize,
255 my_group: Option<u64>,
256 schema: Option<&MessageDescriptor>,
257 all_schemas: Option<&HashMap<String, Arc<MessageDescriptor>>>,
258 out: &mut Vec<u8>,
259) -> (usize, Option<WiretagResult>) {
260 let buflen = buf.len();
261 let mut pos = start;
262
263 loop {
264 if pos == buflen {
265 return (pos, None);
266 }
267
268 let tag = parse_wiretag(buf, pos);
271
272 if let Some(ref wtag_gar) = tag.wtag_gar {
273 render_invalid_tag_type(wtag_gar, out);
275 return (buflen, None);
276 }
277
278 let field_number = tag.wfield.unwrap();
279 let wire_type = tag.wtype.unwrap();
280 let tag_ohb = tag.wfield_ohb;
281 let tag_oor = tag.wfield_oor.is_some();
282 pos = tag.next_pos;
283
284 let field_schema: Option<FieldOrExt> = schema.and_then(|s| {
287 if let Some(f) = s.get_field(field_number as u32) {
288 Some(FieldOrExt::Field(f))
289 } else {
290 s.get_extension(field_number as u32).map(FieldOrExt::Ext)
291 }
292 });
293
294 match wire_type {
297 WT_VARINT => {
299 let vr = parse_varint(buf, pos);
300 if let Some(ref varint_gar) = vr.varint_gar {
301 render_invalid(
302 field_number,
303 field_schema.as_ref(),
304 tag_ohb,
305 tag_oor,
306 "INVALID_VARINT",
307 varint_gar,
308 out,
309 );
310 return (buflen, None);
311 }
312 pos = vr.next_pos;
313 let val_ohb = vr.varint_ohb;
314 let val = vr.varint.unwrap();
315
316 let (content_kind, typed_val) = if let Some(ref fs) = field_schema {
317 decode_varint_typed(val, fs)
318 } else {
319 (VarintKind::Wire, val)
320 };
321
322 render_varint_field(
323 field_number,
324 field_schema.as_ref(),
325 tag_ohb,
326 tag_oor,
327 val_ohb,
328 content_kind,
329 typed_val,
330 all_schemas.is_some(),
331 out,
332 );
333 }
334
335 WT_I64 => {
337 if pos + 8 > buflen {
338 let raw = &buf[pos..];
339 render_invalid(
340 field_number,
341 field_schema.as_ref(),
342 tag_ohb,
343 tag_oor,
344 "INVALID_FIXED64",
345 raw,
346 out,
347 );
348 return (buflen, None);
349 }
350 let data = &buf[pos..pos + 8];
351 pos += 8;
352
353 let is_mismatch;
354 let mut nan_bits: Option<u64> = None;
355 let value_str = if let Some(ref fs) = field_schema {
356 match fs.kind() {
357 Kind::Double => {
358 is_mismatch = false;
359 let v = decode_double(data);
360 if v.is_nan() {
361 let bits = v.to_bits();
362 if bits != f64::NAN.to_bits() {
363 nan_bits = Some(bits);
364 }
365 }
366 format_double_protoc(v)
367 }
368 Kind::Fixed64 => {
369 is_mismatch = false;
370 format_fixed64_protoc(decode_fixed64(data))
371 }
372 Kind::Sfixed64 => {
373 is_mismatch = false;
374 format_sfixed64_protoc(decode_sfixed64(data))
375 }
376 _ => {
377 is_mismatch = true;
378 format_wire_fixed64_protoc(decode_fixed64(data))
379 } }
381 } else {
382 is_mismatch = false;
383 format_wire_fixed64_protoc(decode_fixed64(data)) };
385
386 render_scalar(
387 &ScalarCtx {
388 field_number,
389 field_schema: field_schema.as_ref(),
390 tag_ohb,
391 tag_oor,
392 len_ohb: None,
393 wire_type_name: "fixed64",
394 nan_bits,
395 type_mismatch: is_mismatch,
396 schema_present: all_schemas.is_some(),
397 },
398 &value_str,
399 is_mismatch,
400 out,
401 );
402 }
403
404 WT_LEN => {
406 let lr = parse_varint(buf, pos);
407 if let Some(ref varint_gar) = lr.varint_gar {
408 render_invalid(
409 field_number,
410 field_schema.as_ref(),
411 tag_ohb,
412 tag_oor,
413 "INVALID_LEN",
414 varint_gar,
415 out,
416 );
417 return (buflen, None);
418 }
419 let len_ohb = lr.varint_ohb;
420 pos = lr.next_pos;
421 let length = lr.varint.unwrap() as usize;
422
423 if pos + length > buflen {
424 let missing = (length - (buflen - pos)) as u64;
425 let raw = &buf[pos..];
426 render_truncated_bytes(
427 field_number,
428 tag_ohb,
429 tag_oor,
430 len_ohb,
431 missing,
432 raw,
433 out,
434 );
435 return (buflen, None);
436 }
437 let data = &buf[pos..pos + length];
438 pos += length;
439
440 render_len_field(
441 field_number,
442 field_schema.as_ref(),
443 all_schemas,
444 tag_ohb,
445 tag_oor,
446 len_ohb,
447 data,
448 out,
449 );
450 }
451
452 WT_START_GROUP => {
454 render_group_field(
455 buf,
456 &mut pos,
457 field_number,
458 field_schema.as_ref(),
459 all_schemas,
460 tag_ohb,
461 tag_oor,
462 out,
463 );
464 }
465
466 WT_END_GROUP => {
468 if my_group.is_none() {
469 let raw = &buf[pos..];
471 render_invalid(
472 field_number,
473 field_schema.as_ref(),
474 tag_ohb,
475 tag_oor,
476 "INVALID_GROUP_END",
477 raw,
478 out,
479 );
480 return (buflen, None);
481 }
482 return (pos, Some(tag));
484 }
485
486 WT_I32 => {
488 if pos + 4 > buflen {
489 let raw = &buf[pos..];
490 render_invalid(
491 field_number,
492 field_schema.as_ref(),
493 tag_ohb,
494 tag_oor,
495 "INVALID_FIXED32",
496 raw,
497 out,
498 );
499 return (buflen, None);
500 }
501 let data = &buf[pos..pos + 4];
502 pos += 4;
503
504 let is_mismatch;
505 let mut nan_bits: Option<u64> = None;
506 let value_str = if let Some(ref fs) = field_schema {
507 match fs.kind() {
508 Kind::Float => {
509 is_mismatch = false;
510 let v = decode_float(data);
511 if v.is_nan() {
512 let bits = v.to_bits();
513 if bits != f32::NAN.to_bits() {
514 nan_bits = Some(bits as u64);
515 }
516 }
517 format_float_protoc(v)
518 }
519 Kind::Fixed32 => {
520 is_mismatch = false;
521 format_fixed32_protoc(decode_fixed32(data))
522 }
523 Kind::Sfixed32 => {
524 is_mismatch = false;
525 format_sfixed32_protoc(decode_sfixed32(data))
526 }
527 _ => {
528 is_mismatch = true;
529 format_wire_fixed32_protoc(decode_fixed32(data))
530 } }
532 } else {
533 is_mismatch = false;
534 format_wire_fixed32_protoc(decode_fixed32(data)) };
536
537 render_scalar(
538 &ScalarCtx {
539 field_number,
540 field_schema: field_schema.as_ref(),
541 tag_ohb,
542 tag_oor,
543 len_ohb: None,
544 wire_type_name: "fixed32",
545 nan_bits,
546 type_mismatch: is_mismatch,
547 schema_present: all_schemas.is_some(),
548 },
549 &value_str,
550 is_mismatch,
551 out,
552 );
553 }
554
555 _ => unreachable!("wire type > 5 caught by parse_wiretag"),
556 }
557 }
558}