Last active
November 13, 2017 13:14
-
-
Save neuecc/9d6772153898633292614ca40537ea0d to your computer and use it in GitHub Desktop.
StringSpan(Split, GetBytes, Trim), StringKey(for Dictionary Key as string, char[], StringSpan)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Text; | |
public static class StringSplitExtensions | |
{ | |
public static SplitCharEnumerator SplitSlice(this string str, char separator) | |
{ | |
return new SplitCharEnumerator(str, separator); | |
} | |
public static SplitStringEnumerator SplitSlice(this string str, string separator) | |
{ | |
return new SplitStringEnumerator(str, separator); | |
} | |
public struct SplitCharEnumerator | |
{ | |
readonly string str; | |
readonly char separator; | |
int index; | |
public SplitCharEnumerator(string str, char separator) : this() | |
{ | |
this.str = str; | |
this.separator = separator; | |
} | |
public bool TryMoveNext(out StringSpan split) | |
{ | |
if (index == -1) | |
{ | |
split = default(StringSpan); | |
return false; | |
} | |
var offset = index; | |
index = str.IndexOf(separator, offset); | |
if (index == -1) | |
{ | |
split = new StringSpan(str, offset, str.Length - offset); | |
return true; | |
} | |
else | |
{ | |
split = new StringSpan(str, offset, index - offset); | |
index++; | |
return true; | |
} | |
} | |
} | |
public struct SplitStringEnumerator | |
{ | |
readonly string str; | |
readonly string separator; | |
int index; | |
public SplitStringEnumerator(string str, string separator) : this() | |
{ | |
this.str = str; | |
this.separator = separator; | |
} | |
public bool TryMoveNext(out StringSpan split) | |
{ | |
if (index == -1) | |
{ | |
split = default(StringSpan); | |
return false; | |
} | |
var offset = index; | |
index = str.IndexOf(separator, offset, StringComparison.Ordinal); | |
if (index == -1) | |
{ | |
split = new StringSpan(str, offset, str.Length - offset); | |
return true; | |
} | |
else | |
{ | |
split = new StringSpan(str, offset, index - offset); | |
index += separator.Length; | |
return true; | |
} | |
} | |
} | |
} | |
[Flags] | |
public enum TrimOptions | |
{ | |
None = 0, | |
Whitespace = 1, | |
SingleQuotation = 2, | |
DoubleQuotation = 4 | |
} | |
public struct StringSpan | |
{ | |
readonly string str; | |
readonly int offset; | |
readonly int count; | |
public bool IsNull { get { return str == null; } } | |
public string String { get { return str; } } | |
public int Offset { get { return offset; } } | |
public int Count { get { return count; } } | |
public StringSpan(string str, int offset, int count) | |
{ | |
this.str = str; | |
this.offset = offset; | |
this.count = count; | |
} | |
public StringSpan Trim(TrimOptions options = TrimOptions.Whitespace) | |
{ | |
return TrimStart(options).TrimEnd(options); | |
} | |
public StringSpan TrimStart(TrimOptions options = TrimOptions.Whitespace) | |
{ | |
if (options == TrimOptions.None) return this; | |
var end = count - offset; | |
if ((options & TrimOptions.Whitespace) != 0) | |
{ | |
var i = offset; | |
for (; i < count; i++) | |
{ | |
if (str[i] != ' ') break; | |
} | |
if (i != offset) | |
{ | |
return new StringSpan(str, i, count - (i - offset)).TrimStart(options); | |
} | |
} | |
if ((options & TrimOptions.SingleQuotation) != 0) | |
{ | |
var i = offset; | |
for (; i < count; i++) | |
{ | |
if (str[i] != '\'') break; | |
} | |
if (i != offset) | |
{ | |
return new StringSpan(str, i, count - (i - offset)).TrimStart(options); | |
} | |
} | |
if ((options & TrimOptions.DoubleQuotation) != 0) | |
{ | |
var i = offset; | |
for (; i < count; i++) | |
{ | |
if (str[i] != '\"') break; | |
} | |
if (i != offset) | |
{ | |
return new StringSpan(str, i, count - (i - offset)).TrimStart(options); | |
} | |
} | |
return this; | |
} | |
public StringSpan TrimEnd(TrimOptions options = TrimOptions.Whitespace) | |
{ | |
if (options == TrimOptions.None) return this; | |
var end = count - offset; | |
if ((options & TrimOptions.Whitespace) != 0) | |
{ | |
var last = offset + count - 1; | |
var i = last; | |
for (; i >= offset; i--) | |
{ | |
if (str[i] != ' ') break; | |
} | |
if (last != i) | |
{ | |
return new StringSpan(str, offset, i - offset + 1).TrimEnd(options); | |
} | |
} | |
if ((options & TrimOptions.SingleQuotation) != 0) | |
{ | |
var last = offset + count - 1; | |
var i = last; | |
for (; i >= offset; i--) | |
{ | |
if (str[i] != '\'') break; | |
} | |
if (last != i) | |
{ | |
return new StringSpan(str, offset, i - offset + 1).TrimEnd(options); | |
} | |
} | |
if ((options & TrimOptions.DoubleQuotation) != 0) | |
{ | |
var last = offset + count - 1; | |
var i = last; | |
for (; i >= offset; i--) | |
{ | |
if (str[i] != '\"') break; | |
} | |
if (last != i) | |
{ | |
return new StringSpan(str, offset, i - offset + 1).TrimEnd(options); | |
} | |
} | |
return this; | |
} | |
public static implicit operator StringSpan(string stringKey) | |
{ | |
return new StringSpan(stringKey, 0, stringKey.Length); | |
} | |
public int GetBytes(Encoding encoding, byte[] bytes, int byteOffset) | |
{ | |
return encoding.GetBytes(str, offset, count, bytes, byteOffset); | |
} | |
public override string ToString() | |
{ | |
if (str == null) return null; | |
if (offset == 0 && count == str.Length) return str; | |
return str.Substring(offset, count); | |
} | |
} | |
public struct StringKey : IEquatable<StringKey> | |
{ | |
StringSpan stringKey; | |
ArraySegment<char> charKey; | |
bool isString; | |
public string Key | |
{ | |
get | |
{ | |
return isString | |
? stringKey.ToString() | |
: (charKey.Array == null) ? null | |
: new string(charKey.Array, charKey.Offset, charKey.Count); | |
} | |
} | |
public StringKey(StringSpan stringKey) | |
{ | |
this.stringKey = stringKey; | |
this.charKey = default(ArraySegment<char>); | |
this.isString = true; | |
} | |
public StringKey(ArraySegment<char> charKey) | |
{ | |
this.stringKey = null; | |
this.charKey = charKey; | |
this.isString = false; | |
} | |
public StringKey(char[] charKey, int offset, int count) | |
{ | |
this.stringKey = null; | |
this.charKey = new ArraySegment<char>(charKey, offset, count); | |
this.isString = false; | |
} | |
public static implicit operator StringKey(string stringKey) | |
{ | |
return new StringKey(stringKey); | |
} | |
public static implicit operator StringKey(StringSpan stringKey) | |
{ | |
return new StringKey(stringKey); | |
} | |
public static implicit operator StringKey(ArraySegment<char> charKey) | |
{ | |
return new StringKey(charKey.Array, charKey.Offset, charKey.Count); | |
} | |
// FNV1-1a hash https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function | |
// Same as switch(string) http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/Compiler/MethodBodySynthesizer.Lowered.cs,26 | |
public override int GetHashCode() | |
{ | |
int hashCode = unchecked((int)2166136261); | |
if (isString) | |
{ | |
if (stringKey.IsNull) return 0; | |
var text = stringKey.String; | |
var end = stringKey.Offset + stringKey.Count; | |
for (int i = stringKey.Offset; i < end; i++) | |
{ | |
hashCode = unchecked((hashCode ^ text[i]) * 16777619); | |
} | |
return hashCode; | |
} | |
else | |
{ | |
if (charKey.Array == null) return 0; | |
var text = charKey.Array; | |
var end = charKey.Offset + charKey.Count; | |
for (int i = charKey.Offset; i < end; i++) | |
{ | |
hashCode = unchecked((hashCode ^ text[i]) * 16777619); | |
} | |
return hashCode; | |
} | |
} | |
public bool Equals(StringKey other) | |
{ | |
if (this.isString && other.isString) | |
{ | |
if (this.stringKey.IsNull && other.stringKey.IsNull) return true; | |
if (this.stringKey.IsNull) return false; | |
if (other.stringKey.IsNull) return false; | |
if (this.stringKey.Count != other.stringKey.Count) return false; | |
if (this.stringKey.Offset == 0 && this.stringKey.String.Length == this.stringKey.Count | |
&& other.stringKey.Offset == 0 && other.stringKey.String.Length == other.stringKey.Count) | |
{ | |
return this.stringKey.String == other.stringKey.String; | |
} | |
var i1 = this.stringKey.Offset; | |
for (int i2 = other.stringKey.Offset; i2 < other.stringKey.Count; i2++, i1++) | |
{ | |
if (this.stringKey.String[i1] != other.stringKey.String[i2]) return false; | |
} | |
return true; | |
} | |
if (this.isString && !other.isString) | |
{ | |
if (this.stringKey.IsNull && other.charKey.Array == null) return true; | |
if (this.stringKey.IsNull) return false; | |
if (other.charKey.Array == null) return false; | |
if (this.stringKey.Count != other.charKey.Count) return false; | |
var i1 = this.stringKey.Offset; | |
for (int i2 = other.charKey.Offset; i2 < other.charKey.Count; i2++, i1++) | |
{ | |
if (this.stringKey.String[i1] != other.charKey.Array[i2]) return false; | |
} | |
return true; | |
} | |
if (!this.isString && other.isString) | |
{ | |
if (other.stringKey.IsNull && this.charKey.Array == null) return true; | |
if (other.stringKey.IsNull) return false; | |
if (this.charKey.Array == null) return false; | |
if (other.stringKey.Count != this.charKey.Count) return false; | |
var i1 = other.stringKey.Offset; | |
for (int i2 = this.charKey.Offset; i2 < this.charKey.Count; i2++, i1++) | |
{ | |
if (other.stringKey.String[i1] != this.charKey.Array[i2]) return false; | |
} | |
return true; | |
} | |
{ | |
if (this.charKey.Array == null && other.charKey.Array == null) return true; | |
if (this.charKey.Array == null) return false; | |
if (other.charKey.Array == null) return false; | |
if (this.charKey.Count != other.charKey.Count) return false; | |
var i1 = this.charKey.Offset; | |
for (int i2 = other.charKey.Offset; i2 < other.charKey.Count; i2++, i1++) | |
{ | |
if (this.charKey.Array[i1] != other.charKey.Array[i2]) return false; | |
} | |
return true; | |
} | |
} | |
public override string ToString() | |
{ | |
return Key; | |
} | |
} |
// @ufcpp suggested more efficient StringKey
[StructLayout(LayoutKind.Explicit)]
public struct StringKey : IEquatable<StringKey>
{
[FieldOffset(0)]
StringSpan stringKey;
[FieldOffset(0)]
ArraySegment<char> charKey;
[FieldOffset(0)]
object firstField; // StringSpan.String or ArraySegment<char>.Array
bool IsString { get { return firstField is string; } }
public string Key
{
get
{
return IsString
? stringKey.ToString()
: (charKey.Array == null) ? null
: new string(charKey.Array, charKey.Offset, charKey.Count);
}
}
public StringKey(StringSpan stringKey) : this()
{
this.stringKey = stringKey;
}
public StringKey(ArraySegment<char> charKey) : this()
{
this.charKey = charKey;
}
public StringKey(char[] charKey, int offset, int count) : this()
{
this.charKey = new ArraySegment<char>(charKey, offset, count);
}
public static implicit operator StringKey(string stringKey)
{
return new StringKey(stringKey);
}
public static implicit operator StringKey(StringSpan stringKey)
{
return new StringKey(stringKey);
}
public static implicit operator StringKey(ArraySegment<char> charKey)
{
return new StringKey(charKey.Array, charKey.Offset, charKey.Count);
}
// FNV1-1a hash https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
// Same as switch(string) http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/Compiler/MethodBodySynthesizer.Lowered.cs,26
public override int GetHashCode()
{
int hashCode = unchecked((int)2166136261);
if (IsString)
{
if (stringKey.IsNull) return 0;
var text = stringKey.String;
var end = stringKey.Offset + stringKey.Count;
for (int i = stringKey.Offset; i < end; i++)
{
hashCode = unchecked((hashCode ^ text[i]) * 16777619);
}
return hashCode;
}
else
{
if (charKey.Array == null) return 0;
var text = charKey.Array;
var end = charKey.Offset + charKey.Count;
for (int i = charKey.Offset; i < end; i++)
{
hashCode = unchecked((hashCode ^ text[i]) * 16777619);
}
return hashCode;
}
}
public bool Equals(StringKey other)
{
if (this.IsString && other.IsString)
{
if (this.stringKey.IsNull && other.stringKey.IsNull) return true;
if (this.stringKey.IsNull) return false;
if (other.stringKey.IsNull) return false;
if (this.stringKey.Count != other.stringKey.Count) return false;
if (this.stringKey.Offset == 0 && this.stringKey.String.Length == this.stringKey.Count
&& other.stringKey.Offset == 0 && other.stringKey.String.Length == other.stringKey.Count)
{
return this.stringKey.String == other.stringKey.String;
}
var i1 = this.stringKey.Offset;
for (int i2 = other.stringKey.Offset; i2 < other.stringKey.Count; i2++, i1++)
{
if (this.stringKey.String[i1] != other.stringKey.String[i2]) return false;
}
return true;
}
if (this.IsString && !other.IsString)
{
if (this.stringKey.IsNull && other.charKey.Array == null) return true;
if (this.stringKey.IsNull) return false;
if (other.charKey.Array == null) return false;
if (this.stringKey.Count != other.charKey.Count) return false;
var i1 = this.stringKey.Offset;
for (int i2 = other.charKey.Offset; i2 < other.charKey.Count; i2++, i1++)
{
if (this.stringKey.String[i1] != other.charKey.Array[i2]) return false;
}
return true;
}
if (!this.IsString && other.IsString)
{
if (other.stringKey.IsNull && this.charKey.Array == null) return true;
if (other.stringKey.IsNull) return false;
if (this.charKey.Array == null) return false;
if (other.stringKey.Count != this.charKey.Count) return false;
var i1 = other.stringKey.Offset;
for (int i2 = this.charKey.Offset; i2 < this.charKey.Count; i2++, i1++)
{
if (other.stringKey.String[i1] != this.charKey.Array[i2]) return false;
}
return true;
}
{
if (this.charKey.Array == null && other.charKey.Array == null) return true;
if (this.charKey.Array == null) return false;
if (other.charKey.Array == null) return false;
if (this.charKey.Count != other.charKey.Count) return false;
var i1 = this.charKey.Offset;
for (int i2 = other.charKey.Offset; i2 < other.charKey.Count; i2++, i1++)
{
if (this.charKey.Array[i1] != other.charKey.Array[i2]) return false;
}
return true;
}
}
public override string ToString()
{
return Key;
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage