/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Globalization; using System.IO; using System.Runtime.CompilerServices; using System.Text; namespace QuantConnect.Util { /// /// Extension methods to fetch data from a instance /// /// The value of these methods is performance. The objective is to avoid using /// and having to create intermediate substrings, parsing and splitting public static class StreamReaderExtensions { private const char NoMoreData = unchecked((char)-1); private const char DefaultDelimiter = ','; /// /// Gets a decimal from the provided stream reader /// /// The data stream /// The data delimiter character to use, default is ',' /// The decimal read from the stream [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal GetDecimal(this StreamReader stream, char delimiter = DefaultDelimiter) { bool pastEndLine; return GetDecimal(stream, out pastEndLine, delimiter); } /// /// Gets a decimal from the provided stream reader /// /// The data stream /// The data delimiter character to use, default is ',' /// True if end line was past, useful for consumers to know a line ended /// The decimal read from the stream [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal GetDecimal(this StreamReader stream, out bool pastEndLine, char delimiter = DefaultDelimiter) { long value = 0; var decimalPlaces = 0; var hasDecimals = false; var current = (char)stream.Read(); while (current == ' ') { current = (char)stream.Read(); } var isNegative = current == '-'; if (isNegative) { current = (char)stream.Read(); } pastEndLine = current == '\n' || current == '\r' && (stream.Peek() != '\n' || stream.Read() == '\n') || current == NoMoreData; while (!(current == delimiter || pastEndLine || current == ' ')) { if (current == '.') { hasDecimals = true; decimalPlaces = 0; } else { value = value * 10 + (current - '0'); decimalPlaces++; } current = (char)stream.Read(); pastEndLine = current == '\n' || current == '\r' && (stream.Peek() != '\n' || stream.Read() == '\n') || current == NoMoreData; } var lo = (int)value; var mid = (int)(value >> 32); return new decimal(lo, mid, 0, isNegative, (byte)(hasDecimals ? decimalPlaces : 0)); } /// /// Gets a date time instance from a stream reader /// /// The data stream /// The format in which the date time is /// The data delimiter character to use, default is ',' /// The date time instance read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DateTime GetDateTime(this StreamReader stream, string format = DateFormat.TwelveCharacter, char delimiter = DefaultDelimiter) { var current = (char)stream.Read(); while (current == ' ') { current = (char)stream.Read(); } var builder = new StringBuilder(12); while (!(current == delimiter || current == '\n' || current == '\r' && (stream.Peek() != '\n' || stream.Read() == '\n') || current == NoMoreData)) { builder.Append(current); current = (char)stream.Read(); } return DateTime.ParseExact(builder.ToString(), format, CultureInfo.InvariantCulture); } /// /// Gets an integer from a stream reader /// /// The data stream /// The data delimiter character to use, default is ',' /// The integer instance read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int GetInt32(this StreamReader stream, char delimiter = DefaultDelimiter) { var result = 0; var current = (char)stream.Read(); while (current == ' ') { current = (char)stream.Read(); } var isNegative = current == '-'; if (isNegative) { current = (char)stream.Read(); } while (!(current == delimiter || current == '\n' || current == '\r' && (stream.Peek() != '\n' || stream.Read() == '\n') || current == NoMoreData || current == ' ')) { result = (current - '0') + result * 10; current = (char)stream.Read(); } return isNegative ? result * -1 : result; } /// /// Gets a string from a stream reader /// /// The data stream /// The data delimiter character to use, default is ',' /// The string instance read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string GetString(this StreamReader stream, char delimiter = DefaultDelimiter) { StringBuilder result = null; var current = (char)stream.Read(); while (!(current == delimiter || current == '\n' || current == '\r' && (stream.Peek() != '\n' || stream.Read() == '\n') || current == NoMoreData)) { if (result == null) { result = new StringBuilder(); } result.Append(current); current = (char)stream.Read(); } return result == null ? string.Empty : result.ToString(); } } }