Serialize arithmetic expression-tree - c#

I'm doing a simple task for investigation purposes. The problem is as follows:
Create an arithmetic expression with variables.
Build an AST for the expression.
Send it to the server (using Sockets)
Calculate an result of the server side and return the results.
Now I can to build a tree. This method doing it:
private readonly Stack<Expression> expressionStack = new Stack<Expression>();
private readonly Stack<Symbol> operatorStack = new Stack<Symbol>();
private readonly List<string> parameters = new List<string>();
public Expression<Func<decimal[], decimal>> Parse(string expression)
{
if (string.IsNullOrWhiteSpace(expression))
{
return s => 0;
}
var arrayParameter = Expression.Parameter(typeof(decimal[]), "args");
parameters.Clear();
operatorStack.Clear();
expressionStack.Clear();
using (var reader = new StringReader(expression))
{
int peek;
while ((peek = reader.Peek()) > -1)
{
var next = (char)peek;
if (char.IsDigit(next))
{
expressionStack.Push(ReadOperand(reader));
continue;
}
if (char.IsLetter(next))
{
expressionStack.Push(ReadParameter(reader, arrayParameter));
continue;
}
if (Operation.IsDefined(next))
{
if (next == '-' && expressionStack.Count == 0)
{
reader.Read();
operatorStack.Push(Operation.UnaryMinus);
continue;
}
var currentOperation = ReadOperation(reader);
EvaluateWhile(() => operatorStack.Count > 0 && operatorStack.Peek() != Parentheses.Left &&
currentOperation.Precedence <= ((Operation)operatorStack.Peek()).Precedence);
operatorStack.Push(currentOperation);
continue;
}
if (next == '(')
{
reader.Read();
operatorStack.Push(Parentheses.Left);
if (reader.Peek() == '-')
{
reader.Read();
operatorStack.Push(Operation.UnaryMinus);
}
continue;
}
if (next == ')')
{
reader.Read();
EvaluateWhile(() => operatorStack.Count > 0 && operatorStack.Peek() != Parentheses.Left);
operatorStack.Pop();
continue;
}
if (next == ' ')
{
reader.Read();
}
else
{
throw new ArgumentException(string.Format("Encountered invalid character {0}", next),
"expression");
}
}
}
EvaluateWhile(() => operatorStack.Count > 0);
return Expression.Lambda<Func<decimal[], decimal>>(expressionStack.Pop(), arrayParameter);
}
This method works, and returns the expected result.
Before being sent to the server I want to serialize a tree to binary type. My question is as follows. Which is the simplest way I can apply for this?
I found a lot of solutions for LINQ serialization, but they are too big. I don't need the full functionality of these solutions. In addition, usually, they provide the JSON or XML-serialization, but I need a binary serialization.
Can somebody suggest a simple and easy solution for this problem?

Related

LeetCode MergeTwoSortedLists21 Line 18: error CS1503: Argument 1: cannot convert from 'ListNode' to 'System.Collections.Generic.LinkedList<int>'

