use crate::arena::{ContextStack, DataValue, IterGuard, bvec};
use crate::node::PathSegment;
use crate::operators::meta::ArithOp;
use crate::{CompiledNode, Engine, Result};
use bumpalo::Bump;
use datavalue::{NumberValue, OwnedDataValue};
use std::ops::ControlFlow;
use super::fused::{FieldCursor, FusedMapBody, with_arith, with_ops};
use super::input::{Items, IterSrc, for_each_iter_array, for_each_iter_object};
use crate::operators::arithmetic::try_int_op;
#[inline]
pub(crate) fn evaluate_map<'a>(
items: Items<'a>,
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let body = &args[1];
let src = match items {
Items::Array(src) => src,
Items::Object(pairs) => return map_bridge_object(pairs, body, ctx, engine, arena),
Items::Scalar(value) => return map_bridge_single(value, body, ctx, engine, arena),
};
if super::fast_paths::allowed(ctx, engine)
&& let Some(shape) = FusedMapBody::detect(body)
&& let Some(result) = map_fused(&src, &shape, arena)
{
ctx.charge(shape.body_cost() * src.len() as u64)?;
return Ok(result);
}
map_general(&src, body, ctx, engine, arena)
}
#[inline]
fn map_fused<'a>(
src: &IterSrc<'a>,
shape: &FusedMapBody<'_>,
arena: &'a Bump,
) -> Option<&'a DataValue<'a>> {
match shape {
FusedMapBody::Extract { segments } => Some(map_extract(src, segments, arena)),
FusedMapBody::ArithVarLit {
op,
segments,
lit,
var_is_lhs,
} => map_arith_var_lit(src, *op, segments, lit, *var_is_lhs, arena),
FusedMapBody::ArithVarVar {
op,
a_segments,
b_segments,
} => map_arith_var_var(src, *op, a_segments, b_segments, arena),
}
}
#[inline]
fn map_arith_var_lit<'a>(
src: &IterSrc<'a>,
op: ArithOp,
var_segs: &[PathSegment],
lit_value: &OwnedDataValue,
var_is_lhs: bool,
arena: &'a Bump,
) -> Option<&'a DataValue<'a>> {
let lit = *lit_value.as_number()?;
let lit_f = lit.as_f64();
with_ops!(op, |int_op, float_op| match (lit.as_i64(), var_is_lhs) {
(Some(li), true) => map_numbers(src, var_segs, arena, |v| match v.as_i64() {
Some(x) => try_int_op(x, li, int_op, float_op),
None => NumberValue::from_f64(float_op(v.as_f64(), lit_f)),
}),
(Some(li), false) => map_numbers(src, var_segs, arena, |v| match v.as_i64() {
Some(x) => try_int_op(li, x, int_op, float_op),
None => NumberValue::from_f64(float_op(lit_f, v.as_f64())),
}),
(None, true) => map_numbers(src, var_segs, arena, |v| {
NumberValue::from_f64(float_op(v.as_f64(), lit_f))
}),
(None, false) => map_numbers(src, var_segs, arena, |v| {
NumberValue::from_f64(float_op(lit_f, v.as_f64()))
}),
})
}
#[inline(always)]
fn map_numbers<'a>(
src: &IterSrc<'a>,
segments: &[PathSegment],
arena: &'a Bump,
step: impl Fn(NumberValue) -> NumberValue,
) -> Option<&'a DataValue<'a>> {
let mut field = FieldCursor::new(segments);
let mut results = bvec::<DataValue<'a>>(arena, src.len());
for item in src.0 {
let v = *field.resolve(item)?.as_number()?;
results.push(DataValue::Number(step(v)));
}
Some(arena.alloc(DataValue::Array(results.into_bump_slice())))
}
#[inline]
fn map_arith_var_var<'a>(
src: &IterSrc<'a>,
op: ArithOp,
a_segs: &[PathSegment],
b_segs: &[PathSegment],
arena: &'a Bump,
) -> Option<&'a DataValue<'a>> {
let mut a_field = FieldCursor::new(a_segs);
let mut b_field = FieldCursor::new(b_segs);
let mut results = bvec::<DataValue<'a>>(arena, src.len());
with_arith!(op, |f| {
for item in src.0 {
let a = *a_field.resolve(item)?.as_number()?;
let b = *b_field.resolve(item)?.as_number()?;
results.push(DataValue::Number(f(a, b)));
}
});
Some(arena.alloc(DataValue::Array(results.into_bump_slice())))
}
#[inline]
fn map_extract<'a>(
src: &IterSrc<'a>,
segments: &[PathSegment],
arena: &'a Bump,
) -> &'a DataValue<'a> {
let len = src.len();
let mut results = bvec::<DataValue<'a>>(arena, len);
if segments.is_empty() {
for i in 0..len {
results.push(*src.get(i));
}
} else {
for i in 0..len {
let item = src.get(i);
match crate::arena::value::traverse_segments(item, segments) {
Some(v) => results.push(*v),
None => results.push(DataValue::Null),
}
}
}
arena.alloc(DataValue::Array(results.into_bump_slice()))
}
#[inline]
fn map_general<'a>(
src: &IterSrc<'a>,
body: &'a CompiledNode,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let mut results = bvec::<DataValue<'a>>(arena, src.len());
for_each_iter_array(src.0, body, ctx, engine, arena, |_, _item, av| {
results.push(*av);
Ok(ControlFlow::Continue(()))
})?;
Ok(arena.alloc(DataValue::Array(results.into_bump_slice())))
}
#[inline]
fn map_bridge_object<'a>(
pairs: &'a [(&'a str, DataValue<'a>)],
body: &'a CompiledNode,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let mut results = bvec::<DataValue<'a>>(arena, pairs.len());
for_each_iter_object(pairs, body, ctx, engine, arena, |_, _item, _key, av| {
results.push(*av);
Ok(ControlFlow::Continue(()))
})?;
Ok(arena.alloc(DataValue::Array(results.into_bump_slice())))
}
#[inline]
fn map_bridge_single<'a>(
input: &'a DataValue<'a>,
body: &'a CompiledNode,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let mut guard = IterGuard::new(ctx);
guard.step_indexed(input, 0);
let owned = *engine.run_iter_body(body, guard.stack(), arena, 0, 1)?;
let slice = arena.alloc_slice_fill_iter(std::iter::once(owned));
Ok(arena.alloc(DataValue::Array(slice)))
}