<#/*THIS IS A T4 FILE - see t4_text_templating.md for what it is and how to run codegen*/#> <#@ template debug="True" #> <#@ output extension=".gen.cs" encoding="utf-8" #> <#@ assembly name="System.Core" #> //------------------------------------------------------------------------------ // // This code was generated by a tool. // // TextTransform Samples/Packages/com.unity.collections/Unity.Collections/FixedString.tt // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ using System.Collections.Generic; using System.Collections; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; using UnityEngine.Internal; using UnityEngine; using Unity.Properties; namespace Unity.Collections { // A temporary copy of a struct is made before it is displayed in a C# debugger. // However, only the first element of data members with names is copied at this time. // Therefore, it's important that all data visible in the debugger, has a name // and includes no 'fixed' array. This is why we name every byte in the following struct. /// /// For internal use only. This type is 8 byte aligned /// [Serializable] [StructLayout(LayoutKind.Explicit, Size=16)] [GenerateTestsForBurstCompatibility] internal struct FixedBytes16Align8 { <# for(var i = 0; i < 2; ++i) { var offset = (i*8).ToString("D4"); #> /// /// For internal use only. /// [SerializeField] [FieldOffset(<#=i*8#>)] public ulong byte<#=offset#>; <# } #> } /// /// For internal use only. /// [Serializable] [StructLayout(LayoutKind.Explicit, Size=16)] [GenerateTestsForBurstCompatibility] public struct FixedBytes16 { <# for(var i = 0; i < 16; ++i) { var offset = i.ToString("D4"); #> /// /// For internal use only. /// [FieldOffset(<#=i#>)] public byte byte<#=offset#>; <# } #> } <# { var SIZES = new [] {32,64,128,512,4096}; foreach (var BYTES in SIZES) { #> // A temporary copy of a struct is made before it is displayed in a C# debugger. // However, only the first element of data members with names is copied at this time. // Therefore, it's important that all data visible in the debugger, has a name // and includes no 'fixed' array. This is why we name every byte in the following struct. /// /// For internal use only. /// [Serializable] [StructLayout(LayoutKind.Explicit, Size=<#=BYTES#>)] [GenerateTestsForBurstCompatibility] internal struct FixedBytes<#=BYTES#>Align8 { <# for(var i = 0; i < (BYTES/16)*16; i += 16) { var offset = i.ToString("D4"); #> /// /// For internal use only. /// [SerializeField] [FieldOffset(<#=i#>)] internal FixedBytes16Align8 offset<#=offset#>; <# } #> } <# }} #> <# { var SIZES = new [] {32,64,128,512,4096}; foreach (var BYTES in SIZES) { // 2 bytes for the ushort length var MAXLENGTH = BYTES - 2; var TYPENAME = $"FixedString{BYTES}Bytes"; var OLD_TYPENAME = $"FixedString{BYTES}"; #> // A temporary copy of a struct is made before it is displayed in a C# debugger. // However, only the first element of data members with names is copied at this time. // Therefore, it's important that all data visible in the debugger, has a name // and includes no 'fixed' array. This is why we name every byte in the following struct. /// /// For internal use only. /// [Serializable] [StructLayout(LayoutKind.Explicit, Size=<#=MAXLENGTH#>)] [GenerateTestsForBurstCompatibility] public struct FixedBytes<#=MAXLENGTH#> { <# for(var i = 0; i < (MAXLENGTH/16)*16; i += 16) { var offset = i.ToString("D4"); #> /// /// For internal use only. /// [FieldOffset(<#=i#>)] public FixedBytes16 offset<#=offset#>; <# } for(var i = (MAXLENGTH/16)*16; i < MAXLENGTH; ++i) { var offset = i.ToString("D4"); #> /// /// For internal use only. /// [FieldOffset(<#=i#>)] public byte byte<#=offset#>; <# } #> } /// /// An unmanaged UTF-8 string whose content is stored directly in the <#=BYTES#>-byte struct. /// /// /// The binary layout of this string is guaranteed, for now and all time, to be a length (a little-endian two byte integer) /// followed by the bytes of the characters (with no padding). A zero byte always immediately follows the last character. /// Effectively, the number of bytes for storing characters is 3 less than <#=BYTES#> (two length bytes and one null byte). /// /// This layout is identical to a of bytes, thus allowing reinterpretation between FixedString<#=BYTES#>Bytes and FixedList<#=BYTES#>Bytes. /// /// By virtue of being an unmanaged, non-allocated struct with no pointers, this string is fully compatible with jobs and Burst compilation. /// Unlike managed string types, these strings can be put in any unmanaged ECS components, FixedList, or any other unmanaged structs. /// [Serializable] [StructLayout(LayoutKind.Sequential, Size=<#=BYTES#>)] [GenerateTestsForBurstCompatibility] public partial struct <#=TYPENAME#> : INativeList , IUTF8Bytes , IComparable , IEquatable <# foreach (var OTHERBYTES in SIZES) { #> , IComparableBytes> , IEquatableBytes> <# } #> { internal const ushort utf8MaxLengthInBytes = <#=MAXLENGTH-1#>; [SerializeField] internal ushort utf8LengthInBytes; [SerializeField] internal FixedBytes<#=MAXLENGTH#> bytes; /// /// Returns the maximum number of UTF-8 bytes that can be stored in this string. /// /// /// The maximum number of UTF-8 bytes that can be stored in this string. /// public static int UTF8MaxLengthInBytes => utf8MaxLengthInBytes; /// /// For internal use only. Use instead. /// /// For internal use only. Use instead. [CreateProperty] [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [ExcludeFromBurstCompatTesting("Returns managed string")] public string Value => ToString(); /// /// Returns a pointer to the character bytes. /// /// /// The pointer returned by this method points into the internals of the target FixedString object. It is the /// caller's responsibility to ensure that the pointer is not used after the FixedString object is destroyed or goes /// out of scope. /// /// A pointer to the character bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly unsafe byte* GetUnsafePtr() { fixed(void* b = &bytes) return (byte*)b; } /// /// The current length in bytes of this string's content. /// /// /// The length value does not include the null-terminator byte. /// /// The new length in bytes of the string's content. /// Thrown if the new length is out of bounds. /// /// The current length in bytes of this string's content. /// public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return utf8LengthInBytes; } set { CheckLengthInRange(value); utf8LengthInBytes = (ushort)value; unsafe { GetUnsafePtr()[utf8LengthInBytes] = 0; } } } /// /// The number of bytes this string has for storing UTF-8 characters. /// /// The number of bytes this string has for storing UTF-8 characters. /// /// Does not include the null-terminator byte. /// /// A setter is included for conformity with , but is fixed at <#=BYTES-3#>. /// Setting the value to anything other than <#=BYTES-3#> throws an exception. /// /// In UTF-8 encoding, each Unicode code point (character) requires 1 to 4 bytes, /// so the number of characters that can be stored may be less than the capacity. /// /// Thrown if attempting to set the capacity to anything other than <#=BYTES-3#>. public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { return utf8MaxLengthInBytes; } set { CheckCapacityInRange(value); } } /// /// Attempts to set the length in bytes. Does nothing if the new length is invalid. /// /// The desired length. /// Whether added or removed bytes should be cleared (zeroed). (Increasing the length adds bytes; decreasing the length removes bytes.) /// True if the new length is valid. public bool TryResize(int newLength, NativeArrayOptions clearOptions = NativeArrayOptions.ClearMemory) { if (newLength < 0 || newLength > utf8MaxLengthInBytes) return false; if (newLength == utf8LengthInBytes) return true; unsafe { if (clearOptions == NativeArrayOptions.ClearMemory) { if (newLength > utf8LengthInBytes) UnsafeUtility.MemClear(GetUnsafePtr() + utf8LengthInBytes, newLength - utf8LengthInBytes); else UnsafeUtility.MemClear(GetUnsafePtr() + newLength, utf8LengthInBytes - newLength); } utf8LengthInBytes = (ushort)newLength; // always null terminate GetUnsafePtr()[utf8LengthInBytes] = 0; } return true; } /// /// Returns true if this string is empty (has no characters). /// /// True if this string is empty (has no characters). public readonly bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => utf8LengthInBytes == 0; } /// /// Returns the byte (not character) at an index. /// /// A byte index. /// The byte at the index. /// Thrown if the index is out of bounds. public byte this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly get { unsafe { CheckIndexInRange(index); return GetUnsafePtr()[index]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { unsafe { CheckIndexInRange(index); GetUnsafePtr()[index] = value; } } } /// /// Returns the reference to a byte (not character) at an index. /// /// A byte index. /// A reference to the byte at the index. /// Thrown if the index is out of bounds. [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref byte ElementAt(int index) { unsafe { CheckIndexInRange(index); return ref GetUnsafePtr()[index]; } } /// /// Sets the length to 0. /// public void Clear() { Length = 0; } /// /// Appends a byte. /// /// /// A zero byte will always follow the newly appended byte. /// /// No validation is performed: it is your responsibility for the bytes of the string to form valid UTF-8 when you're done appending bytes. /// /// A byte to append. public void Add(in byte value) { this[Length++] = value; } /// /// An enumerator over the characters (not bytes) of a FixedString<#=BYTES#>Bytes. /// /// /// In an enumerator's initial state, is not valid to read. /// The first call advances the enumerator's index to the first character. /// public struct Enumerator : IEnumerator { FixedString<#=BYTES#>Bytes target; int offset; Unicode.Rune current; /// /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes.Enumerator. /// /// A FixeString<#=BYTES#> for which to create an enumerator. public Enumerator(FixedString<#=BYTES#>Bytes other) { target = other; offset = 0; current = default; } /// /// Does nothing. /// public void Dispose() { } /// /// Advances the enumerator to the next character. /// /// True if is valid to read after the call. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (offset >= target.Length) return false; unsafe { Unicode.Utf8ToUcs(out current, target.GetUnsafePtr(), ref offset, target.Length); } return true; } /// /// Resets the enumerator to its initial state. /// public void Reset() { offset = 0; current = default; } /// /// The current character. /// /// /// In an enumerator's initial state, is not valid to read. /// /// The current character. public Unicode.Rune Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => current; } object IEnumerator.Current => Current; } /// /// Returns an enumerator for iterating over the characters of this string. /// /// An enumerator for iterating over the characters of the FixedString<#=BYTES#>Bytes. public Enumerator GetEnumerator() { return new Enumerator(this); } /// /// Returns the lexicographical sort order of this string relative to another. /// /// A `System.String` to compare with. /// An integer denoting the lexicographical sort order of this string relative to the other: /// /// 0 denotes both strings have the same sort position.
/// -1 denotes that this string should be sorted to precede the other string.
/// +1 denotes that this string should be sorted to follow the other string.
///
[ExcludeFromBurstCompatTesting("Takes managed string")] public int CompareTo(String other) { return ToString().CompareTo(other); } /// /// Returns true if this string and another have the same length and all the same characters. /// /// A string to compare for equality. /// True if this string and the other have the same length and all the same characters. [ExcludeFromBurstCompatTesting("Takes managed string")] public bool Equals(String other) { unsafe { int alen = utf8LengthInBytes; int blen = other.Length; byte* aptr = (byte*) UnsafeUtilityExtensions.AddressOf(bytes); fixed(char* bptr = other) { return UTF8ArrayUnsafeUtility.StrCmp(aptr, alen, bptr, blen) == 0; } } } /// /// Returns a reference to a FixedList<#=BYTES#>Bytes<byte> representation of this string. /// /// /// The referenced FixedListByte<#=BYTES#> is the very same bytes as the original FixedString<#=BYTES#>Bytes, /// so it is only valid as long as the original FixedString<#=BYTES#>Bytes is valid. /// /// A ref to a FixedListByte<#=BYTES#> representation of the FixedString<#=BYTES#>Bytes. public unsafe ref FixedList<#=BYTES#>Bytes AsFixedList() { return ref UnsafeUtility.AsRefBytes>(UnsafeUtility.AddressOf(ref this)); } /// /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes with the characters copied from a string. /// /// The source string to copy. /// Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes. [ExcludeFromBurstCompatTesting("Takes managed string")] public FixedString<#=BYTES#>Bytes(String source) { this = default; var error = Initialize(source); CheckCopyError(error, source); } /// /// Initializes an instance of FixedString<#=BYTES#>Bytes with the characters copied from a string. /// /// The source string to copy. /// If the length of the source string exceeds this fixed string's UTF8 capacity, only the portion that fits is copied in and CopyError.Truncation is returned. [ExcludeFromBurstCompatTesting("Takes managed string")] internal CopyError Initialize(String source) { return this.CopyFromTruncated(source); } /// /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes with a single character repeatedly appended some number of times. /// /// The Unicode.Rune to repeat. /// The number of times to repeat the character. Default is 1. public FixedString<#=BYTES#>Bytes(Unicode.Rune rune, int count = 1) { this = default; Initialize(rune, count); } /// /// Initializes an instance of FixedString<#=BYTES#>Bytes with a single character repeatedly appended some number of times. /// /// The Unicode.Rune to repeat. /// The number of times to repeat the character. Default is 1. /// If the length of the source string exceeds this fixed string's UTF8 capacity, the entire write operation will fail, and FormatError.Overflow is returned. internal FormatError Initialize(Unicode.Rune rune, int count = 1) { this = default; return this.Append(rune, count); } /// /// Initializes an instance of FixedString<#=BYTES#>Bytes that is a copy of another string. /// /// The source buffer. /// The number of bytes to read from the source. /// If the length of the source string exceeds this fixed string's UTF8 capacity, the entire write operation will fail, and FormatError.Overflow is returned. unsafe internal FormatError Initialize(byte* srcBytes, int srcLength) { bytes = default; utf8LengthInBytes = 0; unsafe { int len = 0; byte* dstBytes = GetUnsafePtr(); var error = UTF8ArrayUnsafeUtility.AppendUTF8Bytes(dstBytes, ref len, utf8MaxLengthInBytes, srcBytes, srcLength); if(error != FormatError.None) return error; this.Length = len; } return FormatError.None; } /// /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes that is a copy of another string. /// /// The string to copy. /// Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes. unsafe public FixedString<#=BYTES#>Bytes(NativeText.ReadOnly other) { this = default; var error = Initialize(other.GetUnsafePtr(), other.Length); CheckFormatError(error); } /// /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes that is a copy of another string. /// /// The UnsafeText to copy. /// Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes. unsafe public FixedString<#=BYTES#>Bytes(in UnsafeText other) { this = default; var error = Initialize(other.GetUnsafePtr(), other.Length); CheckFormatError(error); } <# // // Generate easy conversion and comparison between this and other FixedString types // foreach (var OTHERBYTES in SIZES) { #> /// /// Returns the lexicographical sort order of this string relative to another. /// /// A string to compare with. /// A number denoting the lexicographical sort order of this string relative to the other: /// /// 0 denotes that both strings have the same sort position.
/// -1 denotes that this string should be sorted to precede the other.
/// +1 denotes that this string should be sorted to follow the other.
///
public int CompareTo(FixedString<#=OTHERBYTES#>Bytes other) { return FixedStringMethods.CompareTo(ref this, other); } /// /// Initializes and returns an instance of FixedString<#=BYTES#>Bytes that is a copy of another string. /// /// The string to copy. /// Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes. public FixedString<#=BYTES#>Bytes(in FixedString<#=OTHERBYTES#>Bytes other) { this = default; var error = Initialize(other); CheckFormatError(error); } /// /// Initializes an instance of FixedString<#=BYTES#>Bytes that is a copy of another string. /// /// The string to copy. /// If the length of the source string exceeds this fixed string's UTF8 capacity, the entire write operation will fail, and FormatError.Overflow is returned. unsafe internal FormatError Initialize(in FixedString<#=OTHERBYTES#>Bytes other) { return Initialize((byte*) UnsafeUtilityExtensions.AddressOf(other.bytes), other.utf8LengthInBytes); } /// /// Returns true if a FixedString<#=BYTES#>Bytes and another string are equal. /// /// Two strings are equal if they have equal length and all their characters match. /// A FixedString<#=BYTES#>Bytes to compare for equality. /// A FixedString<#=OTHERBYTES#>Bytes to compare for equality. /// True if the two strings are equal. public static bool operator ==(in FixedString<#=BYTES#>Bytes a, in FixedString<#=OTHERBYTES#>Bytes b) { // this must not call any methods on 'a' or 'b' unsafe { int alen = a.utf8LengthInBytes; int blen = b.utf8LengthInBytes; byte* aptr = (byte*) UnsafeUtilityExtensions.AddressOf(a.bytes); byte* bptr = (byte*) UnsafeUtilityExtensions.AddressOf(b.bytes); return UTF8ArrayUnsafeUtility.EqualsUTF8Bytes(aptr, alen, bptr, blen); } } /// /// Returns true if a FixedString<#=BYTES#>Bytes and another string are unequal. /// /// Two strings are equal if they have equal length and all their characters match. /// A FixedString<#=BYTES#>Bytes to compare for inequality. /// A FixedString<#=OTHERBYTES#>Bytes to compare for inequality. /// True if the two strings are unequal. public static bool operator !=(in FixedString<#=BYTES#>Bytes a, in FixedString<#=OTHERBYTES#>Bytes b) { return !(a == b); } /// /// Returns true if this string and another string are equal. /// /// Two strings are equal if they have equal length and all their characters match. /// A FixedString<#=OTHERBYTES#>Bytes to compare for equality. /// True if the two strings are equal. public bool Equals(FixedString<#=OTHERBYTES#>Bytes other) { return this == other; } <# if (OTHERBYTES > BYTES) { // Generate implicit conversions to bigger-sized FixedStrings #> /// /// Returns a new FixedString<#=OTHERBYTES#>Bytes that is a copy of another string. /// /// A FixedString<#=BYTES#>Bytes to copy. /// A new FixedString<#=OTHERBYTES#>Bytes that is a copy of the other string. /// Thrown if the string to copy's length exceeds the capacity of FixedString<#=OTHERBYTES#>Bytes. public static implicit operator FixedString<#=OTHERBYTES#>Bytes(in FixedString<#=BYTES#>Bytes fs) => new FixedString<#=OTHERBYTES#>Bytes(in fs); <# } } #> /// /// Returns a new FixedString<#=BYTES#>Bytes that is a copy of another string. /// /// A string to copy. /// A new FixedString<#=BYTES#>Bytes that is a copy of another string. /// Thrown if the string to copy's length exceeds the capacity of FixedString<#=BYTES#>Bytes. [ExcludeFromBurstCompatTesting("Takes managed string")] public static implicit operator FixedString<#=BYTES#>Bytes(string b) => new FixedString<#=BYTES#>Bytes(b); /// /// Returns a new managed string that is a copy of this string. /// /// A new managed string that is a copy of this string. [ExcludeFromBurstCompatTesting("Returns managed string")] public override String ToString() { return this.ConvertToString(); } /// /// Returns a hash code of this string. /// /// Only the character bytes are included in the hash: any bytes beyond are not part of the hash. /// The hash code of this string. public override int GetHashCode() { return this.ComputeHashCode(); } /// /// Returns true if this string and an object are equal. /// /// /// Returns false if the object is neither a System.String or a FixedString. /// /// Two strings are equal if they have equal length and all their characters match. /// An object to compare for equality. /// True if this string and the object are equal. [ExcludeFromBurstCompatTesting("Takes managed object")] public override bool Equals(object obj) { if(ReferenceEquals(null, obj)) return false; if(obj is String aString) return Equals(aString); <# foreach(var OTHERBYTES in SIZES) { var OTHERTYPENAME = "FixedString" + OTHERBYTES + "Bytes"; WriteLine(" if(obj is {0} a{0}) return Equals(a{0});", OTHERTYPENAME); } #> return false; } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] [MethodImpl(MethodImplOptions.AggressiveInlining)] readonly void CheckIndexInRange(int index) { if (index < 0) throw new IndexOutOfRangeException($"Index {index} must be positive."); if (index >= utf8LengthInBytes) throw new IndexOutOfRangeException($"Index {index} is out of range in FixedString<#=BYTES#>Bytes of '{utf8LengthInBytes}' Length."); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] void CheckLengthInRange(int length) { if (length < 0) throw new ArgumentOutOfRangeException($"Length {length} must be positive."); if (length > utf8MaxLengthInBytes) throw new ArgumentOutOfRangeException($"Length {length} is out of range in FixedString<#=BYTES#>Bytes of '{utf8MaxLengthInBytes}' Capacity."); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] void CheckCapacityInRange(int capacity) { if (capacity > utf8MaxLengthInBytes) throw new ArgumentOutOfRangeException($"Capacity {capacity} must be lower than {utf8MaxLengthInBytes}."); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] static void CheckCopyError(CopyError error, String source) { if (error != CopyError.None) throw new ArgumentException($"FixedString<#=BYTES#>Bytes: {error} while copying \"{source}\""); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] static void CheckFormatError(FormatError error) { if (error != FormatError.None) throw new ArgumentException("Source is too long to fit into fixed string of this size"); } } <#}}#> }