public unsafe void GetString() { UseASCIIBytePointer( "folderonefile.txt", bufferPtr => { LazyUTF8String firstFolder = LazyUTF8String.FromByteArray(bufferPtr + 0, 6); firstFolder.GetString().ShouldEqual("folder"); LazyUTF8String secondFolder = LazyUTF8String.FromByteArray(bufferPtr + 6, 3); secondFolder.GetString().ShouldEqual("one"); LazyUTF8String file = LazyUTF8String.FromByteArray(bufferPtr + 9, 8); file.GetString().ShouldEqual("file.txt"); }); }
public unsafe void GetString_NonASCII() { UseUTF8BytePointer( "folderoneريلٌأكتوبرfile.txt", bufferPtr => { LazyUTF8String firstFolder = LazyUTF8String.FromByteArray(bufferPtr + 0, 6); firstFolder.GetString().ShouldEqual("folder"); LazyUTF8String secondFolder = LazyUTF8String.FromByteArray(bufferPtr + 6, 3); secondFolder.GetString().ShouldEqual("one"); LazyUTF8String utf8 = LazyUTF8String.FromByteArray(bufferPtr + 9, 20); utf8.GetString().ShouldEqual("ريلٌأكتوبر"); LazyUTF8String file = LazyUTF8String.FromByteArray(bufferPtr + 29, 8); file.GetString().ShouldEqual("file.txt"); }); }
public unsafe int Compare(LazyUTF8String other, bool caseSensitive) { // If we've already converted to a .NET String, use their implementation because it's likely to contain // extended characters, which we're not set up to handle below if (this.utf16string != null || other.utf16string != null) { return(string.Compare(this.GetString(), other.GetString(), caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)); } // We now know that both strings are ASCII, because if they had extended characters they would // have already been created as string objects int minLength = this.length <= other.length ? this.length : other.length; byte *thisPtr = bytePool.RawPointer + this.startIndex; byte *otherPtr = bytePool.RawPointer + other.startIndex; int count = 0; // Case-sensitive comparison; always returns and never proceeds to case-insensitive comparison if (caseSensitive) { while (count < minLength) { if (*thisPtr != *otherPtr) { byte thisC = *thisPtr; byte otherC = *otherPtr; return(thisC - otherC); } ++thisPtr; ++otherPtr; ++count; } return(this.length - other.length); } // Case-insensitive comparison while (count < minLength) { if (*thisPtr != *otherPtr) { byte thisC = *thisPtr; byte otherC = *otherPtr; // The more intuitive approach to checking IsLower() is to do two comparisons to see if c is within the range 'a'-'z'. // However since byte is unsigned, we can rely on underflow to satisfy both conditions with one comparison. // if c < 'a', (c - 'a') will underflow and become a large positive number, hence > ('z' - 'a') // if c > 'z', (c - 'a') will naturally be > ('z' - 'a') // else the condition is satisfied and we know it is lower-case // Note: We only want to do the ToUpper calculation if one char is lower-case and the other char is not. // If they are both lower-case, they can be safely compared as is. //// if (thisC.IsLower()) if ((byte)(thisC - 'a') <= 'z' - 'a') { //// if (!otherC.IsLower()) if ((byte)(otherC - 'a') > 'z' - 'a') { //// thisC = thisC.ToUpper(); thisC -= 'a' - 'A'; } } else { //// else, we know !thisC.IsLower() //// if (otherC.IsLower()) if ((byte)(otherC - 'a') <= 'z' - 'a') { //// otherC = otherC.ToUpper(); otherC -= 'a' - 'A'; } } if (thisC != otherC) { return(thisC - otherC); } } ++thisPtr; ++otherPtr; ++count; } return(this.length - other.length); }
private int GetInsertionIndex(LazyUTF8String name) { int insertionIndex = 0; if (this.sortedEntries.Count != 0) { insertionIndex = this.GetSortedEntriesIndexOfName(name); if (insertionIndex >= 0) { throw new InvalidOperationException($"All entries should be unique, non-unique entry: {name.GetString()}"); } // When the name is not found the returned value is the bitwise complement of // where the name should be inserted to keep the sortedEntries in sorted order insertionIndex = ~insertionIndex; } return(insertionIndex); }