/// <summary> /// Gets the name of the file with its extension. /// </summary> /// <value>The name of file.</value> public string GetFullPathWithoutExtension() { var span = new StringSpan(0, FullPath.Length); if (NameSpan.IsValid) { span.Length = NameSpan.Next; } return span.IsValid ? FullPath.Substring(span) : null; }
public string GetFullPathWithoutExtension() { var span = new StringSpan(0, FullPath.Length); if (NameSpan.IsValid) { span.Length = NameSpan.Next; } return(span.IsValid ? FullPath.Substring(span) : null); }
private static string Decode(string pathToNormalize, bool isPathDirectory, out StringSpan drive, out StringSpan directory, out StringSpan fileName, out StringSpan fileExtension) { drive = new StringSpan(); directory = new StringSpan(); fileName = new StringSpan(); fileExtension = new StringSpan(); if (string.IsNullOrWhiteSpace(pathToNormalize)) { return(string.Empty); } // Normalize path // TODO handle network path/http/file path string error; var path = Normalize(pathToNormalize, out drive, out directory, out fileName, out error); if (error != null) { throw new ArgumentException(error); } if (isPathDirectory) { // If we are expecting a directory, merge the fileName with the directory if (fileName.IsValid) { directory.Length = directory.Length + 1 + fileName.Length; fileName = new StringSpan(); } } else { // In case this is only a directory name and we are expecting a filename, gets the directory name as a filename if (directory.IsValid && !fileName.IsValid) { fileName = directory; directory = new StringSpan(); } if (fileName.IsValid) { var extensionIndex = path.LastIndexOf('.', fileName.Start); if (extensionIndex > 0) { fileName.Length = extensionIndex - fileName.Start; fileExtension.Start = extensionIndex; fileExtension.Length = path.Length - extensionIndex; } } } return(path.ToString()); }
/// <summary> /// snake_case_string を CamelCaseString にする。 /// </summary> public unsafe static string SnakeToCamel(this string snake) { var p = stackalloc char[snake.Length]; var buffer = new StringSpan(p, snake.Length); Join(snake, ref buffer, new Splitter('_'), new ToCamel()); var len = (int)(buffer.Pointer - p); return(new string(p, 0, len)); }
public static IEnumerable <QueryCondition> ParseQueryConditions(this string query) { var stringSpan = new StringSpan(query); stringSpan.MoveToTheNextStartOfString(); while (!stringSpan.Eof) { var fieldName = stringSpan.ReadNextString(); var operation = stringSpan.ReadOperation(); if (operation == QueryOperation.In) { var value = stringSpan.ReadInValues(); yield return(new QueryCondition { FieldName = fieldName, Values = value.ToArray(), Operation = operation }); } else { var value = stringSpan.ReadNextString(); yield return(new QueryCondition { FieldName = fieldName, Values = value.ToSingleArray(), Operation = operation }); } stringSpan.MoveToTheNextStartOfString(); if (stringSpan.Eof) { break; } var logicalOperator = stringSpan.ReadNextString(); if (logicalOperator.ToLower() != "and") { throw new Exception("Only and logical operation is supported for a while"); } } }
public static List <T> Load <T>(string filename = null, int headerLines = 1) where T : new() { if (filename == null) { filename = locations[typeof(T)]; } Profiler.BeginSample("Datasheet.Load"); var stopwatch = Stopwatch.StartNew(); Profiler.BeginSample("File.ReadAllText"); var fileSw = Stopwatch.StartNew(); string csv = File.ReadAllText(Application.streamingAssetsPath + "/" + filename); var fileReadMs = fileSw.ElapsedMilliseconds; Profiler.EndSample(); Loader <T> loader = GetLoader <T>(); if (loader == null) { throw new Exception("Datasheet loader for " + typeof(T) + " not found"); } var splitter = new DatasheetLineSplitter(csv); splitter.Skip(headerLines); var records = new List <T>(1024); int lineIndex = headerLines; StringSpan line = new StringSpan(); var stream = new DatasheetStream(); while (splitter.ReadLine(ref line)) { if (line.length == 0) { continue; } stream.SetSource(line); T obj = new T(); try { loader.LoadRecord(ref obj, stream); } catch (Exception e) { throw new Exception("Datasheet parsing error at line " + lineIndex + ": " + line.str.Substring(0, 32), e); } records.Add(obj); } Debug.Log("Load " + filename + " (" + records.Count + " records, elapsed " + stopwatch.Elapsed.Milliseconds + " ms, file read " + fileReadMs + " ms)"); Profiler.EndSample(); return(records); }
protected UPath(string fullPath, StringSpan driveSpan, StringSpan directorySpan) { if (fullPath == null) { throw new ArgumentNullException(nameof(fullPath)); } FullPath = fullPath; hashCode = ComputeStringHashCodeCaseInsensitive(fullPath); DriveSpan = driveSpan; DirectorySpan = directorySpan; }
public CompileError(Document document, StringSpan span, string message) { SourceToken = new Token() { SourceDocument = document, Type = TokenType.None, Value = span }; Message = message; }
public StringSpan?Match(Document context, StringSpan text, List <CompileError> outErrors) { var match = Rule.Match(text); if (match.Success) { return(text.Slice(0, match.Length)); } return(null); }
public void Operators(string spanText, string text) { var span = new StringSpan(spanText); Assert.Multiple(() => { Assert.IsTrue(span <= text); Assert.IsFalse(text <= span); Assert.IsFalse(span >= text); Assert.IsTrue(text >= span); }); }
public bool ReadLine(ref StringSpan result) { if (_index >= _str.Length) { return(false); } int startIndex = _index; int length = ReadLine(); result = new StringSpan(_str, startIndex, length); return(true); }
/// <summary> /// CamelCaseString を snake_case_string に変換する。 /// </summary> public unsafe static string CamelToSnake(this string camel) { // 全部大文字の時がワーストケースで、元の長さの2倍。 var p = stackalloc char[camel.Length * 2]; var buffer = new StringSpan(p, camel.Length); Join(camel, ref buffer, new UpperCaseSplitter(), default(ToSnake)); var len = (int)(buffer.Pointer - p); return(new string(p, 0, len)); }
public static int Split(string input, char c, int start, out StringSpan span) { for (int i = start; i < input.Length; i++) { if (input[i] == c) { span = new StringSpan(input, start, i - start); } } span = new StringSpan(null, -1, -1); return(input.Length); }
/// <summary> /// <paramref name="s"/>を<paramref name="splitter"/>で分割して<paramref name="formatter"/>で結合する。 /// </summary> /// <param name="s">元の文字列。</param> /// <param name="buffer">書き込み先。</param> /// <param name="splitter">分割用。</param> /// <param name="formatter">結合用。</param> public unsafe static void Join <TSplitter, TFormatter>(this string s, ref StringSpan buffer, TSplitter splitter = default, TFormatter formatter = default) where TSplitter : IStringSplitter where TFormatter : IStringFormatter { fixed(char *p = s) { var span = new StringSpan(p, s.Length); while (splitter.TryMoveNext(ref span, out var word)) { formatter.Write(word, ref buffer); } } }
private static string ReadNextString(this StringSpan stringSpan) { stringSpan.MoveToTheNextStartOfString(); var isString = stringSpan.CurrentChar == CharToEscape; if (isString) { stringSpan.MoveEndPosition(EscapeSequence, c => c == CharToEscape, 1); stringSpan.MoveEndPosition(1); return(stringSpan.GetCurrentValue(EscapeSequence, StringToEscape)); } stringSpan.MoveEndPosition(EscapeSequence, c => c <= ' '); return(stringSpan.GetCurrentValue()); }
/// <summary> /// Get a stream for the specified file. /// </summary> public static System.IO.Stream CreateFileStream( StringSpan path, DesiredAccess desiredAccess, ShareModes shareMode, CreationDisposition creationDisposition, FileAttributes fileAttributes = FileAttributes.None, FileFlags fileFlags = FileFlags.None, SecurityQosFlags securityQosFlags = SecurityQosFlags.None) { var fileHandle = CreateFile(path, creationDisposition, desiredAccess, shareMode, fileAttributes, fileFlags, securityQosFlags); // FileStream will own the lifetime of the handle return(new System.IO.FileStream( handle: fileHandle, access: Conversion.DesiredAccessToFileAccess(desiredAccess), bufferSize: 4096, isAsync: (fileFlags & FileFlags.Overlapped) != 0)); }
/// <summary> /// Wrapper that allows using System.IO defines where available. Calls CreateFile2 if available. /// </summary> public static SafeFileHandle CreateFileSystemIo( StringSpan path, System.IO.FileAccess fileAccess, System.IO.FileShare fileShare, System.IO.FileMode fileMode, System.IO.FileAttributes fileAttributes = 0, FileFlags fileFlags = FileFlags.None, SecurityQosFlags securityFlags = SecurityQosFlags.None) { return(CreateFile( path: path, desiredAccess: Conversion.FileAccessToDesiredAccess(fileAccess), shareMode: Conversion.FileShareToShareMode(fileShare), creationDisposition: Conversion.FileModeToCreationDisposition(fileMode), fileAttributes: (FileAttributes)fileAttributes, fileFlags: fileFlags, securityQosFlags: securityFlags)); }
private static IEnumerable <string> ReadInValues(this StringSpan stringSpan) { stringSpan.MoveToTheNextStartOfString(); var arrayIsOpened = stringSpan.CurrentChar == '['; if (!arrayIsOpened) { throw new Exception("Invalid int operation at position: " + stringSpan.PositionStart); } stringSpan.MoveStartPosition(1); stringSpan.SyncEndWithStart(); while (!stringSpan.Eof) { var value = stringSpan.ReadNextStringFromArray(); yield return(value); stringSpan.SyncStartWithEnd(); if (stringSpan.CurrentChar <= ' ') { stringSpan.MoveToTheNextStartOfString(); stringSpan.SyncEndWithStart(); } if (stringSpan.CurrentChar == ']') { stringSpan.MoveStartPosition(1); stringSpan.SyncEndWithStart(); break; } if (stringSpan.CurrentChar != ',') { stringSpan.MoveStartPosition(EscapeSequence, c => c == ',' || c == ']'); } stringSpan.MoveStartPosition(1); stringSpan.SyncEndWithStart(); } }
/// <summary> /// Trims the path by removing unecessary '..' and '.' path items. /// </summary> /// <param name="builder">The builder.</param> /// <param name="currentPath">The current path.</param> /// <param name="paths">The paths.</param> /// <param name="isLastTrim">if set to <c>true</c> is last trim to occur.</param> /// <returns><c>true</c> if trim has been done, <c>false</c> otherwise.</returns> private static unsafe bool TrimParentAndSelfPath(StringBuilder builder, ref int currentPath, StringSpan *paths, bool isLastTrim) { var path = paths[currentPath]; if (currentPath > 0 && IsParentPath(builder, path)) { // If previous path is already a relative path, then we probably can popup var previousPath = paths[currentPath - 1]; if (IsParentPath(builder, previousPath)) { return(false); } // Note: the drive path has a negative Length at that moment so it will also be considered invalid (which is what we want) if (!previousPath.IsValid) { // Swallow the parent path if we reached some root level paths[currentPath].Length = 0; builder.Length = paths[currentPath].Start; return(true); } // We can popup the previous path paths[currentPath] = new StringSpan(); currentPath--; paths[currentPath].Length = 0; builder.Length = paths[currentPath].Start; return(true); } var isRelativeCurrentPath = IsRelativeCurrentPath(builder, path); if (!(isLastTrim && currentPath == 0 && isRelativeCurrentPath) && isRelativeCurrentPath) { // We can popup the previous path paths[currentPath].Length = 0; builder.Length = paths[currentPath].Start; return(true); } return(false); }
private static MetadataTypeReference ReadGenericArgument(ref StringSpan remaining) { var isBracketed = TryRead(ref remaining, '['); var(typeName, genericArguments, nestedArrayRanks) = ReadTypeParts(ref remaining); var assemblyName = default(StringSpan); if (isBracketed) { var endingBracket = remaining.IndexOf(']'); if (endingBracket == -1) { throw new FormatException("Expected ']'"); } assemblyName = TryReadAssemblyName(remaining.Slice(0, endingBracket)); remaining = remaining.Slice(endingBracket + 1); } return(CreateType(typeName, assemblyName, genericArguments, nestedArrayRanks)); }
/// <summary> /// Gets a canonical version of the given file's path. /// </summary> /// <param name="resolveLinks">True to get the destination of links rather than the link itself.</param> public static string GetFinalPathName(StringSpan path, GetFinalPathNameByHandleFlags finalPathFlags, bool resolveLinks) { if (path.IsEmpty) { return(null); } // BackupSemantics is needed to get directory handles FileFlags flags = FileFlags.BackupSemantics; if (!resolveLinks) { flags |= FileFlags.OpenReparsePoint; } using (SafeFileHandle fileHandle = CreateFile(path, CreationDisposition.OpenExisting, 0, ShareModes.ReadWrite, FileAttributes.None, flags)) { return(GetFinalPathNameByHandle(fileHandle, finalPathFlags)); } }
/// <summary> /// Trims the path by removing unecessary '..' and '.' path items. /// </summary> /// <param name="builder">The builder.</param> /// <param name="currentPath">The current path.</param> /// <param name="paths">The paths.</param> /// <param name="isLastTrim">if set to <c>true</c> is last trim to occur.</param> /// <returns><c>true</c> if trim has been done, <c>false</c> otherwise.</returns> private unsafe static bool TrimParentAndSelfPath(StringBuilder builder, ref int currentPath, StringSpan *paths, bool isLastTrim) { var path = paths[currentPath]; if (currentPath > 0 && IsParentPath(builder, path)) { // If the root path is a drive, and we are going to back slash, just don't if (IsInvalidRelativeBacktrackOnDrive(currentPath, paths)) { return(false); } // If previous path is already a relative path, then we probably can popup var previousPath = paths[currentPath - 1]; if (IsParentPath(builder, previousPath)) { return(false); } // We can popup the previous path paths[currentPath] = new StringSpan(); currentPath--; paths[currentPath].Length = 0; builder.Length = paths[currentPath].Start; return(true); } var isRelativeCurrentPath = IsRelativeCurrentPath(builder, path); if (!(isLastTrim && currentPath == 0 && isRelativeCurrentPath) && isRelativeCurrentPath) { // We can popup the previous path paths[currentPath].Length = 0; builder.Length = paths[currentPath].Start; return(true); } return(false); }
public unsafe bool TryMoveNext(ref StringSpan state, out StringSpan word) { var p = state.Pointer; var end = p + state.Length; if (p >= end) { word = default; return(false); } while (++p < end && !char.IsUpper(*p)) { ; } var len = (int)(p - state.Pointer); word = state.Slice(0, len); state = state.Slice(len); return(true); }
private static int ReadArrayRank(ref StringSpan remaining) { var rank = 1; for (;;) { switch (TryRead(ref remaining)) { case ',': rank++; break; case ' ': break; case ']': return(rank); default: throw new FormatException("Expected ' ', ',', or ']'"); } } }
public StringSpan?Match(Document context, StringSpan text, List <CompileError> outErrors) { return(Func(context, text, outErrors)); }
private static string Decode(string pathToNormalize, bool isPathDirectory, out StringSpan drive, out StringSpan directory, out StringSpan fileName, out StringSpan fileExtension) { drive = new StringSpan(); directory = new StringSpan(); fileName = new StringSpan(); fileExtension = new StringSpan(); if (string.IsNullOrWhiteSpace(pathToNormalize)) { return(string.Empty); } // Normalize path // TODO handle network path/http/file path string error; var path = Normalize(pathToNormalize, out drive, out directory, out fileName, out error); if (error != null) { throw new ArgumentException(error); } if (isPathDirectory) { // If we are expecting a directory, merge the fileName with the directory if (fileName.IsValid) { if (directory.IsValid) { // Case of '../file' directory.Length += fileName.Length; } else if (drive.IsValid) { // case of 'C:/file' directory.Start = drive.Next; directory.Length = fileName.Length + 1; } else { // Case of just a file 'file', make sure to include the leading '/' if there is one, // which is why we don't just do 'directory = fileName'. directory.Start = 0; directory.Length = fileName.Next; } fileName = new StringSpan(); } else if (drive.IsValid && !directory.IsValid) { // Case of just C:, we need to add a '/' to be a valid directory path.Append(DirectorySeparatorChar); directory.Start = drive.Next; directory.Length = 1; } } else { // In case this is only a directory name and we are expecting a filename, gets the directory name as a filename if (directory.IsValid && !fileName.IsValid) { fileName = directory; directory = new StringSpan(); } if (fileName.IsValid) { var extensionIndex = path.LastIndexOf('.', fileName.Start); if (extensionIndex >= 0) { fileName.Length = extensionIndex - fileName.Start; fileExtension.Start = extensionIndex; fileExtension.Length = path.Length - extensionIndex; } } } return(path.ToString()); }
/// <summary> /// Normalize a path by replacing '\' by '/' and transforming relative '..' or current path '.' to an absolute path. See remarks. /// </summary> /// <param name="pathToNormalize">The path automatic normalize.</param> /// <param name="drive">The drive character region.</param> /// <param name="directoryOrFileName">The directory.</param> /// <param name="fileName">Name of the file.</param> /// <param name="error">The error or null if no errors.</param> /// <returns>A normalized path or null if there is an error.</returns> /// <remarks>Unlike <see cref="System.IO.Path" /> , this doesn't make a path absolute to the actual file system.</remarks> public static unsafe StringBuilder Normalize(string pathToNormalize, out StringSpan drive, out StringSpan directoryOrFileName, out StringSpan fileName, out string error) { drive = new StringSpan(); directoryOrFileName = new StringSpan(); fileName = new StringSpan(); error = null; string path = pathToNormalize; if (path == null) { return(null); } int countDirectories = pathToNormalize.Count(pathItem => pathItem == DirectorySeparatorChar || pathItem == DirectorySeparatorCharAlt || pathItem == Path.VolumeSeparatorChar); // Safeguard if count directories is going wild if (countDirectories > 1024) { error = "Path contains too many directory '/' separator or ':'"; return(null); } // Optimize the code by using stack alloc in order to avoid allocation of a List<StringSpan>() int currentPath = -1; NormalizationState state = NormalizationState.StartComponent; bool hasDriveSpan = false; var paths = stackalloc StringSpan[countDirectories + 1]; var builder = new StringBuilder(pathToNormalize.Length); // Iterate on all chars on original path foreach (var pathItem in pathToNormalize) { // Check if we have a directory separator if (pathItem == DirectorySeparatorChar || pathItem == DirectorySeparatorCharAlt) { // Add only non consecutive '/' if (state != NormalizationState.DirectorySeparator) { // Special case where path is starting with "/" or with "X:/", we will create // an entry just for the "/". if ((state == NormalizationState.StartComponent) || (state == NormalizationState.VolumeSeparator)) { currentPath++; paths[currentPath] = new StringSpan(builder.Length, 1); } else { paths[currentPath].Length++; } builder.Append(DirectorySeparatorChar); // We are either reading more directory separator or reading a new component. state = NormalizationState.DirectorySeparator; } } else if (pathItem == Path.VolumeSeparatorChar) { // Check in case of volume separator ':' if (hasDriveSpan) { error = "Path contains more than one drive ':' separator"; return(null); } if (state == NormalizationState.DirectorySeparator) { error = "Path cannot contain a drive ':' separator after a backslash"; return(null); } if (state == NormalizationState.StartComponent) { error = "Path cannot start with a drive ':' separator"; return(null); } // Append the volume ':' builder.Append(pathItem); paths[currentPath].Length++; hasDriveSpan = true; state = NormalizationState.VolumeSeparator; // We are expecting to read a directory separator now } else if (!InvalidFileNameChars.Contains(pathItem)) { if (state == NormalizationState.VolumeSeparator) { error = @"Path must contain a separator '/' or '\' after the volume separator ':'"; return(null); } else if ((state == NormalizationState.StartComponent) || (state == NormalizationState.DirectorySeparator)) { // We are starting a new component. Check if previous one is either '..' or '.', in which case // we can simplify TrimParentAndSelfPath(builder, ref currentPath, paths, hasDriveSpan, false); currentPath++; paths[currentPath] = new StringSpan(builder.Length, 0); } builder.Append(pathItem); paths[currentPath].Length++; state = NormalizationState.InComponent; // We are expecting to read either a character, a separator or a volume separator; } else { // Else the character is invalid error = "Invalid character [{0}] found in path [{1}]".ToFormat(pathItem, pathToNormalize); return(null); } } // Remove trailing '..' or '.' TrimParentAndSelfPath(builder, ref currentPath, paths, hasDriveSpan, true); // Remove trailing if and only if the path content is not "/" or "c:/". if ((builder.Length > (hasDriveSpan ? paths[0].Next + 1 : 1)) && (builder[builder.Length - 1] == DirectorySeparatorChar)) { builder.Length = builder.Length - 1; paths[currentPath].Length--; } // Go back to upper path if current is not vaid if (currentPath > 0 && !paths[currentPath].IsValid) { currentPath--; } // Copy the drive, directory, filename information to the output int startDirectory = 0; if (hasDriveSpan) { drive = paths[0]; startDirectory = 1; } // If there is any directory information, process it if (startDirectory <= currentPath) { directoryOrFileName.Start = paths[startDirectory].Start; if (currentPath == startDirectory) { directoryOrFileName.Length = paths[startDirectory].Length; } else { directoryOrFileName.Length = paths[currentPath - 1].Next - directoryOrFileName.Start; if (paths[currentPath].IsValid) { // In case last path is a parent '..' don't include it in fileName if (IsParentComponentPath(builder, paths[currentPath])) { directoryOrFileName.Length += paths[currentPath].Length; } else { fileName.Start = paths[currentPath].Start; fileName.Length = builder.Length - fileName.Start; } } } } return(builder); }
public static string GetSubStrSpan(StringSpan strSpan) { return(strSpan.Slice(11, 12).ToString()); }
public static void Main(string[] args) { const string TestStr = "aaaaaaaaaa bbbbbbbbbbbb cccccccccccc dddddddddddd eeeeeeeeee ffffffffff gggggggggg"; long y = 0; for (int k = 0; k < IterNum; k++) { y += EnumPureString(TestStr); } StringSpan strSpan = TestStr; for (int k = 0; k < IterNum; k++) { y += EnumStringSpan(strSpan); } char[] charArr = TestStr.ToCharArray(); for (int k = 0; k < IterNum; k++) { y += EnumPureCharArr(charArr); } ArraySpan <char> arrSpan = charArr; for (int k = 0; k < IterNum; k++) { y += EnumArraySpan(arrSpan); } for (int k = 0; k < IterNum; k++) { var str1 = GetSubPureStr(TestStr); if (str1 != "bbbbbbbbbbbb") { throw new ArgumentException("str1"); } Hole(ref str1); } for (int k = 0; k < IterNum; k++) { var str2 = GetSubStrSpan(strSpan); if (str2 != "bbbbbbbbbbbb") { throw new ArgumentException("str2"); } Hole(ref str2); } for (int k = 0; k < IterNum; k++) { var str2 = GetSubPureCharArr(charArr); if (str2 != "bbbbbbbbbbbb") { throw new ArgumentException("str2"); } Hole(ref str2); } for (int k = 0; k < IterNum; k++) { var str2 = GetSubArraySpan(arrSpan); if (str2 != "bbbbbbbbbbbb") { throw new ArgumentException("str2"); } Hole(ref str2); } Console.WriteLine(y); }
internal UDirectory([NotNull] string fullPath, StringSpan driveSpan, StringSpan directorySpan) : base(fullPath, driveSpan, directorySpan) { }
public int GetChildIndex(StringSpan nameSpan) => Children.IndexOf(nameSpan, NameEquals);
internal UDirectory(string fullPath, StringSpan driveSpan, StringSpan directorySpan) : base(fullPath, driveSpan, directorySpan) { }
/// <summary> /// Gets the substring with the specified span. If the span is invalid, return null. /// </summary> /// <param name="str">The string.</param> /// <param name="span">The span.</param> /// <returns>A substring with the specified span or null if span is empty.</returns> public static string Substring(this string str, StringSpan span) { return span.IsValid ? str.Substring(span.Start, span.Length) : null; }