The following code compiles and works in VS 2022. It was constructed under a netcoreapp3.1 console application.
When copied into LeetCode, I receive the following error when the "Run Code" button is clicked:
Line 18: Char 48: error CS1503: Argument 1: cannot convert from 'ListNode' to 'System.Collections.Generic.LinkedList' (in Driver.cs)
Is LeetCode using an older C# version, which may be causing the error? Any suggestions would be greatly appreciated.
public class Solution {
public LinkedList<int> MergeTwoLists(LinkedList<int> list1, LinkedList<int> list2) {
LinkedList<int> answerList = new LinkedList<int>();
LinkedListNode<int> list1CurrentNode = list1.First; // This is line 18, which is causing the error
LinkedListNode<int> list2CurrentNode = list2.First;
LinkedListNode<int> answerListNode = answerList.First;
// Console.WriteLine(list1.First.Value);
// Console.WriteLine(list2.First.Value);
// Console.WriteLine(list1CurrentNode.Value);
// Console.WriteLine(list2CurrentNode.Value);
if (list1.First == null && list2.First == null)
{
answerList = null;
return answerList;
}
if (((list1.First.Value == 0) || (list2.First.Value == 0)) && ((list1 == null || list2 == null)))
{
answerList.First.Value = 0;
return answerList;
}
if (((list1 == null) || (list2 == null)) && ((list1.First.Value == 0 || list2.First.Value == 0)))
{
answerList.First.Value = 0;
return answerList;
}
while (list1CurrentNode != null && list2CurrentNode != null)
{
if ((list1CurrentNode == list1.First && list2CurrentNode == list2.First) && (list1.First.Value >= list2.First.Value))
{
answerList.AddFirst(list2CurrentNode.Value);
answerListNode = answerList.First;
// Console.WriteLine(answerListNode.Value);
answerList.AddAfter(answerListNode, list1CurrentNode.Value);
// Console.WriteLine(answerList.First.Value);
// Console.WriteLine(answerList.Last.Value);
}
else if ((list1CurrentNode == list1.First && list2CurrentNode == list2.First) && (list1.First.Value < list2.First.Value))
{
answerList.AddFirst(list1CurrentNode.Value);
answerListNode = answerList.First;
// Console.WriteLine(answerListNode.Value);
answerList.AddAfter(answerListNode, list2CurrentNode.Value);
// Console.WriteLine(answerList.First.Value);
// Console.WriteLine(answerList.Last.Value);
}
else if (list1CurrentNode.Value >= list2CurrentNode.Value)
{
answerList.AddLast(list2CurrentNode.Value);
answerList.AddLast(list1CurrentNode.Value);
}
else if (list2CurrentNode.Value > list1CurrentNode.Value)
{
answerList.AddLast(list1CurrentNode.Value);
answerList.AddLast(list2CurrentNode.Value);
}
list1CurrentNode = list1CurrentNode.Next;
list2CurrentNode = list2CurrentNode.Next;
}
// Console.WriteLine(String.Join(" ", answerList));
return answerList;
static void Main(string[] args)
{
LinkedList<int> firstList = new LinkedList<int>();
LinkedList<int> secondList = new LinkedList<int>();
firstList.AddFirst(5);
firstList.AddLast(6);
firstList.AddLast(2);
secondList.AddFirst(7);
secondList.AddLast(1);
secondList.AddLast(9);
Solution solution = new Solution();
solution.MergeTwoLists(firstList, secondList);
}
}
}
You tested your code outside of Leet Code, but also changed the signature of the function. Leet Code presents the following template code:
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/
public class Solution {
public ListNode MergeTwoLists(ListNode list1, ListNode list2) {
}
}
You are supposed to work with that. But in your code you are using LinkedList, both for the arguments and the return type. There is no hope that the Leet Code tests will pass when you change the data types.
To get you started, in your offline version, you should have driver code that looks like this:
static void Main(string[] args)
{
ListNode firstList = new ListNode(5,
new ListNode(6,
new ListNode(2)));
ListNode secondList = new ListNode(7,
new ListNode(1,
new ListNode(9)));
Solution solution = new Solution();
ListNode mergedList = solution.MergeTwoLists(firstList, secondList);
// Output the returned result
for (ListNode node = mergedList; node != null; node = node.next) {
Console.Write("{0} ", node.val);
}
Console.WriteLine();
}

What 's the best solution to find an element in a deepest binary tree

Recently I had an interview question about finding an element in a binary tree. I coded both recursive and iterative solutions with C# but the problem was that in test cases when we have a tree with 1000000 nodes and all of them are on the left side. The interviewer said to me that my solutions (recursive and iterative) didn't save memory RAM enough for this case and I don't understand how to improve my solution.
// recusive Mode
public Node Find(int v)
{
if(v == value)
{
return this;
}else if(v <value){
if (left == null) return null;
return left.Find(v);
}else{
if (right == null) return null;
return right.Find(v);
}
}
// iterative
public Node Find(int v)
{
Node current = this;
while(value != v && current != null)
{
if (v < current.value)
{
if (current.left == null){ current = null};
else{current = current.left};
}
else
{
if (current.right == null) { current = null};
else{current = current.right };
}
}
return current;
}
Your iterative solution has some bugs in it.
// iterative
public Node Find(int v)
{
Node current = this;
// Here you need to compare current.value instead of just value
// Also, to use short-circuiting you need to put null-check first
// otherwise you might access current.value while current is null
while(current != null && current.value != v)
{
if (v < current.value)
{
//if (current.left == null){ current = null};
//else{current = current.left};
current = current.left; // the same as two commented out lines
}
else
{
//if (current.right == null) { current = null};
//else{current = current.right };
current = current.right; // the same as two commented out lines
}
}
return current;
}

