flash-lso 0.7.0

Fast and safe SOL/AMF0/AMF3 parsing. Supports serde, Adobe flex and cyclic references
Documentation
use crate::PADDING;
/// Support for encoding AMF0
use crate::types::{Element, Reference, Value};
use std::io::Write;

use crate::amf0::type_marker::TypeMarker;
use crate::nom_utils::write_string;
use crate::write::WriteExt;
use std::io::Result;

#[cfg(feature = "amf3")]
use crate::amf3::write::AMF3Encoder;

fn write_type_marker<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, type_: TypeMarker) -> Result<()> {
    writer.write_u8(type_ as u8)
}

fn write_reference_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, r: &Reference) -> Result<()> {
    write_type_marker(writer, TypeMarker::Reference)?;
    writer.write_u16(r.0)?;
    Ok(())
}

fn write_number_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, s: f64) -> Result<()> {
    write_type_marker(writer, TypeMarker::Number)?;
    writer.write_f64(s)?;
    Ok(())
}

fn write_bool_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, s: bool) -> Result<()> {
    write_type_marker(writer, TypeMarker::Boolean)?;
    writer.write_u8(u8::from(s))?;
    Ok(())
}

fn write_long_string_content<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, s: &'b str) -> Result<()> {
    writer.write_u32(s.len() as u32)?;
    writer.write_all(s.as_bytes())?;
    Ok(())
}

fn write_long_string_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, s: &'b str) -> Result<()> {
    write_type_marker(writer, TypeMarker::LongString)?;
    write_long_string_content(writer, s)?;
    Ok(())
}

fn write_string_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, s: &'b str) -> Result<()> {
    write_type_marker(writer, TypeMarker::String)?;
    write_string(writer, s)?;
    Ok(())
}

fn write_object_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, o: &'b [Element]) -> Result<()> {
    write_type_marker(writer, TypeMarker::Object)?;
    for element in o {
        write_element(writer, element)?;
    }
    writer.write_u16(0)?;
    write_type_marker(writer, TypeMarker::ObjectEnd)?;
    Ok(())
}

fn write_null_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W) -> Result<()> {
    write_type_marker(writer, TypeMarker::Null)
}

fn write_undefined_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W) -> Result<()> {
    write_type_marker(writer, TypeMarker::Undefined)
}

fn write_strict_array_element<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    elements: &'b [Value],
) -> Result<()> {
    write_type_marker(writer, TypeMarker::StrictArray)?;
    writer.write_u32(elements.len() as u32)?;
    for element in elements {
        write_value(writer, element)?;
    }
    Ok(())
}

fn write_date_element<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    date: f64,
    tz: Option<u16>,
) -> Result<()> {
    write_type_marker(writer, TypeMarker::Date)?;
    writer.write_f64(date)?;
    writer.write_u16(tz.unwrap_or(0))?;
    Ok(())
}

fn write_unsupported_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W) -> Result<()> {
    write_type_marker(writer, TypeMarker::Unsupported)
}

fn write_xml_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, content: &'b str) -> Result<()> {
    write_type_marker(writer, TypeMarker::Xml)?;
    write_long_string_content(writer, content)?;
    Ok(())
}

fn write_typed_object_element<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    name: &'b str,
    elements: &'b [Element],
) -> Result<()> {
    write_type_marker(writer, TypeMarker::TypedObject)?;
    write_string(writer, name)?;
    for element in elements {
        write_element(writer, element)?;
    }
    writer.write_u16(0)?;
    write_type_marker(writer, TypeMarker::ObjectEnd)?;
    Ok(())
}

fn write_dense_element<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    index: usize,
    element: &'b Value,
) -> Result<()> {
    let index_str = index.to_string();

    writer.write_u16(index_str.len() as u16)?;
    writer.write_all(index_str.as_bytes())?;
    write_value(writer, element)?;

    Ok(())
}

fn write_ecma_array<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    dense: &'b [Value],
    elements: &'b [Element],
    length: u32,
) -> Result<()> {
    //TODO: what is the u16 padding
    //TODO: sometimes array length is ignored (u32) sometimes its: elements.len() as u32

    write_type_marker(writer, TypeMarker::ECMAArray)?;
    writer.write_u32(length)?;
    for (idx, value) in dense.iter().enumerate() {
        write_dense_element(writer, idx, value)?
    }
    for element in elements {
        write_element(writer, element)?
    }
    writer.write_u16(0)?;
    write_type_marker(writer, TypeMarker::ObjectEnd)?;
    Ok(())
}

pub(crate) fn write_value<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    element: &'b Value,
) -> Result<()> {
    match element {
        Value::Number(n) => write_number_element(writer, *n),
        Value::Bool(b) => write_bool_element(writer, *b),
        Value::String(s) => {
            if s.len() > 65535 {
                write_long_string_element(writer, s)
            } else {
                write_string_element(writer, s)
            }
        }
        Value::Object { id: _, data } => {
            if let Some(class_def) = &data.class_definition {
                write_typed_object_element(writer, &class_def.name, &data.elements)
            } else {
                write_object_element(writer, &data.elements)
            }
        }
        Value::Null => write_null_element(writer),
        Value::Undefined => write_undefined_element(writer),
        Value::StrictArray { id: _, values } => write_strict_array_element(writer, values),
        Value::Date {
            time,
            timezone_or_utc,
        } => write_date_element(writer, *time, *timezone_or_utc),
        Value::Unsupported => write_unsupported_element(writer),
        Value::XML {
            value,
            is_string: _,
        } => write_xml_element(writer, value),
        Value::ECMAArray { id: _, data } => {
            write_ecma_array(writer, &data.dense, &data.elements, data.length)
        }
        #[cfg(feature = "amf3")]
        Value::AMF3(e) => {
            write_type_marker(writer, TypeMarker::AMF3)?;
            let encoder = AMF3Encoder::default();
            encoder.write_value_element(writer, e)
        }
        Value::Reference(r) => write_reference_element(writer, r),
        _ => {
            write_unsupported_element(writer) /* Not in amf0, TODO: use the amf3 embedding for every thing else */
        }
    }
}

fn write_element<'a, 'b: 'a, W: Write + 'a>(writer: &mut W, element: &'b Element) -> Result<()> {
    write_string(writer, &element.name)?;
    write_value(writer, &element.value)?;
    Ok(())
}

fn write_element_and_padding<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    element: &'b Element,
) -> Result<()> {
    write_element(writer, element)?;
    writer.write_all(&PADDING)?;
    Ok(())
}

pub(crate) fn write_body<'a, 'b: 'a, W: Write + 'a>(
    writer: &mut W,
    elements: &'b [Element],
) -> Result<()> {
    for element in elements {
        write_element_and_padding(writer, element)?;
    }
    Ok(())
}