use std::collections::HashMap;
use std::convert::TryInto;
use std::io::Read;
use byteorder::{ByteOrder, LE, ReadBytesExt};
use zerocopy::{AsBytes, FromBytes};
use crate::errors::{Error, ErrContext};
use crate::index;
use crate::{Device, Result};
pub struct Handle<'c> {
device: Device<'c>,
handle: u32,
}
impl<'c> Handle<'c> {
pub fn new(device: Device<'c>, symbol: &str) -> Result<Self> {
let mut handle_bytes = [0; 4];
device.write_read_exact(index::GET_SYMHANDLE_BYNAME, 0, symbol.as_bytes(),
&mut handle_bytes)?;
Ok(Self { device, handle: u32::from_le_bytes(handle_bytes) })
}
pub fn raw(&self) -> u32 {
self.handle
}
pub fn read(&self, buf: &mut [u8]) -> Result<()> {
self.device.read_exact(index::RW_SYMVAL_BYHANDLE, self.handle, buf)
}
pub fn write(&self, buf: &[u8]) -> Result<()> {
self.device.write(index::RW_SYMVAL_BYHANDLE, self.handle, buf)
}
pub fn read_value<T: Default + AsBytes + FromBytes>(&self) -> Result<T> {
self.device.read_value(index::RW_SYMVAL_BYHANDLE, self.handle)
}
pub fn write_value<T: AsBytes>(&self, value: &T) -> Result<()> {
self.device.write_value(index::RW_SYMVAL_BYHANDLE, self.handle, value)
}
}
impl<'a> Drop for Handle<'a> {
fn drop(&mut self) {
let _ = self.device.write(index::RELEASE_SYMHANDLE, 0,
&self.handle.to_le_bytes());
}
}
pub fn get_size(device: Device<'_>, symbol: &str) -> Result<usize> {
let mut buf = [0; 12];
device.write_read_exact(index::GET_SYMINFO_BYNAME, 0, symbol.as_bytes(), &mut buf)?;
Ok(u32::from_le_bytes(buf[8..].try_into().expect("size")) as usize)
}
pub fn get_location(device: Device<'_>, symbol: &str) -> Result<(u32, u32)> {
let mut buf = [0; 12];
device.write_read_exact(index::GET_SYMINFO_BYNAME, 0, symbol.as_bytes(), &mut buf)?;
Ok((u32::from_le_bytes(buf[0..4].try_into().expect("size")),
u32::from_le_bytes(buf[4..8].try_into().expect("size"))))
}
pub struct Symbol {
pub name: String,
pub ix_group: u32,
pub ix_offset: u32,
pub typ: String,
pub size: usize,
pub base_type: u32,
pub flags: u32,
}
pub struct Type {
pub name: String,
pub size: usize,
pub array: Vec<(i32, i32)>,
pub fields: Vec<Field>,
pub base_type: u32,
pub flags: u32,
}
pub struct Field {
pub name: String,
pub typ: String,
pub offset: Option<u32>,
pub size: usize,
pub array: Vec<(i32, i32)>,
pub base_type: u32,
pub flags: u32,
}
pub type TypeMap = HashMap<String, Type>;
pub fn get_symbol_info(device: Device<'_>) -> Result<(Vec<Symbol>, TypeMap)> {
let mut read_data = [0; 64];
device.read_exact(index::SYM_UPLOAD_INFO2, 0, &mut read_data)?;
let symbol_len = LE::read_u32(&read_data[4..]) as usize;
let types_len = LE::read_u32(&read_data[12..]) as usize;
let mut type_data = vec![0; types_len];
device.read_exact(index::SYM_DT_UPLOAD, 0, &mut type_data)?;
let mut symbol_data = vec![0; symbol_len];
device.read_exact(index::SYM_UPLOAD, 0, &mut symbol_data)?;
decode_symbol_info(symbol_data, type_data)
}
pub fn decode_symbol_info(symbol_data: Vec<u8>, type_data: Vec<u8>) -> Result<(Vec<Symbol>, TypeMap)> {
let mut buf = [0; 1024];
let mut data_ptr = type_data.as_slice();
let mut type_map = HashMap::new();
fn decode_type_info(mut ptr: &[u8], parent: Option<&mut Type>) -> Result<Option<Type>> {
let ctx = "decoding type info";
let mut buf = [0; 1024];
let version = ptr.read_u32::<LE>().ctx(ctx)?;
if version != 1 {
return Err(Error::Reply(ctx, "unknown type info version", version));
}
let _subitem_index = ptr.read_u16::<LE>().ctx(ctx)?;
let _plc_interface_id = ptr.read_u16::<LE>().ctx(ctx)?;
let _reserved = ptr.read_u32::<LE>().ctx(ctx)?;
let size = ptr.read_u32::<LE>().ctx(ctx)? as usize;
let offset = ptr.read_u32::<LE>().ctx(ctx)?;
let base_type = ptr.read_u32::<LE>().ctx(ctx)?;
let flags = ptr.read_u32::<LE>().ctx(ctx)?;
let len_name = ptr.read_u16::<LE>().ctx(ctx)? as usize;
let len_type = ptr.read_u16::<LE>().ctx(ctx)? as usize;
let len_comment = ptr.read_u16::<LE>().ctx(ctx)? as usize;
let array_dim = ptr.read_u16::<LE>().ctx(ctx)?;
let sub_items = ptr.read_u16::<LE>().ctx(ctx)?;
ptr.read_exact(&mut buf[..len_name + 1]).ctx(ctx)?;
let name = String::from_utf8_lossy(&buf[..len_name]).into_owned();
ptr.read_exact(&mut buf[..len_type + 1]).ctx(ctx)?;
let typ = String::from_utf8_lossy(&buf[..len_type]).into_owned();
ptr = &ptr[len_comment + 1..];
let mut array = vec![];
for _ in 0..array_dim {
let lower = ptr.read_i32::<LE>().ctx(ctx)?;
let total = ptr.read_i32::<LE>().ctx(ctx)?;
array.push((lower, lower + total - 1));
}
if let Some(parent) = parent {
assert_eq!(sub_items, 0);
let offset = if offset == 0xFFFF_FFFF { None } else { Some(offset) };
parent.fields.push(Field { name, typ, offset, size, array, base_type, flags });
Ok(None)
} else {
assert_eq!(offset, 0);
let mut typinfo = Type { name, size, array, base_type, flags, fields: Vec::new() };
for _ in 0..sub_items {
let sub_size = ptr.read_u32::<LE>().ctx(ctx)? as usize;
let (sub_ptr, rest) = ptr.split_at(sub_size - 4);
decode_type_info(sub_ptr, Some(&mut typinfo))?;
ptr = rest;
}
Ok(Some(typinfo))
}
}
while !data_ptr.is_empty() {
let entry_size = data_ptr.read_u32::<LE>()
.ctx("decoding type info")? as usize;
let (entry_ptr, rest) = data_ptr.split_at(entry_size - 4);
let typ = decode_type_info(entry_ptr, None)?.expect("base type");
type_map.insert(typ.name.clone(), typ);
data_ptr = rest;
}
let mut symbols = Vec::new();
let mut data_ptr = symbol_data.as_slice();
let ctx = "decoding symbol info";
while !data_ptr.is_empty() {
let entry_size = data_ptr.read_u32::<LE>().ctx(ctx)? as usize;
let (mut entry_ptr, rest) = data_ptr.split_at(entry_size - 4);
let ix_group = entry_ptr.read_u32::<LE>().ctx(ctx)?;
let ix_offset = entry_ptr.read_u32::<LE>().ctx(ctx)?;
let size = entry_ptr.read_u32::<LE>().ctx(ctx)? as usize;
let base_type = entry_ptr.read_u32::<LE>().ctx(ctx)?;
let flags = entry_ptr.read_u16::<LE>().ctx(ctx)? as u32;
let _legacy_array_dim = entry_ptr.read_u16::<LE>().ctx(ctx)?;
let len_name = entry_ptr.read_u16::<LE>().ctx(ctx)? as usize;
let len_type = entry_ptr.read_u16::<LE>().ctx(ctx)? as usize;
let _len_comment = entry_ptr.read_u16::<LE>().ctx(ctx)? as usize;
entry_ptr.read_exact(&mut buf[..len_name + 1]).ctx(ctx)?;
let name = String::from_utf8_lossy(&buf[..len_name]).into_owned();
entry_ptr.read_exact(&mut buf[..len_type + 1]).ctx(ctx)?;
let typ = String::from_utf8_lossy(&buf[..len_type]).into_owned();
symbols.push(Symbol { name, ix_group, ix_offset, typ, size, base_type, flags });
data_ptr = rest;
}
Ok((symbols, type_map))
}