1mod references;
27
28use std::{
29 fmt::Display,
30 io::{Cursor, Write},
31 ops::RangeInclusive,
32};
33
34use monty_types::{
35 BuiltinsFunctions, CallArgs, MAX_TIMEZONE_OFFSET_SECONDS, MIN_TIMEZONE_OFFSET_SECONDS, MontyComplex, MontyDate,
36 MontyDateTime, MontyFileHandle, MontyTime, MontyTimeDelta, MontyTimeZone, MontyType, MontyUuid, SourceRange,
37 unstable::{self, ClassTypeNode, GraphError, MontyGraph, MontyNode, NodeId},
38};
39use num_bigint::{BigInt, Sign};
40use prost::{
41 DecodeError, Message,
42 bytes::{Buf, BufMut},
43 encoding::{
44 DecodeContext, WireType, decode_varint, encode_key, encode_varint, encoded_len_varint, key_len, skip_field,
45 },
46};
47pub use references::{WireIndexes, WireNamedTuple, WireNodePairs};
48
49use crate::{
50 BudgetVec,
51 budgeted_prost::encoding,
52 convert::ProtoConvertError,
53 decode_budget,
54 pb::{self, monty_node::Kind},
55};
56
57#[derive(Debug, Clone, PartialEq, Default)]
65pub struct WireArena(pub BudgetVec<MontyNode>);
66
67impl WireArena {
68 #[must_use]
71 pub fn new(graph: MontyGraph) -> Self {
72 Self(graph.into_nodes().into())
73 }
74
75 pub fn into_graph(self) -> Result<MontyGraph, ProtoConvertError> {
77 MontyGraph::from_nodes(self.0.into_inner()).map_err(|err| graph_error(&err))
78 }
79}
80
81impl From<MontyGraph> for WireArena {
82 fn from(graph: MontyGraph) -> Self {
83 Self::new(graph)
84 }
85}
86
87const MIN_VEC_CAPACITY: usize = 4;
89
90const MIN_NODE_WIRE_BYTES: usize = 4;
93
94impl Message for WireArena {
95 fn encode_raw(&self, buf: &mut impl BufMut) {
96 encode_uint32(1, arena_len_u32(self.0.len()), buf);
97 for node in &self.0 {
98 encoding::message::encode(2, &NodeRef(node), buf);
99 }
100 }
101
102 fn encoded_len(&self) -> usize {
103 uint32_len(1, arena_len_u32(self.0.len()))
104 + self
105 .0
106 .iter()
107 .map(|node| encoding::message::encoded_len(2, &NodeRef(node)))
108 .sum::<usize>()
109 }
110
111 fn merge_field(
112 &mut self,
113 tag: u32,
114 wire_type: WireType,
115 buf: &mut impl Buf,
116 ctx: DecodeContext,
117 ) -> Result<(), DecodeError> {
118 match tag {
119 1 => {
120 let mut hint = 0u32;
121 encoding::uint32::merge(wire_type, &mut hint, buf, ctx)?;
122 if self.0.capacity() == 0 {
125 let reserve = (hint as usize).min(buf.remaining() / MIN_NODE_WIRE_BYTES);
126 self.0.try_reserve_capacity(reserve)?;
127 }
128 Ok(())
129 }
130 2 => {
131 let node = node_from_proto(merge_message(wire_type, buf, ctx)?)?;
133 self.0.try_push(node)
134 }
135 _ => skip_field(wire_type, tag, buf, ctx),
136 }
137 }
138
139 fn clear(&mut self) {
141 self.0 = BudgetVec::new();
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Default)]
152pub struct WireFunctionCall {
153 pub function_name: String,
155 pub values: WireArena,
157 pub args: BudgetVec<NodeId>,
159 pub kwargs: BudgetVec<(NodeId, NodeId)>,
161 pub call_id: u32,
163 pub object_id: Option<MontyUuid>,
167 pub allow_eager_await: bool,
169 pub position: Option<SourceRange>,
172}
173
174impl WireFunctionCall {
175 #[must_use]
177 pub fn new(
178 function_name: String,
179 args: CallArgs,
180 call_id: u32,
181 object_id: Option<MontyUuid>,
182 allow_eager_await: bool,
183 position: SourceRange,
184 ) -> Self {
185 let (graph, args, kwargs) = unstable::into_call_args_parts(args);
186 Self {
187 function_name,
188 values: WireArena::new(graph),
189 args: args.into(),
190 kwargs: kwargs.into(),
191 call_id,
192 object_id,
193 allow_eager_await,
194 position: Some(position),
195 }
196 }
197
198 pub fn into_call_args(self) -> Result<CallArgs, ProtoConvertError> {
200 unstable::call_args_from_parts(
201 self.values.into_graph()?,
202 self.args.into_inner(),
203 self.kwargs.into_inner(),
204 )
205 .map_err(|err| graph_error(&err))
206 }
207}
208
209impl Message for WireFunctionCall {
210 fn encode_raw(&self, buf: &mut impl BufMut) {
211 encode_str(1, &self.function_name, buf);
212 encode_packed_ids(2, &self.args, buf);
213 encode_node_pairs(3, &self.kwargs, buf);
214 encode_uint32(4, self.call_id, buf);
215 if let Some(id) = &self.object_id {
216 encoding::message::encode(5, &uuid_to_pb(id), buf);
217 }
218 if self.allow_eager_await {
219 encoding::bool::encode(6, &true, buf);
220 }
221 encoding::message::encode(7, &self.values, buf);
222 if let Some(position) = &self.position {
223 encode_source_range(8, position, buf);
224 }
225 }
226
227 fn encoded_len(&self) -> usize {
228 str_len(1, &self.function_name)
229 + packed_ids_len(2, &self.args)
230 + node_pairs_len(3, &self.kwargs)
231 + uint32_len(4, self.call_id)
232 + self
233 .object_id
234 .as_ref()
235 .map_or(0, |id| encoding::message::encoded_len(5, &uuid_to_pb(id)))
236 + if self.allow_eager_await {
237 encoding::bool::encoded_len(6, &true)
238 } else {
239 0
240 }
241 + encoding::message::encoded_len(7, &self.values)
242 + self
243 .position
244 .as_ref()
245 .map_or(0, |position| submessage_len(8, source_range_len(position)))
246 }
247
248 fn merge_field(
249 &mut self,
250 tag: u32,
251 wire_type: WireType,
252 buf: &mut impl Buf,
253 ctx: DecodeContext,
254 ) -> Result<(), DecodeError> {
255 match tag {
256 1 => encoding::string::merge(wire_type, &mut self.function_name, buf, ctx),
257 2 => merge_ids(wire_type, buf, ctx, &mut self.args),
258 3 => {
259 let pair: pb::NodePair = merge_message(wire_type, buf, ctx)?;
260 push_charged(&mut self.kwargs, (NodeId(pair.key), NodeId(pair.value)))
261 }
262 4 => encoding::uint32::merge(wire_type, &mut self.call_id, buf, ctx),
263 5 => {
264 let mut uuid = pb::Uuid::default();
265 encoding::message::merge(wire_type, &mut uuid, buf, ctx)?;
266 self.object_id = Some(pb_uuid_to_monty(&uuid, "FunctionCall.object_id")?);
267 Ok(())
268 }
269 6 => encoding::bool::merge(wire_type, &mut self.allow_eager_await, buf, ctx),
270 7 => encoding::message::merge(wire_type, &mut self.values, buf, ctx),
271 8 => {
272 let mut position = self
276 .position
277 .take()
278 .map_or_else(pb::SourceRange::default, pb::SourceRange::from);
279 encoding::message::merge(wire_type, &mut position, buf, ctx)?;
280 self.position = Some(SourceRange::from(position));
281 Ok(())
282 }
283 _ => skip_field(wire_type, tag, buf, ctx),
284 }
285 }
286
287 fn clear(&mut self) {
288 self.function_name.clear();
289 self.values.clear();
290 self.args = BudgetVec::new();
291 self.kwargs = BudgetVec::new();
292 self.call_id = 0;
293 self.object_id = None;
294 self.allow_eager_await = false;
295 self.position = None;
296 }
297}
298
299pub(crate) fn graph_error(err: &GraphError) -> ProtoConvertError {
301 ProtoConvertError::InvalidValue {
302 field: "Arena",
303 reason: err.to_string(),
304 }
305}
306
307mod tag {
310 pub const ELLIPSIS: u32 = 1;
311 pub const NONE: u32 = 2;
312 pub const NOT_IMPLEMENTED: u32 = 3;
313 pub const BOOLEAN: u32 = 4;
314 pub const INT: u32 = 5;
315 pub const BIGINT: u32 = 6;
316 pub const FLOAT: u32 = 7;
317 pub const STR: u32 = 8;
318 pub const BYTES: u32 = 9;
319 pub const LIST: u32 = 11;
322 pub const TUPLE: u32 = 12;
323 pub const NAMED_TUPLE: u32 = 13;
324 pub const DICT: u32 = 14;
325 pub const SET: u32 = 15;
326 pub const FROZEN_SET: u32 = 16;
327 pub const DATE: u32 = 17;
328 pub const TIME: u32 = 18;
329 pub const DATETIME: u32 = 19;
330 pub const TIMEDELTA: u32 = 20;
331 pub const TIMEZONE: u32 = 21;
332 pub const EXCEPTION: u32 = 22;
333 pub const TYPE: u32 = 23;
334 pub const CLASS_INSTANCE: u32 = 24;
335 pub const FUNCTION: u32 = 25;
336 pub const BUILTIN_FUNCTION: u32 = 26;
337 pub const PATH: u32 = 27;
338 pub const FILE_HANDLE: u32 = 28;
339 pub const REPR: u32 = 29;
340 pub const CYCLE: u32 = 30;
341 pub const COMPLEX: u32 = 31;
342 pub const MODULE: u32 = 32;
343 pub const DECIMAL: u32 = 33;
344}
345
346#[derive(Debug)]
353struct NodeRef<'a>(&'a MontyNode);
354
355impl Message for NodeRef<'_> {
356 fn encode_raw(&self, buf: &mut impl BufMut) {
357 encode_node(self.0, buf);
358 }
359
360 fn encoded_len(&self) -> usize {
361 node_len(self.0)
362 }
363
364 fn merge_field(
365 &mut self,
366 _tag: u32,
367 _wire_type: WireType,
368 _buf: &mut impl Buf,
369 _ctx: DecodeContext,
370 ) -> Result<(), DecodeError> {
371 unreachable!("NodeRef is encode-only")
372 }
373
374 fn clear(&mut self) {
375 unreachable!("NodeRef is encode-only")
376 }
377}
378
379fn arena_len_u32(len: usize) -> u32 {
381 u32::try_from(len).expect("arena exceeds u32::MAX nodes")
382}
383
384fn encode_node(node: &MontyNode, buf: &mut impl BufMut) {
391 match node {
392 MontyNode::Ellipsis => encoding::message::encode(tag::ELLIPSIS, &pb::Unit {}, buf),
393 MontyNode::NotImplemented => encoding::message::encode(tag::NOT_IMPLEMENTED, &pb::Unit {}, buf),
394 MontyNode::None => encoding::message::encode(tag::NONE, &pb::Unit {}, buf),
395 MontyNode::Bool(b) => encoding::bool::encode(tag::BOOLEAN, b, buf),
396 MontyNode::Int(i) => encoding::sint64::encode(tag::INT, i, buf),
397 MontyNode::BigInt(bi) => encoding::message::encode(tag::BIGINT, &bigint_to_proto(bi), buf),
398 MontyNode::Float(f) => encoding::double::encode(tag::FLOAT, f, buf),
399 MontyNode::Complex(c) => encoding::message::encode(tag::COMPLEX, &complex_to_proto(c), buf),
400 MontyNode::String(s) => encoding::string::encode(tag::STR, s, buf),
401 MontyNode::Bytes(b) => encoding::bytes::encode(tag::BYTES, b, buf),
402 MontyNode::List(ids) => encode_indexes(tag::LIST, ids, buf),
403 MontyNode::Tuple(ids) => encode_indexes(tag::TUPLE, ids, buf),
404 MontyNode::NamedTuple {
405 type_name,
406 field_names,
407 values,
408 } => {
409 encode_message_key(tag::NAMED_TUPLE, named_tuple_len(type_name, field_names, values), buf);
410 encode_str(1, type_name, buf);
411 encode_repeated_str(2, field_names, buf);
412 encode_packed_ids(3, values, buf);
413 }
414 MontyNode::Dict(pairs) => {
415 encode_message_key(tag::DICT, node_pairs_len(1, pairs), buf);
416 encode_node_pairs(1, pairs, buf);
417 }
418 MontyNode::Set(ids) => encode_indexes(tag::SET, ids, buf),
419 MontyNode::FrozenSet(ids) => encode_indexes(tag::FROZEN_SET, ids, buf),
420 MontyNode::Date(d) => encoding::message::encode(tag::DATE, &date_to_proto(d), buf),
421 MontyNode::DateTime(dt) => {
422 encode_message_key(tag::DATETIME, datetime_len(dt), buf);
423 encode_datetime(dt, buf);
424 }
425 MontyNode::Time(t) => {
426 encode_message_key(tag::TIME, time_len(t), buf);
427 encode_time(t, buf);
428 }
429 MontyNode::TimeDelta(td) => encoding::message::encode(tag::TIMEDELTA, &timedelta_to_proto(td), buf),
430 MontyNode::TimeZone(tz) => {
431 encode_message_key(tag::TIMEZONE, timezone_len(tz), buf);
432 encode_int32(1, tz.offset_seconds, buf);
433 encode_opt_str(2, tz.name.as_deref(), buf);
434 }
435 MontyNode::Exception { exc_type, arg } => {
436 let name = exc_type.to_string();
437 encode_message_key(tag::EXCEPTION, str_len(1, &name) + opt_str_len(2, arg.as_deref()), buf);
438 encode_str(1, &name, buf);
439 encode_opt_str(2, arg.as_deref(), buf);
440 }
441 MontyNode::Type(t) => encoding::message::encode(tag::TYPE, &builtin_type_to_pb(*t), buf),
442 MontyNode::ClassType(class) => {
443 encode_message_key(tag::TYPE, class_type_len(class), buf);
444 encode_class_type(class, buf);
445 }
446 MontyNode::ClassInstance {
447 class_type,
448 instance_id,
449 attrs,
450 } => {
451 let id = uuid_to_pb(instance_id);
452 encode_message_key(tag::CLASS_INSTANCE, class_instance_len(*class_type, &id, attrs), buf);
453 encode_uint32(1, class_type.0, buf);
454 encoding::message::encode(2, &id, buf);
457 encode_message_key(3, node_pairs_len(1, attrs), buf);
458 encode_node_pairs(1, attrs, buf);
459 }
460 MontyNode::Module { name, id, attrs } => {
461 let id = uuid_to_pb(id);
462 encode_message_key(tag::MODULE, module_len(name, &id, attrs), buf);
463 encode_str(1, name, buf);
464 encoding::message::encode(2, &id, buf);
465 encode_message_key(3, node_pairs_len(1, attrs), buf);
466 encode_node_pairs(1, attrs, buf);
467 }
468 MontyNode::BuiltinFunction(bf) => encoding::string::encode(tag::BUILTIN_FUNCTION, &bf.to_string(), buf),
469 MontyNode::Path(p) => encoding::string::encode(tag::PATH, p, buf),
470 MontyNode::Decimal(d) => encoding::string::encode(tag::DECIMAL, d, buf),
471 MontyNode::FileHandle(fh) => {
472 encode_message_key(tag::FILE_HANDLE, file_handle_len(fh), buf);
473 encode_str(1, &fh.path, buf);
474 encode_str(2, fh.mode.as_str(), buf);
475 encode_uint64(3, fh.position, buf);
476 }
477 MontyNode::Function { name, docstring } => {
478 encode_message_key(
479 tag::FUNCTION,
480 str_len(1, name) + opt_str_len(2, docstring.as_deref()),
481 buf,
482 );
483 encode_str(1, name, buf);
484 encode_opt_str(2, docstring.as_deref(), buf);
485 }
486 MontyNode::Repr(r) => encoding::string::encode(tag::REPR, r, buf),
487 MontyNode::Cycle(placeholder) => encoding::string::encode(tag::CYCLE, placeholder, buf),
488 }
489}
490
491fn node_len(node: &MontyNode) -> usize {
494 match node {
495 MontyNode::Ellipsis => encoding::message::encoded_len(tag::ELLIPSIS, &pb::Unit {}),
496 MontyNode::NotImplemented => encoding::message::encoded_len(tag::NOT_IMPLEMENTED, &pb::Unit {}),
497 MontyNode::None => encoding::message::encoded_len(tag::NONE, &pb::Unit {}),
498 MontyNode::Bool(b) => encoding::bool::encoded_len(tag::BOOLEAN, b),
499 MontyNode::Int(i) => encoding::sint64::encoded_len(tag::INT, i),
500 MontyNode::BigInt(bi) => encoding::message::encoded_len(tag::BIGINT, &bigint_to_proto(bi)),
501 MontyNode::Float(f) => encoding::double::encoded_len(tag::FLOAT, f),
502 MontyNode::Complex(c) => encoding::message::encoded_len(tag::COMPLEX, &complex_to_proto(c)),
503 MontyNode::String(s) => encoding::string::encoded_len(tag::STR, s),
504 MontyNode::Bytes(b) => encoding::bytes::encoded_len(tag::BYTES, b),
505 MontyNode::List(ids) => submessage_len(tag::LIST, packed_ids_len(1, ids)),
506 MontyNode::Tuple(ids) => submessage_len(tag::TUPLE, packed_ids_len(1, ids)),
507 MontyNode::NamedTuple {
508 type_name,
509 field_names,
510 values,
511 } => submessage_len(tag::NAMED_TUPLE, named_tuple_len(type_name, field_names, values)),
512 MontyNode::Dict(pairs) => submessage_len(tag::DICT, node_pairs_len(1, pairs)),
513 MontyNode::Set(ids) => submessage_len(tag::SET, packed_ids_len(1, ids)),
514 MontyNode::FrozenSet(ids) => submessage_len(tag::FROZEN_SET, packed_ids_len(1, ids)),
515 MontyNode::Date(d) => encoding::message::encoded_len(tag::DATE, &date_to_proto(d)),
516 MontyNode::DateTime(dt) => submessage_len(tag::DATETIME, datetime_len(dt)),
517 MontyNode::Time(t) => submessage_len(tag::TIME, time_len(t)),
518 MontyNode::TimeDelta(td) => encoding::message::encoded_len(tag::TIMEDELTA, &timedelta_to_proto(td)),
519 MontyNode::TimeZone(tz) => submessage_len(tag::TIMEZONE, timezone_len(tz)),
520 MontyNode::Exception { exc_type, arg } => {
521 let name = exc_type.to_string();
522 submessage_len(tag::EXCEPTION, str_len(1, &name) + opt_str_len(2, arg.as_deref()))
523 }
524 MontyNode::Type(t) => encoding::message::encoded_len(tag::TYPE, &builtin_type_to_pb(*t)),
525 MontyNode::ClassType(class) => submessage_len(tag::TYPE, class_type_len(class)),
526 MontyNode::ClassInstance {
527 class_type,
528 instance_id,
529 attrs,
530 } => submessage_len(
531 tag::CLASS_INSTANCE,
532 class_instance_len(*class_type, &uuid_to_pb(instance_id), attrs),
533 ),
534 MontyNode::Module { name, id, attrs } => submessage_len(tag::MODULE, module_len(name, &uuid_to_pb(id), attrs)),
535 MontyNode::BuiltinFunction(bf) => encoding::string::encoded_len(tag::BUILTIN_FUNCTION, &bf.to_string()),
536 MontyNode::Path(p) => encoding::string::encoded_len(tag::PATH, p),
537 MontyNode::Decimal(d) => encoding::string::encoded_len(tag::DECIMAL, d),
538 MontyNode::FileHandle(fh) => submessage_len(tag::FILE_HANDLE, file_handle_len(fh)),
539 MontyNode::Function { name, docstring } => {
540 submessage_len(tag::FUNCTION, str_len(1, name) + opt_str_len(2, docstring.as_deref()))
541 }
542 MontyNode::Repr(r) => encoding::string::encoded_len(tag::REPR, r),
543 MontyNode::Cycle(placeholder) => encoding::string::encoded_len(tag::CYCLE, placeholder),
544 }
545}
546
547fn encode_source_range(tag: u32, range: &SourceRange, buf: &mut impl BufMut) {
550 encode_message_key(tag, source_range_len(range), buf);
551 encode_str(1, &range.filename, buf);
552 encode_uint32(2, range.start, buf);
553 encode_uint32(3, range.end, buf);
554}
555
556fn source_range_len(range: &SourceRange) -> usize {
558 str_len(1, &range.filename) + uint32_len(2, range.start) + uint32_len(3, range.end)
559}
560
561fn encode_message_key(tag: u32, body_len: usize, buf: &mut impl BufMut) {
563 encode_key(tag, WireType::LengthDelimited, buf);
564 encode_varint(body_len as u64, buf);
565}
566
567fn submessage_len(tag: u32, body_len: usize) -> usize {
569 key_len(tag) + encoded_len_varint(body_len as u64) + body_len
570}
571
572fn encode_indexes(tag: u32, ids: &[NodeId], buf: &mut impl BufMut) {
574 encode_message_key(tag, packed_ids_len(1, ids), buf);
575 encode_packed_ids(1, ids, buf);
576}
577
578fn encode_packed_ids(tag: u32, ids: &[NodeId], buf: &mut impl BufMut) {
581 if !ids.is_empty() {
582 encode_message_key(tag, packed_body_len(ids), buf);
583 for id in ids {
584 encode_varint(u64::from(id.0), buf);
585 }
586 }
587}
588
589fn packed_ids_len(tag: u32, ids: &[NodeId]) -> usize {
590 if ids.is_empty() {
591 0
592 } else {
593 submessage_len(tag, packed_body_len(ids))
594 }
595}
596
597fn packed_body_len(ids: &[NodeId]) -> usize {
599 ids.iter().map(|id| encoded_len_varint(u64::from(id.0))).sum()
600}
601
602fn encode_node_pairs(tag: u32, pairs: &[(NodeId, NodeId)], buf: &mut impl BufMut) {
605 for (key, value) in pairs {
606 encode_message_key(tag, node_pair_len(*key, *value), buf);
607 encode_uint32(1, key.0, buf);
608 encode_uint32(2, value.0, buf);
609 }
610}
611
612fn node_pairs_len(tag: u32, pairs: &[(NodeId, NodeId)]) -> usize {
613 pairs
614 .iter()
615 .map(|(key, value)| submessage_len(tag, node_pair_len(*key, *value)))
616 .sum()
617}
618
619fn node_pair_len(key: NodeId, value: NodeId) -> usize {
620 uint32_len(1, key.0) + uint32_len(2, value.0)
621}
622
623fn named_tuple_len(type_name: &str, field_names: &[String], values: &[NodeId]) -> usize {
626 str_len(1, type_name) + repeated_str_len(2, field_names) + packed_ids_len(3, values)
627}
628
629fn class_type_len(class: &ClassTypeNode) -> usize {
633 str_len(1, &class.name)
634 + encoding::message::encoded_len(2, &uuid_to_pb(&class.id))
635 + int32_len(3, class_origin(class) as i32)
636 + if class.is_dataclass {
637 encoding::bool::encoded_len(4, &true)
638 } else {
639 0
640 }
641 + if class.attrs.is_empty() {
642 0
643 } else {
644 submessage_len(5, node_pairs_len(1, &class.attrs))
645 }
646}
647
648fn encode_class_type(class: &ClassTypeNode, buf: &mut impl BufMut) {
649 encode_str(1, &class.name, buf);
650 encoding::message::encode(2, &uuid_to_pb(&class.id), buf);
651 encode_int32(3, class_origin(class) as i32, buf);
652 if class.is_dataclass {
653 encoding::bool::encode(4, &true, buf);
654 }
655 if !class.attrs.is_empty() {
656 encode_message_key(5, node_pairs_len(1, &class.attrs), buf);
657 encode_node_pairs(1, &class.attrs, buf);
658 }
659}
660
661fn class_origin(class: &ClassTypeNode) -> pb::TypeOrigin {
662 if class.host_defined {
663 pb::TypeOrigin::Host
664 } else {
665 pb::TypeOrigin::Sandbox
666 }
667}
668
669fn class_instance_len(class_type: NodeId, id: &pb::Uuid, attrs: &[(NodeId, NodeId)]) -> usize {
672 uint32_len(1, class_type.0) + encoding::message::encoded_len(2, id) + submessage_len(3, node_pairs_len(1, attrs))
673}
674
675fn module_len(name: &str, id: &pb::Uuid, attrs: &[(NodeId, NodeId)]) -> usize {
677 str_len(1, name) + encoding::message::encoded_len(2, id) + submessage_len(3, node_pairs_len(1, attrs))
678}
679
680fn datetime_len(dt: &MontyDateTime) -> usize {
683 int32_len(1, dt.year)
684 + uint32_len(2, u32::from(dt.month))
685 + uint32_len(3, u32::from(dt.day))
686 + uint32_len(4, u32::from(dt.hour))
687 + uint32_len(5, u32::from(dt.minute))
688 + uint32_len(6, u32::from(dt.second))
689 + uint32_len(7, dt.microsecond)
690 + dt.offset_seconds.map_or(0, |off| encoding::int32::encoded_len(8, &off))
691 + opt_str_len(9, dt.timezone_name.as_deref())
692}
693
694fn encode_datetime(dt: &MontyDateTime, buf: &mut impl BufMut) {
695 encode_int32(1, dt.year, buf);
696 encode_uint32(2, u32::from(dt.month), buf);
697 encode_uint32(3, u32::from(dt.day), buf);
698 encode_uint32(4, u32::from(dt.hour), buf);
699 encode_uint32(5, u32::from(dt.minute), buf);
700 encode_uint32(6, u32::from(dt.second), buf);
701 encode_uint32(7, dt.microsecond, buf);
702 if let Some(off) = dt.offset_seconds {
703 encoding::int32::encode(8, &off, buf);
704 }
705 encode_opt_str(9, dt.timezone_name.as_deref(), buf);
706}
707
708fn time_len(t: &MontyTime) -> usize {
711 uint32_len(1, u32::from(t.hour))
712 + uint32_len(2, u32::from(t.minute))
713 + uint32_len(3, u32::from(t.second))
714 + uint32_len(4, t.microsecond)
715 + t.offset_seconds.map_or(0, |off| encoding::int32::encoded_len(5, &off))
716 + opt_str_len(6, t.timezone_name.as_deref())
717 + uint32_len(7, u32::from(t.fold))
718}
719
720fn encode_time(t: &MontyTime, buf: &mut impl BufMut) {
721 encode_uint32(1, u32::from(t.hour), buf);
722 encode_uint32(2, u32::from(t.minute), buf);
723 encode_uint32(3, u32::from(t.second), buf);
724 encode_uint32(4, t.microsecond, buf);
725 if let Some(off) = t.offset_seconds {
726 encoding::int32::encode(5, &off, buf);
727 }
728 encode_opt_str(6, t.timezone_name.as_deref(), buf);
729 encode_uint32(7, u32::from(t.fold), buf);
730}
731
732fn timezone_len(tz: &MontyTimeZone) -> usize {
734 int32_len(1, tz.offset_seconds) + opt_str_len(2, tz.name.as_deref())
735}
736
737fn file_handle_len(fh: &MontyFileHandle) -> usize {
740 str_len(1, &fh.path) + str_len(2, fh.mode.as_str()) + uint64_len(3, fh.position)
741}
742
743fn encode_str(tag: u32, s: &str, buf: &mut impl BufMut) {
747 if !s.is_empty() {
748 encode_message_key(tag, s.len(), buf);
749 buf.put_slice(s.as_bytes());
750 }
751}
752
753fn str_len(tag: u32, s: &str) -> usize {
754 if s.is_empty() { 0 } else { submessage_len(tag, s.len()) }
755}
756
757fn encode_opt_str(tag: u32, s: Option<&str>, buf: &mut impl BufMut) {
760 if let Some(s) = s {
761 encode_message_key(tag, s.len(), buf);
762 buf.put_slice(s.as_bytes());
763 }
764}
765
766fn opt_str_len(tag: u32, s: Option<&str>) -> usize {
767 s.map_or(0, |s| submessage_len(tag, s.len()))
768}
769
770fn encode_repeated_str(tag: u32, items: &[String], buf: &mut impl BufMut) {
772 for s in items {
773 encode_message_key(tag, s.len(), buf);
774 buf.put_slice(s.as_bytes());
775 }
776}
777
778fn repeated_str_len(tag: u32, items: &[String]) -> usize {
779 items.iter().map(|s| submessage_len(tag, s.len())).sum()
780}
781
782fn encode_int32(tag: u32, value: i32, buf: &mut impl BufMut) {
784 if value != 0 {
785 encoding::int32::encode(tag, &value, buf);
786 }
787}
788
789fn int32_len(tag: u32, value: i32) -> usize {
790 if value == 0 {
791 0
792 } else {
793 encoding::int32::encoded_len(tag, &value)
794 }
795}
796
797fn encode_uint32(tag: u32, value: u32, buf: &mut impl BufMut) {
799 if value != 0 {
800 encoding::uint32::encode(tag, &value, buf);
801 }
802}
803
804fn uint32_len(tag: u32, value: u32) -> usize {
805 if value == 0 {
806 0
807 } else {
808 encoding::uint32::encoded_len(tag, &value)
809 }
810}
811
812fn encode_uint64(tag: u32, value: u64, buf: &mut impl BufMut) {
814 if value != 0 {
815 encoding::uint64::encode(tag, &value, buf);
816 }
817}
818
819fn uint64_len(tag: u32, value: u64) -> usize {
820 if value == 0 {
821 0
822 } else {
823 encoding::uint64::encoded_len(tag, &value)
824 }
825}
826
827#[expect(clippy::inline_always, reason = "measured improvement in frame decode benchmarks")]
835#[inline(always)]
836fn node_from_proto(node: pb::MontyNode) -> Result<MontyNode, DecodeError> {
837 let kind = node
838 .kind
839 .ok_or_else(|| to_decode_err(ProtoConvertError::MissingField("MontyNode.kind")))?;
840 Ok(match kind {
841 Kind::Ellipsis(_) => MontyNode::Ellipsis,
842 Kind::NotImplemented(_) => MontyNode::NotImplemented,
843 Kind::None(_) => MontyNode::None,
844 Kind::Boolean(value) => MontyNode::Bool(value),
845 Kind::Int(value) => MontyNode::Int(value),
846 Kind::Float(value) => MontyNode::Float(value),
847 Kind::Complex(value) => MontyNode::Complex(complex_from_proto(&value).map_err(to_decode_err)?),
848 Kind::Str(value) => MontyNode::String(value),
849 Kind::Bytes(value) => MontyNode::Bytes(value.into_inner()),
850 Kind::List(value) => MontyNode::List(value.0.into_inner()),
851 Kind::Tuple(value) => MontyNode::Tuple(value.0.into_inner()),
852 Kind::Set(value) => MontyNode::Set(value.0.into_inner()),
853 Kind::FrozenSet(value) => MontyNode::FrozenSet(value.0.into_inner()),
854 Kind::NamedTuple(value) => MontyNode::NamedTuple {
855 type_name: value.type_name,
856 field_names: value.field_names.into_inner(),
857 values: value.values.into_inner(),
858 },
859 Kind::Dict(value) => MontyNode::Dict(value.0.into_inner()),
860 Kind::Path(value) => MontyNode::Path(value),
861 Kind::Decimal(value) => MontyNode::Decimal(value),
864 Kind::Function(value) => MontyNode::Function {
865 name: value.name,
866 docstring: value.docstring,
867 },
868 Kind::Repr(value) => MontyNode::Repr(value),
869 Kind::Cycle(value) => MontyNode::Cycle(value),
870 Kind::Bigint(value) => MontyNode::BigInt(bigint_from_proto(value)?),
871 Kind::Type(value) => type_to_node(value)?,
872 Kind::ClassInstance(value) => {
873 let instance_id = value
874 .instance_id
875 .ok_or_else(|| to_decode_err(ProtoConvertError::MissingField("ClassInstanceNode.instance_id")))?;
876 let attrs = value
877 .attrs
878 .ok_or_else(|| to_decode_err(ProtoConvertError::MissingField("ClassInstanceNode.attrs")))?;
879 MontyNode::ClassInstance {
880 class_type: NodeId(value.class_type),
881 instance_id: pb_uuid_to_monty(&instance_id, "ClassInstanceNode.instance_id")?,
882 attrs: attrs.0.into_inner(),
883 }
884 }
885 Kind::Module(value) => {
886 let id = value
887 .id
888 .ok_or_else(|| to_decode_err(ProtoConvertError::MissingField("ModuleNode.id")))?;
889 let attrs = value
890 .attrs
891 .ok_or_else(|| to_decode_err(ProtoConvertError::MissingField("ModuleNode.attrs")))?;
892 MontyNode::Module {
893 name: value.name,
894 id: pb_uuid_to_monty(&id, "ModuleNode.id")?,
895 attrs: attrs.0.into_inner(),
896 }
897 }
898 Kind::Date(value) => MontyNode::Date(date_from_proto(&value).map_err(to_decode_err)?),
899 Kind::Datetime(value) => MontyNode::DateTime(datetime_from_proto(value).map_err(to_decode_err)?),
900 Kind::Time(value) => MontyNode::Time(time_from_proto(value).map_err(to_decode_err)?),
901 Kind::Timedelta(value) => MontyNode::TimeDelta(timedelta_from_proto(&value).map_err(to_decode_err)?),
902 Kind::Timezone(value) => MontyNode::TimeZone(MontyTimeZone {
903 offset_seconds: timezone_offset(value.offset_seconds, "TimeZone.offset_seconds").map_err(to_decode_err)?,
904 name: value.name,
905 }),
906 Kind::Exception(value) => MontyNode::Exception {
907 exc_type: value
908 .exc_type
909 .parse()
910 .map_err(|_| to_decode_err(ProtoConvertError::UnknownExcType(value.exc_type)))?,
911 arg: value.arg,
912 },
913 Kind::BuiltinFunction(name) => MontyNode::BuiltinFunction(
914 name.parse::<BuiltinsFunctions>()
915 .map_err(|_| to_decode_err(ProtoConvertError::UnknownBuiltinFunction(name)))?,
916 ),
917 Kind::FileHandle(value) => MontyNode::FileHandle(MontyFileHandle {
918 mode: value
919 .mode
920 .parse()
921 .map_err(|_| to_decode_err(ProtoConvertError::InvalidFileMode(value.mode)))?,
922 path: value.path,
923 position: value.position,
924 }),
925 Kind::Uuid(_) => return Err(to_decode_err(ProtoConvertError::MissingField("MontyNode.kind"))),
927 })
928}
929
930fn merge_message<M: Message + Default>(
934 wire_type: WireType,
935 buf: &mut impl Buf,
936 ctx: DecodeContext,
937) -> Result<M, DecodeError> {
938 let mut msg = M::default();
939 encoding::message::merge(wire_type, &mut msg, buf, ctx)?;
940 Ok(msg)
941}
942
943fn merge_ids(
947 wire_type: WireType,
948 buf: &mut impl Buf,
949 ctx: DecodeContext,
950 ids: &mut BudgetVec<NodeId>,
951) -> Result<(), DecodeError> {
952 let mut id = 0u32;
953 if wire_type == WireType::LengthDelimited {
954 let len = decode_varint(buf)?;
956 let len = usize::try_from(len)
957 .ok()
958 .filter(|len| *len <= buf.remaining())
959 .ok_or_else(|| to_decode_err("buffer underflow"))?;
960 reserve_charged(ids, len)?;
961 let end = buf.remaining() - len;
962 while buf.remaining() > end {
963 encoding::uint32::merge(WireType::Varint, &mut id, buf, ctx.clone())?;
964 ids.try_push_reserved(NodeId(id))?;
965 }
966 if buf.remaining() == end {
967 Ok(())
968 } else {
969 Err(to_decode_err("delimited length exceeded"))
970 }
971 } else {
972 encoding::uint32::merge(wire_type, &mut id, buf, ctx)?;
973 push_charged(ids, NodeId(id))
974 }
975}
976
977const REFERENCE_COST: usize = 2 * size_of::<usize>();
982
983trait DecodeCost {
985 const COST: usize;
986}
987
988impl DecodeCost for NodeId {
989 const COST: usize = REFERENCE_COST;
990}
991
992impl DecodeCost for (NodeId, NodeId) {
993 const COST: usize = 2 * REFERENCE_COST;
994}
995
996fn push_charged<T: DecodeCost>(vec: &mut BudgetVec<T>, item: T) -> Result<(), DecodeError> {
998 if vec.len() == vec.capacity() {
999 let new_capacity = vec
1000 .capacity()
1001 .checked_mul(2)
1002 .ok_or_else(decode_budget::exhausted)?
1003 .max(MIN_VEC_CAPACITY);
1004 reserve_charged(vec, new_capacity - vec.len())?;
1005 }
1006 vec.try_push_reserved(item)
1007}
1008
1009fn reserve_charged<T: DecodeCost>(vec: &mut BudgetVec<T>, additional: usize) -> Result<(), DecodeError> {
1011 let capacity = vec.len().checked_add(additional).ok_or_else(decode_budget::exhausted)?;
1012 vec.try_reserve_capacity_with_overhead(capacity, T::COST - size_of::<T>())
1013}
1014
1015#[expect(deprecated)]
1022fn to_decode_err(err: impl Display) -> DecodeError {
1023 let mut buffer = [0; 512];
1024 let mut cursor = Cursor::new(buffer.as_mut_slice());
1025 if write!(cursor, "{err}").is_ok() {
1026 let len = usize::try_from(cursor.position()).expect("bounded error buffer");
1027 DecodeError::new(str::from_utf8(&buffer[..len]).expect("Display writes UTF-8").to_owned())
1028 } else {
1029 DecodeError::new("invalid wire value (error message exceeds 512 bytes)")
1030 }
1031}
1032
1033pub(crate) fn uuid_to_pb(uuid: &MontyUuid) -> pb::Uuid {
1039 pb::Uuid {
1040 data: uuid.as_bytes().to_vec().into(),
1041 }
1042}
1043
1044fn pb_uuid_to_monty(uuid: &pb::Uuid, field: &'static str) -> Result<MontyUuid, DecodeError> {
1046 MontyUuid::try_from_slice(&uuid.data).ok_or_else(|| {
1047 to_decode_err(ProtoConvertError::InvalidValue {
1048 field,
1049 reason: format!("uuid must be 16 bytes, got {}", uuid.data.len()),
1050 })
1051 })
1052}
1053
1054fn builtin_type_to_pb(t: MontyType) -> pb::Type {
1058 pb::Type {
1059 name: t.to_string(),
1060 origin: pb::TypeOrigin::Builtin as i32,
1061 ..pb::Type::default()
1062 }
1063}
1064
1065fn type_to_node(ty: pb::Type) -> Result<MontyNode, DecodeError> {
1069 let origin = pb::TypeOrigin::try_from(ty.origin).map_err(|_| {
1070 to_decode_err(ProtoConvertError::InvalidValue {
1071 field: "Type.origin",
1072 reason: format!("unknown origin {}", ty.origin),
1073 })
1074 })?;
1075 let invalid = |reason: &str| {
1076 to_decode_err(ProtoConvertError::InvalidValue {
1077 field: "Type",
1078 reason: reason.to_owned(),
1079 })
1080 };
1081 match origin {
1082 pb::TypeOrigin::Unspecified => Err(invalid("origin must be specified")),
1083 pb::TypeOrigin::Builtin => {
1084 if ty.id.is_some() {
1085 Err(invalid("a builtin type must not carry an id"))
1086 } else if ty.attrs.is_some() {
1087 Err(invalid("a builtin type must not carry attrs"))
1088 } else {
1089 MontyType::from_type_name(&ty.name)
1090 .map(MontyNode::Type)
1091 .ok_or_else(|| to_decode_err(ProtoConvertError::UnknownType(ty.name)))
1092 }
1093 }
1094 pb::TypeOrigin::Sandbox | pb::TypeOrigin::Host => {
1095 let id = ty.id.ok_or_else(|| invalid("a class type must carry an id"))?;
1096 Ok(MontyNode::ClassType(decode_budget::boxed(ClassTypeNode {
1097 name: ty.name,
1098 id: pb_uuid_to_monty(&id, "Type.id")?,
1099 host_defined: origin == pb::TypeOrigin::Host,
1100 is_dataclass: ty.is_dataclass,
1101 attrs: ty.attrs.map(|attrs| attrs.0.into_inner()).unwrap_or_default(),
1102 })?))
1103 }
1104 }
1105}
1106
1107fn bigint_to_proto(bi: &BigInt) -> pb::BigInt {
1109 let (sign, magnitude) = bi.to_bytes_be();
1110 pb::BigInt {
1111 negative: sign == Sign::Minus,
1112 magnitude: magnitude.into(),
1113 }
1114}
1115
1116fn bigint_from_proto(mut bi: pb::BigInt) -> Result<BigInt, DecodeError> {
1121 let sign = if bi.negative { Sign::Minus } else { Sign::Plus };
1122 bi.magnitude.reverse();
1124 let len = bi.magnitude.iter().rposition(|&byte| byte != 0).map_or(0, |i| i + 1);
1125 bi.magnitude.truncate(len);
1126 if len > 0 {
1127 decode_budget::charge((len.div_ceil(8) * 8).max(32))?;
1129 }
1130 Ok(BigInt::from_bytes_le(sign, &bi.magnitude))
1131}
1132
1133fn date_to_proto(d: &MontyDate) -> pb::Date {
1134 pb::Date {
1135 year: d.year,
1136 month: u32::from(d.month),
1137 day: u32::from(d.day),
1138 }
1139}
1140
1141fn date_from_proto(d: &pb::Date) -> Result<MontyDate, ProtoConvertError> {
1142 let (year, month, day) = date_fields(d.year, d.month, d.day, ["Date.year", "Date.month", "Date.day"])?;
1143 Ok(MontyDate { year, month, day })
1144}
1145
1146fn datetime_from_proto(dt: pb::DateTime) -> Result<MontyDateTime, ProtoConvertError> {
1147 if dt.offset_seconds.is_none() && dt.timezone_name.is_some() {
1148 return Err(ProtoConvertError::InvalidValue {
1149 field: "DateTime.timezone_name",
1150 reason: "timezone_name requires offset_seconds".to_owned(),
1151 });
1152 }
1153 let (year, month, day) = date_fields(
1154 dt.year,
1155 dt.month,
1156 dt.day,
1157 ["DateTime.year", "DateTime.month", "DateTime.day"],
1158 )?;
1159 Ok(MontyDateTime {
1160 year,
1161 month,
1162 day,
1163 hour: ranged_u8(dt.hour, 0..=23, "DateTime.hour")?,
1164 minute: ranged_u8(dt.minute, 0..=59, "DateTime.minute")?,
1165 second: ranged_u8(dt.second, 0..=59, "DateTime.second")?,
1166 microsecond: bounded(dt.microsecond, 999_999, "DateTime.microsecond")?,
1167 offset_seconds: dt
1168 .offset_seconds
1169 .map(|offset| timezone_offset(offset, "DateTime.offset_seconds"))
1170 .transpose()?,
1171 timezone_name: dt.timezone_name,
1172 })
1173}
1174
1175fn time_from_proto(t: pb::Time) -> Result<MontyTime, ProtoConvertError> {
1176 if t.offset_seconds.is_none() && t.timezone_name.is_some() {
1177 return Err(ProtoConvertError::InvalidValue {
1178 field: "Time.timezone_name",
1179 reason: "timezone_name requires offset_seconds".to_owned(),
1180 });
1181 }
1182 Ok(MontyTime {
1183 hour: ranged_u8(t.hour, 0..=23, "Time.hour")?,
1184 minute: ranged_u8(t.minute, 0..=59, "Time.minute")?,
1185 second: ranged_u8(t.second, 0..=59, "Time.second")?,
1186 microsecond: bounded(t.microsecond, 999_999, "Time.microsecond")?,
1187 offset_seconds: t
1188 .offset_seconds
1189 .map(|offset| timezone_offset(offset, "Time.offset_seconds"))
1190 .transpose()?,
1191 timezone_name: t.timezone_name,
1192 fold: ranged_u8(t.fold, 0..=1, "Time.fold")?,
1193 })
1194}
1195
1196fn complex_to_proto(c: &MontyComplex) -> pb::Complex {
1197 pb::Complex {
1198 real: Some(c.real),
1199 imag: Some(c.imag),
1200 }
1201}
1202
1203fn complex_from_proto(c: &pb::Complex) -> Result<MontyComplex, ProtoConvertError> {
1206 Ok(MontyComplex {
1207 real: c.real.ok_or(ProtoConvertError::MissingField("Complex.real"))?,
1208 imag: c.imag.ok_or(ProtoConvertError::MissingField("Complex.imag"))?,
1209 })
1210}
1211
1212fn timedelta_to_proto(td: &MontyTimeDelta) -> pb::TimeDelta {
1213 pb::TimeDelta {
1214 days: td.days,
1215 seconds: td.seconds,
1216 microseconds: td.microseconds,
1217 }
1218}
1219
1220fn timedelta_from_proto(td: &pb::TimeDelta) -> Result<MontyTimeDelta, ProtoConvertError> {
1221 Ok(MontyTimeDelta {
1222 days: td.days,
1223 seconds: normalized(td.seconds, 86_400, "TimeDelta.seconds")?,
1227 microseconds: normalized(td.microseconds, 1_000_000, "TimeDelta.microseconds")?,
1228 })
1229}
1230
1231fn date_fields(year: i32, month: u32, day: u32, fields: [&'static str; 3]) -> Result<(i32, u8, u8), ProtoConvertError> {
1237 let [year_field, month_field, day_field] = fields;
1238 if !(1..=9999).contains(&year) {
1239 return Err(ProtoConvertError::InvalidValue {
1240 field: year_field,
1241 reason: format!("{year} is outside the range 1..=9999"),
1242 });
1243 }
1244 let month = ranged_u8(month, 1..=12, month_field)?;
1245 let day = ranged_u8(day, 1..=u32::from(days_in_month(year, month)), day_field)?;
1246 Ok((year, month, day))
1247}
1248
1249fn days_in_month(year: i32, month: u8) -> u8 {
1251 let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
1252 match month {
1253 2 if leap => 29,
1254 2 => 28,
1255 4 | 6 | 9 | 11 => 30,
1256 _ => 31,
1257 }
1258}
1259
1260fn ranged_u8(value: u32, range: RangeInclusive<u32>, field: &'static str) -> Result<u8, ProtoConvertError> {
1262 if range.contains(&value) {
1263 Ok(u8::try_from(value).expect("range bounds fit in u8"))
1264 } else {
1265 Err(ProtoConvertError::InvalidValue {
1266 field,
1267 reason: format!("{value} is outside the range {}..={}", range.start(), range.end()),
1268 })
1269 }
1270}
1271
1272fn timezone_offset(offset: i32, field: &'static str) -> Result<i32, ProtoConvertError> {
1276 if (MIN_TIMEZONE_OFFSET_SECONDS..=MAX_TIMEZONE_OFFSET_SECONDS).contains(&offset) {
1277 Ok(offset)
1278 } else {
1279 Err(ProtoConvertError::InvalidValue {
1280 field,
1281 reason: format!(
1282 "{offset} is outside the range {MIN_TIMEZONE_OFFSET_SECONDS}..={MAX_TIMEZONE_OFFSET_SECONDS}"
1283 ),
1284 })
1285 }
1286}
1287
1288fn normalized(value: i32, max: i32, field: &'static str) -> Result<i32, ProtoConvertError> {
1290 if (0..max).contains(&value) {
1291 Ok(value)
1292 } else {
1293 Err(ProtoConvertError::InvalidValue {
1294 field,
1295 reason: format!("{value} is outside the normalized range 0..{max}"),
1296 })
1297 }
1298}
1299
1300fn bounded(value: u32, max: u32, field: &'static str) -> Result<u32, ProtoConvertError> {
1302 if value <= max {
1303 Ok(value)
1304 } else {
1305 Err(ProtoConvertError::InvalidValue {
1306 field,
1307 reason: format!("{value} exceeds maximum {max}"),
1308 })
1309 }
1310}