From dd62ec934c67c27e1852bf1d50a3424b6518add1 Mon Sep 17 00:00:00 2001 From: lurchi Date: Tue, 23 Aug 2016 00:04:57 +0200 Subject: [PATCH] first version of the rust bindings: parsing and rendering is mostly functional --- rust/Cargo.toml | 6 + rust/build.rs | 18 ++ rust/src/lib.rs | 9 + rust/src/packet.rs | 347 ++++++++++++++++++++++++++++++++++++++ rust/src/packet_types.rs | 142 ++++++++++++++++ rust/src/parser.rs | 254 ++++++++++++++++++++++++++++ rust/src/parser_types.rs | 300 ++++++++++++++++++++++++++++++++ rust/src/types.rs | 9 + rust/tests/test_packet.rs | 97 +++++++++++ rust/tests/test_parser.rs | 26 +++ 10 files changed, 1208 insertions(+) create mode 100644 rust/Cargo.toml create mode 100644 rust/build.rs create mode 100644 rust/src/lib.rs create mode 100644 rust/src/packet.rs create mode 100644 rust/src/packet_types.rs create mode 100644 rust/src/parser.rs create mode 100644 rust/src/parser_types.rs create mode 100644 rust/src/types.rs create mode 100644 rust/tests/test_packet.rs create mode 100644 rust/tests/test_parser.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..38b5801 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "psyc" +version = "1.0.0" +links = "psyc" +build = "build.rs" +description = "rust bindings for libpsyc" diff --git a/rust/build.rs b/rust/build.rs new file mode 100644 index 0000000..bdb7fe8 --- /dev/null +++ b/rust/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::process::Command; +use std::path::*; + +fn main() { + Command::new("make") + .arg("-C") + .arg("..") + .output() + .unwrap_or_else(|e| panic!("failed to execute process: {}", e)); + + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let path = Path::new(&manifest_dir).parent().unwrap().join("lib"); + + println!("cargo:rustc-link-search=native={}", path.display()); + println!("cargo:rerun-if-changed={}", path.display()); + println!("cargo:rustc-link-lib=static=psyc"); +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..2972e4d --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,9 @@ +#![allow(dead_code)] +mod types; +pub mod parser_types; +pub mod packet_types; +pub mod parser; +pub mod packet; + +pub use parser::*; +pub use packet::*; diff --git a/rust/src/packet.rs b/rust/src/packet.rs new file mode 100644 index 0000000..e098610 --- /dev/null +++ b/rust/src/packet.rs @@ -0,0 +1,347 @@ +use types::*; +use packet_types::*; +use std::mem; +use std::ptr; +use std::os::raw::c_char; + +extern "C" { + /// functions from packet.h + fn psyc_num_length(n: usize) -> usize; + fn psyc_modifier_length_check(m: *const RawPsycModifier) -> PsycModifierFlag; + fn psyc_modifier_init(m: *mut RawPsycModifier, + oper: PsycOperator, + name: *const c_char, + namelen: usize, + value: *const c_char, + valuelen: usize, + flag: PsycModifierFlag); + + fn psyc_elem_length_check(value: *const PsycString, end: c_char) -> PsycElemFlag; + fn psyc_elem_length(elem: *const PsycElem) -> usize; + fn psyc_dict_key_length(elem: *const PsycDictKey) -> usize; + fn psyc_list_length_set(list: *mut RawPsycList) -> usize; + fn psyc_dict_length_set(dict: *mut RawPsycDict) -> usize; + fn psyc_modifier_length(m: *const RawPsycModifier) -> usize; + fn psyc_packet_length_check(p: *const RawPsycPacket) -> PsycPacketFlag; + fn psyc_packet_length_set(p: *mut RawPsycPacket) -> usize; + fn psyc_list_init(list: *mut RawPsycList, elems: *const PsycElem, num_elems: usize); + fn psyc_dict_init(dict: *mut RawPsycDict, elems: *const PsycDictElem, num_elems: usize); + fn psyc_packet_init(packet: *mut RawPsycPacket, + routing: *const RawPsycModifier, + routinglen: usize, + entity: *const RawPsycModifier, + entitylen: usize, + method: *const c_char, + methodlen: usize, + data: *const c_char, + datalen: usize, + stateop: c_char, + flag: PsycPacketFlag); + + fn psyc_packet_init_raw(packet: *mut RawPsycPacket, + routing: *const RawPsycModifier, + routinglen: usize, + content: *const c_char, + contentlen: usize, + flag: PsycPacketFlag); + + fn psyc_packet_id(list: *mut RawPsycList, + elems: *mut PsycElem, + context: *const c_char, + contextlen: usize, + source: *const c_char, + sourcelen: usize, + target: *const c_char, + targetlen: usize, + counter: *const c_char, + counterlen: usize, + fragment: *const c_char, + fragmentlen: usize); + + /// functions from render.h + fn psyc_render(packet: *const RawPsycPacket, buffer: *mut c_char, buflen: usize) -> PsycRenderRC; + fn psyc_render_modifier(modifier: *const RawPsycModifier, buffer: *mut c_char) -> usize; + fn psyc_render_elem(elem: *const PsycElem, buffer: *mut c_char, buflen: usize) -> PsycRenderRC; + fn psyc_render_dict_key(elem: *const PsycDictKey, buffer: *mut c_char, buflen: usize) -> PsycRenderRC; + fn psyc_render_list(list: *const RawPsycList, buffer: *mut c_char, buflen: usize) -> PsycRenderRC; + fn psyc_render_dict(dict: *const RawPsycDict, buffer: *mut c_char, buflen: usize) -> PsycRenderRC; +} + +pub struct PsycList { + rendered_list: Vec +} + +pub struct PsycDict { + rendered_dict: Vec +} + +pub struct PsycModifier<'a> { + name: &'a str, + value: &'a [u8], + operator: PsycOperator, +} + +pub struct PsycPacket<'a> { + raw_packet: RawPsycPacket, + routing_modifiers: &'a [PsycModifier<'a>], + entity_modifiers: &'a [PsycModifier<'a>], + raw_routing_modifiers: Vec, + raw_entity_modifiers: Vec, + method: &'a str, + body: &'a [u8] +} + +#[repr(C)] +#[derive(Debug, PartialEq)] +pub enum PsycRenderError { + MethodMissing = PsycRenderRC::PSYC_RENDER_ERROR_METHOD_MISSING as _, + ModifierNameMissing = PsycRenderRC::PSYC_RENDER_ERROR_MODIFIER_NAME_MISSING as _, + GenericError = PsycRenderRC::PSYC_RENDER_ERROR as _ +} + +impl PsycList { + /// Construct a PsycList from a list of byte lists + pub fn new(list: &[&[u8]]) -> Self { + let mut psyc_list: RawPsycList; + let elements: Vec; + let mut buffer: Vec; + unsafe { + psyc_list = mem::uninitialized(); + let psyc_list_ptr = &mut psyc_list as *mut RawPsycList; + elements = list.iter().map(|e| make_psyc_elem(&e)).collect(); + let elements_ptr = elements.as_ptr() as *const PsycElem; + psyc_list_init(psyc_list_ptr, elements_ptr, list.len()); + buffer = Vec::with_capacity(psyc_list.length); + buffer.set_len(psyc_list.length); + let buffer_ptr = buffer.as_ptr() as *mut c_char; + let _ = psyc_render_list(psyc_list_ptr, buffer_ptr, psyc_list.length); + } + PsycList { + rendered_list: buffer + } + } + + /// Construct a PsycList from a list of strings (comfort function) + pub fn from_strings(list: &[&str]) -> Self { + let list_slices: Vec<&[u8]> = list.iter().map(|e| e.as_bytes()).collect(); + Self::new(&list_slices) + } +} + +impl PsycDict { + /// Construct a PsycDict from a list of key value pairs + pub fn new(dict: &[(&[u8], &[u8])]) -> Self { + let mut psyc_dict: RawPsycDict; + let elements: Vec; + let mut buffer: Vec; + unsafe { + psyc_dict = mem::uninitialized(); + let psyc_dict_ptr = &mut psyc_dict as *mut RawPsycDict; + elements = dict.iter().map(|e| make_psyc_dict_elem(e)).collect(); + let elements_ptr = elements.as_ptr() as *const PsycDictElem; + psyc_dict_init(psyc_dict_ptr, elements_ptr, dict.len()); + buffer = Vec::with_capacity(psyc_dict.length); + buffer.set_len(psyc_dict.length); + let buffer_ptr = buffer.as_ptr() as *mut c_char; + let _ = psyc_render_dict(psyc_dict_ptr, buffer_ptr, psyc_dict.length); + } + PsycDict{ + rendered_dict: buffer + } + } + + /// Construct a PsycDict from a list of key / value string pairs (comfort + /// function) + pub fn from_strings(dict: &[(&str, &str)]) -> Self { + let kv_list_slices: Vec<(&[u8], &[u8])> = dict.iter().map(|e| { + let &(k, v) = e; + (k.as_bytes(), v.as_bytes()) + }).collect(); + Self::new(&kv_list_slices) + } +} + +impl<'a> PsycModifier<'a> { + /// construct a PsycModifier + pub fn new(operator: PsycOperator, name: &'a str, value: &'a [u8]) -> Self { + PsycModifier {name: name, value: value, operator: operator} + } + + /// construct a PsycModifier with string value (comfort function) + pub fn with_string_value(operator: PsycOperator, + name: &'a str, + value: &'a str) + -> Self { + PsycModifier { + name: name, + value: value.as_bytes(), + operator: operator + } + } + + /// construct a PsycModifier with list value + pub fn with_list_value(operator: PsycOperator, + name: &'a str, + value: &'a PsycList) + -> Self { + PsycModifier { + name: name, + value: &value.rendered_list, + operator: operator + } + } + + /// construct a PsycModifier with a dictionary value (comfort function) + pub fn with_dict_value(operator: PsycOperator, + name: &'a str, + value: &'a PsycDict) + -> Self { + PsycModifier { + name: name, + value: &value.rendered_dict, + operator: operator + } + } + + +} + +impl<'a> PsycPacket<'a> { + /// + pub fn new(routing_modifiers: &'a [PsycModifier], + entity_modifiers: &'a [PsycModifier], + method: &'a str, + data: &'a [u8], + state_operator: PsycStateOp) + -> Self { + let mut packet: RawPsycPacket; + let raw_routing_modifiers: Vec; + let raw_entity_modifiers: Vec; + unsafe{ + packet = mem::uninitialized(); + let packet_ptr = &mut packet as *mut RawPsycPacket; + raw_routing_modifiers = routing_modifiers.iter().map( + |m| Self::make_raw_modifier(m, PsycModifierFlag::PSYC_MODIFIER_ROUTING) + ).collect(); + raw_entity_modifiers = entity_modifiers.iter().map( + |m| Self::make_raw_modifier(m, PsycModifierFlag::PSYC_MODIFIER_CHECK_LENGTH) + ).collect(); + psyc_packet_init(packet_ptr, + raw_routing_modifiers.as_ptr() as *const RawPsycModifier, + raw_routing_modifiers.len(), + raw_entity_modifiers.as_ptr() as *const RawPsycModifier, + raw_entity_modifiers.len(), + method.as_ptr() as *const c_char, + method.len(), + data.as_ptr() as *const c_char, + data.len(), + state_operator as c_char, + PsycPacketFlag::PSYC_PACKET_CHECK_LENGTH); + } + PsycPacket { + raw_packet: packet, + routing_modifiers: routing_modifiers, + entity_modifiers: entity_modifiers, + raw_routing_modifiers: raw_routing_modifiers, + raw_entity_modifiers: raw_entity_modifiers, + method: method, + body: data + } + } + + pub fn new_from_raw(routing_modifiers: &'a [PsycModifier], + content: &'a [u8]) -> Self { + let mut packet: RawPsycPacket; + let raw_routing_modifiers: Vec; + unsafe { + packet = mem::uninitialized(); + let packet_ptr = &mut packet as *mut RawPsycPacket; + raw_routing_modifiers = routing_modifiers.iter().map( + |m| Self::make_raw_modifier(m, PsycModifierFlag::PSYC_MODIFIER_ROUTING) + ).collect(); + psyc_packet_init_raw(packet_ptr, + raw_routing_modifiers.as_ptr() as *const RawPsycModifier, + raw_routing_modifiers.len(), + content.as_ptr() as *const c_char, + content.len(), + PsycPacketFlag::PSYC_PACKET_CHECK_LENGTH); + } + PsycPacket { + raw_packet: packet, + routing_modifiers: routing_modifiers, + entity_modifiers: &[], + raw_routing_modifiers: raw_routing_modifiers, + raw_entity_modifiers: vec![], + method: "", + body: content + } + } + + /// the length of the rendered packet + pub fn length(&self) -> usize { + self.raw_packet.length + } + + /// + pub fn render(&self) -> Result, PsycRenderError> { + let raw_packet_ptr = & self.raw_packet as *const RawPsycPacket; + let mut buffer = Vec::with_capacity(self.length()); + let buffer_ptr = buffer.as_mut_ptr() as *mut c_char; + unsafe { + buffer.set_len(self.length()); + let result = psyc_render(raw_packet_ptr, buffer_ptr, buffer.capacity()); + match result { + PsycRenderRC::PSYC_RENDER_SUCCESS => Ok(buffer), + _error => Err(mem::transmute(_error)) + } + } + } + + fn make_raw_modifier(modifier: &'a PsycModifier, + flag: PsycModifierFlag) + -> RawPsycModifier { + let mut raw_modifier: RawPsycModifier; + unsafe { + raw_modifier = mem::uninitialized(); + let raw_modifier_ptr = &mut raw_modifier as *mut RawPsycModifier; + let name_ptr = modifier.name.as_ptr() as *const c_char; + let value_ptr = modifier.value.as_ptr() as *const c_char; + psyc_modifier_init(raw_modifier_ptr, + modifier.operator, + name_ptr, + modifier.name.len(), + value_ptr, + modifier.value.len(), + flag); + } + raw_modifier + } +} + +unsafe fn make_psyc_elem(list_element: &[u8]) -> PsycElem { + let list_element_ptr = list_element.as_ptr() as *const c_char; + PsycElem { + elem_type: PsycString {length: 0, data: ptr::null()}, + value: PsycString {length: list_element.len(), data: list_element_ptr}, + length: 0, + flag: PsycElemFlag::PSYC_ELEM_CHECK_LENGTH + } +} + +unsafe fn make_psyc_dict_elem(dict_element: &(&[u8], &[u8])) -> PsycDictElem { + let &(key, value) = dict_element; + let key_ptr = key.as_ptr() as *const c_char; + let psyc_dict_elem_value = make_psyc_elem(value); + let psyc_dict_elem_key = PsycDictKey { + value: PsycString { + length: key.len(), + data: key_ptr + }, + length: 0, + flag: PsycElemFlag::PSYC_ELEM_CHECK_LENGTH + }; + PsycDictElem { + value: psyc_dict_elem_value, + key: psyc_dict_elem_key + } +} diff --git a/rust/src/packet_types.rs b/rust/src/packet_types.rs new file mode 100644 index 0000000..b595b6c --- /dev/null +++ b/rust/src/packet_types.rs @@ -0,0 +1,142 @@ +use std::os::raw::c_char; +use types::*; + +#[repr(C)] +pub enum PsycModifierFlag { + /// Modifier needs to be checked if it needs length. + PSYC_MODIFIER_CHECK_LENGTH = 0, + /// Modifier needs length. + PSYC_MODIFIER_NEED_LENGTH = 1, + /// Modifier doesn't need length. + PSYC_MODIFIER_NO_LENGTH = 2, + /// Routing modifier, which implies that it doesn't need length. + PSYC_MODIFIER_ROUTING = 4, +} + +#[repr(C)] +pub enum PsycElemFlag { + /// Element needs to be checked if it needs length. + PSYC_ELEM_CHECK_LENGTH = 0, + /// Element needs length. + PSYC_ELEM_NEED_LENGTH = 1, + /// Element doesn't need length. + PSYC_ELEM_NO_LENGTH = 2, +} + +#[repr(C)] +pub enum PsycPacketFlag { + /// Packet needs to be checked if it needs content length. + PSYC_PACKET_CHECK_LENGTH = 0, + /// Packet needs content length. + PSYC_PACKET_NEED_LENGTH = 1, + /// Packet doesn't need content length. + PSYC_PACKET_NO_LENGTH = 2, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub enum PsycOperator { + PSYC_OPERATOR_SET = ':' as _, + PSYC_OPERATOR_ASSIGN = '=' as _, + PSYC_OPERATOR_AUGMENT = '+' as _, + PSYC_OPERATOR_DIMINISH = '-' as _, + PSYC_OPERATOR_UPDATE = '@' as _, + PSYC_OPERATOR_QUERY = '?' as _, +} + +#[repr(C)] +pub enum PsycStateOp { + PSYC_STATE_NOOP = 0, + PSYC_STATE_RESET = '=' as _, + PSYC_STATE_RESYNC = '?' as _, +} + +#[repr(C)] +pub enum PsycPacketId { + PSYC_PACKET_ID_CONTEXT = 0, + PSYC_PACKET_ID_SOURCE = 1, + PSYC_PACKET_ID_TARGET = 2, + PSYC_PACKET_ID_COUNTER = 3, + PSYC_PACKET_ID_FRAGMENT = 4, + PSYC_PACKET_ID_ELEMS = 5, +} + +#[repr(C)] +pub struct PsycElem { + pub elem_type: PsycString, + pub value: PsycString, + pub length: usize, + pub flag: PsycElemFlag +} + +#[repr(C)] +pub struct PsycDictKey { + pub value: PsycString, + pub length: usize, + pub flag: PsycElemFlag +} + +#[repr(C)] +pub struct PsycDictElem { + pub value: PsycElem, + pub key: PsycDictKey +} + +#[repr(C)] +pub struct RawPsycDict { + dict_type: PsycString, + elems: *const PsycDictElem, + num_elems: usize, + pub length: usize +} + +#[repr(C)] +pub struct RawPsycList { + list_type: PsycString, + elems: *const PsycElem, + num_elems: usize, + pub length: usize +} + +#[repr(C)] +pub struct RawPsycModifier { + name: PsycString, + value: PsycString, + flag: PsycModifierFlag, + oper: c_char +} + +#[repr(C)] +pub struct PsycHeader { + lines: usize, + modifiers: *mut RawPsycModifier +} + +#[repr(C)] +pub struct RawPsycPacket { + routing: PsycHeader, + entity: PsycHeader, + method: PsycString, + data: PsycString, + content: PsycString, + routinglen: usize, + contentlen: usize, + pub length: usize, + stateop: PsycStateOp, + flag: PsycPacketFlag +} + +#[repr(C)] +pub enum PsycRenderRC { + /// Error, method is missing, but data is present. + PSYC_RENDER_ERROR_METHOD_MISSING = -3, + /// Error, a modifier name is missing. + PSYC_RENDER_ERROR_MODIFIER_NAME_MISSING = -2, + /// Error, buffer is too small to render the packet. + PSYC_RENDER_ERROR = -1, + /// Packet is rendered successfully in the buffer. + PSYC_RENDER_SUCCESS = 0, +} + + + diff --git a/rust/src/parser.rs b/rust/src/parser.rs new file mode 100644 index 0000000..9d9620c --- /dev/null +++ b/rust/src/parser.rs @@ -0,0 +1,254 @@ +use types::*; +use parser_types::*; +use std::mem; +use std::slice; +use std::os::raw::c_char; + +extern "C" { + fn psyc_parse_state_init(state: *mut PsycParseState, flags: u8); + fn psyc_parse_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize); + fn psyc_parse_list_state_init(state: *mut PsycParseState); + fn psyc_parse_list_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize); + fn psyc_parse_dict_state_init(state: *mut PsycParseState); + fn psyc_parse_dict_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize); + fn psyc_parse_index_state_init(state: *mut PsycParseState); + fn psyc_parse_index_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize); + fn psyc_parse_update_state_init(state: *mut PsycParseState); + fn psyc_parse_update_buffer_set(state: *mut PsycParseState, buffer: *const c_char, length: usize); + fn psyc_parse_content_length(state: *mut PsycParseState) -> usize; + fn psyc_parse_content_length_found(state: *mut PsycParseState) -> bool; + fn psyc_parse_value_length(state: *mut PsycParseState) -> usize; + fn psyc_parse_value_length_found(state: *mut PsycParseState) -> bool; + fn psyc_parse_cursor(state: *mut PsycParseState) -> usize; + fn psyc_parse_buffer_length(state: *mut PsycParseState) -> usize; + fn psyc_parse_remaining_length(state: *mut PsycParseState) -> usize; + fn psyc_parse_remaining_buffer(state: *mut PsycParseState) -> *const c_char; + fn psyc_parse(state: *mut PsycParseState, + oper: *mut c_char, + name: *mut PsycString, + value: *mut PsycString) + -> PsycParseRC; + + fn psyc_parse_list(state: *mut PsycParseListState, + list_type: *mut PsycString, + elem: *mut PsycString) + -> PsycParseListRC; + + fn psyc_parse_dict(state: *mut PsycParseDictState, + dict_type: *mut PsycString, + elem: *mut PsycString) + -> PsycParseDictRC; + + fn psyc_parse_index(state: *mut PsycParseIndexState, + idx: *mut PsycString) + -> PsycParseIndexRC; + + fn psyc_parse_update(state: *mut PsycParseUpdateState, + oper: *mut c_char, + value: *mut PsycString) + -> PsycParseUpdateRC; + + fn psyc_parse_uint(value: *const c_char, len: usize, n: *mut u64) -> usize; + fn psyc_parse_list_index(value: *const c_char, len: usize, n: *mut i64) -> usize; + fn psyc_is_oper(g: c_char) -> bool; + fn psyc_is_numeric(c: c_char) -> bool; + fn psyc_is_alpha(c: c_char) -> bool; + fn psyc_is_alpha_numeric(c: c_char) -> bool; + fn psyc_is_kw_char(c: c_char) -> bool; + fn psyc_is_name_char(c: c_char) -> bool; + fn psyc_is_host_char(c: c_char) -> bool; + fn psyc_parse_keyword(data: *const c_char, len: usize) -> usize; +} + +pub struct PsycParser { + state: PsycParseState +} + +pub struct PsycListParser { + state: PsycParseListState +} + +pub struct PsycDictParser { + state: PsycParseDictState +} + +pub struct PsycIndexParser { + state: PsycParseIndexState +} + +pub struct PsycUpdateParser { + state: PsycParseUpdateState +} + +#[derive(Debug, PartialEq)] +pub enum PsycParserResult { + StateSync, + StateReset, + ParsingComplete, + RoutingModifier { + operator: char, + name: String, + value: Vec + }, + EntityModifier { + operator: char, + name: String, + value: Vec + }, + IncompleteEntityModifier { + operator: char, + name: String, + value: Vec, + cursor: usize + }, + Body { + name: String, + value: Vec + }, + IncompleteBody { + name: String, + value: Vec, + cursor: usize + }, + InsufficientData { + cursor: usize + }, +} + +#[repr(C)] +#[derive(Debug, PartialEq)] +pub enum PsycParserError { + NoModifierLength = PsycParseRC::PSYC_PARSE_ERROR_MOD_NO_LEN as _, + NoContentLength = PsycParseRC::PSYC_PARSE_ERROR_NO_LEN as _, + NoEndDelimiter = PsycParseRC::PSYC_PARSE_ERROR_END as _, + NoNewlineAfterMethod = PsycParseRC::PSYC_PARSE_ERROR_METHOD as _, + NoNewlineAfterModifier = PsycParseRC::PSYC_PARSE_ERROR_MOD_NL as _, + InvalidModifierLength = PsycParseRC::PSYC_PARSE_ERROR_MOD_LEN as _, + NoTabBeforeModifierValue = PsycParseRC::PSYC_PARSE_ERROR_MOD_TAB as _, + NoModifierName = PsycParseRC::PSYC_PARSE_ERROR_MOD_NAME as _, + NoNewlineAfterContentLength = PsycParseRC::PSYC_PARSE_ERROR_LENGTH as _, + GenericError = PsycParseRC::PSYC_PARSE_ERROR as _, +} + +impl PsycParser { + /// Create a PsycParser + pub fn new() -> Self { + let mut state: PsycParseState; + unsafe { + state = mem::uninitialized(); + let state_ptr = &mut state as *mut PsycParseState; + psyc_parse_state_init(state_ptr, PsycParseFlag::PSYC_PARSE_ALL as u8) + } + PsycParser{state: state} + } + + /// Set a buffer of raw bytes for parsing + pub fn set_buffer(&mut self, buffer: &[u8]) { + let state_ptr = &mut self.state as *mut PsycParseState; + let buffer_ptr = &buffer[0] as *const u8 as *const c_char; + unsafe { + psyc_parse_buffer_set(state_ptr, buffer_ptr, buffer.len()) + } + } + + /// Parse the buffer previously set by set_buffer. Call repeatedly until the + /// result is PsycParserResult::ParsingComplete or a PsycParserError. + pub fn parse(&mut self) -> Result { + let state_ptr = &mut self.state as *mut PsycParseState; + let mut operator: char; + let mut name: PsycString; + let mut value: PsycString; + unsafe { + operator = mem::uninitialized(); + name = mem::uninitialized(); + value = mem::uninitialized(); + let operator_ptr = &mut operator as *mut char as *mut c_char; + let name_ptr = &mut name as *mut PsycString; + let value_ptr = &mut value as *mut PsycString; + let parse_result = psyc_parse(state_ptr, operator_ptr, name_ptr, value_ptr); + match parse_result { + PsycParseRC::PSYC_PARSE_INSUFFICIENT => { + let result = + PsycParserResult::InsufficientData { + cursor: psyc_parse_cursor(state_ptr) + }; + Ok(result) + }, + + PsycParseRC::PSYC_PARSE_ROUTING => { + let result = + PsycParserResult::RoutingModifier { + operator: operator, + name: Self::cstring_to_string(name.data, name.length), + value: Self::cstring_to_bytes(value.data, value.length) + }; + Ok(result) + }, + + PsycParseRC::PSYC_PARSE_ENTITY | + PsycParseRC::PSYC_PARSE_ENTITY_END => { + let result = + PsycParserResult::EntityModifier { + operator: operator, + name: Self::cstring_to_string(name.data, name.length), + value: Self::cstring_to_bytes(value.data, value.length) + }; + Ok(result) + }, + + PsycParseRC::PSYC_PARSE_ENTITY_START | + PsycParseRC::PSYC_PARSE_ENTITY_CONT => { + let result = + PsycParserResult::IncompleteEntityModifier { + operator: operator, + name: Self::cstring_to_string(name.data, name.length), + value: Self::cstring_to_bytes(value.data, value.length), + cursor: psyc_parse_cursor(state_ptr) + }; + Ok(result) + }, + + PsycParseRC::PSYC_PARSE_BODY | + PsycParseRC::PSYC_PARSE_BODY_END => { + let result = + PsycParserResult::Body { + name: Self::cstring_to_string(name.data, name.length), + value: Self::cstring_to_bytes(value.data, value.length) + }; + Ok(result) + }, + + PsycParseRC::PSYC_PARSE_BODY_START | + PsycParseRC::PSYC_PARSE_BODY_CONT => { + let result = + PsycParserResult::IncompleteBody { + name: Self::cstring_to_string(name.data, name.length), + value: Self::cstring_to_bytes(value.data, value.length), + cursor: psyc_parse_cursor(state_ptr) + }; + Ok(result) + }, + + PsycParseRC::PSYC_PARSE_STATE_RESYNC => + Ok(PsycParserResult::StateSync), + + PsycParseRC::PSYC_PARSE_STATE_RESET => + Ok(PsycParserResult::StateReset), + + PsycParseRC::PSYC_PARSE_COMPLETE => + Ok(PsycParserResult::ParsingComplete), + + _error => Err(mem::transmute(_error)), + } + } + } + + unsafe fn cstring_to_string(cstring: *const c_char, length: usize) -> String { + let vec = Self::cstring_to_bytes(cstring, length); + String::from_utf8(vec).unwrap() + } + + unsafe fn cstring_to_bytes(cstring: *const c_char, length: usize) -> Vec { + slice::from_raw_parts(cstring as *const u8, length).to_vec() + } +} diff --git a/rust/src/parser_types.rs b/rust/src/parser_types.rs new file mode 100644 index 0000000..3881cf6 --- /dev/null +++ b/rust/src/parser_types.rs @@ -0,0 +1,300 @@ +#![allow(non_camel_case_types)] +use types::*; + +enum PsycPart { } +enum PsycListPart { } +enum PsycDictPart { } +enum PsycIndexPart { } +enum PsycUpdatePart { } + +#[repr(C)] +pub struct PsycParseState { + buffer: PsycString, + cursor: usize, + startc: usize, + routinglen: usize, + contentlen: usize, + content_parsed: usize, + valuelen: usize, + value_parsed: usize, + part: PsycPart, + flags: u8, + contentlen_found: u8, + valuelen_found: u8 +} + +#[repr(C)] +pub struct PsycParseListState { + buffer: PsycString, + cursor: usize, + startc: usize, + list_type: PsycString, + elemlen: usize, + elem_parsed: usize, + part: PsycListPart, + elemlen_found: u8 +} + +#[repr(C)] +pub struct PsycParseDictState { + buffer: PsycString, + cursor: usize, + startc: usize, + elemlen: usize, + elem_parsed: usize, + part: PsycDictPart, + elemlen_found: u8 +} + +#[repr(C)] +pub struct PsycParseIndexState { + buffer: PsycString, + cursor: usize, + startc: usize, + elemlen: usize, + elem_parsed: usize, + part: PsycIndexPart, + elemlen_found: u8 +} + +#[repr(C)] +pub struct PsycParseUpdateState { + buffer: PsycString, + cursor: usize, + startc: usize, + elemlen: usize, + elem_parsed: usize, + part: PsycUpdatePart, + elemlen_found: u8 +} + +#[repr(C)] +pub enum PsycParseFlag { + /// Default Flag. Parse everything. + PSYC_PARSE_ALL = 0, + /// Parse only the header + PSYC_PARSE_ROUTING_ONLY = 1, + /// Parse only the content. + /// Parsing starts at the content and the content must be complete. + PSYC_PARSE_START_AT_CONTENT = 2, +} + +#[repr(C)] +pub enum PsycParseRC { + /// Error, no length is set for a modifier which is longer than PSYC_MODIFIER_SIZE_THRESHOLD. + PSYC_PARSE_ERROR_MOD_NO_LEN = -10, + /// Error, no length is set for the content but it is longer than PSYC_CONTENT_SIZE_THRESHOLD. + PSYC_PARSE_ERROR_NO_LEN = -9, + /// Error, packet is not ending with a valid delimiter. + PSYC_PARSE_ERROR_END = -8, + /// Error, expected NL after the method. + PSYC_PARSE_ERROR_METHOD = -7, + /// Error, expected NL after a modifier. + PSYC_PARSE_ERROR_MOD_NL = -6, + /// Error, modifier length is not numeric. + PSYC_PARSE_ERROR_MOD_LEN = -5, + /// Error, expected TAB before modifier value. + PSYC_PARSE_ERROR_MOD_TAB = -4, + /// Error, modifier name is missing. + PSYC_PARSE_ERROR_MOD_NAME = -3, + /// Error, expected NL after the content length. + PSYC_PARSE_ERROR_LENGTH = -2, + /// Error in packet. + PSYC_PARSE_ERROR = -1, + /// Buffer contains insufficient amount of data. + /// Fill another buffer and concatenate it with the end of the current buffer, + /// from the cursor position to the end. + PSYC_PARSE_INSUFFICIENT = 1, + /// Routing modifier parsing done. + /// Operator, name & value contains the respective parts. + PSYC_PARSE_ROUTING = 2, + /// State sync operation. + PSYC_PARSE_STATE_RESYNC = 3, + /// State reset operation. + PSYC_PARSE_STATE_RESET = 4, + /// Start of an incomplete entity modifier. + /// Operator & name are complete, value is incomplete. + PSYC_PARSE_ENTITY_START = 5, + /// Continuation of an incomplete entity modifier. + PSYC_PARSE_ENTITY_CONT = 6, + /// End of an incomplete entity modifier. + PSYC_PARSE_ENTITY_END = 7, + /// Entity modifier parsing done in one go. + /// Operator, name & value contains the respective parts. + PSYC_PARSE_ENTITY = 8, + /// Start of an incomplete body. + /// Name contains method, value contains part of the body. + /// Used when packet length is given + PSYC_PARSE_BODY_START = 9, + /// Continuation of an incomplete body. + /// Used when packet length is given + PSYC_PARSE_BODY_CONT = 10, + /// End of an incomplete body. + /// Used when packet length is given + PSYC_PARSE_BODY_END = 11, + /// Body parsing done in one go, name contains method, value contains body. + PSYC_PARSE_BODY = 12, + ///// Start of an incomplete content, value contains part of content. + ///// Used when PSYC_PARSE_ROUTING_ONLY is set. + //PSYC_PARSE_CONTENT_START = 9, + ///// Continuation of an incomplete content. + ///// Used when PSYC_PARSE_ROUTING_ONLY is set. + //PSYC_PARSE_CONTENT_CONT = 10, + ///// End of an incomplete content. + ///// Used when PSYC_PARSE_ROUTING_ONLY is set. + //PSYC_PARSE_CONTENT_END = 11, + ///// Content parsing done in one go, value contains the whole content. + ///// Used when PSYC_PARSE_ROUTING_ONLY is set. + //PSYC_PARSE_CONTENT = 12, + /// Finished parsing packet. + PSYC_PARSE_COMPLETE = 13, +} + +#[repr(C)] +pub enum PsycParseListRC { + /// Error, no length is set for an element which is longer than PSYC_ELEM_SIZE_THRESHOLD. + PSYC_PARSE_LIST_ERROR_ELEM_NO_LEN = -6, + PSYC_PARSE_LIST_ERROR_ELEM_LENGTH = -5, + PSYC_PARSE_LIST_ERROR_ELEM_TYPE = -4, + PSYC_PARSE_LIST_ERROR_ELEM_START = -3, + PSYC_PARSE_LIST_ERROR_TYPE = -2, + PSYC_PARSE_LIST_ERROR = -1, + /// Reached end of buffer. + /// Buffer contains insufficient amount of data. + /// Fill another buffer and concatenate it with the end of the current buffer, + /// from the cursor position to the end. + PSYC_PARSE_LIST_INSUFFICIENT = 1, + /// Completed parsing the default type of the list. + PSYC_PARSE_LIST_TYPE = 2, + /// Start of an element is parsed but still not complete. + PSYC_PARSE_LIST_ELEM_START = 3, + /// Continuation of an incomplete element. + PSYC_PARSE_LIST_ELEM_CONT = 4, + /// Element parsing completed. + PSYC_PARSE_LIST_ELEM_END = 5, + /// Completed parsing a list element. + PSYC_PARSE_LIST_ELEM = 6, + /// Completed parsing the last element in the list. + PSYC_PARSE_LIST_ELEM_LAST = 7, + /// Reached end of buffer. + PSYC_PARSE_LIST_END = 8, +} + +#[repr(C)] +pub enum PsycParseDictRC { + PSYC_PARSE_DICT_ERROR_VALUE = -9, + PSYC_PARSE_DICT_ERROR_VALUE_LENGTH = -8, + PSYC_PARSE_DICT_ERROR_VALUE_TYPE = -7, + PSYC_PARSE_DICT_ERROR_VALUE_START = -6, + PSYC_PARSE_DICT_ERROR_KEY = -5, + PSYC_PARSE_DICT_ERROR_KEY_LENGTH = -4, + PSYC_PARSE_DICT_ERROR_KEY_START = -3, + PSYC_PARSE_DICT_ERROR_TYPE = -2, + PSYC_PARSE_DICT_ERROR = -1, + /// Reached end of buffer. + /// Buffer contains insufficient amount of data. + /// Fill another buffer and concatenate it with the end of the current buffer, + /// from the cursor position to the end. + PSYC_PARSE_DICT_INSUFFICIENT = 1, + /// Completed parsing the default type of the dict. + PSYC_PARSE_DICT_TYPE = 2, + /// Start of a key is parsed but still not complete. + PSYC_PARSE_DICT_KEY_START = 3, + /// Continuation of an incomplete key. + PSYC_PARSE_DICT_KEY_CONT = 4, + /// Last continuation of a key. + PSYC_PARSE_DICT_KEY_END = 5, + /// Completed parsing a key in one go. + PSYC_PARSE_DICT_KEY = 6, + /// Start of a value is parsed but still not complete. + PSYC_PARSE_DICT_VALUE_START = 7, + /// Continuation of an incomplete value. + PSYC_PARSE_DICT_VALUE_CONT = 8, + /// Last continuation of a value. + PSYC_PARSE_DICT_VALUE_END = 9, + /// Completed parsing a value. + PSYC_PARSE_DICT_VALUE = 10, + /// Completed parsing the last value. + PSYC_PARSE_DICT_VALUE_LAST = 11, + /// Reached end of buffer. + PSYC_PARSE_DICT_END = 12, +} + +#[repr(C)] +pub enum PsycParseIndexRC { + PSYC_PARSE_INDEX_ERROR_DICT = -6, + PSYC_PARSE_INDEX_ERROR_DICT_LENGTH = -5, + PSYC_PARSE_INDEX_ERROR_STRUCT = -4, + PSYC_PARSE_INDEX_ERROR_LIST = -3, + PSYC_PARSE_INDEX_ERROR_TYPE = -2, + PSYC_PARSE_INDEX_ERROR = -1, + /// Reached end of buffer. + /// Buffer contains insufficient amount of data. + /// Fill another buffer and concatenate it with the end of the current buffer, + /// from the cursor position to the end. + PSYC_PARSE_INDEX_INSUFFICIENT = 1, + // Completed parsing a list index. + PSYC_PARSE_INDEX_LIST = 3, + // Completed parsing a list index at the end of the buffer. + PSYC_PARSE_INDEX_LIST_LAST = 4, + // Completed parsing a struct element name. + PSYC_PARSE_INDEX_STRUCT = 5, + // Completed parsing a struct element name at the end of the buffer. + PSYC_PARSE_INDEX_STRUCT_LAST = 6, + /// Start of a dict key is parsed but still not complete. + PSYC_PARSE_INDEX_DICT_START = 7, + /// Continuation of an incomplete dict key. + PSYC_PARSE_INDEX_DICT_CONT = 8, + /// Last continuation of a dict key. + PSYC_PARSE_INDEX_DICT_END = 9, + /// Completed parsing a dict key in one go. + PSYC_PARSE_INDEX_DICT = 10, + /// Reached end of buffer. + PSYC_PARSE_INDEX_END = 11, +} + +#[repr(C)] +pub enum PsycParseUpdateRC { + PSYC_PARSE_UPDATE_ERROR_VALUE = -24, + PSYC_PARSE_UPDATE_ERROR_LENGTH = -23, + PSYC_PARSE_UPDATE_ERROR_TYPE = -22, + PSYC_PARSE_UPDATE_ERROR_OPER = -21, + PSYC_PARSE_UPDATE_ERROR = -1, + /// Reached end of buffer. + /// Buffer contains insufficient amount of data. + /// Fill another buffer and concatenate it with the end of the current buffer, + /// from the cursor position to the end. + PSYC_PARSE_UPDATE_INSUFFICIENT = 1, + + // Completed parsing a list index. + PSYC_PARSE_UPDATE_INDEX_LIST = 3, + // Completed parsing a struct element name. + PSYC_PARSE_UPDATE_INDEX_STRUCT = 5, + /// Start of a dict key is parsed but still not complete. + PSYC_PARSE_UPDATE_INDEX_DICT_START = 7, + /// Continuation of an incomplete dict key. + PSYC_PARSE_UPDATE_INDEX_DICT_CONT = 8, + /// Last continuation of a dict key. + PSYC_PARSE_UPDATE_INDEX_DICT_END = 9, + /// Completed parsing a dict key in one go. + PSYC_PARSE_UPDATE_INDEX_DICT = 10, + + /// Completed parsing the type. + PSYC_PARSE_UPDATE_TYPE = 21, + /// Completed parsing the type and reached end of buffer. + PSYC_PARSE_UPDATE_TYPE_END = 22, + /// Start of the value is parsed but still not complete. + PSYC_PARSE_UPDATE_VALUE_START = 23, + /// Continuation of incomplete value. + PSYC_PARSE_UPDATE_VALUE_CONT = 24, + /// Last continuation of the value. + PSYC_PARSE_UPDATE_VALUE_END = 25, + /// Completed parsing the value in one go. + PSYC_PARSE_UPDATE_VALUE = 26, + /// Reached end of buffer. + PSYC_PARSE_UPDATE_END = 27, +} + + + + diff --git a/rust/src/types.rs b/rust/src/types.rs new file mode 100644 index 0000000..09ef3e0 --- /dev/null +++ b/rust/src/types.rs @@ -0,0 +1,9 @@ +use std::os::raw::c_char; + +#[repr(C)] +pub struct PsycString { + pub length: usize, + pub data: *const c_char +} + + diff --git a/rust/tests/test_packet.rs b/rust/tests/test_packet.rs new file mode 100644 index 0000000..88c7c64 --- /dev/null +++ b/rust/tests/test_packet.rs @@ -0,0 +1,97 @@ +extern crate psyc; + +use psyc::*; +use psyc::packet_types::*; + +#[test] +fn test_create_render() { + let r1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET, + "_target", + "psyc://ve.symlynx.com/@blog".as_bytes()); + + let r2 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET, + "_tag", + "666666".as_bytes()); + + let e1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET, + "_nick", + "lurchi".as_bytes()); + + let routing_modifiers = vec![r1, r2]; + let entity_modifiers = vec![e1]; + let data = vec![]; + + let packet = PsycPacket::new(&routing_modifiers, + &entity_modifiers, + "_request_context_enter", + &data, + PsycStateOp::PSYC_STATE_NOOP); + + let expected = ":_target\tpsyc://ve.symlynx.com/@blog\n:_tag\t666666\n\n:_nick\tlurchi\n_request_context_enter\n|\n".as_bytes().to_vec(); + + let rendered_packet = packet.render(); + + assert_eq!(rendered_packet, Ok(expected)) +} + +#[test] +fn test_list() { + let r1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET, + "_target", + "psyc://ve.symlynx.com/@blog".as_bytes()); + + let list = PsycList::from_strings(&["str1", "str2", "str3"]); + + let e1 = PsycModifier::with_list_value(PsycOperator::PSYC_OPERATOR_SET, + "_list_test", + &list); + + let routing_modifiers = vec![r1]; + let entity_modifiers = vec![e1]; + let data = vec![]; + + let packet = PsycPacket::new(&routing_modifiers, + &entity_modifiers, + "", + &data, + PsycStateOp::PSYC_STATE_NOOP); + + let expected = ":_target\tpsyc://ve.symlynx.com/@blog\n34\n:_list_test 18\t| str1| str2| str3\n|\n".as_bytes().to_vec(); + + let rendered_packet = packet.render(); + + assert_eq!(rendered_packet, Ok(expected)); +} + +#[test] +fn test_dict() { + let r1 = PsycModifier::new(PsycOperator::PSYC_OPERATOR_SET, + "_target", + "psyc://ve.symlynx.com/@blog".as_bytes()); + + let dict = PsycDict::from_strings(&[("key1", "value1"), + ("key2", "value2"), + ("key3", "value3")]); + + let e1 = PsycModifier::with_dict_value(PsycOperator::PSYC_OPERATOR_SET, + "_dict_test", + &dict); + + let routing_modifiers = vec![r1]; + let entity_modifiers = vec![e1]; + let data = vec![]; + + let packet = PsycPacket::new(&routing_modifiers, + &entity_modifiers, + "", + &data, + PsycStateOp::PSYC_STATE_NOOP); + + let expected = ":_target\tpsyc://ve.symlynx.com/@blog\n58\n:_dict_test 42\t{ key1} value1{ key2} value2{ key3} value3\n|\n".as_bytes().to_vec(); + + let rendered_packet = packet.render(); + + //println!("rendered: {}", String::from_utf8(rendered_packet.unwrap()).unwrap()); + + assert_eq!(rendered_packet, Ok(expected)); +} diff --git a/rust/tests/test_parser.rs b/rust/tests/test_parser.rs new file mode 100644 index 0000000..4b11bb4 --- /dev/null +++ b/rust/tests/test_parser.rs @@ -0,0 +1,26 @@ +extern crate psyc; + +use psyc::parser::*; + +#[test] +fn test_parse() { + let test_data = ":_target\tpsyc://ve.symlynx.com/@blog\n\n?\n|\n".to_string().into_bytes(); + + let expected1 = + PsycParserResult::RoutingModifier{ + operator: ':', + name: "_target".to_string(), + value: "psyc://ve.symlynx.com/@blog".to_string().into_bytes() + }; + + let expected2 = PsycParserResult::StateSync; + + let mut parser = PsycParser::new(); + parser.set_buffer(&test_data); + + let result1 = parser.parse(); + let result2 = parser.parse(); + + assert_eq!(result1, Ok(expected1)); + assert_eq!(result2, Ok(expected2)); +}