Paragraphs formatting slow in VSTO

I am using VSTO to get some paragraphs and save their formatting in a List.The code is working fine but the problem is that this function is taking almost 50% of the execution time of my program.
It is also known that the loop is making performance slow.
I want to know that how can I optimize the time of this function.Any alternative to list or any other thing?
public static void GetFormattedParas(Range content, Driver currentDriver)
{
// TODO add check for tables row end "\r\a" words to ignore them for query
Paragraphs paras = content.Paragraphs;
List<Paragraph> paraList = paras.Cast<Paragraph>().ToList();
List<Range> Ranges = new List<Range>();
if (paraList != null)
{
paraList.RemoveAll(range => range.Range.Text == null);
paraList.RemoveAll(range => range.Range.Text.Equals("\r\a") && range.Range.Tables.Count != 0 && range.Range.Rows[1].Range.End == range.Range.End);
for (int i = 0, ParaCount = paraList.Count; i < ParaCount; i++)
{
Range range = paraList[i].Range;
if (range.Font.Shading.BackgroundPatternColorIndex != WdColorIndex.wdNoHighlight
|| range.HighlightColorIndex != WdColorIndex.wdNoHighlight
|| range.Shading.BackgroundPatternColorIndex != WdColorIndex.wdNoHighlight)
{
Ranges.Add(paraList[i].Range);
}
}
//Ranges = (from range in paraList
// where range.Range.Font.Shading.BackgroundPatternColorIndex != WdColorIndex.wdNoHighlight
// || range.Range.HighlightColorIndex != WdColorIndex.wdNoHighlight
// || range.Range.Shading.BackgroundPatternColorIndex != WdColorIndex.wdNoHighlight
// select range.Range).OrderBy(o => o.Start).ToList();
if (Ranges != null)
{
Ranges = Ranges.OrderBy(o => o.Start).ToList();
if (Ranges.Count > 0)
{
//if (Ranges.Count == 1)
//{
// currentDriver.formattedParas.Add(content);
//}
//else
//{
currentDriver.formattedParas.AddRange(Ranges);
//}
}
}
}
}

Parsing CSV strings (not files) in C#

Using C#, I need to parse a CSV string that doesn't come from a file. I've found a great deal of material on parsing CSV files, but virtually nothing on strings. It seems as though this should be simple, yet thus far I can come up only with inefficient methods, such as this:
using Microsoft.VisualBasic.FileIO;
var csvParser = new TextFieldParser(new StringReader(strCsvLine));
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;
Are there good ways of making this more efficient and less ugly? I will be processing huge volumes of strings, so I wouldn't want to pay the cost of all the above. Thanks.
Here is a lightly tested parser that handles quotes
List<string> Parse(string line)
{
var columns = new List<string>();
var sb = new StringBuilder();
bool isQuoted = false;
int nQuotes = 0;
foreach(var c in line)
{
if (sb.Length == 0 && !isQuoted && c == '"')
{
isQuoted = true;
continue;
}
if (isQuoted)
{
if (c == '"')
{
nQuotes++;
continue;
}
else
{
if (nQuotes > 0)
{
sb.Append('"', nQuotes / 2);
if (nQuotes % 2 != 0)
{
isQuoted = false;
}
nQuotes = 0;
}
}
}
if (!isQuoted && c == ',')
{
columns.Add(sb.ToString());
sb.Clear();
continue;
}
sb.Append(c);
}
if (nQuotes > 0)
{
sb.Append('"', nQuotes / 2);
}
columns.Add(sb.ToString());
return columns;
}

