From 3305605f2e09c8cd39da0f9835985b90cf1db78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Khalid=20Salom=C3=A3o?= Date: Wed, 11 Sep 2013 13:32:22 -0300 Subject: [PATCH 1/2] Some performance related changes Performance related: 1. Caching of Stringbuilder on RecordOperations.cs for "RecordToString" and "RecordValuesToString" methods to lessen a little the burden on the GC (I actually didn't measure anything...) 2. Change to use "CompareOptions.OrdinalIgnoreCase" and "CompareOptions.Ordinal" whenever possible (prety good performance gain whenever we have some heavy string working to do...) 3. Change to use "ToLowerInvariant" instead of "ToLower" for a small performance gain (10%) Access Visibility 1. some access visibility level changes to useful Field properties on "FieldBase", "FixedLengthField" and "DelimitedField" (similar to the previous change on atributes!) Other changes 1. FileHelperAsyncEngine.EOF, it is userful if using this engine using ReadNextRecord () or ReadNexts () (usage in a while loop for example) 2. FileHelperAsyncEngine.Close, add a dispose to the TextWriter just as a precaution... --- .../Tests/Helpers/StringHelperTest.cs | 9 ++++ FileHelpers/Converters/ConvertHelpers.cs | 8 ++-- FileHelpers/Core/RecordInfo.cs | 8 ++-- FileHelpers/Core/RecordOperations.cs | 39 ++++++++++----- FileHelpers/Dynamic/ClassBuilder.cs | 16 +++---- FileHelpers/Engines/FileHelperAsyncEngine.cs | 44 ++++++++++++----- FileHelpers/Engines/FileHelperEngine.cs | 4 +- FileHelpers/Engines/MultiRecordEngine.cs | 8 ++-- FileHelpers/ErrorHandling/ErrorManager.cs | 2 +- FileHelpers/Fields/DelimitedField.cs | 10 ++-- FileHelpers/Fields/FieldBase.cs | 48 +++++++++---------- FileHelpers/Fields/FixedLengthField.cs | 6 +-- FileHelpers/FileHelpers.xml | 6 +++ FileHelpers/FormatDetector/QuoteHelper.cs | 4 +- FileHelpers/Helpers/ConditionHelper.cs | 8 ++-- FileHelpers/Helpers/StringHelper.cs | 27 +++++++++-- .../MasterDetail/MasterDetailEngine.cs | 20 ++++---- 17 files changed, 166 insertions(+), 101 deletions(-) diff --git a/FileHelpers.Tests/Tests/Helpers/StringHelperTest.cs b/FileHelpers.Tests/Tests/Helpers/StringHelperTest.cs index d1dbfad32..f00110fcb 100644 --- a/FileHelpers.Tests/Tests/Helpers/StringHelperTest.cs +++ b/FileHelpers.Tests/Tests/Helpers/StringHelperTest.cs @@ -33,6 +33,15 @@ public void RemoveBlanksAfterSign() public void RemoveTrailingBlanks() { StringHelper.RemoveBlanks(" + 41").AssertEqualTo("+41"); + } + + [Test (Description = "String IsNullOrWhiteSpace help method tests")] + public void IsNullOrWhiteSpace () + { + StringHelper.IsNullOrWhiteSpace (" ").AssertEqualTo (true, "WhiteSpaces not detected"); + StringHelper.IsNullOrWhiteSpace (null).AssertEqualTo (true, "null string not detected"); + StringHelper.IsNullOrWhiteSpace (String.Empty).AssertEqualTo (true, "empty string not detected"); + StringHelper.IsNullOrWhiteSpace (" test ").AssertEqualTo (false, "valid string not detected"); } } } diff --git a/FileHelpers/Converters/ConvertHelpers.cs b/FileHelpers/Converters/ConvertHelpers.cs index b009b7ecf..dc45fbd04 100644 --- a/FileHelpers/Converters/ConvertHelpers.cs +++ b/FileHelpers/Converters/ConvertHelpers.cs @@ -622,7 +622,7 @@ protected override object ParseString(string from) { double res; var blanksRemoved = StringHelper.RemoveBlanks(from); - if (blanksRemoved.EndsWith("%")) + if (blanksRemoved.EndsWith ("%", StringComparison.Ordinal)) { if (!Double.TryParse(blanksRemoved, NumberStyles.Number | NumberStyles.AllowExponent, mCulture, out res)) throw new ConvertException(from, mType); @@ -862,8 +862,8 @@ public BooleanConverter(string trueStr, string falseStr) { mTrueString = trueStr; mFalseString = falseStr; - mTrueStringLower = trueStr.ToLower(); - mFalseStringLower = falseStr.ToLower(); + mTrueStringLower = trueStr.ToLowerInvariant (); + mFalseStringLower = falseStr.ToLowerInvariant (); } /// @@ -874,7 +874,7 @@ public BooleanConverter(string trueStr, string falseStr) public override object StringToField(string from) { object val; - string testTo = from.ToLower(); + string testTo = from.ToLowerInvariant (); if (mTrueString == null) { diff --git a/FileHelpers/Core/RecordInfo.cs b/FileHelpers/Core/RecordInfo.cs index ad15c3d77..e7e8ff0cd 100644 --- a/FileHelpers/Core/RecordInfo.cs +++ b/FileHelpers/Core/RecordInfo.cs @@ -345,7 +345,7 @@ private static void CheckForOptionalAndArrayProblems(List resFields) /// List of fields to use private static void SortFieldsByOrder(List resFields) { - if (resFields.FindAll(x => x.FieldOrder.HasValue).Count > 0) + if (resFields.Exists(x => x.FieldOrder.HasValue)) resFields.Sort( (x,y) => x.FieldOrder.Value.CompareTo(y.FieldOrder.Value)); } @@ -378,8 +378,8 @@ private static void CheckForOrderProblems(FieldBase currentField, List x.FieldOrder.HasValue).Count; - if (othersWithOrder > 0) + var othersWithOrder = resFields.Exists (x => x.FieldOrder.HasValue); + if (othersWithOrder) throw new BadUsageException(Messages.Errors.PartialFieldOrder .FieldName(currentField.FieldInfo.Name) .Text); @@ -401,7 +401,7 @@ public int GetFieldIndex(string fieldName) { if (mMapFieldIndex == null) { - mMapFieldIndex = new Dictionary(FieldCount); + mMapFieldIndex = new Dictionary(FieldCount, StringComparer.Ordinal); for (int i = 0; i < FieldCount; i++) { mMapFieldIndex.Add(Fields[i].FieldInfo.Name, i); diff --git a/FileHelpers/Core/RecordOperations.cs b/FileHelpers/Core/RecordOperations.cs index 0af09213a..b2c6f6f13 100644 --- a/FileHelpers/Core/RecordOperations.cs +++ b/FileHelpers/Core/RecordOperations.cs @@ -10,9 +10,13 @@ namespace FileHelpers /// /// Collection of operations that we perform on a type, cached for reuse /// - internal sealed class RecordOperations + internal sealed class RecordOperations //: IRecordOperations { + // internal buffer for RecordToString and RecordValuesToString operations + // this buffer will be released when this instance goes out of scope + StringBuilder mBuffer = null; + /// /// Record Info we use to parse the record and generate an object instance /// @@ -27,7 +31,7 @@ public RecordOperations(IRecordInfo recordInfo) RecordInfo = recordInfo; } - #region " StringToRecord " + #region " StringToRecord " /// /// Process a line and turn it into an object @@ -128,13 +132,13 @@ public bool StringToRecord(object record, LineInfo line, object[] values) private bool MustIgnoreLine(string line) { if (RecordInfo.IgnoreEmptyLines) - if ((RecordInfo.IgnoreEmptySpaces && line.TrimStart().Length == 0) || + if ((RecordInfo.IgnoreEmptySpaces && StringHelper.IsNullOrWhiteSpace (line)) || line.Length == 0) return true; if (!String.IsNullOrEmpty(RecordInfo.CommentMarker)) - if ((RecordInfo.CommentAnyPlace && line.TrimStart().StartsWith(RecordInfo.CommentMarker)) || - line.StartsWith(RecordInfo.CommentMarker)) + if ((RecordInfo.CommentAnyPlace && line.TrimStart ().StartsWith (RecordInfo.CommentMarker, StringComparison.Ordinal)) || + line.StartsWith (RecordInfo.CommentMarker, StringComparison.Ordinal)) return true; if (RecordInfo.RecordCondition != RecordCondition.None) @@ -182,16 +186,21 @@ private bool MustIgnoreLine(string line) /// String representing the object public string RecordToString(object record) { - var sb = new StringBuilder(RecordInfo.SizeHint); + // create or clear internal string buffer + if (mBuffer == null) + mBuffer = new StringBuilder (RecordInfo.SizeHint); + else + mBuffer.Length = 0; + // parse each field var values = ObjectToValuesHandler(record); - + var fields = RecordInfo.Fields; for (int f = 0; f < RecordInfo.FieldCount; f++) { - RecordInfo.Fields[f].AssignToString(sb, values[f]); + fields[f].AssignToString (mBuffer, values[f]); } - return sb.ToString(); + return mBuffer.ToString (); } /// @@ -201,14 +210,20 @@ public string RecordToString(object record) /// String representing values public string RecordValuesToString(object[] recordValues) { - var sb = new StringBuilder(RecordInfo.SizeHint); + // create or clear internal string buffer + if (mBuffer == null) + mBuffer = new StringBuilder (RecordInfo.SizeHint); + else + mBuffer.Length = 0; + // parse each field + var fields = RecordInfo.Fields; for (int f = 0; f < RecordInfo.FieldCount; f++) { - RecordInfo.Fields[f].AssignToString(sb, recordValues[f]); + fields[f].AssignToString (mBuffer, recordValues[f]); } - return sb.ToString(); + return mBuffer.ToString (); } #endregion diff --git a/FileHelpers/Dynamic/ClassBuilder.cs b/FileHelpers/Dynamic/ClassBuilder.cs index 8a80296ea..c77511ac5 100644 --- a/FileHelpers/Dynamic/ClassBuilder.cs +++ b/FileHelpers/Dynamic/ClassBuilder.cs @@ -108,13 +108,13 @@ public static Type ClassFromString(string classStr, string className, NetLanguag case NetLanguage.VbNet: - if (CultureInfo.CurrentCulture.CompareInfo.IndexOf(classStr, "Imports System", CompareOptions.IgnoreCase) == -1) + if (CultureInfo.CurrentCulture.CompareInfo.IndexOf(classStr, "Imports System", CompareOptions.OrdinalIgnoreCase) == -1) code.Append("Imports System\n"); - if (CultureInfo.CurrentCulture.CompareInfo.IndexOf(classStr, "Imports FileHelpers", CompareOptions.IgnoreCase) == -1) + if (CultureInfo.CurrentCulture.CompareInfo.IndexOf(classStr, "Imports FileHelpers", CompareOptions.OrdinalIgnoreCase) == -1) code.Append("Imports FileHelpers\n"); - if (mustAddSystemData && CultureInfo.CurrentCulture.CompareInfo.IndexOf(classStr, "Imports System.Data", CompareOptions.IgnoreCase) == -1) + if (mustAddSystemData && CultureInfo.CurrentCulture.CompareInfo.IndexOf(classStr, "Imports System.Data", CompareOptions.OrdinalIgnoreCase) == -1) code.Append("Imports System.Data\n"); break; @@ -123,7 +123,7 @@ public static Type ClassFromString(string classStr, string className, NetLanguag code.Append(classStr); CodeDomProvider prov = null; - + switch (lang) { case NetLanguage.CSharp: @@ -157,7 +157,7 @@ public static Type ClassFromString(string classStr, string className, NetLanguag if (ts.Length > 0) foreach (var t in ts) { - if (t.FullName.StartsWith("My.My") == false && t.IsDefined(typeof(TypedRecordAttribute), false)) + if (t.FullName.StartsWith ("My.My", StringComparison.Ordinal) == false && t.IsDefined (typeof (TypedRecordAttribute), false)) return t; } @@ -620,7 +620,7 @@ private void AddAttributesInternal(AttributesBuilder attbs) attbs.AddAttribute("ConditionalRecord(RecordCondition." + mRecordConditionInfo.Condition.ToString() + ", \"" + mRecordConditionInfo.Selector + "\")"); if (!string.IsNullOrEmpty(mIgnoreCommentInfo.CommentMarker)) - attbs.AddAttribute("IgnoreCommentedLines(\"" + mIgnoreCommentInfo.CommentMarker + "\", " + mIgnoreCommentInfo.InAnyPlace.ToString().ToLower() + ")"); + attbs.AddAttribute("IgnoreCommentedLines(\"" + mIgnoreCommentInfo.CommentMarker + "\", " + mIgnoreCommentInfo.InAnyPlace.ToString().ToLowerInvariant() + ")"); } @@ -897,7 +897,7 @@ public static ClassBuilder LoadFromXml(XmlDocument document) if (node != null) res.IgnoreCommentedLines.CommentMarker = node.InnerText; node = document.DocumentElement["CommentInAnyPlace"]; - if (node != null) res.IgnoreCommentedLines.InAnyPlace = bool.Parse(node.InnerText.ToLower()); + if (node != null) res.IgnoreCommentedLines.InAnyPlace = bool.Parse(node.InnerText.ToLowerInvariant()); node = document.DocumentElement["SealedClass"]; res.SealedClass = node != null; @@ -1021,7 +1021,7 @@ public void SaveToXml(TextWriter writer) xml.WriteElement("IgnoreLastLines", this.IgnoreLastLines.ToString(), "0"); xml.WriteElement("CommentMarker", this.IgnoreCommentedLines.CommentMarker, string.Empty); - xml.WriteElement("CommentInAnyPlace", this.IgnoreCommentedLines.InAnyPlace.ToString().ToLower(), true.ToString().ToLower()); + xml.WriteElement("CommentInAnyPlace", this.IgnoreCommentedLines.InAnyPlace.ToString().ToLowerInvariant(), true.ToString().ToLowerInvariant()); xml.WriteElement("RecordCondition", this.RecordCondition.Condition.ToString(), "None"); xml.WriteElement("RecordConditionSelector", this.RecordCondition.Selector, string.Empty); diff --git a/FileHelpers/Engines/FileHelperAsyncEngine.cs b/FileHelpers/Engines/FileHelperAsyncEngine.cs index 781806f28..12b66ef88 100644 --- a/FileHelpers/Engines/FileHelperAsyncEngine.cs +++ b/FileHelpers/Engines/FileHelperAsyncEngine.cs @@ -85,6 +85,16 @@ protected FileHelperAsyncEngine(Type recordType, Encoding encoding) [DebuggerBrowsable(DebuggerBrowsableState.Never)] TextWriter mAsyncWriter; + public bool mEof = false; + /// + /// Indicates if End of File was reached. + /// + /// The EOF flag. + public bool Eof + { + get { return mEof; } + } + #endregion #region " LastRecord " @@ -357,6 +367,8 @@ private void ReadNextRecord() } else { + // mark end of file + mEof = true; mLastRecordValues = null; mLastRecord = default(T); @@ -400,9 +412,15 @@ public T[] ReadNexts(int numberOfRecords) { ReadNextRecord(); if (mLastRecord != null) - arr.Add(mLastRecord); + { + arr.Add(mLastRecord); + } else + { + // mark end of file + mEof = true; break; + } } return arr.ToArray(); } @@ -447,17 +465,19 @@ public void Close() try { - var writer = mAsyncWriter; - if (writer != null) + using (var writer = mAsyncWriter) { - if (!string.IsNullOrEmpty(mFooterText)) - if (mFooterText.EndsWith(StringHelper.NewLine)) - writer.Write(mFooterText); - else - writer.WriteLine(mFooterText); - - writer.Close(); - mAsyncWriter = null; + mAsyncWriter = null; + if (writer != null) + { + if (!string.IsNullOrEmpty (mFooterText)) + if (mFooterText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) + writer.Write (mFooterText); + else + writer.WriteLine (mFooterText); + + writer.Close (); + } } } catch @@ -499,7 +519,7 @@ public IDisposable BeginWriteStream(TextWriter writer) private void WriteHeader() { if (!string.IsNullOrEmpty(mHeaderText)) - if (mHeaderText.EndsWith(StringHelper.NewLine)) + if (mHeaderText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) mAsyncWriter.Write(mHeaderText); else mAsyncWriter.WriteLine(mHeaderText); diff --git a/FileHelpers/Engines/FileHelperEngine.cs b/FileHelpers/Engines/FileHelperEngine.cs index e72972c32..c1ec17892 100644 --- a/FileHelpers/Engines/FileHelperEngine.cs +++ b/FileHelpers/Engines/FileHelperEngine.cs @@ -477,7 +477,7 @@ public void WriteStream(TextWriter writer, IEnumerable records, int maxRecord ResetFields(); if (!string.IsNullOrEmpty(mHeaderText)) - if (mHeaderText.EndsWith(StringHelper.NewLine)) + if (mHeaderText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) writer.Write(mHeaderText); else writer.WriteLine(mHeaderText); @@ -563,7 +563,7 @@ public void WriteStream(TextWriter writer, IEnumerable records, int maxRecord mTotalRecords = recIndex; if (!string.IsNullOrEmpty(mFooterText)) - if (mFooterText.EndsWith(StringHelper.NewLine)) + if (mFooterText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) writer.Write(mFooterText); else writer.WriteLine(mFooterText); diff --git a/FileHelpers/Engines/MultiRecordEngine.cs b/FileHelpers/Engines/MultiRecordEngine.cs index b681eaa07..6b1acda90 100644 --- a/FileHelpers/Engines/MultiRecordEngine.cs +++ b/FileHelpers/Engines/MultiRecordEngine.cs @@ -422,7 +422,7 @@ public void WriteStream(TextWriter writer, IEnumerable records, int maxRecords) ResetFields(); if (!string.IsNullOrEmpty(mHeaderText)) - if (mHeaderText.EndsWith(StringHelper.NewLine)) + if (mHeaderText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) writer.Write(mHeaderText); else writer.WriteLine(mHeaderText); @@ -504,7 +504,7 @@ public void WriteStream(TextWriter writer, IEnumerable records, int maxRecords) mTotalRecords = recIndex; if (!string.IsNullOrEmpty(mFooterText)) - if (mFooterText.EndsWith(StringHelper.NewLine)) + if (mFooterText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) writer.Write(mFooterText); else writer.WriteLine(mFooterText); @@ -701,7 +701,7 @@ public void Close() if (mAsyncWriter != null) { if (!string.IsNullOrEmpty(mFooterText)) - if (mFooterText.EndsWith(StringHelper.NewLine)) + if (mFooterText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) mAsyncWriter.Write(mFooterText); else mAsyncWriter.WriteLine(mFooterText); @@ -1036,7 +1036,7 @@ public void BeginWriteStream(TextWriter writer) private void WriteHeader() { if (!string.IsNullOrEmpty(mHeaderText)) - if (mHeaderText.EndsWith(StringHelper.NewLine)) + if (mHeaderText.EndsWith (StringHelper.NewLine, StringComparison.Ordinal)) mAsyncWriter.Write(mHeaderText); else mAsyncWriter.WriteLine(mHeaderText); diff --git a/FileHelpers/ErrorHandling/ErrorManager.cs b/FileHelpers/ErrorHandling/ErrorManager.cs index 61e2663b2..02ee756c5 100644 --- a/FileHelpers/ErrorHandling/ErrorManager.cs +++ b/FileHelpers/ErrorHandling/ErrorManager.cs @@ -131,7 +131,7 @@ public void SaveErrors(string fileName, string header) { var engine = new FileHelperEngine(typeof (ErrorInfo)); - if (header.IndexOf(StringHelper.NewLine) == header.LastIndexOf(StringHelper.NewLine)) + if (header.IndexOf (StringHelper.NewLine, StringComparison.Ordinal) == header.LastIndexOf (StringHelper.NewLine, StringComparison.Ordinal)) header += StringHelper.NewLine; engine.HeaderText = header; diff --git a/FileHelpers/Fields/DelimitedField.cs b/FileHelpers/Fields/DelimitedField.cs index 9b0feeebe..7d1d247a7 100644 --- a/FileHelpers/Fields/DelimitedField.cs +++ b/FileHelpers/Fields/DelimitedField.cs @@ -48,10 +48,10 @@ internal DelimitedField(FieldInfo fi, string sep) /// Set the separator string /// /// Also sets the discard count - internal string Separator + public string Separator { get { return mSeparator; } - set + internal set { mSeparator = value; @@ -65,17 +65,17 @@ internal string Separator /// /// allow a quoted multiline format /// - internal MultilineMode QuoteMultiline { get; set; } + public MultilineMode QuoteMultiline { get; internal set; } /// /// whether quotes are optional for read and / or write /// - internal QuoteMode QuoteMode { get; set; } + public QuoteMode QuoteMode { get; internal set; } /// /// quote character around field (and repeated within it) /// - internal char QuoteChar { get; set; } + public char QuoteChar { get; internal set; } #endregion diff --git a/FileHelpers/Fields/FieldBase.cs b/FileHelpers/Fields/FieldBase.cs index 013615f5a..3fac1b2e1 100644 --- a/FileHelpers/Fields/FieldBase.cs +++ b/FileHelpers/Fields/FieldBase.cs @@ -43,107 +43,105 @@ public abstract class FieldBase /// Field type of an array or it is just fieldType. /// What actual object will be created /// - internal Type FieldTypeInternal { get; set; } + public Type FieldTypeInternal { get; internal set; } /// /// Is this field an array? /// - public bool IsArray { get; private set; } + public bool IsArray { get; internal set; } /// /// Array must have this many entries /// - public int ArrayMinLength { get; set; } + public int ArrayMinLength { get; internal set; } /// /// Array may have this many entries, if equal to ArrayMinLength then /// it is a fixed length array /// - public int ArrayMaxLength { get; set; } + public int ArrayMaxLength { get; internal set; } /// /// Seems to be duplicate of FieldTypeInternal except it is ONLY set /// for an array /// - internal Type ArrayType { get; set; } - + public Type ArrayType { get; internal set; } /// /// Am I the first field in an array list /// - internal bool IsFirst { get; set; } + public bool IsFirst { get; internal set; } /// /// Am I the last field in the array list /// - internal bool IsLast { get; set; } + public bool IsLast { get; internal set; } /// /// Do we process this field but not store the value /// - public bool Discarded { get; set; } + public bool Discarded { get; internal set; } /// /// Unused! /// - internal bool TrailingArray { get; set; } + public bool TrailingArray { get; internal set; } /// /// Value to use if input is null or empty /// - internal object NullValue { get; set; } + public object NullValue { get; internal set; } /// /// Are we a simple string field we can just assign to /// - internal bool IsStringField { get; set; } + public bool IsStringField { get; internal set; } /// /// Details about the extraction criteria /// - internal FieldInfo FieldInfo { get; set; } - + public FieldInfo FieldInfo { get; internal set; } /// /// indicates whether we trim leading and/or trailing whitespace /// - public TrimMode TrimMode { get; set; } + public TrimMode TrimMode { get; internal set; } /// /// Character to chop off front and / rear of the string /// - internal char[] TrimChars { get; set; } + public char[] TrimChars { get; internal set; } /// /// The field may not be present on the input data (line not long enough) /// - public bool IsOptional { get; set; } + public bool IsOptional { get; internal set; } /// /// The next field along is optional, optimise processing next records /// - internal bool NextIsOptional { get; set; } + public bool NextIsOptional { get; internal set; } /// /// Set from the FieldInNewLIneAtribute. This field begins on a new /// line of the file /// - internal bool InNewLine { get; set; } + public bool InNewLine { get; internal set; } /// /// Order of the field in the file layout /// - internal int? FieldOrder { get; set; } + public int? FieldOrder { get; internal set; } /// /// Can null be assigned to this value type, for example not int or /// DateTime /// - internal bool IsNullableType { get; private set; } + public bool IsNullableType { get; private set; } /// /// Name of the field without extra characters (eg property) /// - internal string FieldFriendlyName { get; set; } + public string FieldFriendlyName { get; internal set; } // -------------------------------------------------------------- // WARNING !!! @@ -153,7 +151,7 @@ public abstract class FieldBase /// /// Fieldname of the field we are storing /// - internal string FieldName + public string FieldName { get { return FieldInfo.Name; } } @@ -313,9 +311,9 @@ public static FieldBase CreateField(FieldInfo fi, TypedRecordAttribute recordAtt if (fi.IsDefined(typeof(CompilerGeneratedAttribute), false)) { - if (fi.Name.EndsWith("__BackingField") && fi.Name.StartsWith("<") && fi.Name.Contains(">")) + if (fi.Name.EndsWith ("__BackingField", StringComparison.Ordinal) && fi.Name.StartsWith ("<", StringComparison.Ordinal) && fi.Name.Contains (">")) { - res.FieldFriendlyName = fi.Name.Substring(1, fi.Name.IndexOf(">") - 1); + res.FieldFriendlyName = fi.Name.Substring (1, fi.Name.IndexOf('>') - 1); } } diff --git a/FileHelpers/Fields/FixedLengthField.cs b/FileHelpers/Fields/FixedLengthField.cs index 61f37a942..28624c2c8 100644 --- a/FileHelpers/Fields/FixedLengthField.cs +++ b/FileHelpers/Fields/FixedLengthField.cs @@ -18,17 +18,17 @@ public sealed class FixedLengthField /// /// Field length of this field in the record /// - internal int FieldLength { get; private set; } + public int FieldLength { get; private set; } /// /// Alignment of this record /// - internal FieldAlignAttribute Align { get; private set; } + public FieldAlignAttribute Align { get; private set; } /// /// Whether we allow more or less characters to be handled /// - internal FixedMode FixedMode { get; set; } + public FixedMode FixedMode { get; internal set; } #endregion diff --git a/FileHelpers/FileHelpers.xml b/FileHelpers/FileHelpers.xml index 1f89df349..4dc634417 100644 --- a/FileHelpers/FileHelpers.xml +++ b/FileHelpers/FileHelpers.xml @@ -6987,6 +6987,12 @@ Destructor + + + Indicates if End of File was reached. + + The EOF flag. + Contains the last Record read by the method. This example shows a simple example of use of the async methods in the FileHelperAsymcEngine: diff --git a/FileHelpers/FormatDetector/QuoteHelper.cs b/FileHelpers/FormatDetector/QuoteHelper.cs index 4d96e564b..bffbac1be 100644 --- a/FileHelpers/FormatDetector/QuoteHelper.cs +++ b/FileHelpers/FormatDetector/QuoteHelper.cs @@ -22,7 +22,7 @@ public static int CountNumberOfDelimiters(string line, char delimiter, char quot var restOfLine = line; while (!string.IsNullOrEmpty(restOfLine)) { - if (restOfLine.StartsWith(quotedChar.ToString())) + if (restOfLine.StartsWith (quotedChar.ToString (), StringComparison.Ordinal)) { restOfLine = DiscardUntilQuotedChar(restOfLine, quotedChar); } @@ -54,7 +54,7 @@ public static int CountNumberOfDelimiters(string line, char delimiter, char quot /// private static string DiscardUntilQuotedChar(string line, char quoteChar) { - if (line.StartsWith(quoteChar.ToString())) + if (line.StartsWith (quoteChar.ToString (), StringComparison.Ordinal)) line = line.Substring(1); var index = line.IndexOf(quoteChar); diff --git a/FileHelpers/Helpers/ConditionHelper.cs b/FileHelpers/Helpers/ConditionHelper.cs index 710131b74..b2a284c67 100644 --- a/FileHelpers/Helpers/ConditionHelper.cs +++ b/FileHelpers/Helpers/ConditionHelper.cs @@ -17,7 +17,7 @@ internal static class ConditionHelper /// true if string begins with the selector public static bool BeginsWith(string line, string selector) { - return line.StartsWith(selector); + return line.StartsWith (selector, StringComparison.Ordinal); } /// @@ -28,7 +28,7 @@ public static bool BeginsWith(string line, string selector) /// true if string ends with the selector public static bool EndsWith(string line, string selector) { - return line.EndsWith(selector); + return line.EndsWith (selector, StringComparison.Ordinal); } /// @@ -39,7 +39,7 @@ public static bool EndsWith(string line, string selector) /// true if string contains the selector public static bool Contains(string line, string selector) { - return line.IndexOf(selector) >= 0; + return line.IndexOf (selector, 0, StringComparison.Ordinal) >= 0; } /// @@ -50,7 +50,7 @@ public static bool Contains(string line, string selector) /// true if string begins and ends with the selector public static bool Enclosed(string line, string selector) { - return line.StartsWith(selector) && line.EndsWith(selector); + return line.StartsWith (selector, StringComparison.Ordinal) && line.EndsWith (selector, StringComparison.Ordinal); } } } diff --git a/FileHelpers/Helpers/StringHelper.cs b/FileHelpers/Helpers/StringHelper.cs index 64486ee9f..aa4d0b384 100644 --- a/FileHelpers/Helpers/StringHelper.cs +++ b/FileHelpers/Helpers/StringHelper.cs @@ -145,14 +145,15 @@ internal static string RemoveBlanks(string source) { int i = 0; + int sz = source.Length; - while (i < source.Length && char.IsWhiteSpace(source[i])) + while (i < sz && char.IsWhiteSpace(source[i])) { i++; } // Only whitespace return an empty string - if (i >= source.Length) + if (i >= sz) return string.Empty; // we are looking for a gap after the sign, if not found then @@ -164,14 +165,14 @@ internal static string RemoveBlanks(string source) return source; // sign is followed by text so just return it // start out with the sign - var sb = new StringBuilder(source[i - 1].ToString()); + var sb = new StringBuilder (source[i - 1].ToString (), sz - i); i++; // I am on whitepsace so skip it - while (i < source.Length && char.IsWhiteSpace(source[i])) + while (i < sz && char.IsWhiteSpace(source[i])) { i++; } - if (i < source.Length) + if (i < sz) sb.Append(source.Substring(i)); return sb.ToString(); @@ -301,5 +302,21 @@ public static string Replace(string original, string oldValue, string newValue, return result; } + + public static bool IsNullOrWhiteSpace (string value) + { + if (value == null) + { + return true; + } + for (int i = 0; i < value.Length; i++) + { + if (!char.IsWhiteSpace (value[i])) + { + return false; + } + } + return true; + } } } \ No newline at end of file diff --git a/FileHelpers/MasterDetail/MasterDetailEngine.cs b/FileHelpers/MasterDetail/MasterDetailEngine.cs index 562425a94..3c69ad6df 100644 --- a/FileHelpers/MasterDetail/MasterDetailEngine.cs +++ b/FileHelpers/MasterDetail/MasterDetailEngine.cs @@ -121,49 +121,49 @@ internal RecordAction CommonSelectorMethod(string recordString) switch(mAction) { case CommonSelector.DetailIfContains: - if (recordString.IndexOf(mSelector) >= 0) + if (recordString.IndexOf (mSelector, StringComparison.Ordinal) >= 0) return RecordAction.Detail; else return RecordAction.Master; case CommonSelector.MasterIfContains: - if (recordString.IndexOf(mSelector) >= 0) + if (recordString.IndexOf (mSelector, StringComparison.Ordinal) >= 0) return RecordAction.Master; else return RecordAction.Detail; case CommonSelector.DetailIfBegins: - if (recordString.StartsWith(mSelector)) + if (recordString.StartsWith (mSelector, StringComparison.Ordinal)) return RecordAction.Detail; else return RecordAction.Master; case CommonSelector.MasterIfBegins: - if (recordString.StartsWith(mSelector)) + if (recordString.StartsWith (mSelector, StringComparison.Ordinal)) return RecordAction.Master; else return RecordAction.Detail; case CommonSelector.DetailIfEnds: - if (recordString.EndsWith(mSelector)) + if (recordString.EndsWith (mSelector, StringComparison.Ordinal)) return RecordAction.Detail; else return RecordAction.Master; case CommonSelector.MasterIfEnds: - if (recordString.EndsWith(mSelector)) + if (recordString.EndsWith (mSelector, StringComparison.Ordinal)) return RecordAction.Master; else return RecordAction.Detail; case CommonSelector.DetailIfEnclosed: - if (recordString.StartsWith(mSelector) && recordString.EndsWith(mSelector)) + if (recordString.StartsWith (mSelector, StringComparison.Ordinal) && recordString.EndsWith (mSelector, StringComparison.Ordinal)) return RecordAction.Detail; else return RecordAction.Master; case CommonSelector.MasterIfEnclosed: - if (recordString.StartsWith(mSelector) && recordString.EndsWith(mSelector)) + if (recordString.StartsWith (mSelector, StringComparison.Ordinal) && recordString.EndsWith (mSelector, StringComparison.Ordinal)) return RecordAction.Master; else return RecordAction.Detail; @@ -528,7 +528,7 @@ public void WriteStream(TextWriter writer, IEnumerable Date: Fri, 13 Sep 2013 12:50:41 -0300 Subject: [PATCH 2/2] Add to ErrorManager an recording limit Add to ErrorManager an recording limit to avoid possible out-of-memory errors in case of layout errors when dealing with large files. After the threshold is reached, successive errors are ignored. --- FileHelpers/ErrorHandling/ErrorManager.cs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/FileHelpers/ErrorHandling/ErrorManager.cs b/FileHelpers/ErrorHandling/ErrorManager.cs index 02ee756c5..57795a078 100644 --- a/FileHelpers/ErrorHandling/ErrorManager.cs +++ b/FileHelpers/ErrorHandling/ErrorManager.cs @@ -15,6 +15,8 @@ namespace FileHelpers public sealed class ErrorManager :IEnumerable { + private int mErrorLimit = 10000; + /// /// Initializes a new instance of the class. /// @@ -32,8 +34,15 @@ public ErrorManager(ErrorMode mode) mErrorMode = mode; } + /// Maximum number of recorded errors. After this limit is reached, successive errors are ignored. + /// Default error limit is 10000. + public int ErrorLimit + { + get { return mErrorLimit; } + set { mErrorLimit = value; } + } - private string ErrorsDescription() + private string ErrorsDescription () { if (ErrorCount == 1) return ErrorCount.ToString() + " Error"; @@ -44,7 +53,7 @@ private string ErrorsDescription() } [DebuggerBrowsable(DebuggerBrowsableState.Never)] - readonly ArrayList mErrorsArray = new ArrayList(); + List mErrorsArray = new List(); /// /// Is an array of that contains the @@ -53,7 +62,7 @@ private string ErrorsDescription() [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public ErrorInfo[] Errors { - get { return (ErrorInfo[]) mErrorsArray.ToArray(typeof (ErrorInfo)); } + get { return mErrorsArray.ToArray(); } } @@ -65,13 +74,13 @@ public ErrorInfo[] Errors /// Indicates the behavior of the /// when it found an error. /// + /// Default error mode is ThrowException. public ErrorMode ErrorMode { get { return mErrorMode; } set { mErrorMode = value; } } - /// Number of contained errors. public int ErrorCount { @@ -94,13 +103,15 @@ public void ClearErrors() /// internal void AddError(ErrorInfo error) { - mErrorsArray.Add(error); + if (mErrorsArray.Count <= mErrorLimit) + mErrorsArray.Add(error); } /// Add the specified ErrorInfo to the contained collection. internal void AddErrors(ErrorManager errors) { - mErrorsArray.AddRange(errors.mErrorsArray); + if (mErrorsArray.Count <= mErrorLimit) + mErrorsArray.AddRange(errors.mErrorsArray); } // public void ProcessError(Exception ex, string line)