use alloc::borrow::Cow;
use alloc::vec;
use alloc::vec::Vec;
use core::slice;
use core::str;
use deser_core::de::DeserializeDriver;
use deser_core::ext::ExtValue;
use deser_core::{Atom, Bytes, ContainerShape, Error, ErrorKind, Event, Text};
use crate::types::{ClassData, Form, FormData, Global, Kind, KindData, Reference, SharedIdData};
use crate::vm::{BytesKind, Graph, Id, Node, Object};
#[derive(Clone, Copy)]
enum Shape<'g> {
Atom(Id),
Seq(Option<Kind>, &'g [Id]),
Map(&'g [(Id, Id)], &'g [(Id, Id)]),
}
impl<'g> Shape<'g> {
fn len(&self) -> usize {
match *self {
Shape::Atom(_) => 0,
Shape::Seq(_, items) => items.len(),
Shape::Map(a, b) => a.len() + b.len(),
}
}
fn for_each_child<F: FnMut(Id)>(&self, mut f: F) {
match *self {
Shape::Atom(_) => {}
Shape::Seq(_, items) => items.iter().for_each(|&x| f(x)),
Shape::Map(a, b) => a.iter().chain(b).for_each(|&(k, v)| {
f(k);
f(v)
}),
}
}
}
fn has_identity(node: &Node<'_>) -> bool {
match node {
Node::List(_) | Node::Dict(_) | Node::Set(_) | Node::Object(_) => true,
Node::Tuple(items) | Node::FrozenSet(items) => !items.is_empty(),
_ => false,
}
}
fn plain_shape<'g>(graph: &'g Graph<'_>, id: Id) -> Shape<'g> {
match graph.node(id) {
Node::List(items) => Shape::Seq(None, items),
Node::Tuple(items) => Shape::Seq(Some(Kind::Tuple), items),
Node::Set(items) => Shape::Seq(Some(Kind::Set), items),
Node::FrozenSet(items) => Shape::Seq(Some(Kind::FrozenSet), items),
Node::Dict(entries) => Shape::Map(entries, &[]),
_ => Shape::Atom(id),
}
}
fn inline_shape<'g>(graph: &'g Graph<'_>, id: &'g Id) -> Shape<'g> {
match graph.node(*id) {
Node::Object(_) => Shape::Seq(Some(Kind::Tuple), slice::from_ref(id)),
_ => plain_shape(graph, *id),
}
}
fn object_shape<'g>(graph: &'g Graph<'_>, object: &'g Object) -> (Shape<'g>, Form) {
if !object.dict_items.is_empty() {
return (Shape::Map(&object.dict_items, &[]), Form::Items);
}
if !object.list_items.is_empty() {
return (Shape::Seq(None, &object.list_items), Form::Items);
}
if let Some(ref state) = object.state {
let mut form = Form::State;
let shape = match graph.node(*state) {
Node::None => None,
Node::Dict(entries) => Some(Shape::Map(entries, &[])),
Node::Tuple(items)
if items.len() == 2
&& items
.iter()
.all(|&x| matches!(graph.node(x), Node::None | Node::Dict(_))) =>
{
let entries = |id: Id| match graph.node(id) {
Node::Dict(entries) => &entries[..],
_ => &[][..],
};
form = Form::Slots;
Some(Shape::Map(entries(items[0]), entries(items[1])))
}
_ => Some(inline_shape(graph, state)),
};
if let Some(shape) = shape {
return (shape, form);
}
}
match object.args[..] {
[] => (Shape::Map(&object.kwargs, &[]), Form::Arguments),
[ref arg] if matches!(graph.node(*arg), Node::Object(_)) => {
(inline_shape(graph, arg), Form::Arguments)
}
[ref arg] => (inline_shape(graph, arg), Form::Argument),
_ => (Shape::Seq(Some(Kind::Tuple), &object.args), Form::Arguments),
}
}
fn shape<'g>(graph: &'g Graph<'_>, id: Id) -> (Shape<'g>, Option<(Id, Form)>) {
match graph.node(id) {
Node::Object(object) => {
let (shape, form) = object_shape(graph, object);
(shape, Some((object.class, form)))
}
_ => (plain_shape(graph, id), None),
}
}
fn count_references(graph: &Graph<'_>) -> Vec<u8> {
let mut counts = vec![0u8; graph.nodes.len()];
let mut seen = vec![false; graph.nodes.len()];
if has_identity(graph.node(graph.root)) {
counts[graph.root as usize] = 1;
}
let mut stack = vec![graph.root];
while let Some(id) = stack.pop() {
if seen[id as usize] {
continue;
}
seen[id as usize] = true;
shape(graph, id).0.for_each_child(|child| {
if has_identity(graph.node(child)) {
let count = &mut counts[child as usize];
*count = count.saturating_add(1).min(2);
if !seen[child as usize] {
stack.push(child);
}
}
});
}
counts
}
struct Frame<'g> {
node: Id,
is_map: bool,
shape: Shape<'g>,
pos: usize,
value: Option<Id>,
copy: bool,
}
struct Emitter<'g, 'i, 'a, 'd> {
graph: &'g Graph<'i>,
driver: &'a mut DeserializeDriver<'d, 'i>,
counts: Vec<u8>,
open: Vec<bool>,
emitted: Vec<bool>,
frames: Vec<Frame<'g>>,
budget: usize,
}
pub(crate) fn emit<'i>(
graph: &Graph<'i>,
driver: &mut DeserializeDriver<'_, 'i>,
max_shared_events: usize,
) -> Result<(), Error> {
let mut emitter = Emitter {
graph,
driver,
counts: count_references(graph),
open: vec![false; graph.nodes.len()],
emitted: vec![false; graph.nodes.len()],
frames: Vec::new(),
budget: max_shared_events,
};
emitter.value(graph.root, false)?;
while let Some(frame) = emitter.frames.last_mut() {
let copy = frame.copy;
let next = if let Some(value) = frame.value.take() {
Some(value)
} else if frame.pos < frame.shape.len() {
let pos = frame.pos;
frame.pos += 1;
match frame.shape {
Shape::Seq(_, items) => Some(items[pos]),
Shape::Map(a, b) => {
let (key, value) = if pos < a.len() {
a[pos]
} else {
b[pos - a.len()]
};
frame.value = Some(value);
Some(key)
}
Shape::Atom(_) => unreachable!(),
}
} else {
None
};
match next {
Some(child) => emitter.value(child, copy)?,
None => {
let frame = emitter.frames.pop().unwrap();
emitter.open[frame.node as usize] = false;
emitter.count(frame.copy)?;
let (start, end) = graph.ranges[frame.node as usize];
emitter.driver.state_mut().set_input_range(start, end);
emitter.driver.emit(match frame.is_map {
true => Event::MapEnd,
false => Event::SeqEnd,
})?;
}
}
}
Ok(())
}
impl<'g, 'i> Emitter<'g, 'i, '_, '_> {
fn count(&mut self, copy: bool) -> Result<(), Error> {
if copy {
if self.budget == 0 {
return Err(Error::new(
ErrorKind::LimitExceeded,
"shared values are emitted too often",
));
}
self.budget -= 1;
}
Ok(())
}
fn value(&mut self, id: Id, copy: bool) -> Result<(), Error> {
let graph = self.graph;
let identity = has_identity(graph.node(id));
let (start, end) = graph.ranges[id as usize];
self.driver.state_mut().set_input_range(start, end);
if identity && self.open[id as usize] {
self.count(copy)?;
return self
.driver
.emit(Atom::Ext(ExtValue::owned(Reference::new(id.into()))));
}
let copy = copy || identity && self.emitted[id as usize];
self.emitted[id as usize] = true;
self.count(copy)?;
let (shape, class) = shape(graph, id);
let state = self.driver.state_mut();
if let Some((class, form)) = class
&& let Node::Global { module, name, .. } = graph.node(class)
{
state.event_mut::<ClassData>().0 = Some(Global::new(module, name));
state.event_mut::<FormData>().0 = Some(form);
}
if self.counts[id as usize] > 1 {
state.event_mut::<SharedIdData>().0 = Some(id.into());
}
let is_map = match shape {
Shape::Atom(atom) => return self.atom(atom),
Shape::Seq(kind, _) => {
if kind.is_some() {
state.event_mut::<KindData>().0 = kind;
}
false
}
Shape::Map(..) => true,
};
let len = shape.len();
let mut container = ContainerShape::with_len(len);
container.set_ambiguous_empty(len == 0);
self.frames.push(Frame {
node: id,
is_map,
shape,
pos: 0,
value: None,
copy,
});
if identity {
self.open[id as usize] = true;
}
self.driver.emit(match is_map {
true => Event::MapStart(container),
false => Event::SeqStart(container),
})
}
fn atom(&mut self, id: Id) -> Result<(), Error> {
let driver = &mut *self.driver;
match self.graph.node(id) {
Node::None => driver.emit(Atom::Null),
Node::Bool(value) => driver.emit(Atom::Bool(*value)),
Node::Int(value) => driver.emit(match u64::try_from(*value) {
Ok(value) => Atom::U64(value),
Err(_) => Atom::I64(*value),
}),
Node::BigInt(value) => driver.emit(value.clone().into_atom()),
Node::Float(value) => driver.emit(Atom::F64(*value)),
Node::Str(text) => match *text {
Cow::Borrowed(text) => {
driver.emit_borrowed(Event::Atom(Atom::Str(Text::borrowed(text))))
}
Cow::Owned(ref text) => driver.emit(Atom::Str(Text::borrowed(text.as_str()))),
},
Node::Bytes(bytes, kind) => {
if *kind == BytesKind::ByteArray {
driver.state_mut().event_mut::<KindData>().0 = Some(Kind::ByteArray);
}
let as_text = *kind == BytesKind::Py2Str && str::from_utf8(bytes).is_ok();
match *bytes {
Cow::Borrowed(bytes) => driver.emit_borrowed(Event::Atom(match as_text {
true => Atom::Str(Text::borrowed(str::from_utf8(bytes).unwrap())),
false => Atom::Bytes(Bytes::borrowed(bytes)),
})),
Cow::Owned(ref bytes) => driver.emit(match as_text {
true => Atom::Str(Text::borrowed(str::from_utf8(bytes).unwrap())),
false => Atom::Bytes(Bytes::borrowed(bytes)),
}),
}
}
Node::Global { module, name, .. } => {
driver.emit(Atom::Ext(ExtValue::owned(Global::new(module, name))))
}
_ => unreachable!(),
}
}
}