What is the safest way to include data in SQL WHERE clause while avoiding SQL injection risk WITHOUT using a parameterized query?

What's a good function to use for making a string safe for inclusion in a SQL query? For example, apostrophes would need to be fixed, and there are doubtless other problems that could arise as well. I'd like a function that is rock-solid and would work given any possible input an evil-doer could devise.
Now, before the masses tell me to use query parameters and/or downvote/close this question, consider these points:
I am stuck using a 3rd-party library with a poorly-designed API. The API is supposed to be called as follows:
dataObjectVariable.FindWhere("WHERE RecordID = '(your string here)'");
Now, I agree with you 100% that this is not a good API as it (1) exposes internal database field names to the user which are implementation details, (2) offers no opportunity for using parameters which would avoid this problem in the first place, (3) really, you could say that SQL itself is an implementation detail and shouldn't have been exposed. But I am stuck using this API because it's required to integrate with one of the leading systems in its industry. We're not really in a position to ask them to change their API, either.
I searched this site for other questions pertaining to this issue, but found that answers tended to strongly suggest parameterized queries. Answers that tried to suggest writing a function to sanitize the string were often downvoted, not well thought out, etc. - I'm not sure if I trust them.
I'm only searching for strings and not other data types like numbers, dates, etc. Again, I'm 100% aware of the benefits of using parameterized queries and I wish I could use them, but I can't because my hands are tied on this one.
I have to use a similar API in one of our applications. Here's the validation routine I use to manually circumvent SQL injection:
internal class SqlInjectionValidator
{
internal static readonly List _s_keywords = new List
{
"alter",
"begin",
"commit",
"create",
"delete",
"drop",
"exec",
"execute",
"grant",
"insert",
"kill",
"load",
"revoke",
"rollback",
"shutdown",
"truncate",
"update",
"use",
"sysobjects"
};
private string _sql;
private int _pos;
private readonly Stack _literalQuotes = new Stack();
private readonly Stack _identifierQuotes = new Stack();
private int _statementCount;
// Returns true if s does not contain SQL keywords.
public SqlValidationStatus Validate(string s)
{
if (String.IsNullOrEmpty(s))
{
return SqlValidationStatus.Ok;
}
_pos = 0;
_sql = s.ToLower();
_literalQuotes.Clear();
_identifierQuotes.Clear();
_statementCount = 0;
List chars = new List();
SqlValidationStatus svs;
while (_pos = _sql.Length)
{
break;
}
if (_statementCount != 0)
{
return SqlValidationStatus.SqlBatchNotAllowed;
}
char c = _sql[_pos];
if (IsEmbeddedQuote(c))
{
_pos++;
chars.Add(_sql[_pos]);
_pos++;
continue;
}
if (c != '\'' &&
IsQuotedString())
{
chars.Add(c);
_pos++;
continue;
}
if (c != ']' &&
c != '[' &&
c != '"' &&
IsQuotedIdentifier())
{
chars.Add(c);
_pos++;
continue;
}
switch (c)
{
case '[':
if (_identifierQuotes.Count != 0)
{
return SqlValidationStatus.MismatchedIdentifierQuote;
}
svs = DisallowWord(chars);
if (svs != SqlValidationStatus.Ok)
{
return svs;
}
_identifierQuotes.Push(c);
break;
case ']':
if (_identifierQuotes.Count != 1 ||
_identifierQuotes.Peek() != '[')
{
return SqlValidationStatus.MismatchedIdentifierQuote;
}
svs = DisallowWord(chars);
if (svs != SqlValidationStatus.Ok)
{
return svs;
}
_identifierQuotes.Pop();
break;
case '"':
if (_identifierQuotes.Count == 0)
{
svs = DisallowWord(chars);
if (svs != SqlValidationStatus.Ok)
{
return svs;
}
_identifierQuotes.Push(c);
}
else if (_identifierQuotes.Count == 1)
{
svs = DisallowWord(chars);
if (svs != SqlValidationStatus.Ok)
{
return svs;
}
_identifierQuotes.Pop();
}
else
{
return SqlValidationStatus.MismatchedIdentifierQuote;
}
break;
case '\'':
if (_literalQuotes.Count == 0)
{
svs = DisallowWord(chars);
if (svs != SqlValidationStatus.Ok)
{
return svs;
}
_literalQuotes.Push(c);
}
else if (_literalQuotes.Count == 1 &&
_literalQuotes.Peek() == c)
{
_literalQuotes.Pop();
chars.Clear();
}
else
{
return SqlValidationStatus.MismatchedLiteralQuote;
}
break;
default:
if (Char.IsLetterOrDigit(c) ||
c == '-')
{
chars.Add(c);
}
else if (Char.IsWhiteSpace(c) ||
Char.IsControl(c) ||
Char.IsPunctuation(c))
{
svs = DisallowWord(chars);
if (svs != SqlValidationStatus.Ok)
{
return svs;
}
if (c == ';')
{
_statementCount++;
}
}
break;
}
_pos++;
}
if (_literalQuotes.Count != 0)
{
return SqlValidationStatus.MismatchedLiteralQuote;
}
if (_identifierQuotes.Count != 0)
{
return SqlValidationStatus.MismatchedIdentifierQuote;
}
if (chars.Count > 0)
{
svs = DisallowWord(chars);
if (svs != SqlValidationStatus.Ok)
{
return svs;
}
}
return SqlValidationStatus.Ok;
}
// Returns true if the string representation of the sequence of characters in
// chars is a SQL keyword.
private SqlValidationStatus DisallowWord(List chars)
{
if (chars.Count == 0)
{
return SqlValidationStatus.Ok;
}
string s = new String(chars.ToArray()).Trim();
chars.Clear();
return DisallowWord(s);
}
private SqlValidationStatus DisallowWord(string word)
{
if (word.Contains("--"))
{
return SqlValidationStatus.CommentNotAllowed;
}
if (_s_keywords.Contains(word))
{
return SqlValidationStatus.KeywordNotAllowed;
}
if (_statementCount > 0)
{
return SqlValidationStatus.SqlBatchNotAllowed;
}
if (word.Equals("go"))
{
_statementCount++;
}
return SqlValidationStatus.Ok;
}
private bool IsEmbeddedQuote(char curChar)
{
if (curChar != '\'' ||
!IsQuotedString() ||
IsQuotedIdentifier())
{
return false;
}
if (_literalQuotes.Peek() == curChar &&
Peek() == curChar)
{
return true;
}
return false;
}
private bool IsQuotedString()
{
return _literalQuotes.Count > 0;
}
private bool IsQuotedIdentifier()
{
return _identifierQuotes.Count > 0;
}
private char Peek()
{
if (_pos + 1 < _sql.Length)
{
return _sql[_pos + 1];
}
return '\0';
}
}
I wound up digging some more and found this "brute-force" solution which seems to work. How to escape strings in SQL Server using PHP?
Basically, just send the string as hexadecimal instead of a string. For example, instead of sending:
WHERE RecordID = 'DEMO'
send this:
WHERE RecordID = 0x44454D4F
If the string is empty, then as a special case I just send ''
This seems reasonable safe; I can't think of an attack that would work.
In general, be careful of this solution because it might not work with non-ANSI characters. However, I found that the column I am comparing against is "varchar" and not "nvarchar", so apparently Unicode wasn't really on the agenda when they designed their database. I'd guess if it was "nvarchar", I'd need to be sending a UTF-16 string (or whatever Unicode encoding is used with nvarchar).
Assuming you're expecting only plain text (for a good record), and if you can make the assumption that you will only receive normal characters (i.e. keyboard-characters), you can take your input, convert to ASCII, then limit it to the characters that you want (by providing a list of allowable characters and filtering from that), and also ensure the string length is appropriate.
Then build a new string with only your acceptable characters, and pass that string in. Once you're there, the single-quote is definitely your only concern.
If you can make those requirements, then this should work for you. There'd be no way to inject funky Unicode characters, or overflow the SQL buffer, or do any of that other crazy stuff that people do to cause problems - you'd be in 100% control of the characters that go into the query.

Categories

Resources