mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-21 09:27:59 +02:00
Initial checkin
This commit is contained in:
parent
c74712dad9
commit
078564ac9e
3242 changed files with 1616395 additions and 0 deletions
10
BeefLibs/corlib/src/System/Action.bf
Normal file
10
BeefLibs/corlib/src/System/Action.bf
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace System
|
||||
{
|
||||
public delegate void Action();
|
||||
public delegate void Action<T>(T obj);
|
||||
public delegate void Action<T1, T2>(T1 p1, T2 p2);
|
||||
public delegate void Action<T1, T2, T3>(T1 p1, T2 p2, T3 p3);
|
||||
public delegate void Action<T1, T2, T3, T4>(T1 p1, T2 p2, T3 p3, T4 p4);
|
||||
|
||||
public delegate int Comparison<T>(T lhs, T rhs);
|
||||
}
|
563
BeefLibs/corlib/src/System/Array.bf
Normal file
563
BeefLibs/corlib/src/System/Array.bf
Normal file
|
@ -0,0 +1,563 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System
|
||||
{
|
||||
#if BF_LARGE_COLLECTIONS
|
||||
typealias int_arsize = int64;
|
||||
#else
|
||||
typealias int_arsize = int32;
|
||||
#endif
|
||||
|
||||
[CRepr]
|
||||
class Array
|
||||
{
|
||||
protected int_arsize mLength;
|
||||
|
||||
public int Count
|
||||
{
|
||||
[Inline]
|
||||
set
|
||||
{
|
||||
// We only allow reducing the length - consider using System.Collections.Generic.List<T> when dynamic sizing is required
|
||||
Runtime.Assert(value <= mLength);
|
||||
mLength = (int_arsize)value;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return mLength == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static int BinarySearch<T>(T* arr, int idx, int length, T value) where T : IComparable<T>
|
||||
{
|
||||
int lo = idx;
|
||||
int hi = idx + length - 1;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
int i = (lo + hi) / 2;
|
||||
T midVal = arr[i];
|
||||
int c = midVal.CompareTo(value);
|
||||
if (c == 0) return i;
|
||||
if (c < 0)
|
||||
lo = i + 1;
|
||||
else
|
||||
hi = i - 1;
|
||||
}
|
||||
return ~lo;
|
||||
}
|
||||
|
||||
public static int BinarySearch<T>(T* arr, int idx, int length, T value, IComparer<T> comp)
|
||||
{
|
||||
int lo = idx;
|
||||
int hi = idx + length - 1;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
int i = (lo + hi) / 2;
|
||||
T midVal = arr[i];
|
||||
int c = comp.Compare(midVal, value);
|
||||
if (c == 0) return i;
|
||||
if (c < 0)
|
||||
lo = i + 1;
|
||||
else
|
||||
hi = i - 1;
|
||||
}
|
||||
return ~lo;
|
||||
}
|
||||
|
||||
public static void Clear<T>(T[] arr, int idx, int length)
|
||||
{
|
||||
for (int i = idx; i < idx + length; i++)
|
||||
arr[i] = default(T);
|
||||
}
|
||||
|
||||
public static void Copy<T, T2>(T[] arrayFrom, int srcOffset, T2[] arrayTo, int dstOffset, int length) where T : var where T2 : var
|
||||
{
|
||||
if (((Object)arrayTo == (Object)arrayFrom) && (dstOffset > srcOffset))
|
||||
{
|
||||
for (int i = length - 1; i >= 0; i--)
|
||||
arrayTo.getRef(i + dstOffset) = (T2)arrayFrom.getRef(i + srcOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
arrayTo.getRef(i + dstOffset) = (T2)arrayFrom.getRef(i + srcOffset);
|
||||
}
|
||||
|
||||
public static void Sort<T>(T[] array, Comparison<T> comp)
|
||||
{
|
||||
var sorter = Sorter<T, void>(&array.[Friend]mFirstElement, null, array.[Friend]mLength, comp);
|
||||
sorter.Sort(0, array.[Friend]mLength);
|
||||
}
|
||||
|
||||
public static void Sort<T, T2>(T[] keys, T2[] items, Comparison<T> comp)
|
||||
{
|
||||
var sorter = Sorter<T, T2>(&keys.[Friend]mFirstElement, &items.[Friend]mFirstElement, keys.[Friend]mLength, comp);
|
||||
sorter.Sort(0, keys.[Friend]mLength);
|
||||
}
|
||||
|
||||
public static void Sort<T>(T[] array, int index, int count, Comparison<T> comp)
|
||||
{
|
||||
var sorter = Sorter<T, void>(&array.[Friend]mFirstElement, null, array.[Friend]mLength, comp);
|
||||
sorter.Sort(index, count);
|
||||
}
|
||||
}
|
||||
|
||||
[CRepr]
|
||||
class Array1<T> : Array
|
||||
{
|
||||
T mFirstElement;
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRef(int idx)
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRefChecked(int idx)
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
public ref T this[int idx]
|
||||
{
|
||||
[Checked, Inline]
|
||||
get
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public T* CArray()
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] arrayTo)
|
||||
{
|
||||
Debug.Assert(arrayTo.mLength >= mLength);
|
||||
Internal.MemCpy(&arrayTo.getRef(0), &getRef(0), strideof(T) * mLength, alignof(T));
|
||||
}
|
||||
|
||||
public void CopyTo(T[] arrayTo, int srcOffset, int dstOffset, int length)
|
||||
{
|
||||
Debug.Assert(length >= 0);
|
||||
Debug.Assert((uint)srcOffset + (uint)length <= (uint)mLength);
|
||||
Debug.Assert((uint)dstOffset + (uint)length <= (uint)arrayTo.mLength);
|
||||
Internal.MemCpy(&arrayTo.getRef(dstOffset), &getRef(srcOffset), strideof(T) * length, alignof(T));
|
||||
}
|
||||
|
||||
public void CopyTo<T2>(T2[] arrayTo, int srcOffset, int dstOffset, int length) where T2 : var
|
||||
{
|
||||
Debug.Assert(length >= 0);
|
||||
Debug.Assert((uint)srcOffset + (uint)length <= (uint)mLength);
|
||||
Debug.Assert((uint)dstOffset + (uint)length <= (uint)arrayTo.[Friend]mLength);
|
||||
|
||||
if (((Object)arrayTo == (Object)this) && (dstOffset > srcOffset))
|
||||
{
|
||||
for (int i = length - 1; i >= 0; i--)
|
||||
arrayTo.getRef(i + dstOffset) = (T2)getRef(i + srcOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
arrayTo.getRef(i + dstOffset) = (T2)getRef(i + srcOffset);
|
||||
}
|
||||
|
||||
public Span<T>.Enumerator GetEnumerator()
|
||||
{
|
||||
return .(this);
|
||||
}
|
||||
|
||||
protected override void GCMarkMembers()
|
||||
{
|
||||
let type = typeof(T);
|
||||
if ((type.mTypeFlags & .WantsMark) == 0)
|
||||
return;
|
||||
for (int i = 0; i < mLength; i++)
|
||||
{
|
||||
GC.Mark((&mFirstElement)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CRepr]
|
||||
class Array2<T> : Array
|
||||
{
|
||||
int_arsize mLength1;
|
||||
T mFirstElement;
|
||||
|
||||
Array GetSelf()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public int GetLength(int dim)
|
||||
{
|
||||
if (dim == 0)
|
||||
return mLength / mLength1;
|
||||
else if (dim == 1)
|
||||
return mLength1;
|
||||
Runtime.FatalError();
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRef(int idx)
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRef(int idx0, int idx1)
|
||||
{
|
||||
return ref (&mFirstElement)[idx0*mLength1 + idx1];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRefChecked(int idx)
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRefChecked(int idx0, int idx1)
|
||||
{
|
||||
int idx = idx0*mLength1 + idx1;
|
||||
if (((uint)idx >= (uint)mLength) ||
|
||||
((uint)idx1 >= (uint)mLength1))
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
public ref T this[int idx]
|
||||
{
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx0, int idx1]
|
||||
{
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref (&mFirstElement)[idx0*mLength1 + idx1];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx0, int idx1]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
int idx = idx0*mLength1 + idx1;
|
||||
if (((uint)idx >= (uint)mLength) ||
|
||||
((uint)idx1 >= (uint)mLength1))
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public T* CArray()
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
|
||||
protected override void GCMarkMembers()
|
||||
{
|
||||
for (int i = 0; i < mLength; i++)
|
||||
{
|
||||
GC.Mark_Unbound((&mFirstElement)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CRepr]
|
||||
class Array3<T> : Array
|
||||
{
|
||||
int_arsize mLength1;
|
||||
int_arsize mLength2;
|
||||
T mFirstElement;
|
||||
|
||||
Array GetSelf()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public int GetLength(int dim)
|
||||
{
|
||||
if (dim == 0)
|
||||
return mLength / (mLength1*mLength2);
|
||||
else if (dim == 1)
|
||||
return mLength1;
|
||||
else if (dim == 2)
|
||||
return mLength2;
|
||||
Runtime.FatalError();
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRef(int idx)
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRef(int idx0, int idx1, int idx2)
|
||||
{
|
||||
return ref (&mFirstElement)[(idx0*mLength1 + idx1)*mLength2 + idx2];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRefChecked(int idx)
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRefChecked(int idx0, int idx1, int idx2)
|
||||
{
|
||||
int idx = (idx0*mLength1 + idx1)*mLength2 + idx2;
|
||||
if (((uint)idx >= (uint)mLength) ||
|
||||
((uint)idx1 >= (uint)mLength1) ||
|
||||
((uint)idx2 >= (uint)mLength2))
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
public ref T this[int idx]
|
||||
{
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx0, int idx1, int idx2]
|
||||
{
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref (&mFirstElement)[(idx0*mLength1 + idx1)*mLength2 + idx2];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx0, int idx1, int idx2]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
int idx = (idx0*mLength1 + idx1)*mLength2 + idx2;
|
||||
if (((uint)idx >= (uint)mLength) ||
|
||||
((uint)idx1 >= (uint)mLength1) ||
|
||||
((uint)idx2 >= (uint)mLength2))
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public T* CArray()
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
|
||||
protected override void GCMarkMembers()
|
||||
{
|
||||
for (int i = 0; i < mLength; i++)
|
||||
{
|
||||
GC.Mark_Unbound((&mFirstElement)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CRepr]
|
||||
class Array4<T> : Array
|
||||
{
|
||||
int_arsize mLength1;
|
||||
int_arsize mLength2;
|
||||
int_arsize mLength3;
|
||||
T mFirstElement;
|
||||
|
||||
Array GetSelf()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public int GetLength(int dim)
|
||||
{
|
||||
if (dim == 0)
|
||||
return mLength / (mLength1*mLength2*mLength3);
|
||||
else if (dim == 1)
|
||||
return mLength1;
|
||||
else if (dim == 2)
|
||||
return mLength2;
|
||||
else if (dim == 3)
|
||||
return mLength3;
|
||||
Runtime.FatalError();
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRef(int idx)
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRef(int idx0, int idx1, int idx2, int idx3)
|
||||
{
|
||||
return ref (&mFirstElement)[((idx0*mLength1 + idx1)*mLength2 + idx2)*mLength3 + idx3];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRefChecked(int idx)
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ref T getRefChecked(int idx0, int idx1, int idx2, int idx3)
|
||||
{
|
||||
int idx = ((idx0*mLength1 + idx1)*mLength2 + idx2)*mLength3 + idx3;
|
||||
if (((uint)idx >= (uint)mLength) ||
|
||||
((uint)idx1 >= (uint)mLength1) ||
|
||||
((uint)idx2 >= (uint)mLength2) ||
|
||||
((uint)idx3 >= (uint)mLength3))
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
|
||||
public ref T this[int idx]
|
||||
{
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx0, int idx1, int idx2, int idx3]
|
||||
{
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref (&mFirstElement)[((idx0*mLength1 + idx1)*mLength2 + idx2)*mLength3 + idx3];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
if ((uint)idx >= (uint)mLength)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int idx0, int idx1, int idx2, int idx3]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
int idx = ((idx0*mLength1 + idx1)*mLength2 + idx2)*mLength3 + idx3;
|
||||
if (((uint)idx >= (uint)mLength) ||
|
||||
((uint)idx1 >= (uint)mLength1) ||
|
||||
((uint)idx2 >= (uint)mLength2) ||
|
||||
((uint)idx3 >= (uint)mLength3))
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
return ref (&mFirstElement)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public T* CArray()
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
|
||||
protected override void GCMarkMembers()
|
||||
{
|
||||
for (int i = 0; i < mLength; i++)
|
||||
{
|
||||
GC.Mark_Unbound((&mFirstElement)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
BeefLibs/corlib/src/System/AsyncCallback.bf
Normal file
4
BeefLibs/corlib/src/System/AsyncCallback.bf
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace System
|
||||
{
|
||||
public delegate void AsyncCallback(IAsyncResult ar);
|
||||
}
|
365
BeefLibs/corlib/src/System/Attribute.bf
Normal file
365
BeefLibs/corlib/src/System/Attribute.bf
Normal file
|
@ -0,0 +1,365 @@
|
|||
namespace System
|
||||
{
|
||||
public struct Attribute
|
||||
{
|
||||
}
|
||||
|
||||
public enum AttributeTargets
|
||||
{
|
||||
Assembly = 0x0001,
|
||||
Module = 0x0002,
|
||||
Class = 0x0004,
|
||||
Struct = 0x0008,
|
||||
Enum = 0x0010,
|
||||
Constructor = 0x0020,
|
||||
Method = 0x0040,
|
||||
Property = 0x0080,
|
||||
Field = 0x0100,
|
||||
StaticField = 0x0200,
|
||||
Interface = 0x0400,
|
||||
Parameter = 0x0800,
|
||||
Delegate = 0x1000,
|
||||
Function = 0x2000,
|
||||
ReturnValue = 0x4000,
|
||||
//@todo GENERICS: document GenericParameter
|
||||
GenericParameter = 0x8000,
|
||||
Invocation = 0x10000,
|
||||
MemberAccess = 0x20000,
|
||||
|
||||
All = Assembly | Module | Class | Struct | Enum | Constructor |
|
||||
Method | Property | Field | StaticField | Interface | Parameter |
|
||||
Delegate | Function | ReturnValue | GenericParameter | Invocation | MemberAccess,
|
||||
}
|
||||
|
||||
public enum ReflectKind
|
||||
{
|
||||
None = 0,
|
||||
Type = 1,
|
||||
NonStaticFields = 2,
|
||||
StaticFields = 4,
|
||||
DefaultConstructor = 8,
|
||||
Constructors = 0x10,
|
||||
StaticMethods = 0x20,
|
||||
Methods = 0x40,
|
||||
All = 0x7F,
|
||||
|
||||
ApplyToInnerTypes = 0x80,
|
||||
}
|
||||
|
||||
public enum AttributeFlags
|
||||
{
|
||||
None,
|
||||
DisallowAllowMultiple = 1,
|
||||
NotInherited = 2,
|
||||
ReflectAttribute = 4,
|
||||
AlwaysIncludeTarget = 8
|
||||
}
|
||||
|
||||
public sealed struct AttributeUsageAttribute : Attribute
|
||||
{
|
||||
internal AttributeTargets mAttributeTarget = AttributeTargets.All;
|
||||
internal AttributeFlags mAttributeFlags = .None;
|
||||
internal ReflectKind mReflectUser = .None;
|
||||
|
||||
public this(AttributeTargets validOn)
|
||||
{
|
||||
mAttributeTarget = validOn;
|
||||
}
|
||||
|
||||
internal this(AttributeTargets validOn, AttributeFlags flags)
|
||||
{
|
||||
mAttributeTarget = validOn;
|
||||
mAttributeFlags = flags;
|
||||
}
|
||||
|
||||
internal this(AttributeTargets validOn, bool allowMultiple, bool inherited)
|
||||
{
|
||||
mAttributeTarget = validOn;
|
||||
if (!allowMultiple)
|
||||
mAttributeFlags |= .DisallowAllowMultiple;
|
||||
if (!inherited)
|
||||
mAttributeFlags |= .NotInherited;
|
||||
}
|
||||
|
||||
public AttributeTargets ValidOn
|
||||
{
|
||||
get { return mAttributeTarget; }
|
||||
}
|
||||
|
||||
public ReflectKind ReflectUser
|
||||
{
|
||||
get { return mReflectUser; }
|
||||
set mut { mReflectUser = value; }
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public struct ReflectAttribute : Attribute
|
||||
{
|
||||
public this(ReflectKind reflectKind = .All)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*1*/ | .Invocation | .Property)]
|
||||
public struct InlineAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Invocation)]
|
||||
public struct UnboundAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(.Class | .Struct | .Interface | .Method | .Constructor)]
|
||||
public struct AlwaysIncludeAttribute : Attribute
|
||||
{
|
||||
bool mAssumeInstantiated;
|
||||
|
||||
public bool AssumeInstantiated
|
||||
{
|
||||
get { return mAssumeInstantiated; }
|
||||
set mut { mAssumeInstantiated = value; }
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.MemberAccess)]
|
||||
public struct FriendAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.MemberAccess)]
|
||||
public struct SkipAccessCheckAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(.Method | .Class | .Struct | .Enum)]
|
||||
public struct OptimizeAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*2*/ | AttributeTargets.StaticField)]
|
||||
public struct CLinkAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*2*/ | AttributeTargets.StaticField)]
|
||||
public struct LinkNameAttribute : Attribute
|
||||
{
|
||||
public this(String linkName)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method | .Delegate | .Function)]
|
||||
public struct StdCallAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*2*/)]
|
||||
public struct CVarArgsAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*2*/)]
|
||||
public struct NoReturnAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*2*/)]
|
||||
public struct SkipCallAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*2*/)]
|
||||
public struct IntrinsicAttribute : Attribute
|
||||
{
|
||||
public this(String intrinName)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct /*2*/)]
|
||||
public struct StaticInitPriorityAttribute : Attribute
|
||||
{
|
||||
public this(int priority)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class /*2*/ | AttributeTargets.Struct /*2*/)]
|
||||
public struct StaticInitAfterAttribute : Attribute
|
||||
{
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public this(Type afterType)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Struct)]
|
||||
public struct ForceAddrAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// This attribute is required on constructors that include 'append' allocations.
|
||||
[AttributeUsage(AttributeTargets.Constructor)]
|
||||
public struct AllowAppendAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public struct PackedAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public struct UnionAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public struct CReprAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public struct OrderedAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | .Method /*2*/)]
|
||||
public struct NoShowAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | .Method /*2*/)]
|
||||
public struct HideAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
[AttributeUsage(.Parameter)]
|
||||
public struct HideNameAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method/*, AlwaysIncludeTarget=true*/)]
|
||||
public struct TestAttribute : Attribute
|
||||
{
|
||||
public bool ShouldFail;
|
||||
public bool Ignore;
|
||||
public bool Profile;
|
||||
}
|
||||
|
||||
namespace Runtime.InteropServices
|
||||
{
|
||||
public enum CallingConvention
|
||||
{
|
||||
Winapi = 1,
|
||||
Cdecl = 2,
|
||||
StdCall = 3,
|
||||
ThisCall = 4,
|
||||
FastCall = 5,
|
||||
|
||||
Default = Cdecl
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method /*3*/)]
|
||||
public struct DllImportAttribute : Attribute
|
||||
{
|
||||
public this(String dllName)
|
||||
{
|
||||
CallingConvention = .Default;
|
||||
CLink = false;
|
||||
}
|
||||
|
||||
public CallingConvention CallingConvention;
|
||||
public bool CLink;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ImportAttribute : Attribute
|
||||
{
|
||||
public this(String libName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.StaticField | AttributeTargets.Field, .NotInherited)]
|
||||
public struct ThreadStaticAttribute : Attribute
|
||||
{
|
||||
public this()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// The [Checked] attribute is used to mark a method or a method invocation as being "checked", meaning
|
||||
/// that the method applies extra runtime checks such as bounds checking or other parameter or state validation.
|
||||
[AttributeUsage(.Invocation | .Method | .Property)]
|
||||
public struct CheckedAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// The [Unchecked] attribute is used to mark a method or a method invocation as being "unchecked", meaning
|
||||
/// that the method omits runtime checks such as bounds checking or other parameter or state validation.
|
||||
[AttributeUsage(.Invocation | .Method | .Property)]
|
||||
public struct UncheckedAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// Generally used as a per-method optimization, [DisableChecks] will cause calls within this method to
|
||||
/// call the unchecked version of methods. By default, only debug builds perform checked calls.
|
||||
[AttributeUsage(.Method | .Constructor)]
|
||||
public struct DisableChecksAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Generally used as a per-method optimization, [DisableObjectAccessChecks] will avoid the runtime per-object-access
|
||||
/// checks which by default are only applied in debug builds anyway.
|
||||
[AttributeUsage(AttributeTargets.Method/*, AlwaysIncludeTarget=true*/)]
|
||||
public struct DisableObjectAccessChecksAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// If [NoDiscard] is used on a method, the the compiler will show a warning if the result is discarded.
|
||||
/// If used on a type, the compiler will show an warning if any method returns that type and the caller discards the result.
|
||||
[AttributeUsage(.Method | .Class | .Struct)]
|
||||
public struct NoDiscardAttribute : Attribute
|
||||
{
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public this(String message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
24
BeefLibs/corlib/src/System/BitConverter.bf
Normal file
24
BeefLibs/corlib/src/System/BitConverter.bf
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Security;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System
|
||||
{
|
||||
public static class BitConverter
|
||||
{
|
||||
#if BIGENDIAN
|
||||
public static readonly bool IsLittleEndian /* = false */;
|
||||
#else
|
||||
public static readonly bool IsLittleEndian = true;
|
||||
#endif
|
||||
|
||||
public static TTo Convert<TFrom, TTo>(TFrom from)
|
||||
{
|
||||
Debug.Assert(sizeof(TFrom) == sizeof(TTo));
|
||||
var from;
|
||||
return *(TTo*)(&from);
|
||||
}
|
||||
}
|
||||
}
|
15
BeefLibs/corlib/src/System/Boolean.bf
Normal file
15
BeefLibs/corlib/src/System/Boolean.bf
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace System
|
||||
{
|
||||
struct Boolean : bool, IHashable
|
||||
{
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
strBuffer.Append(((bool)this) ? "true" : "false");
|
||||
}
|
||||
|
||||
int IHashable.GetHashCode()
|
||||
{
|
||||
return ((bool)this) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
250
BeefLibs/corlib/src/System/BumpAllocator.bf
Normal file
250
BeefLibs/corlib/src/System/BumpAllocator.bf
Normal file
|
@ -0,0 +1,250 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace System
|
||||
{
|
||||
interface IRawAllocator
|
||||
{
|
||||
void* Alloc(int size, int align);
|
||||
void Free(void* ptr);
|
||||
}
|
||||
|
||||
class BumpAllocator : IRawAllocator
|
||||
{
|
||||
struct DtorEntry
|
||||
{
|
||||
public uint16 mPoolIdx;
|
||||
public uint16 mPoolOfs;
|
||||
}
|
||||
|
||||
public enum DestructorHandlingKind
|
||||
{
|
||||
Allow,
|
||||
Fail,
|
||||
Ignore,
|
||||
}
|
||||
|
||||
List<Span<uint8>> mPools;
|
||||
List<DtorEntry> mDtorEntries;
|
||||
List<void*> mLargeRawAllocs;
|
||||
List<Object> mLargeDtorAllocs;
|
||||
int mPoolsSize;
|
||||
int mLageAllocs;
|
||||
public DestructorHandlingKind DestructorHandling = .Allow;
|
||||
|
||||
uint8* mCurAlloc;
|
||||
uint8* mCurPtr;
|
||||
uint8* mCurEnd;
|
||||
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
// We will either contain only data that needs to be marked or not, based on the first
|
||||
// data allocated. The paired allocator will contain the other type of data
|
||||
bool mMarkData;
|
||||
BumpAllocator mPairedAllocator ~ delete _;
|
||||
#endif
|
||||
|
||||
public this(DestructorHandlingKind destructorHandling = .Allow)
|
||||
{
|
||||
mCurAlloc = null;
|
||||
mCurPtr = null;
|
||||
mCurEnd = null;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
if (mDtorEntries != null)
|
||||
{
|
||||
for (var dtorEntry in ref mDtorEntries)
|
||||
{
|
||||
uint8* ptr = mPools[dtorEntry.mPoolIdx].Ptr + dtorEntry.mPoolOfs;
|
||||
Object obj = Internal.UnsafeCastToObject(ptr);
|
||||
delete:null obj;
|
||||
}
|
||||
delete mDtorEntries;
|
||||
}
|
||||
|
||||
if (mPools != null)
|
||||
{
|
||||
for (var span in mPools)
|
||||
FreePool(span);
|
||||
delete mPools;
|
||||
}
|
||||
|
||||
if (mLargeDtorAllocs != null)
|
||||
{
|
||||
for (var obj in mLargeDtorAllocs)
|
||||
{
|
||||
delete obj;
|
||||
FreeLarge(Internal.UnsafeCastToPtr(obj));
|
||||
}
|
||||
delete mLargeDtorAllocs;
|
||||
}
|
||||
|
||||
if (mLargeRawAllocs != null)
|
||||
{
|
||||
for (var ptr in mLargeRawAllocs)
|
||||
FreeLarge(ptr);
|
||||
delete mLargeRawAllocs;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void* AllocLarge(int size, int align)
|
||||
{
|
||||
return new uint8[size]* {?};
|
||||
}
|
||||
|
||||
protected virtual void FreeLarge(void* ptr)
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
protected virtual Span<uint8> AllocPool()
|
||||
{
|
||||
int poolSize = (mPools != null) ? mPools.Count : 0;
|
||||
int allocSize = Math.Clamp((int)Math.Pow(poolSize, 1.5) * 4*1024, 4*1024, 64*1024);
|
||||
return Span<uint8>(new uint8[allocSize]* {?}, allocSize);
|
||||
}
|
||||
|
||||
protected virtual void FreePool(Span<uint8> span)
|
||||
{
|
||||
delete span.Ptr;
|
||||
}
|
||||
|
||||
protected void GrowPool()
|
||||
{
|
||||
var span = AllocPool();
|
||||
mPoolsSize += span.Length;
|
||||
if (mPools == null)
|
||||
mPools = new List<Span<uint8>>();
|
||||
mPools.Add(span);
|
||||
mCurAlloc = span.Ptr;
|
||||
mCurPtr = mCurAlloc;
|
||||
mCurEnd = mCurAlloc + span.Length;
|
||||
}
|
||||
|
||||
public void* Alloc(int size, int align)
|
||||
{
|
||||
mCurPtr = (uint8*)(void*)(((int)(void*)mCurPtr + align - 1) & ~(align - 1));
|
||||
|
||||
while (mCurPtr + size >= mCurEnd)
|
||||
{
|
||||
if ((size > (mCurEnd - mCurAlloc) / 2) && (mCurAlloc != null))
|
||||
{
|
||||
mLageAllocs += size;
|
||||
void* largeAlloc = AllocLarge(size, align);
|
||||
if (mLargeRawAllocs == null)
|
||||
mLargeRawAllocs = new List<void*>();
|
||||
mLargeRawAllocs.Add(largeAlloc);
|
||||
return largeAlloc;
|
||||
}
|
||||
|
||||
GrowPool();
|
||||
}
|
||||
|
||||
uint8* ptr = mCurPtr;
|
||||
mCurPtr += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
protected void* AllocWithDtor(int size, int align)
|
||||
{
|
||||
mCurPtr = (uint8*)(void*)(((int)(void*)mCurPtr + align - 1) & ~(align - 1));
|
||||
|
||||
while (mCurPtr + size >= mCurEnd)
|
||||
{
|
||||
if ((size > (mCurEnd - mCurAlloc) / 2) && (mCurAlloc != null))
|
||||
{
|
||||
mLageAllocs += size;
|
||||
void* largeAlloc = AllocLarge(size, align);
|
||||
if (mLargeDtorAllocs == null)
|
||||
mLargeDtorAllocs = new List<Object>();
|
||||
mLargeDtorAllocs.Add(Internal.UnsafeCastToObject(largeAlloc));
|
||||
return largeAlloc;
|
||||
}
|
||||
|
||||
GrowPool();
|
||||
}
|
||||
|
||||
DtorEntry dtorEntry;
|
||||
dtorEntry.mPoolIdx = (uint16)mPools.Count - 1;
|
||||
dtorEntry.mPoolOfs = (uint16)(mCurPtr - mCurAlloc);
|
||||
if (mDtorEntries == null)
|
||||
mDtorEntries = new List<DtorEntry>();
|
||||
mDtorEntries.Add(dtorEntry);
|
||||
|
||||
uint8* ptr = mCurPtr;
|
||||
mCurPtr += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
public void* AllocTyped(Type type, int size, int align)
|
||||
{
|
||||
bool markData = type.WantsMark;
|
||||
if (mPools == null)
|
||||
{
|
||||
mMarkData = markData;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mMarkData != markData)
|
||||
{
|
||||
if (mPairedAllocator == null)
|
||||
{
|
||||
mPairedAllocator = new BumpAllocator();
|
||||
mPairedAllocator.mMarkData = markData;
|
||||
}
|
||||
return mPairedAllocator.Alloc(size, align);
|
||||
}
|
||||
}
|
||||
|
||||
if ((DestructorHandling != .Ignore) && (type.HasDestructor))
|
||||
{
|
||||
if (DestructorHandling == .Fail)
|
||||
Runtime.FatalError("Destructor not allowed");
|
||||
return AllocWithDtor(size, align);
|
||||
}
|
||||
|
||||
return Alloc(size, align);
|
||||
}
|
||||
|
||||
[DisableObjectAccessChecks]
|
||||
protected override void GCMarkMembers()
|
||||
{
|
||||
GC.Mark(mPools);
|
||||
GC.Mark(mLargeRawAllocs);
|
||||
GC.Mark(mLargeDtorAllocs);
|
||||
GC.Mark(mPairedAllocator);
|
||||
GC.Mark(mDtorEntries);
|
||||
if ((mMarkData) && (mPools != null))
|
||||
{
|
||||
let arr = mPools.[Friend]mItems;
|
||||
let size = mPools.[Friend]mSize;
|
||||
if (arr != null)
|
||||
{
|
||||
for (int idx < size)
|
||||
{
|
||||
var pool = arr[idx];
|
||||
GC.Mark(pool.Ptr, pool.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[SkipCall]
|
||||
public void Free(void* ptr)
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
public int GetAllocSize()
|
||||
{
|
||||
return mPoolsSize - (mCurEnd - mCurPtr);
|
||||
}
|
||||
|
||||
public int GetTotalAllocSize()
|
||||
{
|
||||
return mPoolsSize;
|
||||
}
|
||||
}
|
||||
}
|
98
BeefLibs/corlib/src/System/Char16.bf
Normal file
98
BeefLibs/corlib/src/System/Char16.bf
Normal file
|
@ -0,0 +1,98 @@
|
|||
namespace System
|
||||
{
|
||||
struct Char16 : char16, IHashable
|
||||
{
|
||||
internal const int UNICODE_PLANE00_END = 0x00ffff;
|
||||
// The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff.
|
||||
internal const int UNICODE_PLANE01_START = 0x10000;
|
||||
// The end codepoint for Unicode plane 16. This is the maximum code point value allowed for Unicode.
|
||||
// Plane 16 contains 0x100000 ~ 0x10ffff.
|
||||
internal const int UNICODE_PLANE16_END = 0x10ffff;
|
||||
|
||||
internal const char16 HIGH_SURROGATE_START = (char16)0xd800;
|
||||
internal const char16 LOW_SURROGATE_END = (char16)0xdfff;
|
||||
internal const char16 HIGH_SURROGATE_END = (char16)0xdbff;
|
||||
internal const char16 LOW_SURROGATE_START = (char16)0xdc00;
|
||||
|
||||
int IHashable.GetHashCode()
|
||||
{
|
||||
return (int)this;
|
||||
}
|
||||
|
||||
public extern char16 ToLower
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern char16 ToUpper
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsLower
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsUpper
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsWhiteSpace
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsLetterOrDigit
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsLetter
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsNumber
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsControl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (((char16)this >= (char16)0) && ((char16)this <= (char16)0x1F)) || (((char16)this >= (char16)0x7F) && ((char16)this <= (char16)0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCombiningMark
|
||||
{
|
||||
get
|
||||
{
|
||||
let c = (char16)this;
|
||||
return ((c >= '\u{0300}') && (c <= '\u{036F}')) || ((c >= '\u{1DC0}') && (c <= '\u{1DFF}'));
|
||||
}
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
strBuffer.Append((char16)this);
|
||||
}
|
||||
|
||||
public bool IsSurrogate
|
||||
{
|
||||
get
|
||||
{
|
||||
return (this >= HIGH_SURROGATE_START && this <= LOW_SURROGATE_END);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsSurrogatePair(char16 highSurrogate, char16 lowSurrogate)
|
||||
{
|
||||
return ((highSurrogate >= HIGH_SURROGATE_START && highSurrogate <= HIGH_SURROGATE_END) &&
|
||||
(lowSurrogate >= LOW_SURROGATE_START && lowSurrogate <= LOW_SURROGATE_END));
|
||||
}
|
||||
}
|
||||
}
|
92
BeefLibs/corlib/src/System/Char32.bf
Normal file
92
BeefLibs/corlib/src/System/Char32.bf
Normal file
|
@ -0,0 +1,92 @@
|
|||
namespace System
|
||||
{
|
||||
struct Char32 : char32, IHashable
|
||||
{
|
||||
int IHashable.GetHashCode()
|
||||
{
|
||||
return (int)this;
|
||||
}
|
||||
|
||||
public extern char32 ToLower
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern char32 ToUpper
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsLower
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsUpper
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
extern bool IsWhiteSpace_EX
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsWhiteSpace
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this <= (char32)0xFF)
|
||||
{
|
||||
// U+0009 = <control> HORIZONTAL TAB
|
||||
// U+000a = <control> LINE FEED
|
||||
// U+000b = <control> VERTICAL TAB
|
||||
// U+000c = <contorl> FORM FEED
|
||||
// U+000d = <control> CARRIAGE RETURN
|
||||
// U+0085 = <control> NEXT LINE
|
||||
// U+00a0 = NO-BREAK SPACE
|
||||
return ((this == ' ') || (this >= '\x09' && this <= '\x0d') || this == '\xa0' || this == '\x85');
|
||||
}
|
||||
else
|
||||
return IsWhiteSpace_EX;
|
||||
}
|
||||
}
|
||||
|
||||
public extern bool IsLetterOrDigit
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsLetter
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public extern bool IsNumber
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsControl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (((char32)this >= (char32)0) && ((char32)this <= (char32)0x1F)) || (((char32)this >= (char32)0x7F) && ((char32)this <= (char32)0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCombiningMark
|
||||
{
|
||||
get
|
||||
{
|
||||
let c = (char32)this;
|
||||
return ((c >= '\u{0300}') && (c <= '\u{036F}')) || ((c >= '\u{1DC0}') && (c <= '\u{1DFF}'));
|
||||
}
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
strBuffer.Append((char32)this);
|
||||
}
|
||||
}
|
||||
}
|
122
BeefLibs/corlib/src/System/Char8.bf
Normal file
122
BeefLibs/corlib/src/System/Char8.bf
Normal file
|
@ -0,0 +1,122 @@
|
|||
namespace System
|
||||
{
|
||||
struct Char8 : char8, IHashable, IOpComparable, IIsNaN
|
||||
{
|
||||
public static int operator<=>(Char8 a, Char8 b)
|
||||
{
|
||||
return (char8)a <=> (char8)b;
|
||||
}
|
||||
|
||||
bool IIsNaN.IsNaN
|
||||
{
|
||||
[SkipCall]
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public char8 ToLower
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this >= 'A') && (this <= 'Z'))
|
||||
return (char8)((this - 'A') + 'a');
|
||||
return (char8)this;
|
||||
}
|
||||
}
|
||||
|
||||
public char8 ToUpper
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((this >= 'a') && (this <= 'z'))
|
||||
return (char8)((this - 'a') + 'A');
|
||||
return (char8)this;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLower
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((this >= 'a') && (this <= 'z'));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUpper
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((this >= 'A') && (this <= 'Z'));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWhiteSpace
|
||||
{
|
||||
get
|
||||
{
|
||||
// U+0009 = HORIZONTAL TAB
|
||||
// U+000a = LINE FEED
|
||||
// U+000b = VERTICAL TAB
|
||||
// U+000c = FORM FEED
|
||||
// U+000d = CARRIAGE RETURN
|
||||
switch (this)
|
||||
{
|
||||
case ' ', '\t', '\n', '\r': return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDigit
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((this >= '0') && (this <= '9'));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLetterOrDigit
|
||||
{
|
||||
get
|
||||
{
|
||||
return (((this >= 'A') && (this <= 'Z')) || ((this >= 'a') && (this <= 'z')) || ((this >= '0') && (this <= '9')));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLetter
|
||||
{
|
||||
get
|
||||
{
|
||||
return (((this >= 'A') && (this <= 'Z')) || ((this >= 'a') && (this <= 'z')));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((this >= '0') && (this <= '9'));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsControl
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((this >= (char8)0) && (this <= (char8)0x1F)) || ((this >= (char8)0x7F) && (this <= (char8)0x9F));
|
||||
}
|
||||
}
|
||||
|
||||
int IHashable.GetHashCode()
|
||||
{
|
||||
return (int32)this;
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
strBuffer.Append((char8)this);
|
||||
}
|
||||
}
|
||||
}
|
118
BeefLibs/corlib/src/System/Collections/Generic/BinaryHeap.bf
Normal file
118
BeefLibs/corlib/src/System/Collections/Generic/BinaryHeap.bf
Normal file
|
@ -0,0 +1,118 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
public class BinaryHeap<T>
|
||||
{
|
||||
protected T[] mData ~ delete _;
|
||||
protected int32 mSize = 0;
|
||||
protected Comparison<T> mComparison ~ delete _;
|
||||
|
||||
public this(Comparison<T> comparison)
|
||||
{
|
||||
Constructor(4, comparison);
|
||||
}
|
||||
|
||||
public this(int32 capacity, Comparison<T> comparison)
|
||||
{
|
||||
Constructor(capacity, comparison);
|
||||
}
|
||||
|
||||
private void Constructor(int32 capacity, Comparison<T> comparison)
|
||||
{
|
||||
mData = new T[capacity];
|
||||
mComparison = comparison;
|
||||
}
|
||||
|
||||
public int32 Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an item to the heap
|
||||
public void Add(T item)
|
||||
{
|
||||
if (mSize == mData.Count)
|
||||
Resize();
|
||||
mData[mSize] = item;
|
||||
HeapifyUp(mSize);
|
||||
mSize++;
|
||||
}
|
||||
|
||||
/// Get the item of the root
|
||||
public T Peek()
|
||||
{
|
||||
return mData[0];
|
||||
}
|
||||
|
||||
/// Extract the item of the root
|
||||
public T Pop()
|
||||
{
|
||||
T item = mData[0];
|
||||
mSize--;
|
||||
mData[0] = mData[mSize];
|
||||
HeapifyDown(0);
|
||||
return item;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
private void Resize()
|
||||
{
|
||||
T[] resizedData = new T[mData.Count * 2];
|
||||
Array.Copy(mData, 0, resizedData, 0, mData.Count);
|
||||
delete mData;
|
||||
mData = resizedData;
|
||||
}
|
||||
|
||||
[Optimize]
|
||||
private void HeapifyUp(int32 childIdx)
|
||||
{
|
||||
if (childIdx > 0)
|
||||
{
|
||||
int32 parentIdx = (childIdx - 1) / 2;
|
||||
if (mComparison(mData[childIdx], mData[parentIdx]) > 0)
|
||||
{
|
||||
// swap parent and child
|
||||
T t = mData[parentIdx];
|
||||
mData[parentIdx] = mData[childIdx];
|
||||
mData[childIdx] = t;
|
||||
HeapifyUp(parentIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Optimize]
|
||||
private void HeapifyDown(int32 parentIdx)
|
||||
{
|
||||
int32 leftChildIdx = 2 * parentIdx + 1;
|
||||
int32 rightChildIdx = leftChildIdx + 1;
|
||||
int32 largestChildIdx = parentIdx;
|
||||
if (leftChildIdx < mSize && mComparison(mData[leftChildIdx], mData[largestChildIdx]) > 0)
|
||||
{
|
||||
largestChildIdx = leftChildIdx;
|
||||
}
|
||||
if (rightChildIdx < mSize && mComparison(mData[rightChildIdx], mData[largestChildIdx]) > 0)
|
||||
{
|
||||
largestChildIdx = rightChildIdx;
|
||||
}
|
||||
if (largestChildIdx != parentIdx)
|
||||
{
|
||||
T t = mData[parentIdx];
|
||||
mData[parentIdx] = mData[largestChildIdx];
|
||||
mData[largestChildIdx] = t;
|
||||
HeapifyDown(largestChildIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
982
BeefLibs/corlib/src/System/Collections/Generic/Dictionary.bf
Normal file
982
BeefLibs/corlib/src/System/Collections/Generic/Dictionary.bf
Normal file
|
@ -0,0 +1,982 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
#if PARANOID
|
||||
#define VERSION_DICTIONARY
|
||||
#endif
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
public class Dictionary<TKey, TValue> where TKey : IHashable //: IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>, ISerializable, IDeserializationCallback
|
||||
{
|
||||
private struct Entry
|
||||
{
|
||||
public TKey mKey; // Key of entry
|
||||
public TValue mValue; // Value of entry
|
||||
public int32 mHashCode; // Lower 31 bits of hash code, -1 if unused
|
||||
public int_cosize mNext; // Index of next entry, -1 if last
|
||||
}
|
||||
|
||||
private int_cosize[] mBuckets ~ delete _;
|
||||
private Entry[] mEntries ~ delete _;
|
||||
private int_cosize mCount;
|
||||
private int_cosize mFreeList;
|
||||
private int_cosize mFreeCount;
|
||||
#if VERSION_DICTIONARY
|
||||
private int32 mVersion;
|
||||
const String cVersionError = "Dictionary changed during enumeration";
|
||||
#endif
|
||||
|
||||
public this(): this(0) { }
|
||||
|
||||
public this(int_cosize capacity)
|
||||
{
|
||||
//if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
|
||||
if (capacity > 0) Initialize(capacity);
|
||||
//TODO: this.comparer = comparer ?? EqualityComparer<TKey>.Default;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return mCount - mFreeCount; }
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get { return mCount - mFreeCount == 0; }
|
||||
}
|
||||
|
||||
/*public KeyCollection Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<KeyCollection>() != null);
|
||||
if (keys == null) keys = new KeyCollection(this);
|
||||
return keys;
|
||||
}
|
||||
}*/
|
||||
|
||||
/*ICollection<TKey> IDictionary<TKey, TValue>.Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
if (keys == null) keys = new KeyCollection(this);
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
if (keys == null) keys = new KeyCollection(this);
|
||||
return keys;
|
||||
}
|
||||
}*/
|
||||
|
||||
public ValueEnumerator Values
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<ValueCollection>() != null);
|
||||
return ValueEnumerator(this);
|
||||
}
|
||||
}
|
||||
|
||||
public KeyEnumerator Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<ValueCollection>() != null);
|
||||
return KeyEnumerator(this);
|
||||
}
|
||||
}
|
||||
|
||||
/*ICollection<TValue> IDictionary<TKey, TValue>.Values
|
||||
{
|
||||
get
|
||||
{
|
||||
if (values == null) values = new ValueCollection(this);
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
|
||||
{
|
||||
get
|
||||
{
|
||||
if (values == null) values = new ValueCollection(this);
|
||||
return values;
|
||||
}
|
||||
}*/
|
||||
|
||||
public ref TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0) return ref mEntries[i].mValue;
|
||||
Runtime.FatalError("Key not found");
|
||||
}
|
||||
set
|
||||
{
|
||||
Insert(key, value, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
Insert(key, value, true);
|
||||
}
|
||||
|
||||
public bool TryAdd(TKey key, TValue value)
|
||||
{
|
||||
TKey* keyPtr;
|
||||
TValue* valuePtr;
|
||||
bool inserted = Insert(key, false, out keyPtr, out valuePtr);
|
||||
if (!inserted)
|
||||
return false;
|
||||
*keyPtr = key;
|
||||
*valuePtr = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAdd(TKey key, out TKey* keyPtr, out TValue* valuePtr)
|
||||
{
|
||||
return Insert(key, false, out keyPtr, out valuePtr);
|
||||
}
|
||||
|
||||
public enum AddResult
|
||||
{
|
||||
case Added(TKey* keyPtr, TValue* valuePtr);
|
||||
case Exists(TKey* keyPtr, TValue* valuePtr);
|
||||
}
|
||||
|
||||
public AddResult TryAdd(TKey key)
|
||||
{
|
||||
TKey* keyPtr;
|
||||
TValue* valuePtr;
|
||||
if (Insert(key, false, out keyPtr, out valuePtr))
|
||||
return .Added(keyPtr, valuePtr);
|
||||
return .Exists(keyPtr, valuePtr);
|
||||
}
|
||||
|
||||
/*void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair)
|
||||
{
|
||||
Add(keyValuePair.Key, keyValuePair.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair)
|
||||
{
|
||||
int i = FindEntry(keyValuePair.Key);
|
||||
if (i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair)
|
||||
{
|
||||
int i = FindEntry(keyValuePair.Key);
|
||||
if (i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value))
|
||||
{
|
||||
Remove(keyValuePair.Key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}*/
|
||||
|
||||
public Result<TValue> GetValue(TKey key)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0) return mEntries[i].mValue;
|
||||
return .Err;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (mCount > 0)
|
||||
{
|
||||
for (int_cosize i = 0; i < mBuckets.Count; i++) mBuckets[i] = -1;
|
||||
//for (int_cosize i = 0; i < mCount; i++)
|
||||
//mEntries[i] = default(Entry);
|
||||
//Array.Clear(entries, 0, count);
|
||||
mFreeList = -1;
|
||||
mCount = 0;
|
||||
mFreeCount = 0;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return FindEntry(key) >= 0;
|
||||
}
|
||||
|
||||
public bool ContainsValue(TValue value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (mEntries[i].mHashCode >= 0 && mEntries[i].mValue == null) return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: IMPORTANT!
|
||||
/*EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true;
|
||||
}*/
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return Enumerator(this, Enumerator.KeyValuePair);
|
||||
}
|
||||
|
||||
private int FindEntry(TKey key)
|
||||
{
|
||||
if (mBuckets != null)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF;
|
||||
for (int_cosize i = mBuckets[hashCode % mBuckets.Count]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int FindEntryAlt<TOther>(TOther key) where TOther : IOpEquals<TKey>, IHashable
|
||||
{
|
||||
if (mBuckets != null)
|
||||
{
|
||||
int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF;
|
||||
for (int_cosize i = mBuckets[hashCode % mBuckets.Count]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void Initialize(int capacity)
|
||||
{
|
||||
int_cosize size = GetPrimeish((int_cosize)capacity);
|
||||
mBuckets = new int_cosize[size];
|
||||
for (int_cosize i < (int_cosize)mBuckets.Count) mBuckets[i] = -1;
|
||||
mEntries = new Entry[size];
|
||||
mFreeList = -1;
|
||||
}
|
||||
|
||||
private void Insert(TKey key, TValue value, bool add)
|
||||
{
|
||||
if (mBuckets == null) Initialize(0);
|
||||
int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF;
|
||||
int_cosize targetBucket = hashCode % (int_cosize)mBuckets.Count;
|
||||
|
||||
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key))
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
Runtime.FatalError("Adding duplicate");
|
||||
}
|
||||
mEntries[i].mValue = value;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion++;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
int_cosize index;
|
||||
if (mFreeCount > 0)
|
||||
{
|
||||
index = mFreeList;
|
||||
mFreeList = mEntries[index].mNext;
|
||||
mFreeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mCount == mEntries.Count)
|
||||
{
|
||||
Resize();
|
||||
targetBucket = hashCode % (int_cosize)mBuckets.Count;
|
||||
}
|
||||
index = mCount;
|
||||
mCount++;
|
||||
}
|
||||
|
||||
mEntries[index].mHashCode = hashCode;
|
||||
mEntries[index].mNext = mBuckets[targetBucket];
|
||||
mEntries[index].mKey = key;
|
||||
mEntries[index].mValue = value;
|
||||
mBuckets[targetBucket] = index;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool Insert(TKey key, bool add, out TKey* keyPtr, out TValue* valuePtr)
|
||||
{
|
||||
if (mBuckets == null) Initialize(0);
|
||||
int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF;
|
||||
int_cosize targetBucket = hashCode % (int_cosize)mBuckets.Count;
|
||||
|
||||
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
||||
{
|
||||
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key))
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
Runtime.FatalError("Adding duplicate entry");
|
||||
}
|
||||
keyPtr = &mEntries[i].mKey;
|
||||
valuePtr = &mEntries[i].mValue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int_cosize index;
|
||||
if (mFreeCount > 0)
|
||||
{
|
||||
index = mFreeList;
|
||||
mFreeList = mEntries[index].mNext;
|
||||
mFreeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mCount == mEntries.Count)
|
||||
{
|
||||
Resize();
|
||||
targetBucket = hashCode % (int_cosize)mBuckets.Count;
|
||||
}
|
||||
index = mCount;
|
||||
mCount++;
|
||||
}
|
||||
|
||||
mEntries[index].mHashCode = hashCode;
|
||||
mEntries[index].mNext = mBuckets[targetBucket];
|
||||
mEntries[index].mKey = key;
|
||||
mBuckets[targetBucket] = index;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion++;
|
||||
#endif
|
||||
|
||||
keyPtr = &mEntries[index].mKey;
|
||||
valuePtr = &mEntries[index].mValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close to prime but faster to compute
|
||||
int_cosize GetPrimeish(int_cosize min)
|
||||
{
|
||||
// This is a minimal effort to help address-aligned data
|
||||
return (min | 1);
|
||||
}
|
||||
|
||||
int_cosize ExpandSize(int_cosize oldSize)
|
||||
{
|
||||
int_cosize newSize = 2 * oldSize;
|
||||
return GetPrimeish(newSize);
|
||||
}
|
||||
|
||||
private void Resize()
|
||||
{
|
||||
Resize(ExpandSize(mCount), false);
|
||||
}
|
||||
|
||||
private void Resize(int newSize, bool forceNewHashCodes)
|
||||
{
|
||||
Contract.Assert(newSize >= mEntries.Count);
|
||||
int_cosize[] newBuckets = new int_cosize[newSize];
|
||||
for (int_cosize i = 0; i < newBuckets.Count; i++) newBuckets[i] = -1;
|
||||
Entry[] newEntries = new Entry[newSize];
|
||||
mEntries.CopyTo(newEntries, 0, 0, mCount);
|
||||
if (forceNewHashCodes)
|
||||
{
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (newEntries[i].mHashCode != -1)
|
||||
{
|
||||
newEntries[i].mHashCode = (int_cosize)newEntries[i].mKey.GetHashCode() & 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int_cosize i = 0; i < mCount; i++)
|
||||
{
|
||||
if (newEntries[i].mHashCode >= 0)
|
||||
{
|
||||
int_cosize bucket = (int_cosize)(newEntries[i].mHashCode % newSize);
|
||||
newEntries[i].mNext = newBuckets[bucket];
|
||||
newBuckets[bucket] = i;
|
||||
}
|
||||
}
|
||||
|
||||
delete mBuckets;
|
||||
delete mEntries;
|
||||
|
||||
mBuckets = newBuckets;
|
||||
mEntries = newEntries;
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
ThrowUnimplemented();
|
||||
//ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
|
||||
}
|
||||
|
||||
if (mBuckets != null)
|
||||
{
|
||||
|
||||
int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF;
|
||||
int_cosize bucket = hashCode % (int_cosize)mBuckets.Count;
|
||||
int_cosize last = -1;
|
||||
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (mEntries[i].mKey == key))
|
||||
{
|
||||
if (last < 0)
|
||||
{
|
||||
mBuckets[bucket] = mEntries[i].mNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEntries[last].mNext = mEntries[i].mNext;
|
||||
}
|
||||
mEntries[i].mHashCode = -1;
|
||||
mEntries[i].mNext = mFreeList;
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
mEntries[i].mKey = default;
|
||||
mEntries[i].mValue = default;
|
||||
#endif
|
||||
mFreeList = i;
|
||||
mFreeCount++;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion++;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Result<(TKey key, TValue value)> GetAndRemove(TKey key)
|
||||
{
|
||||
if (mBuckets != null)
|
||||
{
|
||||
|
||||
int_cosize hashCode = (int_cosize)key.GetHashCode() & 0x7FFFFFFF;
|
||||
int_cosize bucket = hashCode % (int_cosize)mBuckets.Count;
|
||||
int_cosize last = -1;
|
||||
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext)
|
||||
{
|
||||
if ((mEntries[i].mHashCode == hashCode) && (mEntries[i].mKey == key))
|
||||
{
|
||||
if (last < 0)
|
||||
{
|
||||
mBuckets[bucket] = mEntries[i].mNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEntries[last].mNext = mEntries[i].mNext;
|
||||
}
|
||||
TKey entryKey = mEntries[i].mKey;
|
||||
TValue result = mEntries[i].mValue;
|
||||
|
||||
mEntries[i].mHashCode = -1;
|
||||
mEntries[i].mNext = mFreeList;
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
mEntries[i].mKey = default(TKey);
|
||||
mEntries[i].mValue = default(TValue);
|
||||
#endif
|
||||
mFreeList = i;
|
||||
mFreeCount++;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion++;
|
||||
#endif
|
||||
return .Ok((entryKey, result));
|
||||
}
|
||||
}
|
||||
}
|
||||
return .Err;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0)
|
||||
{
|
||||
value = mEntries[i].mValue;
|
||||
return true;
|
||||
}
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TKey matchKey, out TValue value)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0)
|
||||
{
|
||||
matchKey = mEntries[i].mKey;
|
||||
value = mEntries[i].mValue;
|
||||
return true;
|
||||
}
|
||||
matchKey = default(TKey);
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
public TValue GetValueOrDefault(TKey key)
|
||||
{
|
||||
int_cosize i = (int_cosize)FindEntry(key);
|
||||
if (i >= 0)
|
||||
{
|
||||
return mEntries[i].mValue;
|
||||
}
|
||||
return default(TValue);
|
||||
}
|
||||
|
||||
private static bool IsCompatibleKey(Object key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
ThrowUnimplemented();
|
||||
//ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
|
||||
}
|
||||
return (key is TKey);
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<(TKey key, TValue value)>//, IDictionaryEnumerator
|
||||
{
|
||||
private Dictionary<TKey, TValue> mDictionary;
|
||||
#if VERSION_DICTIONARY
|
||||
private int_cosize mVersion;
|
||||
#endif
|
||||
private int_cosize mIndex;
|
||||
private int_cosize mCurrentIndex;
|
||||
//private KeyValuePair<TKey, TValue> current;
|
||||
private int_cosize mGetEnumeratorRetType; // What should Enumerator.Current return?
|
||||
|
||||
internal const int_cosize DictEntry = 1;
|
||||
internal const int_cosize KeyValuePair = 2;
|
||||
|
||||
internal this(Dictionary<TKey, TValue> dictionary, int_cosize getEnumeratorRetType)
|
||||
{
|
||||
mDictionary = dictionary;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion = dictionary.mVersion;
|
||||
#endif
|
||||
mIndex = 0;
|
||||
mGetEnumeratorRetType = getEnumeratorRetType;
|
||||
//current = KeyValuePair<TKey, TValue>();
|
||||
//current = default(KeyValuePair<TKey, TValue>);
|
||||
mCurrentIndex = -1;
|
||||
}
|
||||
|
||||
#if VERSION_DICTIONARY
|
||||
void CheckVersion()
|
||||
{
|
||||
if (mVersion != mDictionary.mVersion)
|
||||
Runtime.FatalError(cVersionError);
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
|
||||
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
|
||||
while ((uint)mIndex < (uint)mDictionary.mCount)
|
||||
{
|
||||
if (mDictionary.mEntries[mIndex].mHashCode >= 0)
|
||||
{
|
||||
mCurrentIndex = mIndex;
|
||||
//current = KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
|
||||
mIndex++;
|
||||
return true;
|
||||
}
|
||||
mIndex++;
|
||||
}
|
||||
|
||||
mIndex = mDictionary.mCount + 1;
|
||||
//current = default(KeyValuePair<TKey, TValue>);
|
||||
mCurrentIndex = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
public ref TKey Key
|
||||
{
|
||||
get
|
||||
{
|
||||
return ref mDictionary.mEntries[mCurrentIndex].mKey;
|
||||
}
|
||||
}
|
||||
|
||||
public ref TValue Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return ref mDictionary.mEntries[mCurrentIndex].mValue;
|
||||
}
|
||||
}
|
||||
|
||||
public (TKey key, TValue value) Current
|
||||
{
|
||||
get { return (mDictionary.mEntries[mCurrentIndex].mKey, mDictionary.mEntries[mCurrentIndex].mValue); }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void SetValue(TValue value)
|
||||
{
|
||||
mDictionary.mEntries[mCurrentIndex].mValue = value;
|
||||
}
|
||||
|
||||
public void Reset() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
|
||||
mIndex = 0;
|
||||
//current = default(KeyValuePair<TKey, TValue>);
|
||||
mCurrentIndex = -1;
|
||||
}
|
||||
|
||||
/*DictionaryEntry IDictionaryEnumerator.Entry
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index == 0 || (index == dictionary.count + 1))
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
|
||||
}
|
||||
|
||||
return new DictionaryEntry(current.Key, current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
object IDictionaryEnumerator.Key
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index == 0 || (index == dictionary.count + 1))
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
|
||||
}
|
||||
|
||||
return current.Key;
|
||||
}
|
||||
}
|
||||
|
||||
object IDictionaryEnumerator.Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index == 0 || (index == dictionary.count + 1))
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
|
||||
}
|
||||
|
||||
return current.Value;
|
||||
}
|
||||
}*/
|
||||
|
||||
public Result<(TKey key, TValue value)> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ValueEnumerator : IEnumerator<TValue>, IResettable
|
||||
{
|
||||
private Dictionary<TKey, TValue> mDictionary;
|
||||
#if VERSION_DICTIONARY
|
||||
private int_cosize mVersion;
|
||||
#endif
|
||||
private int_cosize mIndex;
|
||||
private TValue mCurrent;
|
||||
|
||||
internal const int_cosize cDictEntry = 1;
|
||||
internal const int_cosize cKeyValuePair = 2;
|
||||
|
||||
internal this(Dictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
mDictionary = dictionary;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion = dictionary.mVersion;
|
||||
#endif
|
||||
mIndex = 0;
|
||||
mCurrent = default;
|
||||
}
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
|
||||
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
|
||||
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
|
||||
while ((uint)mIndex < (uint)mDictionary.mCount)
|
||||
{
|
||||
if (mDictionary.mEntries[mIndex].mHashCode >= 0)
|
||||
{
|
||||
mCurrent = mDictionary.mEntries[mIndex].mValue;
|
||||
mIndex++;
|
||||
return true;
|
||||
}
|
||||
mIndex++;
|
||||
}
|
||||
|
||||
mIndex = mDictionary.mCount + 1;
|
||||
mCurrent = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public TValue Current
|
||||
{
|
||||
get { return mCurrent; }
|
||||
}
|
||||
|
||||
public ref TKey Key
|
||||
{
|
||||
get
|
||||
{
|
||||
return ref mDictionary.mEntries[mIndex].mKey;
|
||||
}
|
||||
}
|
||||
|
||||
#if VERSION_DICTIONARY
|
||||
void CheckVersion()
|
||||
{
|
||||
if (mVersion != mDictionary.mVersion)
|
||||
Runtime.FatalError(cVersionError);
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Reset() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
|
||||
mIndex = 0;
|
||||
mCurrent = default;
|
||||
}
|
||||
|
||||
public Result<TValue> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public struct RefValueEnumerator : IEnumerator<TValue>, IResettable
|
||||
{
|
||||
private Dictionary<TKey, TValue> mDictionary;
|
||||
#if VERSION_DICTIONARY
|
||||
private int_cosize mVersion;
|
||||
#endif
|
||||
private int_cosize mIndex;
|
||||
private TValue* mCurrent;
|
||||
|
||||
internal const int_cosize DictEntry = 1;
|
||||
internal const int_cosize KeyValuePair = 2;
|
||||
|
||||
internal this(Dictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
mDictionary = dictionary;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion = dictionary.mVersion;
|
||||
#endif
|
||||
mIndex = 0;
|
||||
mCurrent = null;
|
||||
}
|
||||
|
||||
#if VERSION_DICTIONARY
|
||||
void CheckVersion()
|
||||
{
|
||||
if (mVersion != mDictionary.mVersion)
|
||||
Runtime.FatalError(cVersionError);
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
|
||||
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
|
||||
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
|
||||
while ((uint32)mIndex < (uint32)mDictionary.mCount)
|
||||
{
|
||||
if (mDictionary.mEntries[mIndex].mHashCode >= 0)
|
||||
{
|
||||
mCurrent = &mDictionary.mEntries[mIndex].mValue;
|
||||
mIndex++;
|
||||
return true;
|
||||
}
|
||||
mIndex++;
|
||||
}
|
||||
|
||||
mIndex = mDictionary.mCount + 1;
|
||||
mCurrent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public TValue Current
|
||||
{
|
||||
get { return *mCurrent; }
|
||||
}
|
||||
|
||||
public ref TKey Key
|
||||
{
|
||||
get
|
||||
{
|
||||
return ref mDictionary.mEntries[mIndex - 1].mKey;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Reset() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
|
||||
mIndex = 0;
|
||||
mCurrent = null;
|
||||
}
|
||||
|
||||
public Result<TValue> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public struct KeyEnumerator : IEnumerator<TKey>, IResettable
|
||||
{
|
||||
private Dictionary<TKey, TValue> mDictionary;
|
||||
# if VERSION_DICTIONARY
|
||||
private int32 mVersion;
|
||||
#endif
|
||||
private int_cosize mIndex;
|
||||
private TKey* mCurrent;
|
||||
|
||||
internal const int_cosize DictEntry = 1;
|
||||
internal const int_cosize KeyValuePair = 2;
|
||||
|
||||
internal this(Dictionary<TKey, TValue> dictionary)
|
||||
{
|
||||
mDictionary = dictionary;
|
||||
#if VERSION_DICTIONARY
|
||||
mVersion = dictionary.mVersion;
|
||||
#endif
|
||||
mIndex = 0;
|
||||
mCurrent = null;
|
||||
}
|
||||
|
||||
#if VERSION_DICTIONARY
|
||||
void CheckVersion()
|
||||
{
|
||||
if (mVersion != mDictionary.mVersion)
|
||||
Runtime.FatalError(cVersionError);
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
|
||||
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
|
||||
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
|
||||
while ((uint32)mIndex < (uint32)mDictionary.mCount)
|
||||
{
|
||||
if (mDictionary.mEntries[mIndex].mHashCode >= 0)
|
||||
{
|
||||
mCurrent = &mDictionary.mEntries[mIndex].mKey;
|
||||
mIndex++;
|
||||
return true;
|
||||
}
|
||||
mIndex++;
|
||||
}
|
||||
|
||||
mIndex = mDictionary.mCount + 1;
|
||||
mCurrent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public TKey Current
|
||||
{
|
||||
get { return *mCurrent; }
|
||||
}
|
||||
|
||||
public ref TValue Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return ref mDictionary.mEntries[mIndex - 1].mValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Reset() mut
|
||||
{
|
||||
#if VERSION_DICTIONARY
|
||||
CheckVersion();
|
||||
#endif
|
||||
mIndex = 0;
|
||||
mCurrent = null;
|
||||
}
|
||||
|
||||
public Result<TKey> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1140
BeefLibs/corlib/src/System/Collections/Generic/HashSet.bf
Normal file
1140
BeefLibs/corlib/src/System/Collections/Generic/HashSet.bf
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,4 @@
|
|||
namespace System.Collections.Generic
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace System.Collections.Generic
|
||||
{
|
||||
using System;
|
||||
|
||||
public interface IEqualityComparer</*in*/ T>
|
||||
{
|
||||
bool Equals(T x, T y);
|
||||
int GetHashCode(T obj);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
namespace System.Collections.Generic
|
||||
{
|
||||
public struct KeyValuePair<TKey, TValue>
|
||||
{
|
||||
private TKey mKey;
|
||||
private TValue mValue;
|
||||
|
||||
public this(TKey key, TValue value)
|
||||
{
|
||||
this.mKey = key;
|
||||
this.mValue = value;
|
||||
}
|
||||
|
||||
public TKey Key
|
||||
{
|
||||
get { return mKey; }
|
||||
}
|
||||
|
||||
public TValue Value
|
||||
{
|
||||
get { return mValue; }
|
||||
}
|
||||
|
||||
public override void ToString(String strOut)
|
||||
{
|
||||
strOut.Append('[');
|
||||
if (Key != null)
|
||||
{
|
||||
Key.ToString(strOut);
|
||||
}
|
||||
strOut.Append(", ");
|
||||
if (Value != null)
|
||||
{
|
||||
Value.ToString(strOut);
|
||||
}
|
||||
strOut.Append(']');
|
||||
}
|
||||
}
|
||||
}
|
778
BeefLibs/corlib/src/System/Collections/Generic/List.bf
Normal file
778
BeefLibs/corlib/src/System/Collections/Generic/List.bf
Normal file
|
@ -0,0 +1,778 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
#if PARANOID
|
||||
#define VERSION_LIST
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Runtime;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
interface IList
|
||||
{
|
||||
Variant this[int index]
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
public class List<T> : IEnumerable<T>, IList
|
||||
{
|
||||
private const int_cosize cDefaultCapacity = 4;
|
||||
|
||||
const int_cosize SizeFlags = 0x7FFFFFFF;
|
||||
const int_cosize DynAllocFlag = (int_cosize)0x80000000;
|
||||
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
static DbgRawAllocData sRawAllocData;
|
||||
public static this()
|
||||
{
|
||||
sRawAllocData.mMarkFunc = null;
|
||||
sRawAllocData.mMaxStackTrace = 1;
|
||||
sRawAllocData.mType = typeof(T);
|
||||
}
|
||||
#endif
|
||||
|
||||
private T* mItems;
|
||||
private int_cosize mSize;
|
||||
private int_cosize mAllocSizeAndFlags;
|
||||
#if VERSION_LIST
|
||||
private int32 mVersion;
|
||||
const String cVersionError = "List changed during enumeration";
|
||||
#endif
|
||||
|
||||
public int AllocSize
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return mAllocSizeAndFlags & SizeFlags;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDynAlloc
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return (mAllocSizeAndFlags & DynAllocFlag) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public this(IEnumerator<T> enumerator)
|
||||
{
|
||||
for (var item in enumerator)
|
||||
Add(item);
|
||||
}
|
||||
|
||||
[AllowAppend]
|
||||
public this(int capacity)
|
||||
{
|
||||
Debug.Assert((uint)capacity <= (uint)SizeFlags);
|
||||
T* items = append T[capacity]* (?);
|
||||
if (capacity > 0)
|
||||
{
|
||||
mItems = items;
|
||||
mAllocSizeAndFlags = (int_cosize)(capacity & SizeFlags);
|
||||
}
|
||||
}
|
||||
/*public this(int capacity)
|
||||
{
|
||||
Debug.Assert((uint)capacity <= (uint)SizeFlags);
|
||||
if (capacity > 0)
|
||||
{
|
||||
mItems = Alloc(capacity);
|
||||
mAllocSizeAndFlags = (int_cosize)(capacity | DynAllocFlag);
|
||||
}
|
||||
}*/
|
||||
|
||||
public ~this()
|
||||
{
|
||||
#if DBG
|
||||
int typeId = typeof(T).GetTypeId();
|
||||
if (typeId == sDebugTypeId)
|
||||
{
|
||||
Debug.WriteLine("Dealloc {0} {1}", scope Object[] { this, mItems } );
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsDynAlloc)
|
||||
{
|
||||
var items = mItems;
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
mItems = null;
|
||||
Interlocked.Fence();
|
||||
#endif
|
||||
Free(items);
|
||||
}
|
||||
}
|
||||
|
||||
public T* Ptr
|
||||
{
|
||||
get
|
||||
{
|
||||
return mItems;
|
||||
}
|
||||
}
|
||||
|
||||
#if DBG
|
||||
static int_cosize sDebugTypeId = 470;
|
||||
static int_cosize sDebugIdx = 0;
|
||||
#endif
|
||||
|
||||
public int Capacity
|
||||
{
|
||||
get
|
||||
{
|
||||
return mAllocSizeAndFlags & SizeFlags;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Debug.Assert((uint)value <= (uint)SizeFlags);
|
||||
if (value != AllocSize)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
T* newItems = Alloc(value);
|
||||
|
||||
#if DBG
|
||||
int typeId = typeof(T).GetTypeId();
|
||||
if (typeId == sDebugTypeId)
|
||||
{
|
||||
Debug.WriteLine("Alloc {0} {1} {2}", scope Object[] { this, newItems, sDebugIdx } );
|
||||
sDebugIdx++;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mSize > 0)
|
||||
Internal.MemCpy(newItems, mItems, mSize * strideof(T), alignof(T));
|
||||
|
||||
var oldItems = mItems;
|
||||
mItems = newItems;
|
||||
if (IsDynAlloc)
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
// We need to avoid scanning a deleted mItems
|
||||
Interlocked.Fence();
|
||||
#endif
|
||||
Free(oldItems);
|
||||
}
|
||||
mAllocSizeAndFlags = (.)(value | DynAllocFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDynAlloc)
|
||||
Free(mItems);
|
||||
mItems = null;
|
||||
mAllocSizeAndFlags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return mSize == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ref T this[int index]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
Runtime.Assert((uint)index < (uint)mSize);
|
||||
return ref mItems[index];
|
||||
}
|
||||
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref mItems[index];
|
||||
}
|
||||
|
||||
[Checked]
|
||||
set
|
||||
{
|
||||
Runtime.Assert((uint)index < (uint)mSize);
|
||||
mItems[index] = value;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
[Unchecked, Inline]
|
||||
set
|
||||
{
|
||||
mItems[index] = value;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public ref T Back
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(mSize != 0);
|
||||
return ref mItems[mSize - 1];
|
||||
}
|
||||
}
|
||||
|
||||
Variant IList.this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return [Unbound]Variant.Create(this[index]);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
ThrowUnimplemented();
|
||||
}
|
||||
}
|
||||
|
||||
protected T* Alloc(int size)
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
// We don't want to use the default mark function because the GC will mark the entire array,
|
||||
// whereas we have a custom marking routine because we only want to mark up to mSize
|
||||
return (T*)Internal.Dbg_RawAlloc(size * strideof(T), &sRawAllocData);
|
||||
#else
|
||||
return new T[size]*(?);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected void Free(T* val)
|
||||
{
|
||||
delete val;
|
||||
}
|
||||
/*protected T[] Alloc(int size)
|
||||
{
|
||||
return new:this T[size];
|
||||
}
|
||||
|
||||
protected void Free(Object obj)
|
||||
{
|
||||
delete:this obj;
|
||||
}
|
||||
|
||||
protected virtual Object AllocObject(TypeInstance type, int size)
|
||||
{
|
||||
return Internal.ObjectAlloc(type, size);
|
||||
}
|
||||
|
||||
protected virtual void FreeObject(Object obj)
|
||||
{
|
||||
delete obj;
|
||||
}*/
|
||||
|
||||
/// Adds an item to the back of the list.
|
||||
public void Add(T item)
|
||||
{
|
||||
if (mSize == AllocSize) EnsureCapacity(mSize + 1);
|
||||
mItems[mSize++] = item;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Adds an item to the back of the list.
|
||||
public void Add(Span<T> addSpan)
|
||||
{
|
||||
if (mSize == AllocSize) EnsureCapacity(mSize + addSpan.Length);
|
||||
for (var val in ref addSpan)
|
||||
mItems[mSize++] = val;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Returns a pointer to the start of the added uninitialized section
|
||||
public T* GrowUnitialized(int addSize)
|
||||
{
|
||||
if (mSize + addSize > AllocSize) EnsureCapacity(mSize + addSize);
|
||||
mSize += (int_cosize)addSize;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
if (addSize == 0)
|
||||
return null;
|
||||
return &mItems[mSize - addSize];
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (mSize > 0)
|
||||
{
|
||||
mSize = 0;
|
||||
}
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*public static void DeleteItemsAndClear<T>(List<T> list) where T : delete
|
||||
{
|
||||
foreach (var item in list)
|
||||
delete item;
|
||||
list.Clear();
|
||||
}*/
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
for (int i = 0; i < mSize; i++)
|
||||
if (mItems[i] == null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < mSize; i++)
|
||||
if (mItems[i] == item)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array)
|
||||
{
|
||||
CopyTo(array, 0);
|
||||
}
|
||||
|
||||
public void CopyTo(List<T> destList)
|
||||
{
|
||||
destList.EnsureCapacity(mSize);
|
||||
destList.mSize = mSize;
|
||||
if (mSize > 0)
|
||||
Internal.MemCpy(destList.mItems, mItems, mSize * strideof(T), alignof(T));
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
// Delegate rest of error checking to Array.Copy.
|
||||
for (int i = 0; i < mSize; i++)
|
||||
array[i + arrayIndex] = mItems[i];
|
||||
}
|
||||
|
||||
public void CopyTo(int index, T[] array, int arrayIndex, int count)
|
||||
{
|
||||
// Delegate rest of error checking to Array.Copy.
|
||||
for (int i = 0; i < count; i++)
|
||||
array[i + arrayIndex] = mItems[i + index];
|
||||
}
|
||||
|
||||
public void EnsureCapacity(int min)
|
||||
{
|
||||
int allocSize = AllocSize;
|
||||
if (allocSize < min)
|
||||
{
|
||||
int_cosize newCapacity = (int_cosize)(allocSize == 0 ? cDefaultCapacity : allocSize * 2);
|
||||
// Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
|
||||
// Note that this check works even when mItems.Length overflowed thanks to the (uint) cast
|
||||
//if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
|
||||
if (newCapacity < min) newCapacity = (int_cosize)min;
|
||||
Capacity = newCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reserve(int size)
|
||||
{
|
||||
EnsureCapacity(size);
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return Enumerator(this);
|
||||
}
|
||||
|
||||
public int_cosize FindIndex(Predicate<T> match)
|
||||
{
|
||||
for (int_cosize i = 0; i < mSize; i++)
|
||||
if (match(mItems[i]))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int_cosize IndexOf(T item)
|
||||
{
|
||||
//return Array.IndexOf(mItems, item, 0, mSize);
|
||||
for (int i = 0; i < mSize; i++)
|
||||
if (mItems[i] == item)
|
||||
return (int_cosize)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int_cosize IndexOf(T item, int index)
|
||||
{
|
||||
for (int i = index; i < mSize; i++)
|
||||
if (mItems[i] == item)
|
||||
return (int_cosize)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int_cosize IndexOf(T item, int index, int count)
|
||||
{
|
||||
for (int i = index; i < index + count; i++)
|
||||
if (mItems[i] == item)
|
||||
return (int_cosize)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
if (mSize == AllocSize) EnsureCapacity(mSize + 1);
|
||||
if (index < mSize)
|
||||
{
|
||||
Internal.MemCpy(mItems + index + 1, mItems + index, (mSize - index) * strideof(T), alignof(T));
|
||||
}
|
||||
mItems[index] = item;
|
||||
mSize++;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
Debug.Assert((uint)index < (uint)mSize);
|
||||
if (index < mSize - 1)
|
||||
{
|
||||
Internal.MemCpy(mItems + index, mItems + index + 1, (mSize - index - 1) * strideof(T), alignof(T));
|
||||
}
|
||||
mSize--;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void RemoveRange(int index, int count)
|
||||
{
|
||||
Debug.Assert((uint)index + (uint)count <= (uint)mSize);
|
||||
if (index + count < mSize - 1)
|
||||
{
|
||||
for (int i = index; i < mSize - count; i++)
|
||||
mItems[i] = mItems[i + count];
|
||||
}
|
||||
mSize -= (.)count;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void RemoveAtFast(int index)
|
||||
{
|
||||
Debug.Assert((uint32)index < (uint32)mSize);
|
||||
mSize--;
|
||||
if (mSize > 0)
|
||||
mItems[index] = mItems[mSize];
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Sort(Comparison<T> comp)
|
||||
{
|
||||
var sorter = Sorter<T, void>(mItems, null, mSize, comp);
|
||||
sorter.Sort(0, mSize);
|
||||
}
|
||||
|
||||
public int RemoveAll(Predicate<T> match)
|
||||
{
|
||||
int_cosize freeIndex = 0; // the first free slot in items array
|
||||
|
||||
// Find the first item which needs to be removed.
|
||||
while (freeIndex < mSize && !match(mItems[freeIndex])) freeIndex++;
|
||||
if (freeIndex >= mSize) return 0;
|
||||
|
||||
int_cosize current = freeIndex + 1;
|
||||
while (current < mSize)
|
||||
{
|
||||
// Find the first item which needs to be kept.
|
||||
while (current < mSize && match(mItems[current])) current++;
|
||||
|
||||
if (current < mSize)
|
||||
{
|
||||
// copy item to the free slot.
|
||||
mItems[freeIndex++] = mItems[current++];
|
||||
}
|
||||
}
|
||||
|
||||
int_cosize result = mSize - freeIndex;
|
||||
mSize = freeIndex;
|
||||
#if VERSION_LIST
|
||||
mVersion++;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
public T PopBack()
|
||||
{
|
||||
T backVal = mItems[mSize - 1];
|
||||
mSize--;
|
||||
return backVal;
|
||||
}
|
||||
|
||||
public T PopFront()
|
||||
{
|
||||
T backVal = mItems[0];
|
||||
RemoveAt(0);
|
||||
return backVal;
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
int_cosize index = IndexOf(item);
|
||||
if (index >= 0)
|
||||
{
|
||||
RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Searches a section of the list for a given element using a binary search
|
||||
/// algorithm. Elements of the list are compared to the search value using
|
||||
/// the given IComparer interface. If comparer is null, elements of
|
||||
/// the list are compared to the search value using the IComparable
|
||||
/// interface, which in that case must be implemented by all elements of the
|
||||
/// list and the given search value. This method assumes that the given
|
||||
/// section of the list is already sorted; if this is not the case, the
|
||||
/// result will be incorrect.
|
||||
///
|
||||
/// The method returns the index of the given value in the list. If the
|
||||
/// list does not contain the given value, the method returns a negative
|
||||
/// integer. The bitwise complement operator (~) can be applied to a
|
||||
/// negative result to produce the index of the first element (if any) that
|
||||
/// is larger than the given search value. This is also the index at which
|
||||
/// the search value should be inserted into the list in order for the list
|
||||
/// to remain sorted.
|
||||
///
|
||||
/// The method uses the Array.BinarySearch method to perform the
|
||||
/// search.
|
||||
///
|
||||
/// @brief Searches a section of the list for a given element using a binary search algorithm.
|
||||
public int BinarySearch(int index, int count, T item, IComparer<T> comparer)
|
||||
{
|
||||
return (int_cosize)Array.BinarySearch(mItems, index, count, item, comparer);
|
||||
}
|
||||
|
||||
public int_cosize BinarySearch(T item, IComparer<T> comparer)
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() <= Count);
|
||||
return (int_cosize)BinarySearch(0, Count, item, comparer);
|
||||
}
|
||||
|
||||
public static operator Span<T>(List<T> list)
|
||||
{
|
||||
return Span<T>(list.mItems, list.mSize);
|
||||
}
|
||||
|
||||
protected override void GCMarkMembers()
|
||||
{
|
||||
if (mItems == null)
|
||||
return;
|
||||
let type = typeof(T);
|
||||
if ((type.mTypeFlags & .WantsMark) == 0)
|
||||
return;
|
||||
for (int i < mSize)
|
||||
{
|
||||
GC.Mark_Unbound(mItems[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public struct Enumerator : IRefEnumerator<T>, IResettable
|
||||
{
|
||||
private List<T> mList;
|
||||
private int mIndex;
|
||||
#if VERSION_LIST
|
||||
private int32 mVersion;
|
||||
#endif
|
||||
private T* mCurrent;
|
||||
|
||||
internal this(List<T> list)
|
||||
{
|
||||
mList = list;
|
||||
mIndex = 0;
|
||||
#if VERSION_LIST
|
||||
mVersion = list.mVersion;
|
||||
#endif
|
||||
mCurrent = null;
|
||||
}
|
||||
|
||||
#if VERSION_LIST
|
||||
void CheckVersion()
|
||||
{
|
||||
if (mVersion != mList.mVersion)
|
||||
Runtime.FatalError(cVersionError);
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
List<T> localList = mList;
|
||||
if ((uint(mIndex) < uint(localList.mSize)))
|
||||
{
|
||||
mCurrent = &localList.mItems[mIndex];
|
||||
mIndex++;
|
||||
return true;
|
||||
}
|
||||
return MoveNextRare();
|
||||
}
|
||||
|
||||
private bool MoveNextRare() mut
|
||||
{
|
||||
#if VERSION_LIST
|
||||
CheckVersion();
|
||||
#endif
|
||||
mIndex = mList.mSize + 1;
|
||||
mCurrent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return *mCurrent;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
*mCurrent = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ref T CurrentRef
|
||||
{
|
||||
get
|
||||
{
|
||||
return ref *mCurrent;
|
||||
}
|
||||
}
|
||||
|
||||
public int Index
|
||||
{
|
||||
get
|
||||
{
|
||||
return mIndex - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return mList.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove() mut
|
||||
{
|
||||
int curIdx = mIndex - 1;
|
||||
mList.RemoveAt(curIdx);
|
||||
#if VERSION_LIST
|
||||
mVersion = mList.mVersion;
|
||||
#endif
|
||||
mIndex = curIdx;
|
||||
}
|
||||
|
||||
public void RemoveFast() mut
|
||||
{
|
||||
int curIdx = mIndex - 1;
|
||||
int lastIdx = mList.Count - 1;
|
||||
if (curIdx < lastIdx)
|
||||
mList[curIdx] = mList[lastIdx];
|
||||
mList.RemoveAt(lastIdx);
|
||||
#if VERSION_LIST
|
||||
mVersion = mList.mVersion;
|
||||
#endif
|
||||
mIndex = curIdx;
|
||||
}
|
||||
|
||||
public void Reset() mut
|
||||
{
|
||||
mIndex = 0;
|
||||
mCurrent = null;
|
||||
}
|
||||
|
||||
public Result<T> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
|
||||
public Result<T*> GetNextRef() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return &CurrentRef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension List<T> where T : IOpComparable
|
||||
{
|
||||
public int_cosize BinarySearch(T item)
|
||||
{
|
||||
return (int_cosize)BinarySearch(0, Count, item, scope CompareWrapper<T>());
|
||||
}
|
||||
|
||||
public void Sort()
|
||||
{
|
||||
Sort(scope (lhs, rhs) => lhs <=> rhs);
|
||||
}
|
||||
}
|
||||
|
||||
class ListWithAlloc<T> : List<T>
|
||||
{
|
||||
IRawAllocator mAlloc;
|
||||
|
||||
public this(IRawAllocator alloc)
|
||||
{
|
||||
mAlloc = alloc;
|
||||
}
|
||||
|
||||
/*protected override void* Alloc(int size, int align)
|
||||
{
|
||||
return mAlloc.Alloc(size, align);
|
||||
}
|
||||
|
||||
protected override void Free(void* ptr)
|
||||
{
|
||||
mAlloc.Free(ptr);
|
||||
}*/
|
||||
}
|
||||
}
|
350
BeefLibs/corlib/src/System/Collections/Generic/Queue.bf
Normal file
350
BeefLibs/corlib/src/System/Collections/Generic/Queue.bf
Normal file
|
@ -0,0 +1,350 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
#if PARANOID
|
||||
#define VERSION_QUEUE
|
||||
#endif
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
/// A simple Queue of generic items. Internally it is implemented as a
|
||||
/// circular buffer, so Enqueue can be O(n). Dequeue is O(1).
|
||||
public class Queue<T> : IEnumerable<T> //, System.Collections.ICollection, IReadOnlyCollection<T>
|
||||
{
|
||||
private T[] mArray;
|
||||
private int_cosize mHead; // First valid element in the queue
|
||||
private int_cosize mTail; // Last valid element in the queue
|
||||
private int_cosize mSize; // Number of elements.
|
||||
#if VERSION_QUEUE
|
||||
private int32 mVersion;
|
||||
const String cVersionError = "Queue changed during enumeration";
|
||||
#endif
|
||||
private Object mSyncRoot;
|
||||
|
||||
private const int32 cMinimumGrow = 4;
|
||||
private const int32 cShrinkThreshold = 32;
|
||||
private const int32 cGrowFactor = 200; // double each time
|
||||
private const int32 cDefaultCapacity = 4;
|
||||
static T[] sEmptyArray = new T[0] ~ delete _;
|
||||
|
||||
/// Creates a queue with room for capacity objects. The default initial
|
||||
/// capacity and grow factor are used.
|
||||
public this()
|
||||
{
|
||||
mArray = sEmptyArray;
|
||||
}
|
||||
|
||||
/// Creates a queue with room for capacity objects. The default grow factor
|
||||
/// is used.
|
||||
public this(int capacity)
|
||||
{
|
||||
Debug.Assert(capacity >= 0);
|
||||
mArray = new T[capacity];
|
||||
mHead = 0;
|
||||
mTail = 0;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
// Fills a Queue with the elements of an ICollection. Uses the enumerator
|
||||
// to get each of the elements.
|
||||
//
|
||||
/// <include file='doc\Queue.uex' path='docs/doc[@for="Queue.Queue3"]/*' />
|
||||
/*public void Set<T>(T collection) where T : IEnumerable<T>
|
||||
{
|
||||
if (collection == null)
|
||||
//ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
|
||||
Runtime.FatalError();
|
||||
|
||||
_array = new T[_DefaultCapacity];
|
||||
_size = 0;
|
||||
_version = 0;
|
||||
|
||||
using (var en = collection.GetEnumerator())
|
||||
{
|
||||
while (en.MoveNext())
|
||||
{
|
||||
Enqueue(en.Current);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return mSize; }
|
||||
}
|
||||
|
||||
/// Removes all items from the queue.
|
||||
public void Clear()
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
if (mHead < mTail)
|
||||
Array.Clear(mArray, mHead, mSize);
|
||||
else
|
||||
{
|
||||
Array.Clear(mArray, mHead, mArray.Count - mHead);
|
||||
Array.Clear(mArray, 0, mTail);
|
||||
}
|
||||
#endif
|
||||
mHead = 0;
|
||||
mTail = 0;
|
||||
mSize = 0;
|
||||
#if VERSION_QUEUE
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// CopyTo copies a collection into an Array, starting at a particular
|
||||
/// index into the array.
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
Debug.Assert((uint)arrayIndex <= (uint)array.Count);
|
||||
int arrayLen = array.Count;
|
||||
Debug.Assert(arrayLen >= mSize);
|
||||
|
||||
int numToCopy = (arrayLen - arrayIndex < mSize) ? (arrayLen - arrayIndex) : mSize;
|
||||
if (numToCopy == 0) return;
|
||||
|
||||
int firstPart = (mArray.Count - mHead < numToCopy) ? mArray.Count - mHead : numToCopy;
|
||||
Array.Copy(mArray, mHead, array, arrayIndex, firstPart);
|
||||
numToCopy -= firstPart;
|
||||
if (numToCopy > 0)
|
||||
{
|
||||
Array.Copy(mArray, 0, array, arrayIndex + mArray.Count - mHead, numToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds item to the tail of the queue.
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
if (mSize == mArray.Count)
|
||||
{
|
||||
int newcapacity = (int)((int64)mArray.Count * (int64)cGrowFactor / 100);
|
||||
if (newcapacity < mArray.Count + cMinimumGrow)
|
||||
{
|
||||
newcapacity = mArray.Count + cMinimumGrow;
|
||||
}
|
||||
SetCapacity(newcapacity);
|
||||
}
|
||||
|
||||
mArray[mTail] = item;
|
||||
mTail = (mTail + 1) % (int_cosize)mArray.Count;
|
||||
mSize++;
|
||||
#if VERSION_QUEUE
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// GetEnumerator returns an IEnumerator over this Queue. This
|
||||
/// Enumerator will support removing.
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return Enumerator(this);
|
||||
}
|
||||
|
||||
/// Removes the object at the head of the queue and returns it. If the queue
|
||||
/// is empty, this method simply returns null.
|
||||
public T Dequeue()
|
||||
{
|
||||
if (mSize == 0)
|
||||
//ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue);
|
||||
Runtime.FatalError();
|
||||
|
||||
T removed = mArray[mHead];
|
||||
mArray[mHead] = default(T);
|
||||
mHead = (mHead + 1) % (int_cosize)mArray.Count;
|
||||
mSize--;
|
||||
#if VERSION_QUEUE
|
||||
mVersion++;
|
||||
#endif
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// Returns the object at the head of the queue. The object remains in the
|
||||
/// queue. If the queue is empty, this method throws an
|
||||
/// InvalidOperationException.
|
||||
public T Peek()
|
||||
{
|
||||
Debug.Assert(mSize != 0);
|
||||
return mArray[mHead];
|
||||
}
|
||||
|
||||
/// Returns true if the queue contains at least one object equal to item.
|
||||
/// Equality is determined using item.Equals().
|
||||
public bool Contains(T item)
|
||||
{
|
||||
int index = mHead;
|
||||
int count = mSize;
|
||||
while (count-- > 0)
|
||||
{
|
||||
if (((Object)item) == null)
|
||||
{
|
||||
if (((Object)mArray[index]) == null)
|
||||
return true;
|
||||
}
|
||||
else if (mArray[index] != null && mArray[index] == item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
index = (index + 1) % mArray.Count;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal T GetElement(int i)
|
||||
{
|
||||
return mArray[(mHead + i) % mArray.Count];
|
||||
}
|
||||
|
||||
/// Iterates over the objects in the queue, returning an array of the
|
||||
/// objects in the Queue, or an empty array if the queue is empty.
|
||||
/// The order of elements in the array is first in to last in, the same
|
||||
/// order produced by successive calls to Dequeue.
|
||||
public T[] ToArray()
|
||||
{
|
||||
T[] arr = new T[mSize];
|
||||
if (mSize == 0)
|
||||
return arr;
|
||||
|
||||
if (mHead < mTail)
|
||||
{
|
||||
Array.Copy(mArray, mHead, arr, 0, mSize);
|
||||
}else
|
||||
{
|
||||
Array.Copy(mArray, mHead, arr, 0, mArray.Count - mHead);
|
||||
Array.Copy(mArray, 0, arr, mArray.Count - mHead, mTail);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
// PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity
|
||||
// must be >= _size.
|
||||
private void SetCapacity(int capacity)
|
||||
{
|
||||
T[] newarray = new T[capacity];
|
||||
if (mSize > 0)
|
||||
{
|
||||
if (mHead < mTail)
|
||||
{
|
||||
Array.Copy(mArray, mHead, newarray, 0, mSize);
|
||||
}else
|
||||
{
|
||||
Array.Copy(mArray, mHead, newarray, 0, mArray.Count - mHead);
|
||||
Array.Copy(mArray, 0, newarray, mArray.Count - mHead, mTail);
|
||||
}
|
||||
}
|
||||
|
||||
mArray = newarray;
|
||||
mHead = 0;
|
||||
mTail = (mSize == capacity) ? 0 : mSize;
|
||||
#if VERSION_QUEUE
|
||||
mVersion++;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void TrimExcess()
|
||||
{
|
||||
int32 threshold = (int32)(((double)mArray.Count) * 0.9);
|
||||
if (mSize < threshold)
|
||||
{
|
||||
SetCapacity(mSize);
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements an enumerator for a Queue. The enumerator uses the
|
||||
/// internal version number of the list to ensure that no modifications are
|
||||
/// made to the list while an enumeration is in progress.
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
private Queue<T> mQueue;
|
||||
private int32 mIndex; // -1 = not started, -2 = ended/disposed
|
||||
#if VERSION_QUEUE
|
||||
private int32 mVersion;
|
||||
#endif
|
||||
private T mCurrentElement;
|
||||
|
||||
internal this(Queue<T> q)
|
||||
{
|
||||
mQueue = q;
|
||||
#if VERSION_QUEUE
|
||||
mVersion = mQueue.mVersion;
|
||||
#endif
|
||||
mIndex = -1;
|
||||
mCurrentElement = default(T);
|
||||
}
|
||||
|
||||
#if VERSION_QUEUE
|
||||
void CheckVersion()
|
||||
{
|
||||
if (mVersion != mQueue.mVersion)
|
||||
Runtime.FatalError(cVersionError);
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Dispose() mut
|
||||
{
|
||||
mIndex = -2;
|
||||
mCurrentElement = default(T);
|
||||
}
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
#if VERSION_QUEUE
|
||||
CheckVersion();
|
||||
#endif
|
||||
if (mIndex == -2)
|
||||
return false;
|
||||
|
||||
mIndex++;
|
||||
|
||||
if (mIndex == mQueue.mSize)
|
||||
{
|
||||
mIndex = -2;
|
||||
mCurrentElement = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
mCurrentElement = mQueue.GetElement(mIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mIndex < 0)
|
||||
{
|
||||
if (mIndex == -1)
|
||||
Runtime.FatalError("Enumeration not started");
|
||||
else
|
||||
Runtime.FatalError("Enumeration ended");
|
||||
}
|
||||
return mCurrentElement;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset() mut
|
||||
{
|
||||
#if VERSION_QUEUE
|
||||
CheckVersion();
|
||||
#endif
|
||||
mIndex = -1;
|
||||
mCurrentElement = default;
|
||||
}
|
||||
|
||||
public Result<T> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
326
BeefLibs/corlib/src/System/Collections/Generic/Sorter.bf
Normal file
326
BeefLibs/corlib/src/System/Collections/Generic/Sorter.bf
Normal file
|
@ -0,0 +1,326 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
struct Sorter<T, T2>
|
||||
{
|
||||
// This is the threshold where Introspective sort switches to Insertion sort.
|
||||
// Empirically, 16 seems to speed up most cases without slowing down others, at least for integers.
|
||||
// Large value types may benefit from a smaller number.
|
||||
const int IntrosortSizeThreshold = 16;
|
||||
|
||||
private T* keys;
|
||||
private T2* items;
|
||||
private int mCount;
|
||||
private Comparison<T> comparer;
|
||||
|
||||
internal this(T* keys, T2* items, int count, Comparison<T> comparer)
|
||||
{
|
||||
this.keys = keys;
|
||||
this.items = items;
|
||||
mCount = count;
|
||||
this.comparer = comparer;
|
||||
}
|
||||
|
||||
internal static int FloorLog2(int n)
|
||||
{
|
||||
int result = 0;
|
||||
int val = n;
|
||||
while (val >= 1)
|
||||
{
|
||||
result++;
|
||||
val = val / 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int GetMedian(int low, int hi)
|
||||
{
|
||||
// Note both may be negative, if we are dealing with arrays w/ negative lower bounds.
|
||||
//Contract.Requires(low <= hi);
|
||||
//Contract.Assert( hi - low >= 0, "Length overflow!");
|
||||
return low + ((hi - low) >> 1);
|
||||
}
|
||||
|
||||
internal void SwapIfGreaterWithItems(int a, int b)
|
||||
{
|
||||
if (a != b)
|
||||
{
|
||||
if (comparer(keys[a], keys[b]) > 0)
|
||||
{
|
||||
T temp = keys[a];
|
||||
keys[a] = keys[b];
|
||||
keys[b] = temp;
|
||||
if ((items != null) && (sizeof(T2) != 0))
|
||||
{
|
||||
T2 item = items[a];
|
||||
items[a] = items[b];
|
||||
items[b] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Swap(int i, int j)
|
||||
{
|
||||
T t = keys[i];
|
||||
keys[i] = keys[j];
|
||||
keys[j] = t;
|
||||
|
||||
if (items != null)
|
||||
{
|
||||
T2 item = items[i];
|
||||
items[i] = items[j];
|
||||
items[j] = item;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Sort(int left, int length)
|
||||
{
|
||||
IntrospectiveSort(left, length);
|
||||
}
|
||||
|
||||
private void DepthLimitedQuickSort(int left, int right, int depthLimit)
|
||||
{
|
||||
// Can use the much faster jit helpers for array access.
|
||||
repeat
|
||||
{
|
||||
if (depthLimit == 0)
|
||||
{
|
||||
Heapsort(left, right);
|
||||
return;
|
||||
}
|
||||
|
||||
int curLeft = left;
|
||||
int curRight = right;
|
||||
int curDepthLimit = depthLimit;
|
||||
|
||||
int i = curLeft;
|
||||
int j = curRight;
|
||||
|
||||
// pre-sort the low, middle (pivot), and high values in place.
|
||||
// this improves performance in the face of already sorted data, or
|
||||
// data that is made up of multiple sorted runs appended together.
|
||||
int middle = GetMedian(i, j);
|
||||
|
||||
SwapIfGreaterWithItems(i, middle); // swap the low with the mid point
|
||||
SwapIfGreaterWithItems(i, j); // swap the low with the high
|
||||
SwapIfGreaterWithItems(middle, j); // swap the middle with the high
|
||||
|
||||
T x = keys[middle];
|
||||
repeat
|
||||
{
|
||||
while (comparer(keys[i], x) < 0) i++;
|
||||
while (comparer(x, keys[j]) < 0) j--;
|
||||
|
||||
//Contract.Assert(i >= left && j <= right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
|
||||
if (i > j) break;
|
||||
if (i < j)
|
||||
{
|
||||
T key = keys[i];
|
||||
keys[i] = keys[j];
|
||||
keys[j] = key;
|
||||
if (items != null)
|
||||
{
|
||||
T2 item = items[i];
|
||||
items[i] = items[j];
|
||||
items[j] = item;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
j--;
|
||||
} while (i <= j);
|
||||
|
||||
// The next iteration of the while loop is to "recursively" sort the larger half of the array and the
|
||||
// following calls recrusively sort the smaller half. So we subtrack one from depthLimit here so
|
||||
// both sorts see the new value.
|
||||
curDepthLimit--;
|
||||
|
||||
if (j - curLeft <= curRight - i)
|
||||
{
|
||||
if (curLeft < j) DepthLimitedQuickSort(curLeft, j, curDepthLimit);
|
||||
curLeft = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < curRight) DepthLimitedQuickSort(i, curRight, curDepthLimit);
|
||||
curRight = j;
|
||||
}
|
||||
} while (left < right);
|
||||
}
|
||||
|
||||
private void IntrospectiveSort(int left, int length)
|
||||
{
|
||||
if (length < 2)
|
||||
return;
|
||||
|
||||
IntroSort(left, length + left - 1, 2 * FloorLog2(mCount));
|
||||
}
|
||||
|
||||
private void IntroSort(int lo, int hi, int depthLimit)
|
||||
{
|
||||
int curHi = hi;
|
||||
int curDepthLimit = depthLimit;
|
||||
|
||||
while (curHi > lo)
|
||||
{
|
||||
int partitionSize = curHi - lo + 1;
|
||||
if (partitionSize <= IntrosortSizeThreshold)
|
||||
{
|
||||
if (partitionSize == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (partitionSize == 2)
|
||||
{
|
||||
SwapIfGreaterWithItems(lo, curHi);
|
||||
return;
|
||||
}
|
||||
if (partitionSize == 3)
|
||||
{
|
||||
SwapIfGreaterWithItems(lo, curHi-1);
|
||||
SwapIfGreaterWithItems(lo, curHi);
|
||||
SwapIfGreaterWithItems(curHi-1, curHi);
|
||||
return;
|
||||
}
|
||||
|
||||
InsertionSort(lo, curHi);
|
||||
return;
|
||||
}
|
||||
|
||||
if (curDepthLimit == 0)
|
||||
{
|
||||
Heapsort(lo, curHi);
|
||||
return;
|
||||
}
|
||||
curDepthLimit--;
|
||||
|
||||
int p = PickPivotAndPartition(lo, curHi);
|
||||
IntroSort(p + 1, curHi, curDepthLimit);
|
||||
curHi = p - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private int PickPivotAndPartition(int lo, int hi)
|
||||
{
|
||||
// Compute median-of-three. But also partition them, since we've done the comparison.
|
||||
int mid = lo + (hi - lo) / 2;
|
||||
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
|
||||
SwapIfGreaterWithItems(lo, mid);
|
||||
SwapIfGreaterWithItems(lo, hi);
|
||||
SwapIfGreaterWithItems(mid, hi);
|
||||
|
||||
T pivot = keys[mid];
|
||||
Swap(mid, hi - 1);
|
||||
int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
while (comparer(keys[++left], pivot) < 0) {}
|
||||
while (comparer(pivot, keys[--right]) < 0) {}
|
||||
|
||||
if(left >= right)
|
||||
break;
|
||||
|
||||
Swap(left, right);
|
||||
}
|
||||
|
||||
// Put pivot in the right location.
|
||||
Swap(left, (hi - 1));
|
||||
return left;
|
||||
}
|
||||
|
||||
private void Heapsort(int lo, int hi)
|
||||
{
|
||||
int n = hi - lo + 1;
|
||||
for (int i = n / 2; i >= 1; i = i - 1)
|
||||
{
|
||||
DownHeap(i, n, lo);
|
||||
}
|
||||
for (int i = n; i > 1; i = i - 1)
|
||||
{
|
||||
Swap(lo, lo + i - 1);
|
||||
|
||||
DownHeap(1, i - 1, lo);
|
||||
}
|
||||
}
|
||||
|
||||
private void DownHeap(int i, int n, int lo)
|
||||
{
|
||||
int curI = i;
|
||||
|
||||
T d = keys[lo + curI - 1];
|
||||
//T dt = (items != null) ? items[lo + i - 1] : null;
|
||||
T2* dt = (items != null) ? &items[lo + curI - 1] : null;
|
||||
int child;
|
||||
while (curI <= n / 2)
|
||||
{
|
||||
child = 2 * curI;
|
||||
if (child < n && comparer(keys[lo + child - 1], keys[lo + child]) < 0)
|
||||
{
|
||||
child++;
|
||||
}
|
||||
if (!(comparer(d, keys[lo + child - 1]) < 0))
|
||||
break;
|
||||
keys[lo + curI - 1] = keys[lo + child - 1];
|
||||
if(items != null)
|
||||
items[lo + curI - 1] = items[lo + child - 1];
|
||||
curI = child;
|
||||
}
|
||||
keys[lo + curI - 1] = d;
|
||||
if (items != null)
|
||||
items[lo + curI - 1] = *dt;
|
||||
}
|
||||
|
||||
private void InsertionSortZ(int lo, int hi)
|
||||
{
|
||||
int i, j;
|
||||
T t;
|
||||
T2 ti = ?;
|
||||
for (i = lo; i < hi; i++)
|
||||
{
|
||||
j = i;
|
||||
t = keys[i + 1];
|
||||
//ti = (items != null) ? items[i + 1] : null;
|
||||
if (items != null)
|
||||
ti = items[i + 1];
|
||||
while (j >= lo && comparer(t, keys[j]) < 0)
|
||||
{
|
||||
keys[j + 1] = keys[j];
|
||||
if(items != null)
|
||||
items[j + 1] = items[j];
|
||||
j--;
|
||||
}
|
||||
keys[j + 1] = t;
|
||||
if (items != null)
|
||||
items[j + 1] = ti;
|
||||
}
|
||||
}
|
||||
|
||||
private void InsertionSort(int lo, int hi)
|
||||
{
|
||||
int i, j;
|
||||
T t;
|
||||
T2 ti = ?;
|
||||
for (i = lo; i < hi; i++)
|
||||
{
|
||||
j = i;
|
||||
t = keys[i + 1];
|
||||
if (items != null)
|
||||
ti = items[i + 1];
|
||||
while (j >= lo && comparer(t, keys[j]) < 0)
|
||||
{
|
||||
keys[j + 1] = keys[j];
|
||||
if(items != null)
|
||||
items[j + 1] = items[j];
|
||||
j--;
|
||||
}
|
||||
keys[j + 1] = t;
|
||||
if (items != null)
|
||||
items[j + 1] = ti;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
BeefLibs/corlib/src/System/Collections/IEnumerator.bf
Normal file
25
BeefLibs/corlib/src/System/Collections/IEnumerator.bf
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
interface IEnumerator<T>
|
||||
{
|
||||
Result<T> GetNext() mut;
|
||||
}
|
||||
|
||||
interface IResettable
|
||||
{
|
||||
void Reset() mut;
|
||||
}
|
||||
|
||||
interface IRefEnumerator<T> : IEnumerator<T>
|
||||
{
|
||||
Result<T*> GetNextRef() mut;
|
||||
}
|
||||
|
||||
concrete interface IEnumerable<T>
|
||||
{
|
||||
concrete IEnumerator<T> GetEnumerator();
|
||||
}
|
||||
}
|
117
BeefLibs/corlib/src/System/Console.bf
Normal file
117
BeefLibs/corlib/src/System/Console.bf
Normal file
|
@ -0,0 +1,117 @@
|
|||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace System
|
||||
{
|
||||
public static class Console
|
||||
{
|
||||
public static Encoding InputEncoding = Encoding.ASCII;
|
||||
public static Encoding OutputEncoding = Encoding.ASCII;
|
||||
|
||||
static StreamWriter OpenStreamWriter(Platform.BfpFileStdKind stdKind, ref StreamWriter outStreamWriter)
|
||||
{
|
||||
if (outStreamWriter == null)
|
||||
{
|
||||
FileStream fileStream = new .();
|
||||
Stream stream = fileStream;
|
||||
if (fileStream.OpenStd(stdKind) case .Err)
|
||||
{
|
||||
DeleteAndNullify!(fileStream);
|
||||
stream = new NullStream();
|
||||
}
|
||||
|
||||
StreamWriter newStreamWriter = new StreamWriter(stream, InputEncoding, 4096, true);
|
||||
newStreamWriter.AutoFlush = true;
|
||||
|
||||
let prevValue = Interlocked.CompareExchange(ref outStreamWriter, null, newStreamWriter);
|
||||
if (prevValue != null)
|
||||
{
|
||||
// This was already set - race condition
|
||||
delete newStreamWriter;
|
||||
return prevValue;
|
||||
}
|
||||
return newStreamWriter;
|
||||
}
|
||||
return outStreamWriter;
|
||||
}
|
||||
|
||||
static StreamReader OpenStreamReader(Platform.BfpFileStdKind stdKind, ref StreamReader outStreamReader)
|
||||
{
|
||||
if (outStreamReader == null)
|
||||
{
|
||||
FileStream fileStream = new .();
|
||||
Stream stream = fileStream;
|
||||
if (fileStream.OpenStd(stdKind) case .Err)
|
||||
{
|
||||
DeleteAndNullify!(fileStream);
|
||||
stream = new NullStream();
|
||||
}
|
||||
|
||||
StreamReader newStreamReader = new StreamReader(stream, InputEncoding, false, 4096, true);
|
||||
|
||||
let prevValue = Interlocked.CompareExchange(ref outStreamReader, null, newStreamReader);
|
||||
if (prevValue != null)
|
||||
{
|
||||
// This was already set - race condition
|
||||
delete newStreamReader;
|
||||
return prevValue;
|
||||
}
|
||||
return newStreamReader;
|
||||
}
|
||||
return outStreamReader;
|
||||
}
|
||||
|
||||
public static volatile StreamWriter mOut ~ delete _;
|
||||
public static StreamWriter Out
|
||||
{
|
||||
get
|
||||
{
|
||||
return OpenStreamWriter(.Out, ref mOut);
|
||||
}
|
||||
}
|
||||
|
||||
public static volatile StreamWriter mError ~ delete _;
|
||||
public static StreamWriter Error
|
||||
{
|
||||
get
|
||||
{
|
||||
return OpenStreamWriter(.Error, ref mError);
|
||||
}
|
||||
}
|
||||
|
||||
public static volatile StreamReader mIn ~ delete _;
|
||||
public static StreamReader In
|
||||
{
|
||||
get
|
||||
{
|
||||
return OpenStreamReader(.In, ref mIn);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Write(String line)
|
||||
{
|
||||
Out.Write(line).IgnoreError();
|
||||
}
|
||||
|
||||
public static void Write(String fmt, params Object[] args)
|
||||
{
|
||||
String str = scope String();
|
||||
str.AppendF(fmt, params args);
|
||||
Write(str);
|
||||
}
|
||||
|
||||
public static void WriteLine(String line)
|
||||
{
|
||||
//PrintF("Hey!");
|
||||
Out.WriteLine(line).IgnoreError();
|
||||
}
|
||||
|
||||
public static void WriteLine(StringView fmt, params Object[] args)
|
||||
{
|
||||
String str = scope String();
|
||||
str.AppendF(fmt, params args);
|
||||
WriteLine(str);
|
||||
}
|
||||
}
|
||||
}
|
897
BeefLibs/corlib/src/System/DateTime.bf
Normal file
897
BeefLibs/corlib/src/System/DateTime.bf
Normal file
|
@ -0,0 +1,897 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace System
|
||||
{
|
||||
struct DateTime : IHashable
|
||||
{
|
||||
// Number of 100ns ticks per time unit
|
||||
private const int64 TicksPerMillisecond = 10000;
|
||||
private const int64 TicksPerSecond = TicksPerMillisecond * 1000;
|
||||
private const int64 TicksPerMinute = TicksPerSecond * 60;
|
||||
private const int64 TicksPerHour = TicksPerMinute * 60;
|
||||
private const int64 TicksPerDay = TicksPerHour * 24;
|
||||
|
||||
// Number of milliseconds per time unit
|
||||
private const int32 MillisPerSecond = 1000;
|
||||
private const int32 MillisPerMinute = MillisPerSecond * 60;
|
||||
private const int32 MillisPerHour = MillisPerMinute * 60;
|
||||
private const int32 MillisPerDay = MillisPerHour * 24;
|
||||
|
||||
// Number of days in a non-leap year
|
||||
private const int32 DaysPerYear = 365;
|
||||
// Number of days in 4 years
|
||||
private const int32 DaysPer4Years = DaysPerYear * 4 + 1; // 1461
|
||||
// Number of days in 100 years
|
||||
private const int32 DaysPer100Years = DaysPer4Years * 25 - 1; // 36524
|
||||
// Number of days in 400 years
|
||||
private const int32 DaysPer400Years = DaysPer100Years * 4 + 1; // 146097
|
||||
|
||||
// Number of days from 1/1/0001 to 12/31/1600
|
||||
private const int32 DaysTo1601 = DaysPer400Years * 4; // 584388
|
||||
// Number of days from 1/1/0001 to 12/30/1899
|
||||
private const int32 DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367;
|
||||
// Number of days from 1/1/0001 to 12/31/1969
|
||||
internal const int32 DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162
|
||||
// Number of days from 1/1/0001 to 12/31/9999
|
||||
private const int32 DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059
|
||||
|
||||
internal const int64 MinTicks = 0;
|
||||
internal const int64 MaxTicks = DaysTo10000 * TicksPerDay - 1;
|
||||
private const int64 MaxMillis = (int64)DaysTo10000 * MillisPerDay;
|
||||
|
||||
private const int64 FileTimeOffset = DaysTo1601 * TicksPerDay;
|
||||
private const int64 DoubleDateOffset = DaysTo1899 * TicksPerDay;
|
||||
// The minimum OA date is 0100/01/01 (Note it's year 100).
|
||||
// The maximum OA date is 9999/12/31
|
||||
private const int64 OADateMinAsTicks = (DaysPer100Years - DaysPerYear) * TicksPerDay;
|
||||
// All OA dates must be greater than (not >=) OADateMinAsDouble
|
||||
private const double OADateMinAsDouble = -657435.0;
|
||||
// All OA dates must be less than (not <=) OADateMaxAsDouble
|
||||
private const double OADateMaxAsDouble = 2958466.0;
|
||||
|
||||
private const int32 DatePartYear = 0;
|
||||
private const int32 DatePartDayOfYear = 1;
|
||||
private const int32 DatePartMonth = 2;
|
||||
private const int32 DatePartDay = 3;
|
||||
|
||||
private static readonly int32[] DaysToMonth365 = new int32[]{
|
||||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365} ~ delete _;
|
||||
private static readonly int32[] DaysToMonth366 = new int32[]{
|
||||
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} ~ delete _;
|
||||
|
||||
public static readonly DateTime MinValue = DateTime(MinTicks, DateTimeKind.Unspecified);
|
||||
public static readonly DateTime MaxValue = DateTime(MaxTicks, DateTimeKind.Unspecified);
|
||||
|
||||
private const uint64 TicksMask = 0x3FFFFFFFFFFFFFFFUL;
|
||||
private const uint64 FlagsMask = 0xC000000000000000;
|
||||
private const uint64 LocalMask = 0x8000000000000000;
|
||||
private const int64 TicksCeiling = 0x4000000000000000;
|
||||
private const uint64 KindUnspecified = 0x0000000000000000UL;
|
||||
private const uint64 KindUtc = 0x4000000000000000UL;
|
||||
private const uint64 KindLocal = 0x8000000000000000;
|
||||
private const uint64 KindLocalAmbiguousDst = 0xC000000000000000;
|
||||
private const int32 KindShift = 62;
|
||||
|
||||
private const String TicksField = "ticks";
|
||||
private const String DateDataField = "dateData";
|
||||
|
||||
// The data is stored as an unsigned 64-bit integeter
|
||||
// Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value
|
||||
// 12/31/9999 23:59:59.9999999
|
||||
// Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd
|
||||
// value for the rare case where the date time is local, but is in an overlapped daylight
|
||||
// savings time hour and it is in daylight savings time. This allows distinction of these
|
||||
// otherwise ambiguous local times and prevents data loss when round tripping from Local to
|
||||
// UTC time.
|
||||
private uint64 dateData;
|
||||
|
||||
internal int64 InternalTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int64)(dateData & TicksMask);
|
||||
}
|
||||
}
|
||||
|
||||
private uint64 InternalKind
|
||||
{
|
||||
get
|
||||
{
|
||||
return (dateData & FlagsMask);
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
dateData = 0;
|
||||
}
|
||||
|
||||
public this(int64 ticks)
|
||||
{
|
||||
if (ticks < MinTicks || ticks > MaxTicks)
|
||||
Runtime.FatalError();
|
||||
//Contract.EndContractBlock();
|
||||
dateData = (uint64)ticks;
|
||||
}
|
||||
|
||||
private this(uint64 dateData)
|
||||
{
|
||||
this.dateData = dateData;
|
||||
}
|
||||
|
||||
public this(int64 ticks, DateTimeKind kind)
|
||||
{
|
||||
if (ticks < MinTicks || ticks > MaxTicks)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
this.dateData = ((uint64)ticks | ((uint64)kind << KindShift));
|
||||
}
|
||||
|
||||
internal this(int64 ticks, DateTimeKind kind, bool isAmbiguousDst)
|
||||
{
|
||||
if (ticks < MinTicks || ticks > MaxTicks)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
//Contract.Requires(kind == DateTimeKind.Local, "Internal Constructor is for local times only");
|
||||
//Contract.EndContractBlock();
|
||||
dateData = ((uint64)ticks | (isAmbiguousDst ? KindLocalAmbiguousDst : KindLocal));
|
||||
}
|
||||
|
||||
// Constructs a DateTime from a given year, month, and day. The
|
||||
// time-of-day of the resulting DateTime is always midnight.
|
||||
//
|
||||
public this(int year, int month, int day)
|
||||
{
|
||||
this.dateData = (uint64)DateToTicks(year, month, day);
|
||||
}
|
||||
|
||||
public this(int year, int month, int day, int hour, int minute, int second)
|
||||
{
|
||||
this.dateData = (uint64)(DateToTicks(year, month, day).Get() + TimeToTicks(hour, minute, second).Get());
|
||||
}
|
||||
|
||||
private static Result<int64> DateToTicks(int year, int month, int day)
|
||||
{
|
||||
if (year >= 1 && year <= 9999 && month >= 1 && month <= 12)
|
||||
{
|
||||
int32[] days = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
|
||||
if (day >= 1 && day <= days[month] - days[month - 1])
|
||||
{
|
||||
int y = year - 1;
|
||||
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
|
||||
return n * TicksPerDay;
|
||||
}
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadYearMonthDay"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Constructs a DateTime from a given year, month, day, hour,
|
||||
// minute, and second.
|
||||
//
|
||||
public this(int year, int month, int day, int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
if (millisecond < 0 || millisecond >= MillisPerSecond)
|
||||
{
|
||||
//throw new ArgumentOutOfRangeException("millisecond", Environment.GetResourceString("ArgumentOutOfRange_Range", 0, MillisPerSecond - 1));
|
||||
Runtime.FatalError();
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
int64 ticks = DateToTicks(year, month, day).Get() + TimeToTicks(hour, minute, second).Get();
|
||||
ticks += millisecond * TicksPerMillisecond;
|
||||
if (ticks < MinTicks || ticks > MaxTicks)
|
||||
Runtime.FatalError();
|
||||
//throw new ArgumentException(Environment.GetResourceString("Arg_DateTimeRange"));
|
||||
this.dateData = (uint64)ticks;
|
||||
}
|
||||
|
||||
private static Result<int64> TimeToTicks(int hour, int minute, int second)
|
||||
{
|
||||
//TimeSpan.TimeToTicks is a family access function which does no error checking, so
|
||||
//we need to put some error checking out here.
|
||||
if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60)
|
||||
{
|
||||
return (TimeSpan.TimeToTicks(hour, minute, second));
|
||||
}
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Returns the number of days in the month given by the year and
|
||||
// month arguments.
|
||||
//
|
||||
public static Result<int> DaysInMonth(int year, int month)
|
||||
{
|
||||
if (month < 1 || month > 12)
|
||||
return .Err;
|
||||
//throw new ArgumentOutOfRangeException("month", Environment.GetResourceString("ArgumentOutOfRange_Month"));
|
||||
//Contract.EndContractBlock();
|
||||
// IsLeapYear checks the year argument
|
||||
int32[] days = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
|
||||
return days[month] - days[month - 1];
|
||||
}
|
||||
|
||||
public static Result<bool> IsLeapYear(int year)
|
||||
{
|
||||
if (year < 1 || year > 9999)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
|
||||
// Returns the date part of this DateTime. The resulting value
|
||||
// corresponds to this DateTime with the time-of-day part set to
|
||||
// zero (midnight).
|
||||
//
|
||||
public DateTime Date
|
||||
{
|
||||
get
|
||||
{
|
||||
int64 ticks = InternalTicks;
|
||||
return DateTime((uint64)(ticks - ticks % TicksPerDay) | InternalKind);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a given date part of this DateTime. This method is used
|
||||
// to compute the year, day-of-year, month, or day part.
|
||||
private int32 GetDatePart(int32 part)
|
||||
{
|
||||
int64 ticks = InternalTicks;
|
||||
// n = number of days since 1/1/0001
|
||||
int32 n = (int32)(ticks / TicksPerDay);
|
||||
// y400 = number of whole 400-year periods since 1/1/0001
|
||||
int32 y400 = n / DaysPer400Years;
|
||||
// n = day number within 400-year period
|
||||
n -= y400 * DaysPer400Years;
|
||||
// y100 = number of whole 100-year periods within 400-year period
|
||||
int32 y100 = n / DaysPer100Years;
|
||||
// Last 100-year period has an extra day, so decrement result if 4
|
||||
if (y100 == 4) y100 = 3;
|
||||
// n = day number within 100-year period
|
||||
n -= y100 * DaysPer100Years;
|
||||
// y4 = number of whole 4-year periods within 100-year period
|
||||
int32 y4 = n / DaysPer4Years;
|
||||
// n = day number within 4-year period
|
||||
n -= y4 * DaysPer4Years;
|
||||
// y1 = number of whole years within 4-year period
|
||||
int32 y1 = n / DaysPerYear;
|
||||
// Last year has an extra day, so decrement result if 4
|
||||
if (y1 == 4) y1 = 3;
|
||||
// If year was requested, compute and return it
|
||||
if (part == DatePartYear)
|
||||
{
|
||||
return y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1;
|
||||
}
|
||||
// n = day number within year
|
||||
n -= y1 * DaysPerYear;
|
||||
// If day-of-year was requested, return it
|
||||
if (part == DatePartDayOfYear) return n + 1;
|
||||
// Leap year calculation looks different from IsLeapYear since y1, y4,
|
||||
// and y100 are relative to year 1, not year 0
|
||||
bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3);
|
||||
int32[] days = leapYear ? DaysToMonth366 : DaysToMonth365;
|
||||
// All months have less than 32 days, so n >> 5 is a good conservative
|
||||
// estimate for the month
|
||||
int32 m = n >> 5 + 1;
|
||||
// m = 1-based month number
|
||||
while (n >= days[m]) m++;
|
||||
// If month was requested, return it
|
||||
if (part == DatePartMonth) return m;
|
||||
// Return 1-based day-of-month
|
||||
return n - days[m - 1] + 1;
|
||||
}
|
||||
|
||||
// Returns the day-of-month part of this DateTime. The returned
|
||||
// value is an integer between 1 and 31.
|
||||
//
|
||||
public int32 Day
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1);
|
||||
//Contract.Ensures(Contract.Result<int>() <= 31);
|
||||
return GetDatePart(DatePartDay);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the day-of-week part of this DateTime. The returned value
|
||||
// is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
|
||||
// Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
|
||||
// Thursday, 5 indicates Friday, and 6 indicates Saturday.
|
||||
//
|
||||
public DayOfWeek DayOfWeek
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<DayOfWeek>() >= DayOfWeek.Sunday);
|
||||
//Contract.Ensures(Contract.Result<DayOfWeek>() <= DayOfWeek.Saturday);
|
||||
return (DayOfWeek)((InternalTicks / TicksPerDay + 1) % 7);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the day-of-year part of this DateTime. The returned value
|
||||
// is an integer between 1 and 366.
|
||||
//
|
||||
public int32 DayOfYear
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1);
|
||||
//Contract.Ensures(Contract.Result<int>() <= 366); // leap year
|
||||
return GetDatePart(DatePartDayOfYear);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the hash code for this DateTime.
|
||||
//
|
||||
public int GetHashCode()
|
||||
{
|
||||
return (int)InternalTicks;
|
||||
}
|
||||
|
||||
// Returns the hour part of this DateTime. The returned value is an
|
||||
// integer between 0 and 23.
|
||||
//
|
||||
public int Hour
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 24);
|
||||
return (int32)((InternalTicks / TicksPerHour) % 24);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsAmbiguousDaylightSavingTime()
|
||||
{
|
||||
return (InternalKind == KindLocalAmbiguousDst);
|
||||
}
|
||||
|
||||
|
||||
public DateTimeKind Kind
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (InternalKind) {
|
||||
case KindUnspecified:
|
||||
return DateTimeKind.Unspecified;
|
||||
case KindUtc:
|
||||
return DateTimeKind.Utc;
|
||||
default:
|
||||
return DateTimeKind.Local;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the millisecond part of this DateTime. The returned value
|
||||
// is an integer between 0 and 999.
|
||||
//
|
||||
public int Millisecond
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 1000);
|
||||
return (int)((InternalTicks / TicksPerMillisecond) % 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the minute part of this DateTime. The returned value is
|
||||
// an integer between 0 and 59.
|
||||
//
|
||||
public int Minute
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 60);
|
||||
return (int)((InternalTicks / TicksPerMinute) % 60);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the second part of this DateTime. The returned value is
|
||||
/// an integer between 0 and 59.
|
||||
//
|
||||
public int Second
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 60);
|
||||
return (int)((InternalTicks / TicksPerSecond) % 60);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the month part of this DateTime. The returned value is an
|
||||
// integer between 1 and 12.
|
||||
//
|
||||
public int Month
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1);
|
||||
return GetDatePart(DatePartMonth);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a DateTime representing the current date and time. The
|
||||
// resolution of the returned value depends on the system timer. For
|
||||
// Windows NT 3.5 and later the timer resolution is approximately 10ms,
|
||||
// for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98
|
||||
// it is approximately 55ms.
|
||||
//
|
||||
public static DateTime Now
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Local);
|
||||
|
||||
DateTime utc = UtcNow;
|
||||
bool isAmbiguousLocalDst = false;
|
||||
int64 offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks;
|
||||
int64 tick = utc.Ticks + offset;
|
||||
if (tick > DateTime.MaxTicks)
|
||||
{
|
||||
return DateTime(DateTime.MaxTicks, DateTimeKind.Local);
|
||||
}
|
||||
if (tick < DateTime.MinTicks)
|
||||
{
|
||||
return DateTime(DateTime.MinTicks, DateTimeKind.Local);
|
||||
}
|
||||
return DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime UtcNow
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Utc);
|
||||
// following code is tuned for speed. Don't change it without running benchmark.
|
||||
int64 ticks = 0;
|
||||
ticks = (int64)Platform.BfpSystem_GetTimeStamp();
|
||||
return DateTime(((uint64)(ticks + FileTimeOffset)) | KindUtc);
|
||||
}
|
||||
}
|
||||
|
||||
/*static int64 GetSystemTimeAsFileTime()
|
||||
{
|
||||
ThrowUnimplemented();
|
||||
}*/
|
||||
|
||||
public int64 Ticks
|
||||
{
|
||||
get
|
||||
{
|
||||
return InternalTicks;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the time-of-day part of this DateTime. The returned value
|
||||
// is a TimeSpan that indicates the time elapsed since midnight.
|
||||
//
|
||||
public TimeSpan TimeOfDay
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan(InternalTicks % TicksPerDay);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a DateTime representing the current date. The date part
|
||||
// of the returned value is the current date, and the time-of-day part of
|
||||
// the returned value is zero (midnight).
|
||||
//
|
||||
public static DateTime Today
|
||||
{
|
||||
get
|
||||
{
|
||||
return DateTime.Now.Date;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the year part of this DateTime. The returned value is an
|
||||
// integer between 1 and 9999.
|
||||
//
|
||||
public int32 Year
|
||||
{
|
||||
get
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1 && Contract.Result<int>() <= 9999);
|
||||
return GetDatePart(DatePartYear);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether a given year is a leap year. This method returns true if
|
||||
// year is a leap year, or false if not.
|
||||
//
|
||||
public static bool IsLeapYear(int32 year)
|
||||
{
|
||||
if (year < 1 || year > 9999)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
|
||||
public static DateTime SpecifyKind(DateTime value, DateTimeKind kind)
|
||||
{
|
||||
return DateTime(value.InternalTicks, kind);
|
||||
}
|
||||
|
||||
// Creates a DateTime from a Windows filetime. A Windows filetime is
|
||||
// a long representing the date and time as the number of
|
||||
// 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am.
|
||||
//
|
||||
public static DateTime FromFileTime(int64 fileTime)
|
||||
{
|
||||
return FromFileTimeUtc(fileTime).ToLocalTime();
|
||||
}
|
||||
|
||||
public static DateTime FromFileTimeUtc(int64 fileTime)
|
||||
{
|
||||
int64 universalTicks = fileTime + FileTimeOffset;
|
||||
return DateTime(universalTicks, .Utc);
|
||||
}
|
||||
|
||||
public int64 ToFileTime()
|
||||
{
|
||||
return ToUniversalTime().ToFileTimeUtc();
|
||||
}
|
||||
|
||||
public int64 ToFileTimeUtc()
|
||||
{
|
||||
// Treats the input as universal if it is not specified
|
||||
int64 ticks = ((InternalKind & LocalMask) != 0UL) ? ToUniversalTime().InternalTicks : this.InternalTicks;
|
||||
ticks -= FileTimeOffset;
|
||||
if (ticks < 0)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
return ticks;
|
||||
}
|
||||
|
||||
public TimeSpan Subtract(DateTime value)
|
||||
{
|
||||
return TimeSpan(InternalTicks - value.InternalTicks);
|
||||
}
|
||||
|
||||
public static Result<DateTime> FromBinaryRaw(int64 dateData)
|
||||
{
|
||||
int64 ticks = dateData & (int64)TicksMask;
|
||||
if ((ticks < MinTicks) || (ticks > MaxTicks))
|
||||
return .Err;
|
||||
return DateTime((uint64)dateData);
|
||||
}
|
||||
|
||||
public int64 ToBinaryRaw()
|
||||
{
|
||||
return (int64)dateData;
|
||||
}
|
||||
|
||||
public Result<DateTime> AddTicks(int64 value)
|
||||
{
|
||||
int64 ticks = InternalTicks;
|
||||
if (value > MaxTicks - ticks)
|
||||
{
|
||||
//throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_DateArithmetic"));
|
||||
return .Err;
|
||||
}
|
||||
if (value < MinTicks - ticks)
|
||||
{
|
||||
//throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_DateArithmetic"));
|
||||
return .Err;
|
||||
}
|
||||
return DateTime((uint64)(ticks + value) | InternalKind);
|
||||
}
|
||||
|
||||
private Result<DateTime> Add(double value, int scale)
|
||||
{
|
||||
int64 millis = (int64)(value * scale + (value >= 0 ? 0.5 : -0.5));
|
||||
if (millis <= -MaxMillis || millis >= MaxMillis)
|
||||
return .Err;
|
||||
//throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue"));
|
||||
return AddTicks(millis * TicksPerMillisecond);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding the given number of
|
||||
// years to this DateTime. The result is computed by incrementing
|
||||
// (or decrementing) the year part of this DateTime by value
|
||||
// years. If the month and day of this DateTime is 2/29, and if the
|
||||
// resulting year is not a leap year, the month and day of the resulting
|
||||
// DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
|
||||
// parts of the result are the same as those of this DateTime.
|
||||
//
|
||||
public Result<DateTime> AddYears(int value)
|
||||
{
|
||||
if (value < -10000 || value > 10000) return .Err;
|
||||
return AddMonths(value * 12);
|
||||
}
|
||||
|
||||
/// Returns the DateTime resulting from adding a fractional number of
|
||||
/// days to this DateTime. The result is computed by rounding the
|
||||
/// fractional number of days given by value to the nearest
|
||||
/// millisecond, and adding that interval to this DateTime. The
|
||||
/// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTime AddDays(double value)
|
||||
{
|
||||
return Add(value, MillisPerDay);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding a fractional number of
|
||||
// hours to this DateTime. The result is computed by rounding the
|
||||
// fractional number of hours given by value to the nearest
|
||||
// millisecond, and adding that interval to this DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTime AddHours(double value)
|
||||
{
|
||||
return Add(value, MillisPerHour);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from the given number of
|
||||
// milliseconds to this DateTime. The result is computed by rounding
|
||||
// the number of milliseconds given by value to the nearest integer,
|
||||
// and adding that interval to this DateTime. The value
|
||||
// argument is permitted to be negative.
|
||||
//
|
||||
public DateTime AddMilliseconds(double value)
|
||||
{
|
||||
return Add(value, 1);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding a fractional number of
|
||||
// minutes to this DateTime. The result is computed by rounding the
|
||||
// fractional number of minutes given by value to the nearest
|
||||
// millisecond, and adding that interval to this DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTime AddMinutes(double value)
|
||||
{
|
||||
return Add(value, MillisPerMinute);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding the given number of
|
||||
// months to this DateTime. The result is computed by incrementing
|
||||
// (or decrementing) the year and month parts of this DateTime by
|
||||
// months months, and, if required, adjusting the day part of the
|
||||
// resulting date downwards to the last day of the resulting month in the
|
||||
// resulting year. The time-of-day part of the result is the same as the
|
||||
// time-of-day part of this DateTime.
|
||||
//
|
||||
// In more precise terms, considering this DateTime to be of the
|
||||
// form y / m / d + t, where y is the
|
||||
// year, m is the month, d is the day, and t is the
|
||||
// time-of-day, the result is y1 / m1 / d1 + t,
|
||||
// where y1 and m1 are computed by adding months months
|
||||
// to y and m, and d1 is the largest value less than
|
||||
// or equal to d that denotes a valid day in month m1 of year
|
||||
// y1.
|
||||
//
|
||||
public Result<DateTime> AddMonths(int months)
|
||||
{
|
||||
if (months < -120000 || months > 120000)
|
||||
return .Err;
|
||||
//throw new ArgumentOutOfRangeException("months", Environment.GetResourceString("ArgumentOutOfRange_DateTimeBadMonths"));
|
||||
|
||||
int y = GetDatePart(DatePartYear);
|
||||
int m = GetDatePart(DatePartMonth);
|
||||
int d = GetDatePart(DatePartDay);
|
||||
int i = m - 1 + months;
|
||||
if (i >= 0)
|
||||
{
|
||||
m = i % 12 + 1;
|
||||
y = y + i / 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = 12 + (i + 1) % 12;
|
||||
y = y + (i - 11) / 12;
|
||||
}
|
||||
if (y < 1 || y > 9999)
|
||||
{
|
||||
//throw new ArgumentOutOfRangeException("months", Environment.GetResourceString("ArgumentOutOfRange_DateArithmetic"));
|
||||
return .Err;
|
||||
}
|
||||
int days = DaysInMonth(y, m);
|
||||
if (d > days) d = days;
|
||||
return DateTime((uint64)(Try!(DateToTicks(y, m, d)) + InternalTicks % TicksPerDay) | InternalKind);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding a fractional number of
|
||||
// seconds to this DateTime. The result is computed by rounding the
|
||||
// fractional number of seconds given by value to the nearest
|
||||
// millisecond, and adding that interval to this DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTime AddSeconds(double value)
|
||||
{
|
||||
return Add(value, MillisPerSecond);
|
||||
}
|
||||
|
||||
public static int operator <=>(DateTime lhs, DateTime rhs)
|
||||
{
|
||||
return lhs.InternalTicks <=> rhs.InternalTicks;
|
||||
}
|
||||
|
||||
public static Result<DateTime> operator +(DateTime d, TimeSpan t)
|
||||
{
|
||||
int64 ticks = d.InternalTicks;
|
||||
int64 valueTicks = (int64)t;
|
||||
if (valueTicks > MaxTicks - ticks || valueTicks < MinTicks - ticks)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
return DateTime((uint64)(ticks + valueTicks) | d.InternalKind);
|
||||
}
|
||||
|
||||
public Result<DateTime> Subtract(TimeSpan t)
|
||||
{
|
||||
int64 ticks = InternalTicks;
|
||||
int64 valueTicks = (int64)t;
|
||||
if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
return DateTime((uint64)(ticks + valueTicks) | InternalKind);
|
||||
}
|
||||
|
||||
public static DateTime operator -(DateTime d, TimeSpan t)
|
||||
{
|
||||
int64 ticks = d.InternalTicks;
|
||||
int64 valueTicks = (int64)t;
|
||||
Runtime.Assert((ticks - MinTicks >= valueTicks && ticks - MaxTicks <= valueTicks));
|
||||
return DateTime((uint64)(ticks + valueTicks) | d.InternalKind);
|
||||
}
|
||||
|
||||
public static TimeSpan operator -(DateTime lhs, DateTime rhs)
|
||||
{
|
||||
return TimeSpan(lhs.InternalTicks - rhs.InternalTicks);
|
||||
}
|
||||
|
||||
public DateTime ToLocalTime()
|
||||
{
|
||||
return ToLocalTime(false);
|
||||
}
|
||||
|
||||
internal DateTime ToLocalTime(bool throwOnOverflow)
|
||||
{
|
||||
if (Kind == DateTimeKind.Local)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
#unwarn
|
||||
bool isDaylightSavings = false;
|
||||
bool isAmbiguousLocalDst = false;
|
||||
//int64 offset = 0;
|
||||
//ThrowUnimplemented();
|
||||
int64 offset = TimeZoneInfo.GetUtcOffsetFromUtc(this, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
|
||||
#unwarn
|
||||
int64 tick = Ticks + offset;
|
||||
if (tick > DateTime.MaxTicks)
|
||||
{
|
||||
if (throwOnOverflow)
|
||||
Runtime.FatalError();
|
||||
else
|
||||
return DateTime(DateTime.MaxTicks, DateTimeKind.Local);
|
||||
}
|
||||
if (tick < DateTime.MinTicks)
|
||||
{
|
||||
if (throwOnOverflow)
|
||||
Runtime.FatalError();
|
||||
else
|
||||
return DateTime(DateTime.MinTicks, DateTimeKind.Local);
|
||||
}
|
||||
return DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
|
||||
}
|
||||
|
||||
|
||||
public void ToLongDateString(String outString)
|
||||
{
|
||||
DateTimeFormat.Format(this, "D", DateTimeFormatInfo.CurrentInfo, outString);
|
||||
}
|
||||
|
||||
public void ToLongTimeString(String outString)
|
||||
{
|
||||
DateTimeFormat.Format(this, "T", DateTimeFormatInfo.CurrentInfo, outString);
|
||||
}
|
||||
|
||||
public void ToShortDateString(String outString)
|
||||
{
|
||||
DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo, outString);
|
||||
}
|
||||
|
||||
public void ToShortTimeString(String outString)
|
||||
{
|
||||
DateTimeFormat.Format(this, "t", DateTimeFormatInfo.CurrentInfo, outString);
|
||||
}
|
||||
|
||||
public void ToString(String outString)
|
||||
{
|
||||
DateTimeFormat.Format(this, .(), DateTimeFormatInfo.CurrentInfo, outString);
|
||||
}
|
||||
|
||||
public void ToString(String outString, String format)
|
||||
{
|
||||
DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo, outString);
|
||||
}
|
||||
|
||||
public void ToString(String outString, IFormatProvider provider)
|
||||
{
|
||||
DateTimeFormat.Format(this, .(), DateTimeFormatInfo.GetInstance(provider), outString);
|
||||
}
|
||||
|
||||
public void ToString(String outString, String format, IFormatProvider provider)
|
||||
{
|
||||
DateTimeFormat.Format(this, format, DateTimeFormatInfo.GetInstance(provider), outString);
|
||||
}
|
||||
|
||||
public DateTime ToUniversalTime()
|
||||
{
|
||||
return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
|
||||
}
|
||||
|
||||
|
||||
/*public static Boolean TryParse(String s, out DateTime result) {
|
||||
return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result);
|
||||
}
|
||||
|
||||
public static Boolean TryParse(String s, IFormatProvider provider, DateTimeStyles styles, out DateTime result) {
|
||||
DateTimeFormatInfo.ValidateStyles(styles, "styles");
|
||||
return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result);
|
||||
}
|
||||
|
||||
public static Boolean TryParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style, out DateTime result) {
|
||||
DateTimeFormatInfo.ValidateStyles(style, "style");
|
||||
return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result);
|
||||
}
|
||||
|
||||
public static Boolean TryParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result) {
|
||||
DateTimeFormatInfo.ValidateStyles(style, "style");
|
||||
return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result);
|
||||
}*/
|
||||
|
||||
internal static Result<DateTime> TryCreate(int year, int month, int day, int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
if (year < 1 || year > 9999 || month < 1 || month > 12)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
int32[] days = Try!(IsLeapYear(year)) ? DaysToMonth366 : DaysToMonth365;
|
||||
if (day < 1 || day > days[month] - days[month - 1])
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
if (millisecond < 0 || millisecond >= MillisPerSecond)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
int64 ticks = Try!(DateToTicks(year, month, day)) + Try!(TimeToTicks(hour, minute, second));
|
||||
|
||||
ticks += millisecond * TicksPerMillisecond;
|
||||
if (ticks < MinTicks || ticks > MaxTicks)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
return DateTime(ticks, DateTimeKind.Unspecified);
|
||||
}
|
||||
}
|
||||
}
|
9
BeefLibs/corlib/src/System/DateTimeKind.bf
Normal file
9
BeefLibs/corlib/src/System/DateTimeKind.bf
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace System
|
||||
{
|
||||
enum DateTimeKind
|
||||
{
|
||||
Unspecified = 0,
|
||||
Utc = 1,
|
||||
Local = 2,
|
||||
}
|
||||
}
|
850
BeefLibs/corlib/src/System/DateTimeOffset.bf
Normal file
850
BeefLibs/corlib/src/System/DateTimeOffset.bf
Normal file
|
@ -0,0 +1,850 @@
|
|||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
namespace System {
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
// DateTimeOffset is a value type that consists of a DateTime and a time zone offset,
|
||||
// ie. how far away the time is from GMT. The DateTime is stored whole, and the offset
|
||||
// is stored as an Int16 internally to save space, but presented as a TimeSpan.
|
||||
//
|
||||
// The range is constrained so that both the represented clock time and the represented
|
||||
// UTC time fit within the boundaries of MaxValue. This gives it the same range as DateTime
|
||||
// for actual UTC times, and a slightly constrained range on one end when an offset is
|
||||
// present.
|
||||
//
|
||||
// This class should be substitutable for date time in most cases; so most operations
|
||||
// effectively work on the clock time. However, the underlying UTC time is what counts
|
||||
// for the purposes of identity, sorting and subtracting two instances.
|
||||
//
|
||||
//
|
||||
// There are theoretically two date times stored, the UTC and the relative local representation
|
||||
// or the 'clock' time. It actually does not matter which is stored in m_dateTime, so it is desirable
|
||||
// for most methods to go through the helpers UtcDateTime and ClockDateTime both to abstract this
|
||||
// out and for internal readability.
|
||||
|
||||
public struct DateTimeOffset : IHashable // : IFormattable,IComparable<DateTimeOffset>, IEquatable<DateTimeOffset>
|
||||
{
|
||||
// Constants
|
||||
internal const int64 MaxOffset = TimeSpan.TicksPerHour * 14;
|
||||
internal const int64 MinOffset = -MaxOffset;
|
||||
|
||||
private const int64 UnixEpochTicks = TimeSpan.TicksPerDay * DateTime.DaysTo1970; // 621,355,968,000,000,000
|
||||
private const int64 UnixEpochSeconds = UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800
|
||||
private const int64 UnixEpochMilliseconds = UnixEpochTicks / TimeSpan.TicksPerMillisecond; // 62,135,596,800,000
|
||||
|
||||
// Static Fields
|
||||
public static readonly DateTimeOffset MinValue = DateTimeOffset(DateTime.MinTicks, TimeSpan.Zero);
|
||||
public static readonly DateTimeOffset MaxValue = DateTimeOffset(DateTime.MaxTicks, TimeSpan.Zero);
|
||||
|
||||
// Instance Fields
|
||||
private DateTime m_dateTime;
|
||||
private int16 m_offsetMinutes;
|
||||
|
||||
// Constructors
|
||||
|
||||
// Constructs a DateTimeOffset from a tick count and offset
|
||||
public this(int64 ticks, TimeSpan offset)
|
||||
{
|
||||
m_offsetMinutes = ValidateOffset(offset);
|
||||
// Let the DateTime constructor do the range checks
|
||||
DateTime dateTime = .(ticks);
|
||||
m_dateTime = ValidateDate(dateTime, offset);
|
||||
}
|
||||
|
||||
// Constructs a DateTimeOffset from a DateTime. For Local and Unspecified kinds,
|
||||
// extracts the local offset. For UTC, creates a UTC instance with a zero offset.
|
||||
public this(DateTime dateTime)
|
||||
{
|
||||
TimeSpan offset;
|
||||
if (dateTime.Kind != DateTimeKind.Utc)
|
||||
{
|
||||
// Local and Unspecified are both treated as Local
|
||||
offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime);
|
||||
}
|
||||
else {
|
||||
offset = TimeSpan(0);
|
||||
}
|
||||
m_offsetMinutes = ValidateOffset(offset);
|
||||
m_dateTime = ValidateDate(dateTime, offset);
|
||||
}
|
||||
|
||||
// Constructs a DateTimeOffset from a DateTime. And an offset. Always makes the clock time
|
||||
// consistent with the DateTime. For Utc ensures the offset is zero. For local, ensures that
|
||||
// the offset corresponds to the local.
|
||||
public this(DateTime dateTime, TimeSpan offset) {
|
||||
if (dateTime.Kind == DateTimeKind.Local) {
|
||||
if (offset != TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime)) {
|
||||
//throw new ArgumentException(Environment.GetResourceString("Argument_OffsetLocalMismatch"), "offset");
|
||||
Runtime.FatalError();
|
||||
}
|
||||
}
|
||||
else if (dateTime.Kind == DateTimeKind.Utc) {
|
||||
if (offset != TimeSpan.Zero) {
|
||||
//throw new ArgumentException(Environment.GetResourceString("Argument_OffsetUtcMismatch"), "offset");
|
||||
Runtime.FatalError();
|
||||
}
|
||||
}
|
||||
m_offsetMinutes = ValidateOffset(offset);
|
||||
m_dateTime = ValidateDate(dateTime, offset);
|
||||
}
|
||||
|
||||
// Constructs a DateTimeOffset from a given year, month, day, hour,
|
||||
// minute, second and offset.
|
||||
public this(int year, int month, int day, int hour, int minute, int second, TimeSpan offset) {
|
||||
m_offsetMinutes = ValidateOffset(offset);
|
||||
m_dateTime = ValidateDate(.(year, month, day, hour, minute, second), offset);
|
||||
}
|
||||
|
||||
// Constructs a DateTimeOffset from a given year, month, day, hour,
|
||||
// minute, second, millsecond and offset
|
||||
public this(int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset) {
|
||||
m_offsetMinutes = ValidateOffset(offset);
|
||||
m_dateTime = ValidateDate(.(year, month, day, hour, minute, second, millisecond), offset);
|
||||
}
|
||||
|
||||
|
||||
// Constructs a DateTimeOffset from a given year, month, day, hour,
|
||||
// minute, second, millsecond, Calendar and offset.
|
||||
/*public this(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset) {
|
||||
m_offsetMinutes = ValidateOffset(offset);
|
||||
m_dateTime = ValidateDate(.(year, month, day, hour, minute, second, millisecond, calendar), offset);
|
||||
}*/
|
||||
|
||||
// Returns a DateTimeOffset representing the current date and time. The
|
||||
// resolution of the returned value depends on the system timer. For
|
||||
// Windows NT 3.5 and later the timer resolution is approximately 10ms,
|
||||
// for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98
|
||||
// it is approximately 55ms.
|
||||
//
|
||||
public static DateTimeOffset Now {
|
||||
get {
|
||||
return DateTimeOffset(DateTime.Now);
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTimeOffset UtcNow {
|
||||
get {
|
||||
return DateTimeOffset(DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime DateTime {
|
||||
get {
|
||||
return ClockDateTime;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime UtcDateTime {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Utc);
|
||||
return System.DateTime.SpecifyKind(m_dateTime, DateTimeKind.Utc);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime LocalDateTime {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Local);
|
||||
return UtcDateTime.ToLocalTime();
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust to a given offset with the same UTC time. Can throw ArgumentException
|
||||
//
|
||||
public DateTimeOffset ToOffset(TimeSpan offset) {
|
||||
return .((m_dateTime + offset).Get().Ticks, offset);
|
||||
}
|
||||
|
||||
|
||||
// Instance Properties
|
||||
|
||||
// The clock or visible time represented. This is just a wrapper around the internal date because this is
|
||||
// the chosen storage mechanism. Going through this helper is good for readability and maintainability.
|
||||
// This should be used for display but not identity.
|
||||
private DateTime ClockDateTime {
|
||||
get {
|
||||
return .((m_dateTime + Offset).Get().Ticks, DateTimeKind.Unspecified);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the date part of this DateTimeOffset. The resulting value
|
||||
// corresponds to this DateTimeOffset with the time-of-day part set to
|
||||
// zero (midnight).
|
||||
//
|
||||
public DateTime Date {
|
||||
get {
|
||||
return ClockDateTime.Date;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the day-of-month part of this DateTimeOffset. The returned
|
||||
// value is an integer between 1 and 31.
|
||||
//
|
||||
public int Day {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1);
|
||||
//Contract.Ensures(Contract.Result<int>() <= 31);
|
||||
return ClockDateTime.Day;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the day-of-week part of this DateTimeOffset. The returned value
|
||||
// is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
|
||||
// Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
|
||||
// Thursday, 5 indicates Friday, and 6 indicates Saturday.
|
||||
//
|
||||
public DayOfWeek DayOfWeek {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<DayOfWeek>() >= DayOfWeek.Sunday);
|
||||
//Contract.Ensures(Contract.Result<DayOfWeek>() <= DayOfWeek.Saturday);
|
||||
return ClockDateTime.DayOfWeek;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the day-of-year part of this DateTimeOffset. The returned value
|
||||
// is an integer between 1 and 366.
|
||||
//
|
||||
public int DayOfYear {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1);
|
||||
//Contract.Ensures(Contract.Result<int>() <= 366); // leap year
|
||||
return ClockDateTime.DayOfYear;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the hour part of this DateTimeOffset. The returned value is an
|
||||
// integer between 0 and 23.
|
||||
//
|
||||
public int Hour {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 24);
|
||||
return ClockDateTime.Hour;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the millisecond part of this DateTimeOffset. The returned value
|
||||
// is an integer between 0 and 999.
|
||||
//
|
||||
public int Millisecond {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 1000);
|
||||
return ClockDateTime.Millisecond;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the minute part of this DateTimeOffset. The returned value is
|
||||
// an integer between 0 and 59.
|
||||
//
|
||||
public int Minute {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 60);
|
||||
return ClockDateTime.Minute;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the month part of this DateTimeOffset. The returned value is an
|
||||
// integer between 1 and 12.
|
||||
//
|
||||
public int Month {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1);
|
||||
return ClockDateTime.Month;
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan Offset {
|
||||
get {
|
||||
return TimeSpan(0, m_offsetMinutes, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the second part of this DateTimeOffset. The returned value is
|
||||
// an integer between 0 and 59.
|
||||
//
|
||||
public int Second {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
//Contract.Ensures(Contract.Result<int>() < 60);
|
||||
return ClockDateTime.Second;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the tick count for this DateTimeOffset. The returned value is
|
||||
// the number of 100-nanosecond intervals that have elapsed since 1/1/0001
|
||||
// 12:00am.
|
||||
//
|
||||
public int64 Ticks {
|
||||
get {
|
||||
return ClockDateTime.Ticks;
|
||||
}
|
||||
}
|
||||
|
||||
public int64 UtcTicks {
|
||||
get {
|
||||
return UtcDateTime.Ticks;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the time-of-day part of this DateTimeOffset. The returned value
|
||||
// is a TimeSpan that indicates the time elapsed since midnight.
|
||||
//
|
||||
public TimeSpan TimeOfDay {
|
||||
get {
|
||||
return ClockDateTime.TimeOfDay;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the year part of this DateTimeOffset. The returned value is an
|
||||
// integer between 1 and 9999.
|
||||
//
|
||||
public int Year {
|
||||
get {
|
||||
//Contract.Ensures(Contract.Result<int>() >= 1 && Contract.Result<int>() <= 9999);
|
||||
return ClockDateTime.Year;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from adding the given
|
||||
// TimeSpan to this DateTimeOffset.
|
||||
//
|
||||
public DateTimeOffset Add(TimeSpan timeSpan) {
|
||||
return DateTimeOffset(ClockDateTime + timeSpan, Offset);
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from adding a fractional number of
|
||||
// days to this DateTimeOffset. The result is computed by rounding the
|
||||
// fractional number of days given by value to the nearest
|
||||
// millisecond, and adding that interval to this DateTimeOffset. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTimeOffset AddDays(double days) {
|
||||
return DateTimeOffset(ClockDateTime.AddDays(days), Offset);
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from adding a fractional number of
|
||||
// hours to this DateTimeOffset. The result is computed by rounding the
|
||||
// fractional number of hours given by value to the nearest
|
||||
// millisecond, and adding that interval to this DateTimeOffset. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTimeOffset AddHours(double hours) {
|
||||
return DateTimeOffset(ClockDateTime.AddHours(hours), Offset);
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from the given number of
|
||||
// milliseconds to this DateTimeOffset. The result is computed by rounding
|
||||
// the number of milliseconds given by value to the nearest integer,
|
||||
// and adding that interval to this DateTimeOffset. The value
|
||||
// argument is permitted to be negative.
|
||||
//
|
||||
public DateTimeOffset AddMilliseconds(double milliseconds) {
|
||||
return DateTimeOffset(ClockDateTime.AddMilliseconds(milliseconds), Offset);
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from adding a fractional number of
|
||||
// minutes to this DateTimeOffset. The result is computed by rounding the
|
||||
// fractional number of minutes given by value to the nearest
|
||||
// millisecond, and adding that interval to this DateTimeOffset. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTimeOffset AddMinutes(double minutes) {
|
||||
return DateTimeOffset(ClockDateTime.AddMinutes(minutes), Offset);
|
||||
}
|
||||
|
||||
public DateTimeOffset AddMonths(int months) {
|
||||
return DateTimeOffset(ClockDateTime.AddMonths(months), Offset);
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from adding a fractional number of
|
||||
// seconds to this DateTimeOffset. The result is computed by rounding the
|
||||
// fractional number of seconds given by value to the nearest
|
||||
// millisecond, and adding that interval to this DateTimeOffset. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
public DateTimeOffset AddSeconds(double seconds) {
|
||||
return DateTimeOffset(ClockDateTime.AddSeconds(seconds), Offset);
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from adding the given number of
|
||||
// 100-nanosecond ticks to this DateTimeOffset. The value argument
|
||||
// is permitted to be negative.
|
||||
//
|
||||
public DateTimeOffset AddTicks(int64 ticks) {
|
||||
return DateTimeOffset(ClockDateTime.AddTicks(ticks), Offset);
|
||||
}
|
||||
|
||||
// Returns the DateTimeOffset resulting from adding the given number of
|
||||
// years to this DateTimeOffset. The result is computed by incrementing
|
||||
// (or decrementing) the year part of this DateTimeOffset by value
|
||||
// years. If the month and day of this DateTimeOffset is 2/29, and if the
|
||||
// resulting year is not a leap year, the month and day of the resulting
|
||||
// DateTimeOffset becomes 2/28. Otherwise, the month, day, and time-of-day
|
||||
// parts of the result are the same as those of this DateTimeOffset.
|
||||
//
|
||||
public DateTimeOffset AddYears(int years) {
|
||||
return DateTimeOffset(ClockDateTime.AddYears(years), Offset);
|
||||
}
|
||||
|
||||
// Compares two DateTimeOffset values, returning an integer that indicates
|
||||
// their relationship.
|
||||
//
|
||||
/*public static int Compare(DateTimeOffset first, DateTimeOffset second) {
|
||||
return DateTime.Compare(first.UtcDateTime, second.UtcDateTime);
|
||||
}*/
|
||||
|
||||
// Compares this DateTimeOffset to a given object. This method provides an
|
||||
// implementation of the IComparable interface. The object
|
||||
// argument must be another DateTimeOffset, or otherwise an exception
|
||||
// occurs. Null is considered less than any instance.
|
||||
//
|
||||
/*int IComparable.CompareTo(Object obj) {
|
||||
if (obj == null) return 1;
|
||||
if (!(obj is DateTimeOffset)) {
|
||||
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDateTimeOffset"));
|
||||
}
|
||||
|
||||
DateTime objUtc = ((DateTimeOffset)obj).UtcDateTime;
|
||||
DateTime utc = UtcDateTime;
|
||||
if (utc > objUtc) return 1;
|
||||
if (utc < objUtc) return -1;
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
public int CompareTo(DateTimeOffset other) {
|
||||
DateTime otherUtc = other.UtcDateTime;
|
||||
DateTime utc = UtcDateTime;
|
||||
if (utc > otherUtc) return 1;
|
||||
if (utc < otherUtc) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Checks if this DateTimeOffset is equal to a given object. Returns
|
||||
// true if the given object is a boxed DateTimeOffset and its value
|
||||
// is equal to the value of this DateTimeOffset. Returns false
|
||||
// otherwise.
|
||||
//
|
||||
/*public override bool Equals(Object obj) {
|
||||
if (obj is DateTimeOffset) {
|
||||
return UtcDateTime.Equals(((DateTimeOffset)obj).UtcDateTime);
|
||||
}
|
||||
return false;
|
||||
}*/
|
||||
|
||||
public bool Equals(DateTimeOffset other) {
|
||||
return UtcDateTime == other.UtcDateTime;
|
||||
}
|
||||
|
||||
public bool EqualsExact(DateTimeOffset other)
|
||||
{
|
||||
//
|
||||
// returns true when the ClockDateTime, Kind, and Offset match
|
||||
//
|
||||
// currently the Kind should always be Unspecified, but there is always the possibility that a future version
|
||||
// of DateTimeOffset overloads the Kind field
|
||||
//
|
||||
return (ClockDateTime == other.ClockDateTime && Offset == other.Offset && ClockDateTime.Kind == other.ClockDateTime.Kind);
|
||||
}
|
||||
|
||||
// Compares two DateTimeOffset values for equality. Returns true if
|
||||
// the two DateTimeOffset values are equal, or false if they are
|
||||
// not equal.
|
||||
//
|
||||
public static bool Equals(DateTimeOffset first, DateTimeOffset second) {
|
||||
return DateTime.Equals(first.UtcDateTime, second.UtcDateTime);
|
||||
}
|
||||
|
||||
// Creates a DateTimeOffset from a Windows filetime. A Windows filetime is
|
||||
// a long representing the date and time as the number of
|
||||
// 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am.
|
||||
//
|
||||
public static DateTimeOffset FromFileTime(int64 fileTime) {
|
||||
return DateTimeOffset(DateTime.FromFileTime(fileTime));
|
||||
}
|
||||
|
||||
public static Result<DateTimeOffset> FromUnixTimeSeconds(int64 seconds) {
|
||||
const int64 MinSeconds = DateTime.MinTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds;
|
||||
const int64 MaxSeconds = DateTime.MaxTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds;
|
||||
|
||||
if (seconds < MinSeconds || seconds > MaxSeconds) {
|
||||
return .Err;
|
||||
/*throw new ArgumentOutOfRangeException("seconds",
|
||||
string.Format(Environment.GetResourceString("ArgumentOutOfRange_Range"), MinSeconds, MaxSeconds));*/
|
||||
}
|
||||
|
||||
int64 ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks;
|
||||
return DateTimeOffset(ticks, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public static Result<DateTimeOffset> FromUnixTimeMilliseconds(int64 milliseconds) {
|
||||
const int64 MinMilliseconds = DateTime.MinTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds;
|
||||
const int64 MaxMilliseconds = DateTime.MaxTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds;
|
||||
|
||||
if (milliseconds < MinMilliseconds || milliseconds > MaxMilliseconds) {
|
||||
return .Err;
|
||||
/*throw new ArgumentOutOfRangeException("milliseconds",
|
||||
string.Format(Environment.GetResourceString("ArgumentOutOfRange_Range"), MinMilliseconds, MaxMilliseconds));*/
|
||||
}
|
||||
|
||||
int64 ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks;
|
||||
return DateTimeOffset(ticks, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
// ----- SECTION: private serialization instance methods ----------------*
|
||||
|
||||
#if FEATURE_SERIALIZATION
|
||||
void IDeserializationCallback.OnDeserialization(Object sender) {
|
||||
try {
|
||||
m_offsetMinutes = ValidateOffset(Offset);
|
||||
m_dateTime = ValidateDate(ClockDateTime, Offset);
|
||||
}
|
||||
catch (ArgumentException e) {
|
||||
throw new SerializationException(Environment.GetResourceString("Serialization_InvalidData"), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[System.Security.SecurityCritical] // auto-generated_required
|
||||
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
|
||||
if (info == null) {
|
||||
throw new ArgumentNullException("info");
|
||||
}
|
||||
|
||||
Contract.EndContractBlock();
|
||||
|
||||
info.AddValue("DateTime", m_dateTime);
|
||||
info.AddValue("OffsetMinutes", m_offsetMinutes);
|
||||
}
|
||||
|
||||
|
||||
DateTimeOffset(SerializationInfo info, StreamingContext context) {
|
||||
if (info == null) {
|
||||
throw new ArgumentNullException("info");
|
||||
}
|
||||
|
||||
m_dateTime = (DateTime)info.GetValue("DateTime", typeof(DateTime));
|
||||
m_offsetMinutes = (Int16)info.GetValue("OffsetMinutes", typeof(Int16));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Returns the hash code for this DateTimeOffset.
|
||||
//
|
||||
public int GetHashCode()
|
||||
{
|
||||
return UtcDateTime.GetHashCode();
|
||||
}
|
||||
|
||||
// Constructs a DateTimeOffset from a string. The string must specify a
|
||||
// date and optionally a time in a culture-specific or universal format.
|
||||
// Leading and trailing whitespace characters are allowed.
|
||||
//
|
||||
/*public static DateTimeOffset Parse(String input) {
|
||||
TimeSpan offset;
|
||||
DateTime dateResult = DateTimeParse.Parse(input,
|
||||
DateTimeFormatInfo.CurrentInfo,
|
||||
DateTimeStyles.None,
|
||||
out offset);
|
||||
return new DateTimeOffset(dateResult.Ticks, offset);
|
||||
}*/
|
||||
|
||||
// Constructs a DateTimeOffset from a string. The string must specify a
|
||||
// date and optionally a time in a culture-specific or universal format.
|
||||
// Leading and trailing whitespace characters are allowed.
|
||||
//
|
||||
/*public static DateTimeOffset Parse(String input, IFormatProvider formatProvider) {
|
||||
return Parse(input, formatProvider, DateTimeStyles.None);
|
||||
}*/
|
||||
|
||||
/*public static DateTimeOffset Parse(String input, IFormatProvider formatProvider, DateTimeStyles styles) {
|
||||
styles = ValidateStyles(styles, "styles");
|
||||
TimeSpan offset;
|
||||
DateTime dateResult = DateTimeParse.Parse(input,
|
||||
DateTimeFormatInfo.GetInstance(formatProvider),
|
||||
styles,
|
||||
out offset);
|
||||
return new DateTimeOffset(dateResult.Ticks, offset);
|
||||
}*/
|
||||
|
||||
// Constructs a DateTimeOffset from a string. The string must specify a
|
||||
// date and optionally a time in a culture-specific or universal format.
|
||||
// Leading and trailing whitespace characters are allowed.
|
||||
//
|
||||
/*public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider) {
|
||||
return ParseExact(input, format, formatProvider, DateTimeStyles.None);
|
||||
}*/
|
||||
|
||||
// Constructs a DateTimeOffset from a string. The string must specify a
|
||||
// date and optionally a time in a culture-specific or universal format.
|
||||
// Leading and trailing whitespace characters are allowed.
|
||||
//
|
||||
/*public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles)
|
||||
{
|
||||
styles = ValidateStyles(styles, "styles");
|
||||
TimeSpan offset;
|
||||
DateTime dateResult = DateTimeParse.ParseExact(input,
|
||||
format,
|
||||
DateTimeFormatInfo.GetInstance(formatProvider),
|
||||
styles,
|
||||
out offset);
|
||||
return new DateTimeOffset(dateResult.Ticks, offset);
|
||||
}
|
||||
|
||||
public static DateTimeOffset ParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles)
|
||||
{
|
||||
styles = ValidateStyles(styles, "styles");
|
||||
TimeSpan offset;
|
||||
DateTime dateResult = DateTimeParse.ParseExactMultiple(input,
|
||||
formats,
|
||||
DateTimeFormatInfo.GetInstance(formatProvider),
|
||||
styles,
|
||||
out offset);
|
||||
return DateTimeOffset(dateResult.Ticks, offset);
|
||||
}*/
|
||||
|
||||
public TimeSpan Subtract(DateTimeOffset value) {
|
||||
return UtcDateTime.Subtract(value.UtcDateTime);
|
||||
}
|
||||
|
||||
public DateTimeOffset Subtract(TimeSpan value) {
|
||||
return DateTimeOffset(ClockDateTime - value, Offset);
|
||||
}
|
||||
|
||||
|
||||
public int64 ToFileTime() {
|
||||
return UtcDateTime.ToFileTime();
|
||||
}
|
||||
|
||||
public int64 ToUnixTimeSeconds() {
|
||||
// Truncate sub-second precision before offsetting by the Unix Epoch to avoid
|
||||
// the last digit being off by one for dates that result in negative Unix times.
|
||||
//
|
||||
// For example, consider the DateTimeOffset 12/31/1969 12:59:59.001 +0
|
||||
// ticks = 621355967990010000
|
||||
// ticksFromEpoch = ticks - UnixEpochTicks = -9990000
|
||||
// secondsFromEpoch = ticksFromEpoch / TimeSpan.TicksPerSecond = 0
|
||||
//
|
||||
// Notice that secondsFromEpoch is rounded *up* by the truncation induced by integer division,
|
||||
// whereas we actually always want to round *down* when converting to Unix time. This happens
|
||||
// automatically for positive Unix time values. Now the example becomes:
|
||||
// seconds = ticks / TimeSpan.TicksPerSecond = 62135596799
|
||||
// secondsFromEpoch = seconds - UnixEpochSeconds = -1
|
||||
//
|
||||
// In other words, we want to consistently round toward the time 1/1/0001 00:00:00,
|
||||
// rather than toward the Unix Epoch (1/1/1970 00:00:00).
|
||||
int64 seconds = UtcDateTime.Ticks / TimeSpan.TicksPerSecond;
|
||||
return seconds - UnixEpochSeconds;
|
||||
}
|
||||
|
||||
public int64 ToUnixTimeMilliseconds() {
|
||||
// Truncate sub-millisecond precision before offsetting by the Unix Epoch to avoid
|
||||
// the last digit being off by one for dates that result in negative Unix times
|
||||
int64 milliseconds = UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond;
|
||||
return milliseconds - UnixEpochMilliseconds;
|
||||
}
|
||||
|
||||
public DateTimeOffset ToLocalTime() {
|
||||
return ToLocalTime(false);
|
||||
}
|
||||
|
||||
internal DateTimeOffset ToLocalTime(bool throwOnOverflow)
|
||||
{
|
||||
return DateTimeOffset(UtcDateTime.ToLocalTime(throwOnOverflow));
|
||||
}
|
||||
|
||||
/*public override String ToString() {
|
||||
//Contract.Ensures(Contract.Result<String>() != null);
|
||||
return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.CurrentInfo, Offset);
|
||||
}
|
||||
|
||||
public String ToString(String format) {
|
||||
//Contract.Ensures(Contract.Result<String>() != null);
|
||||
return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.CurrentInfo, Offset);
|
||||
}
|
||||
|
||||
public String ToString(IFormatProvider formatProvider) {
|
||||
//Contract.Ensures(Contract.Result<String>() != null);
|
||||
return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.GetInstance(formatProvider), Offset);
|
||||
}
|
||||
|
||||
public String ToString(String format, IFormatProvider formatProvider) {
|
||||
//Contract.Ensures(Contract.Result<String>() != null);
|
||||
return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.GetInstance(formatProvider), Offset);
|
||||
}*/
|
||||
|
||||
public DateTimeOffset ToUniversalTime()
|
||||
{
|
||||
return DateTimeOffset(UtcDateTime);
|
||||
}
|
||||
|
||||
/*public static Boolean TryParse(String input, out DateTimeOffset result) {
|
||||
TimeSpan offset;
|
||||
DateTime dateResult;
|
||||
Boolean parsed = DateTimeParse.TryParse(input,
|
||||
DateTimeFormatInfo.CurrentInfo,
|
||||
DateTimeStyles.None,
|
||||
out dateResult,
|
||||
out offset);
|
||||
result = DateTimeOffset(dateResult.Ticks, offset);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
public static Boolean TryParse(String input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result) {
|
||||
styles = ValidateStyles(styles, "styles");
|
||||
TimeSpan offset;
|
||||
DateTime dateResult;
|
||||
Boolean parsed = DateTimeParse.TryParse(input,
|
||||
DateTimeFormatInfo.GetInstance(formatProvider),
|
||||
styles,
|
||||
out dateResult,
|
||||
out offset);
|
||||
result = new DateTimeOffset(dateResult.Ticks, offset);
|
||||
return parsed;
|
||||
}*/
|
||||
|
||||
/*public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles,
|
||||
out DateTimeOffset result) {
|
||||
styles = ValidateStyles(styles, "styles");
|
||||
TimeSpan offset;
|
||||
DateTime dateResult;
|
||||
Boolean parsed = DateTimeParse.TryParseExact(input,
|
||||
format,
|
||||
DateTimeFormatInfo.GetInstance(formatProvider),
|
||||
styles,
|
||||
out dateResult,
|
||||
out offset);
|
||||
result = DateTimeOffset(dateResult.Ticks, offset);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles,
|
||||
out DateTimeOffset result) {
|
||||
styles = ValidateStyles(styles, "styles");
|
||||
TimeSpan offset;
|
||||
DateTime dateResult;
|
||||
Boolean parsed = DateTimeParse.TryParseExactMultiple(input,
|
||||
formats,
|
||||
DateTimeFormatInfo.GetInstance(formatProvider),
|
||||
styles,
|
||||
out dateResult,
|
||||
out offset);
|
||||
result = DateTimeOffset(dateResult.Ticks, offset);
|
||||
return parsed;
|
||||
}*/
|
||||
|
||||
// Ensures the TimeSpan is valid to go in a DateTimeOffset.
|
||||
private static Result<int16> ValidateOffset(TimeSpan offset) {
|
||||
int64 ticks = offset.Ticks;
|
||||
if (ticks % TimeSpan.TicksPerMinute != 0) {
|
||||
//throw new ArgumentException(Environment.GetResourceString("Argument_OffsetPrecision"), "offset");
|
||||
return .Err;
|
||||
}
|
||||
if (ticks < MinOffset || ticks > MaxOffset) {
|
||||
//throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("Argument_OffsetOutOfRange"));
|
||||
return .Err;
|
||||
}
|
||||
return (int16)(offset.Ticks / TimeSpan.TicksPerMinute);
|
||||
}
|
||||
|
||||
// Ensures that the time and offset are in range.
|
||||
private static Result<DateTime> ValidateDate(DateTime dateTime, TimeSpan offset) {
|
||||
// The key validation is that both the UTC and clock times fit. The clock time is validated
|
||||
// by the DateTime constructor.
|
||||
Contract.Assert(offset.Ticks >= MinOffset && offset.Ticks <= MaxOffset, "Offset not validated.");
|
||||
// This operation cannot overflow because offset should have already been validated to be within
|
||||
// 14 hours and the DateTime instance is more than that distance from the boundaries of Int64.
|
||||
int64 utcTicks = dateTime.Ticks - offset.Ticks;
|
||||
if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks)
|
||||
{
|
||||
return .Err;
|
||||
//throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("Argument_UTCOutOfRange"));
|
||||
}
|
||||
// make sure the Kind is set to Unspecified
|
||||
//
|
||||
return DateTime(utcTicks, DateTimeKind.Unspecified);
|
||||
}
|
||||
|
||||
private static Result<DateTimeStyles> ValidateStyles(DateTimeStyles style, String parameterName)
|
||||
{
|
||||
var style;
|
||||
|
||||
/*if ((style & DateTimeFormatInfo.InvalidDateTimeStyles) != 0) {
|
||||
return .Err;
|
||||
//throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDateTimeStyles"), parameterName);
|
||||
}*/
|
||||
if (((style & (DateTimeStyles.AssumeLocal)) != 0) && ((style & (DateTimeStyles.AssumeUniversal)) != 0)) {
|
||||
//throw new ArgumentException(Environment.GetResourceString("Argument_ConflictingDateTimeStyles"), parameterName);
|
||||
return .Err;
|
||||
}
|
||||
if ((style & DateTimeStyles.NoCurrentDateDefault) != 0) {
|
||||
//throw new ArgumentException(Environment.GetResourceString("Argument_DateTimeOffsetInvalidDateTimeStyles"), parameterName);
|
||||
return .Err;
|
||||
}
|
||||
|
||||
Contract.EndContractBlock();
|
||||
// RoundtripKind does not make sense for DateTimeOffset; ignore this flag for backward compatability with DateTime
|
||||
style &= ~DateTimeStyles.RoundtripKind;
|
||||
|
||||
// AssumeLocal is also ignored as that is what we do by default with DateTimeOffset.Parse
|
||||
style &= ~DateTimeStyles.AssumeLocal;
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
// Operators
|
||||
|
||||
public static implicit operator DateTimeOffset (DateTime dateTime)
|
||||
{
|
||||
return DateTimeOffset(dateTime);
|
||||
}
|
||||
|
||||
public static DateTimeOffset operator +(DateTimeOffset dateTimeOffset, TimeSpan timeSpan) {
|
||||
return DateTimeOffset(dateTimeOffset.ClockDateTime + timeSpan, dateTimeOffset.Offset);
|
||||
}
|
||||
|
||||
|
||||
public static DateTimeOffset operator -(DateTimeOffset dateTimeOffset, TimeSpan timeSpan) {
|
||||
return DateTimeOffset(dateTimeOffset.ClockDateTime - timeSpan, dateTimeOffset.Offset);
|
||||
}
|
||||
|
||||
public static TimeSpan operator -(DateTimeOffset left, DateTimeOffset right) {
|
||||
return left.UtcDateTime - right.UtcDateTime;
|
||||
}
|
||||
|
||||
public static bool operator ==(DateTimeOffset left, DateTimeOffset right) {
|
||||
return left.UtcDateTime == right.UtcDateTime;
|
||||
}
|
||||
|
||||
public static bool operator !=(DateTimeOffset left, DateTimeOffset right) {
|
||||
return left.UtcDateTime != right.UtcDateTime;
|
||||
}
|
||||
|
||||
public static bool operator <(DateTimeOffset left, DateTimeOffset right) {
|
||||
return left.UtcDateTime < right.UtcDateTime;
|
||||
}
|
||||
|
||||
public static bool operator <=(DateTimeOffset left, DateTimeOffset right) {
|
||||
return left.UtcDateTime <= right.UtcDateTime;
|
||||
}
|
||||
|
||||
public static bool operator >(DateTimeOffset left, DateTimeOffset right) {
|
||||
return left.UtcDateTime > right.UtcDateTime;
|
||||
}
|
||||
|
||||
public static bool operator >=(DateTimeOffset left, DateTimeOffset right) {
|
||||
return left.UtcDateTime >= right.UtcDateTime;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
13
BeefLibs/corlib/src/System/DayOfWeek.bf
Normal file
13
BeefLibs/corlib/src/System/DayOfWeek.bf
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace System
|
||||
{
|
||||
enum DayOfWeek
|
||||
{
|
||||
Sunday = 0,
|
||||
Monday = 1,
|
||||
Tuesday = 2,
|
||||
Wednesday = 3,
|
||||
Thursday = 4,
|
||||
Friday = 5,
|
||||
Saturday = 6,
|
||||
}
|
||||
}
|
40
BeefLibs/corlib/src/System/Delegate.bf
Normal file
40
BeefLibs/corlib/src/System/Delegate.bf
Normal file
|
@ -0,0 +1,40 @@
|
|||
namespace System
|
||||
{
|
||||
class Delegate
|
||||
{
|
||||
void* mFuncPtr;
|
||||
void* mTarget;
|
||||
|
||||
public static bool Equals(Delegate a, Delegate b)
|
||||
{
|
||||
if ((Object)a == (Object)b)
|
||||
return true;
|
||||
if ((Object)a == null || (Object)b == null)
|
||||
return false;
|
||||
return (a.mFuncPtr == b.mFuncPtr) && (a.mTarget == b.mTarget);
|
||||
}
|
||||
|
||||
public Result<void*> GetFuncPtr()
|
||||
{
|
||||
if (mTarget != null)
|
||||
return .Err; //("Delegate target method must be static");
|
||||
return mFuncPtr;
|
||||
}
|
||||
|
||||
public void* GetTarget()
|
||||
{
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
public void SetFuncPtr(void* ptr, void* target = null)
|
||||
{
|
||||
mTarget = target;
|
||||
mFuncPtr = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
struct Function : int
|
||||
{
|
||||
|
||||
}
|
||||
}
|
301
BeefLibs/corlib/src/System/Diagnostics/AsyncStreamReader.bf
Normal file
301
BeefLibs/corlib/src/System/Diagnostics/AsyncStreamReader.bf
Normal file
|
@ -0,0 +1,301 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
internal delegate void UserCallBack(String data);
|
||||
|
||||
class AsyncStreamReader
|
||||
{
|
||||
internal const int32 DefaultBufferSize = 1024; // Byte buffer size
|
||||
private const int32 MinBufferSize = 128;
|
||||
|
||||
private Stream stream;
|
||||
private Encoding encoding;
|
||||
private Decoder decoder;
|
||||
private uint8[] byteBuffer;
|
||||
private char8[] char8Buffer;
|
||||
// Record the number of valid bytes in the byteBuffer, for a few checks.
|
||||
|
||||
// This is the maximum number of char8s we can get from one call to
|
||||
// ReadBuffer. Used so ReadBuffer can tell when to copy data into
|
||||
// a user's char8[] directly, instead of our internal char8[].
|
||||
private int32 mMaxCharsPerBuffer;
|
||||
|
||||
// Store a backpointer to the process class, to check for user callbacks
|
||||
private Process process;
|
||||
|
||||
// Delegate to call user function.
|
||||
private UserCallBack userCallBack;
|
||||
|
||||
// Internal Cancel operation
|
||||
private bool cancelOperation;
|
||||
private WaitEvent eofEvent = new WaitEvent() ~ delete _;
|
||||
private Queue<String> messageQueue ~ delete _;
|
||||
private String sb;
|
||||
private bool bLastCarriageReturn;
|
||||
private Monitor mMonitor = new Monitor();
|
||||
|
||||
// Cache the last position scanned in sb when searching for lines.
|
||||
private int currentLinePos;
|
||||
|
||||
internal this(Process process, Stream stream, UserCallBack callback, Encoding encoding)
|
||||
: this(process, stream, callback, encoding, DefaultBufferSize)
|
||||
{
|
||||
}
|
||||
|
||||
internal ~this()
|
||||
{
|
||||
for (var msg in messageQueue)
|
||||
delete msg;
|
||||
Close();
|
||||
}
|
||||
|
||||
// Creates a new AsyncStreamReader for the given stream. The
|
||||
// character encoding is set by encoding and the buffer size,
|
||||
// in number of 16-bit char8acters, is set by bufferSize.
|
||||
//
|
||||
internal this(Process process, Stream stream, UserCallBack callback, Encoding encoding, int32 bufferSize)
|
||||
{
|
||||
Debug.Assert(process != null && stream != null && encoding != null && callback != null, "Invalid arguments!");
|
||||
Debug.Assert(stream.CanRead, "Stream must be readable!");
|
||||
Debug.Assert(bufferSize > 0, "Invalid buffer size!");
|
||||
|
||||
Init(process, stream, callback, encoding, bufferSize);
|
||||
messageQueue = new Queue<String>();
|
||||
}
|
||||
|
||||
public virtual Encoding CurrentEncoding
|
||||
{
|
||||
get { return encoding; }
|
||||
}
|
||||
|
||||
public virtual Stream BaseStream
|
||||
{
|
||||
get { return stream; }
|
||||
}
|
||||
|
||||
private void Init(Process process, Stream stream, UserCallBack callback, Encoding encoding, int32 bufferSize)
|
||||
{
|
||||
this.process = process;
|
||||
this.stream = stream;
|
||||
this.encoding = encoding;
|
||||
this.userCallBack = callback;
|
||||
int32 useBufferSize = bufferSize;
|
||||
if (useBufferSize < MinBufferSize) useBufferSize = MinBufferSize;
|
||||
byteBuffer = new uint8[useBufferSize];
|
||||
mMaxCharsPerBuffer = (int32)encoding.GetMaxCharCount(useBufferSize);
|
||||
char8Buffer = new char8[mMaxCharsPerBuffer];
|
||||
cancelOperation = false;
|
||||
sb = null;
|
||||
this.bLastCarriageReturn = false;
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (stream != null)
|
||||
stream.Close();
|
||||
}
|
||||
if (stream != null)
|
||||
{
|
||||
stream = null;
|
||||
encoding = null;
|
||||
decoder = null;
|
||||
byteBuffer = null;
|
||||
char8Buffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// User calls BeginRead to start the asynchronous read
|
||||
internal void BeginReadLine()
|
||||
{
|
||||
if (cancelOperation)
|
||||
{
|
||||
cancelOperation = false;
|
||||
}
|
||||
|
||||
if (sb == null)
|
||||
{
|
||||
sb = new String(DefaultBufferSize);
|
||||
stream.BeginRead(byteBuffer, 0, byteBuffer.Count, new => ReadBuffer, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
FlushMessageQueue();
|
||||
}
|
||||
}
|
||||
|
||||
internal void CancelOperation()
|
||||
{
|
||||
cancelOperation = true;
|
||||
}
|
||||
|
||||
// This is the async callback function. Only one thread could/should call this.
|
||||
private void ReadBuffer(IAsyncResult ar)
|
||||
{
|
||||
int byteLen = stream.EndRead(ar);
|
||||
|
||||
// We should ideally consume errors from operations getting cancelled
|
||||
// so that we don't crash the unsuspecting parent with an unhandled exc.
|
||||
// This seems to come in 2 forms of exceptions (depending on platform and scenario),
|
||||
// namely OperationCanceledException and IOException (for errorcode that we don't
|
||||
// map explicitly).
|
||||
byteLen = 0; // Treat this as EOF
|
||||
|
||||
|
||||
if (byteLen == 0)
|
||||
{
|
||||
// We're at EOF, we won't call this function again from here on.
|
||||
using (mMonitor.Enter())
|
||||
{
|
||||
if (sb.Length != 0)
|
||||
{
|
||||
messageQueue.Enqueue(new String(sb));
|
||||
sb.Clear();
|
||||
}
|
||||
messageQueue.Enqueue(null);
|
||||
}
|
||||
|
||||
// UserCallback could throw, we should still set the eofEvent
|
||||
FlushMessageQueue();
|
||||
|
||||
eofEvent.Set(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
int char8Len = decoder.GetChars(byteBuffer, 0, byteLen, char8Buffer, 0);
|
||||
sb.Append(char8Buffer, 0, char8Len);
|
||||
GetLinesFromStringBuilder();
|
||||
stream.BeginRead(byteBuffer, 0, byteBuffer.Count, new => ReadBuffer, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read lines stored in StringBuilder and the buffer we just read into.
|
||||
// A line is defined as a sequence of char8acters followed by
|
||||
// a carriage return ('\r'), a line feed ('\n'), or a carriage return
|
||||
// immediately followed by a line feed. The resulting string does not
|
||||
// contain the terminating carriage return and/or line feed. The returned
|
||||
// value is null if the end of the input stream has been reached.
|
||||
//
|
||||
|
||||
private void GetLinesFromStringBuilder()
|
||||
{
|
||||
int currentIndex = currentLinePos;
|
||||
int lineStart = 0;
|
||||
int len = sb.Length;
|
||||
|
||||
// skip a beginning '\n' char8acter of new block if last block ended
|
||||
// with '\r'
|
||||
if (bLastCarriageReturn && (len > 0) && sb[0] == '\n')
|
||||
{
|
||||
currentIndex = 1;
|
||||
lineStart = 1;
|
||||
bLastCarriageReturn = false;
|
||||
}
|
||||
|
||||
while (currentIndex < len)
|
||||
{
|
||||
char8 ch = sb[currentIndex];
|
||||
// Note the following common line feed char8s:
|
||||
// \n - UNIX \r\n - DOS \r - Mac
|
||||
if (ch == '\r' || ch == '\n')
|
||||
{
|
||||
String s = new String();
|
||||
s.Append(sb, lineStart, currentIndex - lineStart);
|
||||
|
||||
lineStart = currentIndex + 1;
|
||||
// skip the "\n" char8acter following "\r" char8acter
|
||||
if ((ch == '\r') && (lineStart < len) && (sb[lineStart] == '\n'))
|
||||
{
|
||||
lineStart++;
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
using (mMonitor.Enter())
|
||||
{
|
||||
messageQueue.Enqueue(s);
|
||||
}
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
if (sb[len - 1] == '\r')
|
||||
{
|
||||
bLastCarriageReturn = true;
|
||||
}
|
||||
// Keep the rest char8acaters which can't form a new line in string builder.
|
||||
if (lineStart < len)
|
||||
{
|
||||
if (lineStart == 0)
|
||||
{
|
||||
// we found no breaklines, in this case we cache the position
|
||||
// so next time we don't have to restart from the beginning
|
||||
currentLinePos = currentIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Remove(0, lineStart);
|
||||
currentLinePos = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear();
|
||||
currentLinePos = 0;
|
||||
}
|
||||
|
||||
FlushMessageQueue();
|
||||
}
|
||||
|
||||
private void FlushMessageQueue()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// When we call BeginReadLine, we also need to flush the queue
|
||||
// So there could be a ---- between the ReadBuffer and BeginReadLine
|
||||
// We need to take lock before DeQueue.
|
||||
if (messageQueue.Count > 0)
|
||||
{
|
||||
using (mMonitor.Enter())
|
||||
{
|
||||
if (messageQueue.Count > 0)
|
||||
{
|
||||
String s = messageQueue.Dequeue();
|
||||
// skip if the read is the read is cancelled
|
||||
// this might happen inside UserCallBack
|
||||
// However, continue to drain the queue
|
||||
if (!cancelOperation)
|
||||
{
|
||||
userCallBack(s);
|
||||
}
|
||||
delete s;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until we hit EOF. This is called from Process.WaitForExit
|
||||
// We will lose some information if we don't do this.
|
||||
internal void WaitUtilEOF()
|
||||
{
|
||||
if (eofEvent != null)
|
||||
{
|
||||
eofEvent.WaitFor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
BeefLibs/corlib/src/System/Diagnostics/Check.bf
Normal file
37
BeefLibs/corlib/src/System/Diagnostics/Check.bf
Normal file
|
@ -0,0 +1,37 @@
|
|||
namespace System.Diagnostics
|
||||
{
|
||||
class Check
|
||||
{
|
||||
|
||||
[Unchecked, SkipCall]
|
||||
public static void Assert(bool condition)
|
||||
{
|
||||
}
|
||||
|
||||
[Checked]
|
||||
public static void Assert(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
Internal.FatalError("Assert failed", 1);
|
||||
}
|
||||
|
||||
[Unchecked, SkipCall]
|
||||
public static void Assert(bool condition, String error)
|
||||
{
|
||||
}
|
||||
|
||||
[Checked]
|
||||
public static void Assert(bool condition, String error)
|
||||
{
|
||||
if (!condition)
|
||||
Internal.FatalError(error, 1);
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
[SkipCall]
|
||||
public static void FatalError(String msg = "Fatal error encountered")
|
||||
{
|
||||
Internal.FatalError(msg, 1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
namespace System.Diagnostics.Contracts
|
||||
{
|
||||
class Contract
|
||||
{
|
||||
public enum ContractFailureKind
|
||||
{
|
||||
Precondition,
|
||||
//[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
|
||||
Postcondition,
|
||||
//[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Postcondition")]
|
||||
PostconditionOnException,
|
||||
Invariant,
|
||||
Assert,
|
||||
Assume,
|
||||
}
|
||||
|
||||
static extern void ReportFailure(ContractFailureKind failureKind, char8* userMessage, int32 userMessageLen, char8* conditionText, int32 conditionTextLen);
|
||||
|
||||
/// <summary>
|
||||
/// This method is used internally to trigger a failure indicating to the "programmer" that he is using the interface incorrectly.
|
||||
/// It is NEVER used to indicate failure of actual contracts at runtime.
|
||||
/// </summary>
|
||||
static void AssertMustUseRewriter(ContractFailureKind kind, String contractKind)
|
||||
{
|
||||
}
|
||||
|
||||
public static void Assume(bool condition, StringView userMessage)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
ReportFailure(ContractFailureKind.Assume, userMessage.Ptr, (int32)userMessage.Length, null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Assert(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
ReportFailure(ContractFailureKind.Assert, null, 0, null, 0);
|
||||
}
|
||||
|
||||
public static void Assert(bool condition, String userMessage)
|
||||
{
|
||||
if (!condition)
|
||||
ReportFailure(ContractFailureKind.Assert, userMessage.Ptr, (int32)userMessage.Length, null, 0);
|
||||
}
|
||||
|
||||
public static void Requires(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
ReportFailure(ContractFailureKind.Assert, null, 0, null, 0);
|
||||
//AssertMustUseRewriter(ContractFailureKind.Precondition, "Requires");
|
||||
}
|
||||
|
||||
public static void Requires(bool condition, StringView userMessage)
|
||||
{
|
||||
AssertMustUseRewriter(ContractFailureKind.Precondition, "Requires");
|
||||
}
|
||||
|
||||
public static void EndContractBlock() { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace System.Diagnostics.Contracts
|
||||
{
|
||||
class ContractsBcl
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Process.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
using System;
|
||||
|
||||
public delegate void DataReceivedEventHandler(Object sender, DataReceivedEventArgs e);
|
||||
|
||||
public class DataReceivedEventArgs : EventArgs
|
||||
{
|
||||
internal String _data;
|
||||
|
||||
internal this(String data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public String Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
58
BeefLibs/corlib/src/System/Diagnostics/Debug.bf
Normal file
58
BeefLibs/corlib/src/System/Diagnostics/Debug.bf
Normal file
|
@ -0,0 +1,58 @@
|
|||
namespace System.Diagnostics
|
||||
{
|
||||
class Debug
|
||||
{
|
||||
#if !DEBUG
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static void Assert(bool condition)
|
||||
{
|
||||
if (!condition)
|
||||
Internal.FatalError("Assert failed", 1);
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static void Assert(bool condition, String error)
|
||||
{
|
||||
if (!condition)
|
||||
Internal.FatalError(error, 1);
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static void FatalError(String msg = "Fatal error encountered")
|
||||
{
|
||||
Internal.FatalError(msg, 1);
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static void AssertNotStack(Object obj)
|
||||
{
|
||||
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
|
||||
if ((obj != null) && (obj.GetFlags() & 8 != 0))
|
||||
Internal.FatalError("Assert failed", 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static extern void Write(char8* str, int strLen);
|
||||
|
||||
public static void WriteLine(StringView line)
|
||||
{
|
||||
Write(line.Ptr, line.Length);
|
||||
Write("\n", 1);
|
||||
}
|
||||
|
||||
public static void WriteLine(StringView strFormat, params Object[] args)
|
||||
{
|
||||
String paramStr = scope String();
|
||||
paramStr.AppendF(strFormat, params args);
|
||||
paramStr.Append('\n');
|
||||
Write(paramStr.Ptr, paramStr.Length);
|
||||
}
|
||||
}
|
||||
}
|
119
BeefLibs/corlib/src/System/Diagnostics/Process.bf
Normal file
119
BeefLibs/corlib/src/System/Diagnostics/Process.bf
Normal file
|
@ -0,0 +1,119 @@
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
class Process
|
||||
{
|
||||
Platform.BfpProcess* mProcess;
|
||||
String mProcessName ~ delete _;
|
||||
|
||||
public int32 Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return Platform.BfpProcess_GetProcessId(mProcess);
|
||||
}
|
||||
}
|
||||
|
||||
public static int32 CurrentId
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int32)Platform.BfpProcess_GetCurrentId();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAttached
|
||||
{
|
||||
get
|
||||
{
|
||||
return mProcess != null;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView ProcessName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mProcessName == null)
|
||||
{
|
||||
mProcessName = new String();
|
||||
Platform.GetStrHelper(mProcessName, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpProcess_GetProcessName(mProcess, outPtr, outSize, (Platform.BfpProcessResult*)outResult);
|
||||
});
|
||||
}
|
||||
return mProcessName;
|
||||
}
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (mProcess != null)
|
||||
{
|
||||
Platform.BfpProcess_Release(mProcess);
|
||||
mProcess = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Result<void> GetProcessById(String machineName, int32 processId)
|
||||
{
|
||||
if (mProcess != null)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
let bfpProcess = Platform.BfpProcess_GetById((machineName != null) ? machineName : null, processId, null);
|
||||
if (bfpProcess == null)
|
||||
return .Err;
|
||||
mProcess = bfpProcess;
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> GetProcessById(int32 processId)
|
||||
{
|
||||
return GetProcessById(null, processId);
|
||||
}
|
||||
|
||||
public void GetMainWindowTitle(String outTitle)
|
||||
{
|
||||
Platform.GetStrHelper(outTitle, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpProcess_GetMainWindowTitle(mProcess, outPtr, outSize, (Platform.BfpProcessResult*)outResult);
|
||||
});
|
||||
}
|
||||
|
||||
public static Result<void> GetProcesses(List<Process> processes)
|
||||
{
|
||||
let result = Platform.GetSizedHelper<Platform.BfpProcess*>(scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpProcess_Enumerate(null, outPtr, outSize, (Platform.BfpProcessResult*)outResult);
|
||||
});
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case .Err:
|
||||
return .Err;
|
||||
case .Ok(let bfpProcSpan):
|
||||
for (var proc in bfpProcSpan)
|
||||
{
|
||||
let process = new Process();
|
||||
process.mProcess = proc;
|
||||
processes.Add(process);
|
||||
}
|
||||
delete bfpProcSpan.Ptr;
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
}
|
18
BeefLibs/corlib/src/System/Diagnostics/ProcessManager.bf
Normal file
18
BeefLibs/corlib/src/System/Diagnostics/ProcessManager.bf
Normal file
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
internal static class ProcessManager
|
||||
{
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
|
||||
public static bool IsRemoteMachine(StringView machineName)
|
||||
{
|
||||
return Platform.BfpProcess_IsRemoteMachine(machineName.ToScopeCStr!());
|
||||
}
|
||||
|
||||
#endif //BF_PLATFORM_WINDOWS
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace System.Diagnostics
|
||||
{
|
||||
public enum ProcessPriorityClass
|
||||
{
|
||||
Normal = 0x20,
|
||||
Idle = 0x40,
|
||||
High = 0x80,
|
||||
RealTime = 0x100,
|
||||
|
||||
BelowNormal = 0x4000,
|
||||
AboveNormal = 0x8000
|
||||
}
|
||||
}
|
83
BeefLibs/corlib/src/System/Diagnostics/ProcessStartInfo.bf
Normal file
83
BeefLibs/corlib/src/System/Diagnostics/ProcessStartInfo.bf
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
class ProcessStartInfo
|
||||
{
|
||||
bool mUseShellExecute = true;
|
||||
bool mRedirectStandardInput = false;
|
||||
bool mRedirectStandardOutput = false;
|
||||
bool mRedirectStandardError = false;
|
||||
bool mCreateNoWindow = false;
|
||||
public bool ErrorDialog;
|
||||
//public Windows.Handle ErrorDialogParentHandle;
|
||||
//public ProcessWindowStyle WindowStyle;
|
||||
|
||||
internal String mFileName = new String() ~ delete _;
|
||||
internal String mArguments = new String() ~ delete _;
|
||||
internal String mDirectory = new String() ~ delete _;
|
||||
internal String mVerb = new String("Open") ~ delete _;
|
||||
|
||||
public Dictionary<String, String> mEnvironmentVariables ~ DeleteDictionyAndKeysAndItems!(_);
|
||||
|
||||
public bool UseShellExecute { get { return mUseShellExecute; } set { mUseShellExecute = value; } };
|
||||
public bool RedirectStandardInput { get { return mRedirectStandardInput; } set { mRedirectStandardInput = value; } };
|
||||
public bool RedirectStandardOutput { get { return mRedirectStandardOutput; } set { mRedirectStandardOutput = value; } };
|
||||
public bool RedirectStandardError { get { return mRedirectStandardError; } set { mRedirectStandardError = value; } };
|
||||
public bool CreateNoWindow { get { return mCreateNoWindow; } set { mCreateNoWindow = value; } };
|
||||
|
||||
internal Encoding StandardOutputEncoding;
|
||||
internal Encoding StandardErrorEncoding;
|
||||
|
||||
//public bool redirectStandardInput { get { return redirectStandardInput; } set { redirectStandardInput = value; } };
|
||||
//public bool redirectStandardInput { get { return redirectStandardInput; } set { redirectStandardInput = value; } };
|
||||
|
||||
public void GetFileName(String outFileName)
|
||||
{
|
||||
if (mFileName != null)
|
||||
outFileName.Append(mFileName);
|
||||
}
|
||||
|
||||
public void SetFileName(StringView fileName)
|
||||
{
|
||||
mFileName.Set(fileName);
|
||||
}
|
||||
|
||||
public void SetWorkingDirectory(StringView fileName)
|
||||
{
|
||||
mDirectory.Set(fileName);
|
||||
}
|
||||
|
||||
public void SetArguments(StringView arguments)
|
||||
{
|
||||
mArguments.Set(arguments);
|
||||
}
|
||||
|
||||
public void SetVerb(StringView verb)
|
||||
{
|
||||
mVerb.Set(verb);
|
||||
}
|
||||
|
||||
public void AddEnvironmentVariable(StringView key, StringView value)
|
||||
{
|
||||
if (mEnvironmentVariables == null)
|
||||
{
|
||||
mEnvironmentVariables = new Dictionary<String, String>();
|
||||
Environment.GetEnvironmentVariables(mEnvironmentVariables);
|
||||
}
|
||||
|
||||
Environment.SetEnvironmentVariable(mEnvironmentVariables, key, value);
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
//Debug.WriteLine("ProcStartInfo {0} Verb: {1} Tick: {2}", this, mVerb, (int32)Platform.BfpSystem_TickCount());
|
||||
}
|
||||
|
||||
public this(Process process)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
10
BeefLibs/corlib/src/System/Diagnostics/ProcessWindowStyle.bf
Normal file
10
BeefLibs/corlib/src/System/Diagnostics/ProcessWindowStyle.bf
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace System.Diagnostics
|
||||
{
|
||||
public enum ProcessWindowStyle
|
||||
{
|
||||
Normal,
|
||||
Hidden,
|
||||
Minimized,
|
||||
Maximized
|
||||
}
|
||||
}
|
95
BeefLibs/corlib/src/System/Diagnostics/Profiler.bf
Normal file
95
BeefLibs/corlib/src/System/Diagnostics/Profiler.bf
Normal file
|
@ -0,0 +1,95 @@
|
|||
using System.Threading;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
enum ProfilerScope
|
||||
{
|
||||
Thread,
|
||||
Process
|
||||
}
|
||||
|
||||
struct ProfileInstance : int32
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
String str = scope String();
|
||||
str.Append("StopSampling\t");
|
||||
((int32)this).ToString(str);
|
||||
Internal.ProfilerCmd(str);
|
||||
}
|
||||
}
|
||||
|
||||
class Profiler
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
Low,
|
||||
Normal,
|
||||
High
|
||||
}
|
||||
|
||||
static int32 gProfileId = 1;
|
||||
|
||||
public struct AutoLeave
|
||||
{
|
||||
//Profiler mProfiler;
|
||||
|
||||
public this(/*Profiler profiler*/)
|
||||
{
|
||||
//mProfiler = profiler;
|
||||
}
|
||||
|
||||
void Dispose()
|
||||
{
|
||||
Profiler.LeaveSection();
|
||||
}
|
||||
}
|
||||
|
||||
static Result<ProfileInstance> StartSampling(int threadId, StringView profileDesc, int sampleRate)
|
||||
{
|
||||
int32 curId = Interlocked.Increment(ref gProfileId);
|
||||
|
||||
String str = scope String();
|
||||
str.Append("StartSampling\t");
|
||||
curId.ToString(str);
|
||||
str.Append("\t");
|
||||
threadId.ToString(str);
|
||||
str.Append("\t");
|
||||
sampleRate.ToString(str);
|
||||
str.Append("\t");
|
||||
str.Append(profileDesc);
|
||||
Internal.ProfilerCmd(str);
|
||||
return (ProfileInstance)curId;
|
||||
}
|
||||
|
||||
public static Result<ProfileInstance> StartSampling(Thread thread, StringView profileDesc = default, int sampleRate = -1)
|
||||
{
|
||||
return StartSampling(thread.Id, profileDesc, sampleRate);
|
||||
}
|
||||
|
||||
public static Result<ProfileInstance> StartSampling(StringView profileDesc = default, int sampleRate = -1)
|
||||
{
|
||||
return StartSampling(0, profileDesc, sampleRate);
|
||||
}
|
||||
|
||||
public static void ClearSampling()
|
||||
{
|
||||
Internal.ProfilerCmd("ClearSampling");
|
||||
}
|
||||
|
||||
public void Mark()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static AutoLeave EnterSection(StringView name, Priority priority = Priority.Normal)
|
||||
{
|
||||
return AutoLeave();
|
||||
}
|
||||
|
||||
public static void LeaveSection()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
151
BeefLibs/corlib/src/System/Diagnostics/SpawnedProcess.bf
Normal file
151
BeefLibs/corlib/src/System/Diagnostics/SpawnedProcess.bf
Normal file
|
@ -0,0 +1,151 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
class SpawnedProcess
|
||||
{
|
||||
public enum KillFlags
|
||||
{
|
||||
None = 0,
|
||||
KillChildren = 1
|
||||
}
|
||||
|
||||
Platform.BfpSpawn* mSpawn;
|
||||
int mExitCode = 0;
|
||||
bool mIsDone;
|
||||
|
||||
public int ExitCode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!mIsDone)
|
||||
WaitFor(-1);
|
||||
return mExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasExited
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!mIsDone)
|
||||
WaitFor(0);
|
||||
return mIsDone;
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
mSpawn = null;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public Result<void> Start(ProcessStartInfo startInfo)
|
||||
{
|
||||
String fileName = startInfo.mFileName;
|
||||
|
||||
Platform.BfpSpawnFlags spawnFlags = .None;
|
||||
if (startInfo.ErrorDialog)
|
||||
spawnFlags |= .ErrorDialog;
|
||||
if (startInfo.UseShellExecute)
|
||||
{
|
||||
spawnFlags |= .UseShellExecute;
|
||||
if (!startInfo.mVerb.IsEmpty)
|
||||
fileName = scope:: String(fileName, "|", startInfo.mVerb);
|
||||
}
|
||||
if (startInfo.CreateNoWindow)
|
||||
spawnFlags |= .NoWindow;
|
||||
if (startInfo.RedirectStandardInput)
|
||||
spawnFlags |= .RedirectStdInput;
|
||||
if (startInfo.RedirectStandardOutput)
|
||||
spawnFlags |= .RedirectStdOutput;
|
||||
if (startInfo.RedirectStandardError)
|
||||
spawnFlags |= .RedirectStdError;
|
||||
|
||||
//startInfo.mEnvironmentVariables
|
||||
|
||||
List<char8> env = scope List<char8>();
|
||||
if (startInfo.mEnvironmentVariables != null)
|
||||
Environment.EncodeEnvironmentVariables(startInfo.mEnvironmentVariables, env);
|
||||
Span<char8> envSpan = env;
|
||||
|
||||
Platform.BfpSpawnResult result = .Ok;
|
||||
mSpawn = Platform.BfpSpawn_Create(fileName, startInfo.mArguments, startInfo.mDirectory, envSpan.Ptr, spawnFlags, &result);
|
||||
|
||||
if ((mSpawn == null) || (result != .Ok))
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> AttachStandardInput(FileStream stream)
|
||||
{
|
||||
if (mSpawn == null)
|
||||
return .Err;
|
||||
Platform.BfpFile* bfpFile = null;
|
||||
Platform.BfpSpawn_GetStdHandles(mSpawn, &bfpFile, null, null);
|
||||
if (bfpFile == null)
|
||||
return .Err;
|
||||
stream.Attach(bfpFile);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> AttachStandardOutput(FileStream stream)
|
||||
{
|
||||
if (mSpawn == null)
|
||||
return .Err;
|
||||
Platform.BfpFile* bfpFile = null;
|
||||
Platform.BfpSpawn_GetStdHandles(mSpawn, null, &bfpFile, null);
|
||||
if (bfpFile == null)
|
||||
return .Err;
|
||||
stream.Attach(bfpFile);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> AttachStandardError(FileStream stream)
|
||||
{
|
||||
if (mSpawn == null)
|
||||
return .Err;
|
||||
Platform.BfpFile* bfpFile = null;
|
||||
Platform.BfpSpawn_GetStdHandles(mSpawn, null, null, &bfpFile);
|
||||
if (bfpFile == null)
|
||||
return .Err;
|
||||
stream.Attach(bfpFile);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public bool WaitFor(int waitMS = -1)
|
||||
{
|
||||
if (mSpawn == null)
|
||||
return true;
|
||||
|
||||
if (!Platform.BfpSpawn_WaitFor(mSpawn, waitMS, &mExitCode, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mIsDone = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (mSpawn != null)
|
||||
{
|
||||
Platform.BfpSpawn_Release(mSpawn);
|
||||
mSpawn = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Kill(int32 exitCode = 0, KillFlags killFlags = .None)
|
||||
{
|
||||
if (mSpawn != null)
|
||||
{
|
||||
Platform.BfpSpawn_Kill(mSpawn, exitCode, (Platform.BfpKillFlags)killFlags, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
142
BeefLibs/corlib/src/System/Diagnostics/Stopwatch.bf
Normal file
142
BeefLibs/corlib/src/System/Diagnostics/Stopwatch.bf
Normal file
|
@ -0,0 +1,142 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
public class Stopwatch
|
||||
{
|
||||
private const int64 TicksPerMillisecond = 1000;
|
||||
private const int64 TicksPerSecond = TicksPerMillisecond * 1000;
|
||||
|
||||
private int64 elapsed;
|
||||
private int64 startTimeStamp;
|
||||
private bool isRunning;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
// Calling start on a running Stopwatch is a no-op.
|
||||
if (!isRunning)
|
||||
{
|
||||
startTimeStamp = GetTimestamp();
|
||||
isRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyFrom(Stopwatch sw)
|
||||
{
|
||||
elapsed = sw.elapsed;
|
||||
startTimeStamp = sw.startTimeStamp;
|
||||
isRunning = sw.isRunning;
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public this(bool startNow)
|
||||
{
|
||||
if (startNow)
|
||||
Start();
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
}
|
||||
|
||||
public static Stopwatch StartNew()
|
||||
{
|
||||
Stopwatch s = new Stopwatch();
|
||||
s.Start();
|
||||
return s;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
// Calling stop on a stopped Stopwatch is a no-op.
|
||||
if (isRunning)
|
||||
{
|
||||
int64 endTimeStamp = GetTimestamp();
|
||||
int64 elapsedThisPeriod = endTimeStamp - startTimeStamp;
|
||||
elapsed += elapsedThisPeriod;
|
||||
isRunning = false;
|
||||
|
||||
if (elapsed < 0)
|
||||
{
|
||||
// When measuring small time periods the StopWatch.Elapsed*
|
||||
// properties can return negative values. This is due to
|
||||
// bugs in the basic input/output system (BIOS) or the hardware
|
||||
// abstraction layer (HAL) on machines with variable-speed CPUs
|
||||
// (e.g. Intel SpeedStep).
|
||||
elapsed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
elapsed = 0;
|
||||
isRunning = false;
|
||||
startTimeStamp = 0;
|
||||
}
|
||||
|
||||
// Convenience method for replacing {sw.Reset(); sw.Start();} with a single sw.Restart()
|
||||
public void Restart()
|
||||
{
|
||||
elapsed = 0;
|
||||
startTimeStamp = GetTimestamp();
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return isRunning; }
|
||||
}
|
||||
|
||||
public TimeSpan Elapsed
|
||||
{
|
||||
get { return TimeSpan(GetElapsedDateTimeTicks()); }
|
||||
}
|
||||
|
||||
public int64 ElapsedMilliseconds
|
||||
{
|
||||
get { return GetRawElapsedTicks() / TicksPerMillisecond; }
|
||||
}
|
||||
|
||||
public int64 ElapsedMicroseconds
|
||||
{
|
||||
get { return GetRawElapsedTicks(); }
|
||||
}
|
||||
|
||||
public static int64 GetTimestamp()
|
||||
{
|
||||
return Internal.GetTickCountMicro();
|
||||
}
|
||||
|
||||
// Get the elapsed ticks.
|
||||
private int64 GetRawElapsedTicks()
|
||||
{
|
||||
int64 timeElapsed = elapsed;
|
||||
|
||||
if (isRunning)
|
||||
{
|
||||
// If the StopWatch is running, add elapsed time since
|
||||
// the Stopwatch is started last time.
|
||||
int64 currentTimeStamp = GetTimestamp();
|
||||
int64 elapsedUntilNow = currentTimeStamp - startTimeStamp;
|
||||
timeElapsed += elapsedUntilNow;
|
||||
}
|
||||
return timeElapsed;
|
||||
}
|
||||
|
||||
// Get the elapsed ticks.
|
||||
private int64 GetElapsedDateTimeTicks()
|
||||
{
|
||||
return GetRawElapsedTicks() * 10;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
190
BeefLibs/corlib/src/System/Double.bf
Normal file
190
BeefLibs/corlib/src/System/Double.bf
Normal file
|
@ -0,0 +1,190 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Diagnostics;
|
||||
|
||||
public struct Double : double, IFloating, ISigned, IFormattable, IHashable, IOpComparable, IOpNegatable, ICanBeNaN
|
||||
{
|
||||
public const double MinValue = -1.7976931348623157E+308;
|
||||
public const double MaxValue = 1.7976931348623157E+308;
|
||||
|
||||
// Note Epsilon should be a double whose hex representation is 0x1
|
||||
// on little endian machines.
|
||||
public const double Epsilon = 4.9406564584124654E-324;
|
||||
public const double NegativeInfinity = (double)(- 1.0 / (double)(0.0));
|
||||
public const double PositiveInfinity = (double)1.0 / (double)(0.0);
|
||||
public const double NaN = (double)0.0 / (double)0.0;
|
||||
|
||||
internal static double NegativeZero = BitConverter.Convert<int64, double>(0x8000000000000000UL);
|
||||
|
||||
public static int operator<=>(Double a, Double b)
|
||||
{
|
||||
return (double)a <=> (double)b;
|
||||
}
|
||||
|
||||
public static Double operator-(Double value)
|
||||
{
|
||||
return (double)value;
|
||||
}
|
||||
|
||||
int IHashable.GetHashCode()
|
||||
{
|
||||
double d = (double)this;
|
||||
if (d == 0)
|
||||
{
|
||||
// Ensure that 0 and -0 have the same hash code
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sizeof(int) == sizeof(double))
|
||||
{
|
||||
var val = (double)this;
|
||||
return *(int*)(&val);
|
||||
}
|
||||
else
|
||||
{
|
||||
int64 value = *(int64*)(&d);
|
||||
return ((int32)value) ^ ((int32)(value >> 32));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInfinity
|
||||
{
|
||||
get
|
||||
{
|
||||
double val = (double)this;
|
||||
return (*(int64*)(&val) & 0x7FFFFFFFFFFFFFFFL) == 0x7FF0000000000000L;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPositiveInfinity
|
||||
{
|
||||
get
|
||||
{
|
||||
return (double)this == double.PositiveInfinity;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNegativeInfinity
|
||||
{
|
||||
get
|
||||
{
|
||||
return (double)this == double.NegativeInfinity;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsNegative
|
||||
{
|
||||
get
|
||||
{
|
||||
double val = (double)this;
|
||||
return (*(uint64*)(&val) & 0x8000000000000000UL) == 0x8000000000000000UL;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNaN
|
||||
{
|
||||
get
|
||||
{
|
||||
double val = (double)this;
|
||||
return (*(uint64*)(&val) & 0x7FFFFFFFFFFFFFFFUL) > 0x7FF0000000000000UL;
|
||||
}
|
||||
}
|
||||
|
||||
// Compares this object to another object, returning an instance of System.Relation.
|
||||
// Null is considered less than any instance.
|
||||
//
|
||||
// If object is not of type Double, this method throws an ArgumentException.
|
||||
//
|
||||
// Returns a value less than zero if this object
|
||||
//
|
||||
public int32 CompareTo(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (value is double)
|
||||
{
|
||||
double d = (double)value;
|
||||
if ((double)this < d) return -1;
|
||||
if ((double)this > d) return 1;
|
||||
if ((double)this == d) return 0;
|
||||
|
||||
// At least one of the values is NaN.
|
||||
if (IsNaN)
|
||||
return (d.IsNaN ? 0 : -1);
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
Runtime.FatalError();
|
||||
}
|
||||
|
||||
public int32 CompareTo(double value)
|
||||
{
|
||||
if ((double)this < value) return -1;
|
||||
if ((double)this > value) return 1;
|
||||
if ((double)this == value) return 0;
|
||||
|
||||
// At least one of the values is NaN.
|
||||
if (IsNaN)
|
||||
return (value.IsNaN) ? 0 : -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
public bool Equals(double obj)
|
||||
{
|
||||
if (obj == (double)this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return obj.IsNaN && IsNaN;
|
||||
}
|
||||
|
||||
[CLink]
|
||||
static extern double strtod(char8* str, char8** endPtr);
|
||||
|
||||
public static Result<double> Parse(StringView val)
|
||||
{
|
||||
var tempStr = scope String(val);
|
||||
return .Ok(strtod(tempStr, null));
|
||||
}
|
||||
|
||||
[StdCall, CLink]
|
||||
static extern int32 ftoa(float val, char8* str);
|
||||
|
||||
static extern int32 ToString(double val, char8* str);
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
char8[128] outBuff = ?;
|
||||
int len = ToString((float)this, &outBuff);
|
||||
strBuffer.Append(&outBuff, len);
|
||||
}
|
||||
|
||||
public void ToString(String outString, String format, IFormatProvider formatProvider)
|
||||
{
|
||||
if (format.IsEmpty)
|
||||
{
|
||||
ToString(outString);
|
||||
return;
|
||||
}
|
||||
NumberFormatter.NumberToString(format, (double)this, formatProvider, outString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void Test()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
41
BeefLibs/corlib/src/System/Enum.bf
Normal file
41
BeefLibs/corlib/src/System/Enum.bf
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace System
|
||||
{
|
||||
struct Enum
|
||||
{
|
||||
public static void EnumToString(Type type, String strBuffer, int64 iVal)
|
||||
{
|
||||
for (var field in type.GetFields())
|
||||
{
|
||||
if (field.mFieldData.mConstValue == iVal)
|
||||
{
|
||||
strBuffer.Append(field.Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
((int32)iVal).ToString(strBuffer);
|
||||
}
|
||||
|
||||
public static Result<T> Parse<T>(StringView str, bool ignoreCase = false) where T : Enum
|
||||
{
|
||||
var typeInst = (TypeInstance)typeof(T);
|
||||
for (var field in typeInst.GetFields())
|
||||
{
|
||||
if (str.Equals(field.mFieldData.mName, ignoreCase))
|
||||
return .Ok(*((T*)(&field.mFieldData.mConstValue)));
|
||||
}
|
||||
|
||||
return .Err;
|
||||
}
|
||||
|
||||
/*public override void ToString(String strBuffer) mut
|
||||
{
|
||||
Type type = GetType();
|
||||
int32* iPtr = (int32*)((int)(&this) + (int)type.Size);
|
||||
EnumToString(type, strBuffer, *iPtr);
|
||||
//EnumToString(GetType(), )
|
||||
}*/
|
||||
}
|
||||
}
|
177
BeefLibs/corlib/src/System/Environment.bf
Normal file
177
BeefLibs/corlib/src/System/Environment.bf
Normal file
|
@ -0,0 +1,177 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System
|
||||
{
|
||||
public static class Environment
|
||||
{
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
public static readonly String NewLine = "\r\n";
|
||||
#else
|
||||
public static readonly String NewLine = new String("\n");
|
||||
#endif // BF_PLATFORM_WINDOWS
|
||||
|
||||
public static OperatingSystem OSVersion = new OperatingSystem() ~ delete _;
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
public const bool IsFileSystemCaseSensitive = false;
|
||||
#else
|
||||
public const bool IsFileSystemCaseSensitive = true;
|
||||
#endif
|
||||
|
||||
internal static String GetResourceString(String key)
|
||||
{
|
||||
return key;
|
||||
//return GetResourceFromDefault(key);
|
||||
}
|
||||
|
||||
internal static String GetResourceString(String key, params Object[] values)
|
||||
{
|
||||
return key;
|
||||
//return GetResourceFromDefault(key);
|
||||
}
|
||||
|
||||
internal static String GetRuntimeResourceString(String key, String defaultValue = null)
|
||||
{
|
||||
if (defaultValue != null)
|
||||
return defaultValue;
|
||||
return key;
|
||||
//return GetResourceFromDefault(key);
|
||||
}
|
||||
|
||||
public static void GetExecutableFilePath(String outPath)
|
||||
{
|
||||
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpDynLib_GetFilePath(null, outPtr, outSize, (Platform.BfpLibResult*)outResult);
|
||||
});
|
||||
}
|
||||
|
||||
public static uint32 TickCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return Platform.BfpSystem_TickCount();
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetEnvironmentVariables(Dictionary<String, String> envVars)
|
||||
{
|
||||
String envStr = scope String();
|
||||
Platform.GetStrHelper(envStr, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpSystem_GetEnvironmentStrings(outPtr, outSize, (Platform.BfpSystemResult*)outResult);
|
||||
});
|
||||
|
||||
readonly char8* cPtrHead = envStr;
|
||||
char8* cPtr = cPtrHead;
|
||||
char8* cPtrEntry = cPtr;
|
||||
|
||||
while (true)
|
||||
{
|
||||
char16 c = *(cPtr++);
|
||||
if (c == 0)
|
||||
{
|
||||
let str = scope String(cPtrEntry);
|
||||
|
||||
int eqPos = str.IndexOf('=', 1);
|
||||
if (eqPos != -1)
|
||||
{
|
||||
let key = new String(str, 0, eqPos);
|
||||
let value = new String(str, eqPos + 1);
|
||||
envVars[key] = value;
|
||||
}
|
||||
|
||||
cPtrEntry = cPtr;
|
||||
if (*cPtrEntry == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetEnvironmentVariable(Dictionary<String, String> envVars, StringView key, StringView value)
|
||||
{
|
||||
let newKey = new String(key);
|
||||
switch (envVars.TryAdd(newKey))
|
||||
{
|
||||
case .Added(let keyPtr, let valuePtr):
|
||||
*valuePtr = new String(value);
|
||||
case .Exists(let keyPtr, let valuePtr):
|
||||
delete newKey;
|
||||
(*valuePtr).Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EncodeEnvironmentVariablesW(Dictionary<String, String> envVars, List<uint8> data)
|
||||
{
|
||||
var keys = scope String[envVars.Count];
|
||||
var values = scope String[envVars.Count];
|
||||
int idx = 0;
|
||||
for (let kv in envVars)
|
||||
{
|
||||
keys[idx] = kv.key;
|
||||
values[idx] = kv.value;
|
||||
++idx;
|
||||
}
|
||||
|
||||
//TODO: This is really supposed to be a UTF16-ordinal compare
|
||||
Array.Sort(keys, values, scope (lhs, rhs) => { return String.Compare(lhs, rhs, true); } );
|
||||
|
||||
for (var key in keys)
|
||||
{
|
||||
int allocSize = UTF16.GetEncodedLen(key);
|
||||
char16* encodedData = scope char16[allocSize]*;
|
||||
int encodedLen = UTF16.Encode(key, encodedData, allocSize);
|
||||
int byteLen = (encodedLen - 1) * 2;
|
||||
Internal.MemCpy(data.GrowUnitialized(byteLen), encodedData, byteLen);
|
||||
|
||||
data.Add((uint8)'='); data.Add((uint8)0);
|
||||
|
||||
let value = values[@key];
|
||||
allocSize = UTF16.GetEncodedLen(value);
|
||||
encodedData = scope char16[allocSize]*;
|
||||
encodedLen = UTF16.Encode(value, encodedData, allocSize);
|
||||
byteLen = (encodedLen - 1) * 2;
|
||||
Internal.MemCpy(data.GrowUnitialized(byteLen), encodedData, byteLen);
|
||||
|
||||
data.Add(0); data.Add(0); // Single UTF16 char
|
||||
}
|
||||
|
||||
data.Add(0); data.Add(0); // Single UTF16 char
|
||||
}
|
||||
|
||||
public static void EncodeEnvironmentVariables(Dictionary<String, String> envVars, List<char8> data)
|
||||
{
|
||||
var keys = scope String[envVars.Count];
|
||||
var values = scope String[envVars.Count];
|
||||
int idx = 0;
|
||||
for (let kv in envVars)
|
||||
{
|
||||
keys[idx] = kv.key;
|
||||
values[idx] = kv.value;
|
||||
++idx;
|
||||
}
|
||||
|
||||
//TODO: This is really supposed to be a UTF16-ordinal compare
|
||||
Array.Sort(keys, values, scope (lhs, rhs) => { return String.Compare(lhs, rhs, true); } );
|
||||
|
||||
for (var key in keys)
|
||||
{
|
||||
int byteLen = key.Length;
|
||||
Internal.MemCpy(data.GrowUnitialized(byteLen), key.Ptr, byteLen);
|
||||
|
||||
data.Add('=');
|
||||
|
||||
let value = values[@key];
|
||||
byteLen = value.Length;
|
||||
Internal.MemCpy(data.GrowUnitialized(byteLen), value.Ptr, byteLen);
|
||||
|
||||
data.Add(0);
|
||||
}
|
||||
|
||||
data.Add(0);
|
||||
}
|
||||
}
|
||||
}
|
306
BeefLibs/corlib/src/System/Event.bf
Normal file
306
BeefLibs/corlib/src/System/Event.bf
Normal file
|
@ -0,0 +1,306 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System
|
||||
{
|
||||
// Event owns the delegates it contains
|
||||
// Event supports removals and additions while enumerating without invalidating the enumerator.
|
||||
// Event can also safely be Disposed and even have its memory reclaimed during enumeration
|
||||
// Event is not thread-safe: access must be protected during modification, and during enumeration
|
||||
struct Event<T> where T : Delegate
|
||||
{
|
||||
// mData can be null with no listeners, T for one listener, or List<T> for multiple
|
||||
// If we are enumerating then mData points to the enumerator.
|
||||
int mData;
|
||||
|
||||
const int sIsEnumerating = 1;
|
||||
const int sHadEnumRemoves = 2;
|
||||
const int sFlagsMask = 3;
|
||||
const int sDataMask = ~sFlagsMask;
|
||||
|
||||
public bool HasListeners
|
||||
{
|
||||
get
|
||||
{
|
||||
return Target != null;
|
||||
}
|
||||
}
|
||||
|
||||
public int32 Count
|
||||
{
|
||||
get
|
||||
{
|
||||
Object data = Internal.UnsafeCastToObject((void*)(mData & sDataMask));
|
||||
|
||||
if (data == null)
|
||||
return 0;
|
||||
|
||||
var type = data.GetType();
|
||||
if (type == typeof(List<T>))
|
||||
{
|
||||
var list = (List<T>)data;
|
||||
return (int32)list.Count;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public Object Target
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mData & sIsEnumerating != 0)
|
||||
{
|
||||
Enumerator* enumerator = (Enumerator*)(mData & sDataMask);
|
||||
return enumerator.[Friend]mTarget;
|
||||
}
|
||||
return Internal.UnsafeCastToObject((void*)mData);
|
||||
}
|
||||
|
||||
set mut
|
||||
{
|
||||
if (mData & sIsEnumerating != 0)
|
||||
{
|
||||
Enumerator* enumerator = (Enumerator*)(mData & sDataMask);
|
||||
enumerator.[Friend]mTarget = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
mData = (int)Internal.UnsafeCastToPtr(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void GCMarkMembers()
|
||||
{
|
||||
if (mData & sDataMask != 0)
|
||||
GC.Mark(Internal.UnsafeCastToObject((void*)mData));
|
||||
}
|
||||
|
||||
public void Add(T dlg) mut
|
||||
{
|
||||
Object data = Target;
|
||||
if (data == null)
|
||||
{
|
||||
Target = dlg;
|
||||
return;
|
||||
}
|
||||
|
||||
var type = data.GetType();
|
||||
|
||||
if (type == typeof(List<T>))
|
||||
{
|
||||
var list = (List<T>)data;
|
||||
list.Add(dlg);
|
||||
}
|
||||
else
|
||||
{
|
||||
var list = new List<T>();
|
||||
list.Add((T)data);
|
||||
list.Add(dlg);
|
||||
Target = list;
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(T dlg, bool deleteDelegate = false) mut
|
||||
{
|
||||
Object data = Target;
|
||||
|
||||
var type = data.GetType();
|
||||
if (type == typeof(List<T>))
|
||||
{
|
||||
var list = (List<T>)data;
|
||||
int32 idx = -1;
|
||||
for (int32 i = 0; i < list.Count; i++)
|
||||
if (Delegate.Equals(list[i], dlg))
|
||||
{
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
if (idx == -1)
|
||||
{
|
||||
Runtime.FatalError("Not found");
|
||||
}
|
||||
|
||||
if (deleteDelegate)
|
||||
delete list[idx];
|
||||
|
||||
if (mData & sIsEnumerating != 0)
|
||||
{
|
||||
// In order to avoid invalidating the enumerator's index during enumeration, we
|
||||
// just null this entry out and then we physically remove the entries upon
|
||||
// Dispose of the enumerator
|
||||
list[idx] = null;
|
||||
mData |= sHadEnumRemoves;
|
||||
}
|
||||
else
|
||||
list.RemoveAt(idx);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
delete list;
|
||||
Target = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
T dlgMember = (T)data;
|
||||
if (Delegate.Equals(dlg, dlgMember))
|
||||
{
|
||||
if (deleteDelegate)
|
||||
delete dlgMember;
|
||||
Target = null;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Runtime.FatalError("Not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public rettype(T) Invoke(params T p) mut
|
||||
{
|
||||
var result = default(rettype(T));
|
||||
for (var dlg in this)
|
||||
{
|
||||
result = dlg(params p);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Dispose() mut
|
||||
{
|
||||
if (mData == 0)
|
||||
return;
|
||||
var data = Target;
|
||||
if (data.GetType() == typeof(List<T>))
|
||||
{
|
||||
var list = (List<T>)data;
|
||||
for (var dlg in list)
|
||||
delete dlg;
|
||||
}
|
||||
delete data;
|
||||
Target = null;
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator() mut
|
||||
{
|
||||
return Enumerator(ref this);
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
Event<T>* mEvent;
|
||||
Object mTarget;
|
||||
int32 mIdx;
|
||||
Enumerator* mRootEnumerator;
|
||||
T mCurrent;
|
||||
|
||||
public this(ref Event<T> event)
|
||||
{
|
||||
mEvent = &event;
|
||||
mIdx = -2;
|
||||
mRootEnumerator = null;
|
||||
mCurrent = null;
|
||||
mTarget = null;
|
||||
}
|
||||
|
||||
public void Dispose() mut
|
||||
{
|
||||
if (mRootEnumerator == &this)
|
||||
{
|
||||
if ((mEvent.mData & Event<T>.sHadEnumRemoves != 0) && (mTarget != null))
|
||||
{
|
||||
var list = (List<T>)mTarget;
|
||||
|
||||
// Remove nulls
|
||||
for (int32 i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (list[i] == null)
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
delete list;
|
||||
mEvent.mData = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mEvent.mData = (int)Internal.UnsafeCastToPtr(mTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset() mut
|
||||
{
|
||||
mIdx = -1;
|
||||
}
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
if (mIdx == -2)
|
||||
{
|
||||
// Attach
|
||||
if (mEvent.mData & sIsEnumerating == 0)
|
||||
{
|
||||
mTarget = mEvent.Target;
|
||||
mEvent.mData = (int)(&this) | sIsEnumerating;
|
||||
mRootEnumerator = &this;
|
||||
}
|
||||
else
|
||||
{
|
||||
mRootEnumerator = (Enumerator*)(mEvent.mData & Event<T>.sDataMask);
|
||||
}
|
||||
mIdx = -1;
|
||||
}
|
||||
|
||||
var data = mRootEnumerator.mTarget;
|
||||
if (data == null)
|
||||
return false;
|
||||
|
||||
var type = data.GetType();
|
||||
if (type == typeof(List<T>))
|
||||
{
|
||||
var list = (List<T>)data;
|
||||
repeat
|
||||
{
|
||||
mIdx++;
|
||||
if (mIdx >= list.Count)
|
||||
return false;
|
||||
mCurrent = list[mIdx];
|
||||
}
|
||||
while (mCurrent == null);
|
||||
}
|
||||
else
|
||||
{
|
||||
mIdx++;
|
||||
if (mIdx > 0)
|
||||
return false;
|
||||
mCurrent = (T)data;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return mCurrent;
|
||||
}
|
||||
}
|
||||
|
||||
public Result<T> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return mCurrent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
BeefLibs/corlib/src/System/EventArgs.bf
Normal file
11
BeefLibs/corlib/src/System/EventArgs.bf
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace System
|
||||
{
|
||||
public class EventArgs
|
||||
{
|
||||
public static readonly EventArgs Empty = new EventArgs() ~ delete _;
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
5
BeefLibs/corlib/src/System/EventHandler.bf
Normal file
5
BeefLibs/corlib/src/System/EventHandler.bf
Normal file
|
@ -0,0 +1,5 @@
|
|||
namespace System
|
||||
{
|
||||
public delegate void EventHandler(Object sender, EventArgs e);
|
||||
public delegate void EventHandler<TEventArgs>(Object sender, TEventArgs e); // Removed TEventArgs constraint post-.NET 4
|
||||
}
|
220
BeefLibs/corlib/src/System/FFI/Function.bf
Normal file
220
BeefLibs/corlib/src/System/FFI/Function.bf
Normal file
|
@ -0,0 +1,220 @@
|
|||
namespace System.FFI
|
||||
{
|
||||
[CRepr]
|
||||
struct FFIType
|
||||
{
|
||||
public enum TypeKind : uint16
|
||||
{
|
||||
Void,
|
||||
Int,
|
||||
Float,
|
||||
Double,
|
||||
LongDouble,
|
||||
UInt8,
|
||||
SInt8,
|
||||
UInt16,
|
||||
SInt16,
|
||||
UInt32,
|
||||
SInt32,
|
||||
UInt64,
|
||||
SInt64,
|
||||
Struct,
|
||||
Pointer
|
||||
}
|
||||
|
||||
public int mSize;
|
||||
public uint16 mAlignment;
|
||||
public TypeKind mTypeKind;
|
||||
public FFIType** mElements;
|
||||
|
||||
public this(int size, int align, TypeKind typeKind, FFIType** elements = null)
|
||||
{
|
||||
mSize = size;
|
||||
mAlignment = (.)align;
|
||||
mTypeKind = typeKind;
|
||||
mElements = elements;
|
||||
}
|
||||
|
||||
public this(int size, TypeKind typeKind)
|
||||
{
|
||||
mSize = size;
|
||||
mAlignment = (uint16)size;
|
||||
mTypeKind = typeKind;
|
||||
mElements = null;
|
||||
}
|
||||
|
||||
public static FFIType Void = .(1, .Void);
|
||||
public static FFIType UInt8 = .(1, .UInt8);
|
||||
public static FFIType Int8 = .(1, .SInt8);
|
||||
public static FFIType UInt16 = .(2, .UInt16);
|
||||
public static FFIType Int16 = .(2, .SInt16);
|
||||
public static FFIType UInt32 = .(4, .UInt32);
|
||||
public static FFIType Int32 = .(4, .SInt32);
|
||||
public static FFIType UInt64 = .(8, .UInt64);
|
||||
public static FFIType Int64 = .(8, .SInt64);
|
||||
public static FFIType Float = .(4, .Float);
|
||||
public static FFIType Double = .(8, .Double);
|
||||
public static FFIType Pointer = .(sizeof(void*), .Pointer);
|
||||
|
||||
public static FFIType* Get(Type type, void* allocBytes = null, int* inOutSize = null)
|
||||
{
|
||||
var type;
|
||||
|
||||
if (type.IsTypedPrimitive)
|
||||
type = type.UnderlyingType;
|
||||
|
||||
switch (type.mTypeCode)
|
||||
{
|
||||
case .None:
|
||||
return &FFIType.Void;
|
||||
case .Boolean,
|
||||
.Char8,
|
||||
.UInt8:
|
||||
return &FFIType.UInt8;
|
||||
case .Int8:
|
||||
return &FFIType.Int8;
|
||||
case .Char16,
|
||||
.UInt16:
|
||||
return &FFIType.UInt16;
|
||||
case .Int16:
|
||||
return &FFIType.Int16;
|
||||
case .Char32,
|
||||
.UInt32:
|
||||
return &FFIType.UInt32;
|
||||
case .Int32:
|
||||
return &FFIType.Int32;
|
||||
case .UInt64:
|
||||
return &FFIType.UInt64;
|
||||
case .Int64:
|
||||
return &FFIType.Int64;
|
||||
case .Float:
|
||||
return &FFIType.Float;
|
||||
case .Double:
|
||||
return &FFIType.Double;
|
||||
case .Struct:
|
||||
int wantSize = sizeof(FFIType);
|
||||
if (allocBytes == null)
|
||||
{
|
||||
*inOutSize = wantSize;
|
||||
return null;
|
||||
}
|
||||
|
||||
FFIType* ffiType = (FFIType*)allocBytes;
|
||||
ffiType.mSize = type.mSize;
|
||||
ffiType.mAlignment = type.mAlign;
|
||||
ffiType.mElements = null;
|
||||
ffiType.mTypeKind = .Struct;
|
||||
return ffiType;
|
||||
case .Pointer,
|
||||
.Interface,
|
||||
.Object:
|
||||
return &FFIType.Pointer;
|
||||
|
||||
#if BF_64_BIT
|
||||
case .Int:
|
||||
return &FFIType.Int64;
|
||||
case .UInt:
|
||||
return &FFIType.UInt64;
|
||||
#else
|
||||
case .Int:
|
||||
return &FFIType.Int32;
|
||||
case .UInt:
|
||||
return &FFIType.UInt32;
|
||||
#endif
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum FFIResult : int32
|
||||
{
|
||||
OK,
|
||||
BadTypeDef,
|
||||
BadABI
|
||||
}
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
#if BF_64_BIT
|
||||
enum FFIABI : int32
|
||||
{
|
||||
NotSet = 0,
|
||||
StdCall = 1,
|
||||
ThisCall = 1,
|
||||
FastCall = 1,
|
||||
MS_CDecl = 1,
|
||||
Default = 1
|
||||
}
|
||||
#else
|
||||
enum FFIABI : int32
|
||||
{
|
||||
NotSet = 0,
|
||||
SysV,
|
||||
StdCall,
|
||||
ThisCall,
|
||||
FastCall,
|
||||
MS_CDecl,
|
||||
|
||||
Default = MS_CDecl
|
||||
}
|
||||
#endif
|
||||
|
||||
#else //!BF_PLATFORM_WINDOWS
|
||||
|
||||
#if BF_64_BIT
|
||||
enum FFIABI : int32
|
||||
{
|
||||
NotSet = 0,
|
||||
SysV,
|
||||
Unix64,
|
||||
Default = Unix64
|
||||
}
|
||||
#else
|
||||
enum FFIABI : int32
|
||||
{
|
||||
NotSet = 0,
|
||||
SysV,
|
||||
Unix64,
|
||||
Default = SysV
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
struct FFILIB
|
||||
{
|
||||
[CRepr]
|
||||
public struct FFICIF
|
||||
{
|
||||
FFIABI mAbi;
|
||||
uint32 mNArgs;
|
||||
FFIType **mArgTypes;
|
||||
FFIType *mRType;
|
||||
uint32 mBytes;
|
||||
uint32 mFlags;
|
||||
}
|
||||
|
||||
public static extern void* ClosureAlloc(int size, void** outFunc);
|
||||
public static extern FFIResult PrepCif(FFICIF* cif, FFIABI abi, int32 nargs, FFIType* rtype, FFIType** argTypes);
|
||||
public static extern void Call(FFICIF* cif, void* funcPtr, void* rvalue, void** args);
|
||||
}
|
||||
|
||||
struct FFICaller
|
||||
{
|
||||
FFILIB.FFICIF mCIF;
|
||||
|
||||
public Result<void, FFIResult> Prep(FFIABI abi, int32 nargs, FFIType* rtype, FFIType** argTypes) mut
|
||||
{
|
||||
let res = FFILIB.PrepCif(&mCIF, abi, nargs, rtype, argTypes);
|
||||
if (res == .OK)
|
||||
return .Ok;
|
||||
return .Err(res);
|
||||
}
|
||||
|
||||
public void Call(void* funcPtr, void* rvalue, void** args) mut
|
||||
{
|
||||
FFILIB.Call(&mCIF, funcPtr, rvalue, args);
|
||||
}
|
||||
}
|
||||
}
|
195
BeefLibs/corlib/src/System/Float.bf
Normal file
195
BeefLibs/corlib/src/System/Float.bf
Normal file
|
@ -0,0 +1,195 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace System
|
||||
{
|
||||
struct Float : float, IFloating, ISigned, IFormattable, IHashable, IEquatable<float>, IOpComparable, IOpNegatable, ICanBeNaN
|
||||
{
|
||||
public const float MinValue = (float)-3.40282346638528859e+38;
|
||||
public const float Epsilon = (float)1.4e-45;
|
||||
public const float MaxValue = (float)3.40282346638528859e+38;
|
||||
public const float PositiveInfinity = 1.0f / 0.0f;
|
||||
public const float NegativeInfinity = -1.0f / 0.0f;
|
||||
public const float NaN = 0.0f / 0.0f;
|
||||
|
||||
// We use this explicit definition to avoid the confusion between 0.0 and -0.0.
|
||||
public const float NegativeZero = (float)-0.0;
|
||||
|
||||
public static int operator<=>(Float a, Float b)
|
||||
{
|
||||
return (float)a <=> (float)b;
|
||||
}
|
||||
|
||||
public static Float operator-(Float value)
|
||||
{
|
||||
return (float)value;
|
||||
}
|
||||
|
||||
/*public bool IsNegative
|
||||
{
|
||||
get
|
||||
{
|
||||
return this < 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
int IHashable.GetHashCode()
|
||||
{
|
||||
var val = (float)this;
|
||||
return *(int32*)(&val);
|
||||
}
|
||||
|
||||
public bool IsNegative
|
||||
{
|
||||
get
|
||||
{
|
||||
return this < 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFinite
|
||||
{
|
||||
get
|
||||
{
|
||||
float val = (float)this;
|
||||
return (*(int32*)(&val) & 0x7FFFFFFF) < 0x7F800000;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInfinity
|
||||
{
|
||||
get
|
||||
{
|
||||
float val = (float)this;
|
||||
return (*(int32*)(&val) & 0x7FFFFFFF) == 0x7F800000;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPositiveInfinity
|
||||
{
|
||||
get
|
||||
{
|
||||
float val = (float)this;
|
||||
return *(int32*)(&val) == 0x7F800000;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNegativeInfinity
|
||||
{
|
||||
get
|
||||
{
|
||||
float val = (float)this;
|
||||
return *(int32*)(&val) == (int32)0xFF800000;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNaN
|
||||
{
|
||||
get
|
||||
{
|
||||
float val = (float)this;
|
||||
return (*(int32*)(&val) & 0x7FFFFFFF) > 0x7F800000;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSubnormal
|
||||
{
|
||||
get
|
||||
{
|
||||
float val = (float)this;
|
||||
var bits = *(int32*)(&val);
|
||||
bits &= 0x7FFFFFFF;
|
||||
return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(float other)
|
||||
{
|
||||
return (float)this == other;
|
||||
}
|
||||
|
||||
public int CompareTo(float value)
|
||||
{
|
||||
if ((float)this < value) return -1;
|
||||
if ((float)this > value) return 1;
|
||||
if ((float)this == value) return 0;
|
||||
|
||||
// At least one of the values is NaN.
|
||||
if (IsNaN)
|
||||
return (value.IsNaN ? 0 : -1);
|
||||
else // f is NaN.
|
||||
return 1;
|
||||
}
|
||||
|
||||
[StdCall, CLink]
|
||||
static extern int32 ftoa(float val, char8* str);
|
||||
|
||||
static extern int32 ToString(float val, char8* str);
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
char8[128] outBuff = ?;
|
||||
//ftoa((float)this, &outBuff);
|
||||
int len = ToString((float)this, &outBuff);
|
||||
strBuffer.Append(&outBuff, len);
|
||||
}
|
||||
|
||||
public void ToString(String outString, String format, IFormatProvider formatProvider)
|
||||
{
|
||||
if (format.IsEmpty)
|
||||
{
|
||||
ToString(outString);
|
||||
return;
|
||||
}
|
||||
NumberFormatter.NumberToString(format, (float)this, formatProvider, outString);
|
||||
}
|
||||
|
||||
public static Result<float> Parse(StringView val)
|
||||
{
|
||||
bool isNeg = false;
|
||||
double result = 0;
|
||||
double decimalMultiplier = 0;
|
||||
|
||||
//TODO: Use Number.ParseNumber
|
||||
for (int32 i = 0; i < val.Length; i++)
|
||||
{
|
||||
char8 c = val.Ptr[i];
|
||||
|
||||
if (c == '.')
|
||||
{
|
||||
if (decimalMultiplier != 0)
|
||||
return .Err;
|
||||
decimalMultiplier = 0.1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (decimalMultiplier != 0)
|
||||
{
|
||||
if ((c >= '0') && (c <= '9'))
|
||||
{
|
||||
result += (int32)(c - '0') * decimalMultiplier;
|
||||
decimalMultiplier *= 0.1;
|
||||
}
|
||||
else
|
||||
return .Err;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((i == 0) && (c == '-'))
|
||||
{
|
||||
isNeg = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((c >= '0') && (c <= '9'))
|
||||
{
|
||||
result *= 10;
|
||||
result += (int32)(c - '0');
|
||||
}
|
||||
else
|
||||
return .Err;
|
||||
}
|
||||
return isNeg ? (float)(-result) : (float)result;
|
||||
}
|
||||
}
|
||||
}
|
7
BeefLibs/corlib/src/System/Func.bf
Normal file
7
BeefLibs/corlib/src/System/Func.bf
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace System
|
||||
{
|
||||
delegate TResult Func<TResult>();
|
||||
delegate TResult Func<T1, TResult>(T1 p1);
|
||||
delegate TResult Func<T1, T2, TResult>(T1 p1, T2 p2);
|
||||
delegate TResult Func<T1, T2, T3, TResult>(T1 p1, T2 p2, T3 p3);
|
||||
}
|
196
BeefLibs/corlib/src/System/GC.bf
Normal file
196
BeefLibs/corlib/src/System/GC.bf
Normal file
|
@ -0,0 +1,196 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace System
|
||||
{
|
||||
public interface ILeakIdentifiable
|
||||
{
|
||||
void ToLeakString(String str);
|
||||
}
|
||||
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK || BF_DEBUG_ALLOC
|
||||
[AlwaysInclude]
|
||||
#endif
|
||||
public static class GC
|
||||
{
|
||||
enum RootResult
|
||||
{
|
||||
Ok
|
||||
}
|
||||
|
||||
public delegate Result<void> RootCallback();
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
static Event<RootCallback> sRootCallbacks ~ _.Dispose();
|
||||
static Thread sThread;
|
||||
static Monitor sMonitor = new Monitor() ~ delete _;
|
||||
|
||||
static this()
|
||||
{
|
||||
FindAllTLSMembers();
|
||||
Init();
|
||||
}
|
||||
|
||||
static ~this()
|
||||
{
|
||||
StopCollecting();
|
||||
}
|
||||
#elif BF_DEBUG_ALLOC
|
||||
static ~this()
|
||||
{
|
||||
//Report();
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void AddRootCallback(RootCallback rootDelegate)
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
using (sMonitor.Enter())
|
||||
sRootCallbacks.Add(rootDelegate);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void RemoveRootCallback(RootCallback rootDelegate)
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
using (sMonitor.Enter())
|
||||
sRootCallbacks.Remove(rootDelegate);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool CallRootCallbacks()
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
using (sMonitor.Enter())
|
||||
{
|
||||
var result = sRootCallbacks();
|
||||
if (result case .Err)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
extern static void ReportTLSMember(int moduleTLSIndex, void* addr, void* markFunc);
|
||||
static void ReportTLSMember(void* addr, void* markFunc)
|
||||
{
|
||||
ReportTLSMember(Thread.ModuleTLSIndex, addr, markFunc);
|
||||
}
|
||||
|
||||
extern static void Run();
|
||||
static void ThreadProc(Object param)
|
||||
{
|
||||
Run();
|
||||
}
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
sThread = new Thread(new => ThreadProc);
|
||||
sThread.Start(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK || BF_DEBUG_ALLOC
|
||||
public extern static void Report();
|
||||
public extern static void Shutdown();
|
||||
public extern static void SetMaxRawDeferredObjectFreePercentage(int maxPercentage);
|
||||
#else
|
||||
public static void Report() {}
|
||||
public static void Shutdown() {}
|
||||
public static void SetMaxRawDeferredObjectFreePercentage(int maxPercentage) {}
|
||||
#endif
|
||||
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
private extern static void Init();
|
||||
public extern static void Collect(bool async = true);
|
||||
private extern static void StopCollecting();
|
||||
private extern static void AddStackMarkableObject(Object obj);
|
||||
private extern static void RemoveStackMarkableObject(Object obj);
|
||||
[AlwaysInclude]
|
||||
private extern static void MarkAllStaticMembers();
|
||||
private extern static void FindAllTLSMembers();
|
||||
public extern static void DebugDumpLeaks();
|
||||
public extern static void Mark(Object obj);
|
||||
public extern static void Mark(void* ptr, int size);
|
||||
public extern static void SetAutoCollectPeriod(int periodMS); // <= -1 to disable, 0 to constantly run. Defaults to -1
|
||||
public extern static void SetCollectFreeThreshold(int freeBytes); // -1 to disable, 0 to trigger collection after every single free. Defaults to 64MB
|
||||
public extern static void SetMaxPausePercentage(int maxPausePercentage); // 0 = disabled. Defaults to 20.
|
||||
#else
|
||||
public static void Collect(bool async = true) {}
|
||||
private static void MarkAllStaticMembers() {}
|
||||
public static void DebugDumpLeaks() {}
|
||||
public static void Mark(Object obj) {}
|
||||
public static void Mark(void* ptr, int size) {}
|
||||
public static void SetAutoCollectPeriod(int periodMS) {}
|
||||
public static void SetCollectFreeThreshold(int freeBytes) {}
|
||||
public static void SetMaxPausePercentage(int maxPausePercentage) {}
|
||||
#endif
|
||||
|
||||
static void MarkDerefedObject(Object* obj)
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
Mark(*obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Mark<T>(T val) where T : class
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
Mark((Object)val);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Mark<T>(T val) where T : struct
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
val.[Friend]GCMarkMembers();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Mark<T>(T val) where T : struct*
|
||||
{
|
||||
// Memory pointed to by struct*'s will already-scanned stack memory,
|
||||
// or the memory would already be registered with the GC
|
||||
}
|
||||
|
||||
public static void Mark<TSizedArray, T, Size>(TSizedArray val) where Size : const int where TSizedArray : SizedArray<T, Size>
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
for (var element in val)
|
||||
{
|
||||
Mark(element);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Mark_Enumerate<T>(T val) where T : var
|
||||
{
|
||||
/*for (var element in val)
|
||||
{
|
||||
Mark(element);
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
public static void Mark_Unbound<T>(T val) where T : var
|
||||
{
|
||||
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
||||
Mark(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void ToLeakString(Object obj, String strBuffer)
|
||||
{
|
||||
obj.GetType().GetName(strBuffer);
|
||||
/*strBuffer.Append("@");
|
||||
((intptr)(void*)obj).ToString(strBuffer, "X", null);
|
||||
|
||||
var leakIdentifier = obj as ILeakIdentifiable;
|
||||
if (leakIdentifier != null)
|
||||
{
|
||||
strBuffer.Append(" : ");
|
||||
leakIdentifier.ToLeakString(strBuffer);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
856
BeefLibs/corlib/src/System/Globalization/Calendar.bf
Normal file
856
BeefLibs/corlib/src/System/Globalization/Calendar.bf
Normal file
|
@ -0,0 +1,856 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization {
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
// This abstract class represents a calendar. A calendar reckons time in
|
||||
// divisions such as weeks, months and years. The number, length and start of
|
||||
// the divisions vary in each calendar.
|
||||
//
|
||||
// Any instant in time can be represented as an n-tuple of numeric values using
|
||||
// a particular calendar. For example, the next vernal equinox occurs at (0.0, 0
|
||||
// , 46, 8, 20, 3, 1999) in the Gregorian calendar. An implementation of
|
||||
// Calendar can map any DateTime value to such an n-tuple and vice versa. The
|
||||
// DateTimeFormat class can map between such n-tuples and a textual
|
||||
// representation such as "8:46 AM [....] 20th 1999 AD".
|
||||
//
|
||||
// Most calendars identify a year which begins the current era. There may be any
|
||||
// number of previous eras. The Calendar class identifies the eras as enumerated
|
||||
// integers where the current era (CurrentEra) has the value zero.
|
||||
//
|
||||
// For consistency, the first unit in each interval, e.g. the first month, is
|
||||
// assigned the value one.
|
||||
// The calculation of hour/minute/second is moved to Calendar from GregorianCalendar,
|
||||
// since most of the calendars (or all?) have the same way of calcuating hour/minute/second.
|
||||
|
||||
public abstract class Calendar //: ICloneable
|
||||
{
|
||||
|
||||
// Number of 100ns (10E-7 second) ticks per time unit
|
||||
internal const int64 TicksPerMillisecond = 10000;
|
||||
internal const int64 TicksPerSecond = TicksPerMillisecond * 1000;
|
||||
internal const int64 TicksPerMinute = TicksPerSecond * 60;
|
||||
internal const int64 TicksPerHour = TicksPerMinute * 60;
|
||||
internal const int64 TicksPerDay = TicksPerHour * 24;
|
||||
|
||||
// Number of milliseconds per time unit
|
||||
internal const int MillisPerSecond = 1000;
|
||||
internal const int MillisPerMinute = MillisPerSecond * 60;
|
||||
internal const int MillisPerHour = MillisPerMinute * 60;
|
||||
internal const int MillisPerDay = MillisPerHour * 24;
|
||||
|
||||
// Number of days in a non-leap year
|
||||
internal const int DaysPerYear = 365;
|
||||
// Number of days in 4 years
|
||||
internal const int DaysPer4Years = DaysPerYear * 4 + 1;
|
||||
// Number of days in 100 years
|
||||
internal const int DaysPer100Years = DaysPer4Years * 25 - 1;
|
||||
// Number of days in 400 years
|
||||
internal const int DaysPer400Years = DaysPer100Years * 4 + 1;
|
||||
|
||||
// Number of days from 1/1/0001 to 1/1/10000
|
||||
internal const int DaysTo10000 = DaysPer400Years * 25 - 366;
|
||||
|
||||
internal const int64 MaxMillis = (int64)DaysTo10000 * MillisPerDay;
|
||||
|
||||
//
|
||||
// Calendar ID Values. This is used to get data from calendar.nlp.
|
||||
// The order of calendar ID means the order of data items in the table.
|
||||
//
|
||||
|
||||
internal const int CAL_GREGORIAN = 1 ; // Gregorian (localized) calendar
|
||||
internal const int CAL_GREGORIAN_US = 2 ; // Gregorian (U.S.) calendar
|
||||
internal const int CAL_JAPAN = 3 ; // Japanese Emperor Era calendar
|
||||
internal const int CAL_TAIWAN = 4 ; // Taiwan Era calendar
|
||||
internal const int CAL_KOREA = 5 ; // Korean Tangun Era calendar
|
||||
internal const int CAL_HIJRI = 6 ; // Hijri (Arabic Lunar) calendar
|
||||
internal const int CAL_THAI = 7 ; // Thai calendar
|
||||
internal const int CAL_HEBREW = 8 ; // Hebrew (Lunar) calendar
|
||||
internal const int CAL_GREGORIAN_ME_FRENCH = 9 ; // Gregorian Middle East French calendar
|
||||
internal const int CAL_GREGORIAN_ARABIC = 10; // Gregorian Arabic calendar
|
||||
internal const int CAL_GREGORIAN_XLIT_ENGLISH = 11; // Gregorian Transliterated English calendar
|
||||
internal const int CAL_GREGORIAN_XLIT_FRENCH = 12;
|
||||
internal const int CAL_JULIAN = 13;
|
||||
internal const int CAL_JAPANESELUNISOLAR = 14;
|
||||
internal const int CAL_CHINESELUNISOLAR = 15;
|
||||
internal const int CAL_SAKA = 16; // reserved to match Office but not implemented in our code
|
||||
internal const int CAL_LUNAR_ETO_CHN = 17; // reserved to match Office but not implemented in our code
|
||||
internal const int CAL_LUNAR_ETO_KOR = 18; // reserved to match Office but not implemented in our code
|
||||
internal const int CAL_LUNAR_ETO_ROKUYOU = 19; // reserved to match Office but not implemented in our code
|
||||
internal const int CAL_KOREANLUNISOLAR = 20;
|
||||
internal const int CAL_TAIWANLUNISOLAR = 21;
|
||||
internal const int CAL_PERSIAN = 22;
|
||||
internal const int CAL_UMALQURA = 23;
|
||||
|
||||
internal int m_currentEraValue = -1;
|
||||
|
||||
private bool m_isReadOnly = false;
|
||||
|
||||
// The minimum supported DateTime range for the calendar.
|
||||
|
||||
public virtual DateTime MinSupportedDateTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DateTime.MinValue);
|
||||
}
|
||||
}
|
||||
|
||||
// The maximum supported DateTime range for the calendar.
|
||||
public virtual DateTime MaxSupportedDateTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DateTime.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
protected this() {
|
||||
//Do-nothing constructor.
|
||||
}
|
||||
|
||||
///
|
||||
// This can not be abstract, otherwise no one can create a subclass of Calendar.
|
||||
//
|
||||
internal virtual int ID {
|
||||
get {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
// Return the Base calendar ID for calendars that didn't have defined data in calendarData
|
||||
//
|
||||
|
||||
internal virtual int BaseCalendarID
|
||||
{
|
||||
get { return ID; }
|
||||
}
|
||||
|
||||
// Returns the type of the calendar.
|
||||
//
|
||||
public virtual CalendarAlgorithmType AlgorithmType
|
||||
{
|
||||
get
|
||||
{
|
||||
return CalendarAlgorithmType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IsReadOnly
|
||||
//
|
||||
// Detect if the object is readonly.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return (m_isReadOnly); }
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Clone
|
||||
//
|
||||
// Is the implementation of IColnable.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/*public virtual Object Clone()
|
||||
{
|
||||
object o = MemberwiseClone();
|
||||
((Calendar) o).SetReadOnlyState(false);
|
||||
return (o);
|
||||
}*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ReadOnly
|
||||
//
|
||||
// Create a cloned readonly instance or return the input one if it is
|
||||
// readonly.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
public static Calendar ReadOnly(Calendar calendar)
|
||||
{
|
||||
/*if (calendar == null) { throw new ArgumentNullException("calendar"); }
|
||||
Contract.EndContractBlock();
|
||||
if (calendar.IsReadOnly) { return (calendar); }
|
||||
|
||||
Calendar clonedCalendar = (Calendar)(calendar.MemberwiseClone());
|
||||
clonedCalendar.SetReadOnlyState(true);
|
||||
|
||||
return (clonedCalendar);*/
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
|
||||
internal Result<void> VerifyWritable()
|
||||
{
|
||||
if (m_isReadOnly)
|
||||
{
|
||||
//throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
|
||||
return .Err;
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
internal void SetReadOnlyState(bool readOnly)
|
||||
{
|
||||
m_isReadOnly = readOnly;
|
||||
}
|
||||
|
||||
|
||||
/*=================================CurrentEraValue==========================
|
||||
**Action: This is used to convert CurretEra(0) to an appropriate era value.
|
||||
**Returns:
|
||||
**Arguments:
|
||||
**Exceptions:
|
||||
**Notes:
|
||||
** The value is from calendar.nlp.
|
||||
============================================================================*/
|
||||
|
||||
internal virtual int CurrentEraValue
|
||||
{
|
||||
get
|
||||
{
|
||||
// The following code assumes that the current era value can not be -1.
|
||||
if (m_currentEraValue == -1) {
|
||||
Contract.Assert(BaseCalendarID > 0, "[Calendar.CurrentEraValue] Expected ID > 0");
|
||||
m_currentEraValue = CalendarData.GetCalendarData(BaseCalendarID).iCurrentEra;
|
||||
}
|
||||
return (m_currentEraValue);
|
||||
}
|
||||
}
|
||||
|
||||
// The current era for a calendar.
|
||||
|
||||
public const int CurrentEra = 0;
|
||||
|
||||
internal int twoDigitYearMax = -1;
|
||||
|
||||
internal static Result<void> CheckAddResult(int64 ticks, DateTime minValue, DateTime maxValue) {
|
||||
if (ticks < minValue.Ticks || ticks > maxValue.Ticks) {
|
||||
return .Err;
|
||||
/*throw new ArgumentException(
|
||||
String.Format(CultureInfo.InvariantCulture, Environment.GetResourceString("Argument_ResultCalendarRange"),
|
||||
minValue, maxValue));*/
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
internal Result<DateTime> Add(DateTime time, double value, int scale) {
|
||||
// From ECMA CLI spec, Partition III, section 3.27:
|
||||
//
|
||||
// If overflow occurs converting a floating-point type to an integer, or if the floating-point value
|
||||
// being converted to an integer is a NaN, the value returned is unspecified.
|
||||
//
|
||||
// Based upon this, this method should be performing the comparison against the double
|
||||
// before attempting a cast. Otherwise, the result is undefined.
|
||||
double tempMillis = (value * scale + (value >= 0 ? 0.5 : -0.5));
|
||||
if (!((tempMillis > -(double)MaxMillis) && (tempMillis < (double)MaxMillis)))
|
||||
{
|
||||
//throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
int64 millis = (int64)tempMillis;
|
||||
int64 ticks = time.Ticks + millis * TicksPerMillisecond;
|
||||
CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
|
||||
return (DateTime(ticks));
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding the given number of
|
||||
// milliseconds to the specified DateTime. The result is computed by rounding
|
||||
// the number of milliseconds given by value to the nearest integer,
|
||||
// and adding that interval to the specified DateTime. The value
|
||||
// argument is permitted to be negative.
|
||||
//
|
||||
|
||||
public virtual Result<DateTime> AddMilliseconds(DateTime time, double milliseconds) {
|
||||
return (Add(time, milliseconds, 1));
|
||||
}
|
||||
|
||||
|
||||
// Returns the DateTime resulting from adding a fractional number of
|
||||
// days to the specified DateTime. The result is computed by rounding the
|
||||
// fractional number of days given by value to the nearest
|
||||
// millisecond, and adding that interval to the specified DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
|
||||
public virtual Result<DateTime> AddDays(DateTime time, int days) {
|
||||
return (Add(time, days, MillisPerDay));
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding a fractional number of
|
||||
// hours to the specified DateTime. The result is computed by rounding the
|
||||
// fractional number of hours given by value to the nearest
|
||||
// millisecond, and adding that interval to the specified DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
|
||||
public virtual Result<DateTime> AddHours(DateTime time, int hours) {
|
||||
return (Add(time, hours, MillisPerHour));
|
||||
}
|
||||
|
||||
|
||||
// Returns the DateTime resulting from adding a fractional number of
|
||||
// minutes to the specified DateTime. The result is computed by rounding the
|
||||
// fractional number of minutes given by value to the nearest
|
||||
// millisecond, and adding that interval to the specified DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
|
||||
public virtual Result<DateTime> AddMinutes(DateTime time, int minutes) {
|
||||
return (Add(time, minutes, MillisPerMinute));
|
||||
}
|
||||
|
||||
|
||||
// Returns the DateTime resulting from adding the given number of
|
||||
// months to the specified DateTime. The result is computed by incrementing
|
||||
// (or decrementing) the year and month parts of the specified DateTime by
|
||||
// value months, and, if required, adjusting the day part of the
|
||||
// resulting date downwards to the last day of the resulting month in the
|
||||
// resulting year. The time-of-day part of the result is the same as the
|
||||
// time-of-day part of the specified DateTime.
|
||||
//
|
||||
// In more precise terms, considering the specified DateTime to be of the
|
||||
// form y / m / d + t, where y is the
|
||||
// year, m is the month, d is the day, and t is the
|
||||
// time-of-day, the result is y1 / m1 / d1 + t,
|
||||
// where y1 and m1 are computed by adding value months
|
||||
// to y and m, and d1 is the largest value less than
|
||||
// or equal to d that denotes a valid day in month m1 of year
|
||||
// y1.
|
||||
//
|
||||
|
||||
public abstract Result<DateTime> AddMonths(DateTime time, int months);
|
||||
|
||||
// Returns the DateTime resulting from adding a number of
|
||||
// seconds to the specified DateTime. The result is computed by rounding the
|
||||
// fractional number of seconds given by value to the nearest
|
||||
// millisecond, and adding that interval to the specified DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
|
||||
public virtual Result<DateTime> AddSeconds(DateTime time, int seconds) {
|
||||
return Add(time, seconds, MillisPerSecond);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding a number of
|
||||
// weeks to the specified DateTime. The
|
||||
// value argument is permitted to be negative.
|
||||
//
|
||||
|
||||
public virtual Result<DateTime> AddWeeks(DateTime time, int weeks) {
|
||||
return (AddDays(time, weeks * 7));
|
||||
}
|
||||
|
||||
|
||||
// Returns the DateTime resulting from adding the given number of
|
||||
// years to the specified DateTime. The result is computed by incrementing
|
||||
// (or decrementing) the year part of the specified DateTime by value
|
||||
// years. If the month and day of the specified DateTime is 2/29, and if the
|
||||
// resulting year is not a leap year, the month and day of the resulting
|
||||
// DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
|
||||
// parts of the result are the same as those of the specified DateTime.
|
||||
//
|
||||
|
||||
public abstract Result<DateTime> AddYears(DateTime time, int years);
|
||||
|
||||
// Returns the day-of-month part of the specified DateTime. The returned
|
||||
// value is an integer between 1 and 31.
|
||||
//
|
||||
|
||||
public abstract Result<int> GetDayOfMonth(DateTime time);
|
||||
|
||||
// Returns the day-of-week part of the specified DateTime. The returned value
|
||||
// is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
|
||||
// Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
|
||||
// Thursday, 5 indicates Friday, and 6 indicates Saturday.
|
||||
//
|
||||
|
||||
public abstract Result<DayOfWeek> GetDayOfWeek(DateTime time);
|
||||
|
||||
// Returns the day-of-year part of the specified DateTime. The returned value
|
||||
// is an integer between 1 and 366.
|
||||
//
|
||||
|
||||
public abstract Result<int> GetDayOfYear(DateTime time);
|
||||
|
||||
// Returns the number of days in the month given by the year and
|
||||
// month arguments.
|
||||
//
|
||||
|
||||
public virtual Result<int> GetDaysInMonth(int year, int month)
|
||||
{
|
||||
return (GetDaysInMonth(year, month, CurrentEra));
|
||||
}
|
||||
|
||||
// Returns the number of days in the month given by the year and
|
||||
// month arguments for the specified era.
|
||||
//
|
||||
|
||||
public abstract Result<int> GetDaysInMonth(int year, int month, int era);
|
||||
|
||||
// Returns the number of days in the year given by the year argument for the current era.
|
||||
//
|
||||
|
||||
public virtual Result<int> GetDaysInYear(int year)
|
||||
{
|
||||
return (GetDaysInYear(year, CurrentEra));
|
||||
}
|
||||
|
||||
// Returns the number of days in the year given by the year argument for the current era.
|
||||
//
|
||||
|
||||
public abstract Result<int> GetDaysInYear(int year, int era);
|
||||
|
||||
// Returns the era for the specified DateTime value.
|
||||
|
||||
public abstract Result<int> GetEra(DateTime time);
|
||||
|
||||
/*=================================Eras==========================
|
||||
**Action: Get the list of era values.
|
||||
**Returns: The int array of the era names supported in this calendar.
|
||||
** null if era is not used.
|
||||
**Arguments: None.
|
||||
**Exceptions: None.
|
||||
============================================================================*/
|
||||
|
||||
|
||||
public abstract int[] Eras {
|
||||
get;
|
||||
}
|
||||
|
||||
|
||||
// Returns the hour part of the specified DateTime. The returned value is an
|
||||
// integer between 0 and 23.
|
||||
//
|
||||
|
||||
public virtual Result<int> GetHour(DateTime time) {
|
||||
return ((int)((time.Ticks / TicksPerHour) % 24));
|
||||
}
|
||||
|
||||
// Returns the millisecond part of the specified DateTime. The returned value
|
||||
// is an integer between 0 and 999.
|
||||
//
|
||||
|
||||
public virtual Result<double> GetMilliseconds(DateTime time) {
|
||||
return (double)((time.Ticks / TicksPerMillisecond) % 1000);
|
||||
}
|
||||
|
||||
// Returns the minute part of the specified DateTime. The returned value is
|
||||
// an integer between 0 and 59.
|
||||
//
|
||||
|
||||
public virtual Result<int> GetMinute(DateTime time) {
|
||||
return ((int)((time.Ticks / TicksPerMinute) % 60));
|
||||
}
|
||||
|
||||
// Returns the month part of the specified DateTime. The returned value is an
|
||||
// integer between 1 and 12.
|
||||
//
|
||||
|
||||
public abstract Result<int> GetMonth(DateTime time);
|
||||
|
||||
// Returns the number of months in the specified year in the current era.
|
||||
|
||||
public virtual Result<int> GetMonthsInYear(int year)
|
||||
{
|
||||
return (GetMonthsInYear(year, CurrentEra));
|
||||
}
|
||||
|
||||
// Returns the number of months in the specified year and era.
|
||||
|
||||
public abstract Result<int> GetMonthsInYear(int year, int era);
|
||||
|
||||
// Returns the second part of the specified DateTime. The returned value is
|
||||
// an integer between 0 and 59.
|
||||
//
|
||||
|
||||
public virtual Result<int> GetSecond(DateTime time) {
|
||||
return ((int)((time.Ticks / TicksPerSecond) % 60));
|
||||
}
|
||||
|
||||
/*=================================GetFirstDayWeekOfYear==========================
|
||||
**Action: Get the week of year using the FirstDay rule.
|
||||
**Returns: the week of year.
|
||||
**Arguments:
|
||||
** time
|
||||
** firstDayOfWeek the first day of week (0=Sunday, 1=Monday, ... 6=Saturday)
|
||||
**Notes:
|
||||
** The CalendarWeekRule.FirstDay rule: Week 1 begins on the first day of the year.
|
||||
** Assume f is the specifed firstDayOfWeek,
|
||||
** and n is the day of week for January 1 of the specified year.
|
||||
** Assign offset = n - f;
|
||||
** Case 1: offset = 0
|
||||
** E.g.
|
||||
** f=1
|
||||
** weekday 0 1 2 3 4 5 6 0 1
|
||||
** date 1/1
|
||||
** week# 1 2
|
||||
** then week of year = (GetDayOfYear(time) - 1) / 7 + 1
|
||||
**
|
||||
** Case 2: offset < 0
|
||||
** e.g.
|
||||
** n=1 f=3
|
||||
** weekday 0 1 2 3 4 5 6 0
|
||||
** date 1/1
|
||||
** week# 1 2
|
||||
** This means that the first week actually starts 5 days before 1/1.
|
||||
** So week of year = (GetDayOfYear(time) + (7 + offset) - 1) / 7 + 1
|
||||
** Case 3: offset > 0
|
||||
** e.g.
|
||||
** f=0 n=2
|
||||
** weekday 0 1 2 3 4 5 6 0 1 2
|
||||
** date 1/1
|
||||
** week# 1 2
|
||||
** This means that the first week actually starts 2 days before 1/1.
|
||||
** So Week of year = (GetDayOfYear(time) + offset - 1) / 7 + 1
|
||||
============================================================================*/
|
||||
|
||||
internal Result<int> GetFirstDayWeekOfYear(DateTime time, int firstDayOfWeek) {
|
||||
int dayOfYear = Try!(GetDayOfYear(time)) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0.
|
||||
// Calculate the day of week for the first day of the year.
|
||||
// dayOfWeek - (dayOfYear % 7) is the day of week for the first day of this year. Note that
|
||||
// this value can be less than 0. It's fine since we are making it positive again in calculating offset.
|
||||
int dayForJan1 = (int)Try!(GetDayOfWeek(time)) - (dayOfYear % 7);
|
||||
int offset = (dayForJan1 - firstDayOfWeek + 14) % 7;
|
||||
Contract.Assert(offset >= 0, "Calendar.GetFirstDayWeekOfYear(): offset >= 0");
|
||||
return ((dayOfYear + offset) / 7 + 1);
|
||||
}
|
||||
|
||||
private Result<int> GetWeekOfYearFullDays(DateTime time, int firstDayOfWeek, int fullDays) {
|
||||
int dayForJan1;
|
||||
int offset;
|
||||
int day;
|
||||
|
||||
int dayOfYear = Try!(GetDayOfYear(time)) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0.
|
||||
//
|
||||
// Calculate the number of days between the first day of year (1/1) and the first day of the week.
|
||||
// This value will be a positive value from 0 ~ 6. We call this value as "offset".
|
||||
//
|
||||
// If offset is 0, it means that the 1/1 is the start of the first week.
|
||||
// Assume the first day of the week is Monday, it will look like this:
|
||||
// Sun Mon Tue Wed Thu Fri Sat
|
||||
// 12/31 1/1 1/2 1/3 1/4 1/5 1/6
|
||||
// +--> First week starts here.
|
||||
//
|
||||
// If offset is 1, it means that the first day of the week is 1 day ahead of 1/1.
|
||||
// Assume the first day of the week is Monday, it will look like this:
|
||||
// Sun Mon Tue Wed Thu Fri Sat
|
||||
// 1/1 1/2 1/3 1/4 1/5 1/6 1/7
|
||||
// +--> First week starts here.
|
||||
//
|
||||
// If offset is 2, it means that the first day of the week is 2 days ahead of 1/1.
|
||||
// Assume the first day of the week is Monday, it will look like this:
|
||||
// Sat Sun Mon Tue Wed Thu Fri Sat
|
||||
// 1/1 1/2 1/3 1/4 1/5 1/6 1/7 1/8
|
||||
// +--> First week starts here.
|
||||
|
||||
|
||||
|
||||
// Day of week is 0-based.
|
||||
// Get the day of week for 1/1. This can be derived from the day of week of the target day.
|
||||
// Note that we can get a negative value. It's ok since we are going to make it a positive value when calculating the offset.
|
||||
dayForJan1 = (int)Try!(GetDayOfWeek(time)) - (dayOfYear % 7);
|
||||
|
||||
// Now, calculate the offset. Subtract the first day of week from the dayForJan1. And make it a positive value.
|
||||
offset = (firstDayOfWeek - dayForJan1 + 14) % 7;
|
||||
if (offset != 0 && offset >= fullDays)
|
||||
{
|
||||
//
|
||||
// If the offset is greater than the value of fullDays, it means that
|
||||
// the first week of the year starts on the week where Jan/1 falls on.
|
||||
//
|
||||
offset -= 7;
|
||||
}
|
||||
//
|
||||
// Calculate the day of year for specified time by taking offset into account.
|
||||
//
|
||||
day = dayOfYear - offset;
|
||||
if (day >= 0) {
|
||||
//
|
||||
// If the day of year value is greater than zero, get the week of year.
|
||||
//
|
||||
return (day/7 + 1);
|
||||
}
|
||||
//
|
||||
// Otherwise, the specified time falls on the week of previous year.
|
||||
// Call this method again by passing the last day of previous year.
|
||||
//
|
||||
// the last day of the previous year may "underflow" to no longer be a valid date time for
|
||||
// this calendar if we just subtract so we need the subclass to provide us with
|
||||
// that information
|
||||
if (time <= MinSupportedDateTime.AddDays(dayOfYear))
|
||||
{
|
||||
return GetWeekOfYearOfMinSupportedDateTime(firstDayOfWeek, fullDays);
|
||||
}
|
||||
return (GetWeekOfYearFullDays(time.AddDays(-(dayOfYear + 1)), firstDayOfWeek, fullDays));
|
||||
}
|
||||
|
||||
private Result<int> GetWeekOfYearOfMinSupportedDateTime(int firstDayOfWeek, int minimumDaysInFirstWeek)
|
||||
{
|
||||
int dayOfYear = Try!(GetDayOfYear(MinSupportedDateTime)) - 1; // Make the day of year to be 0-based, so that 1/1 is day 0.
|
||||
int dayOfWeekOfFirstOfYear = (int)Try!(GetDayOfWeek(MinSupportedDateTime)) - dayOfYear % 7;
|
||||
|
||||
// Calculate the offset (how many days from the start of the year to the start of the week)
|
||||
int offset = (firstDayOfWeek + 7 - dayOfWeekOfFirstOfYear) % 7;
|
||||
if (offset == 0 || offset >= minimumDaysInFirstWeek)
|
||||
{
|
||||
// First of year falls in the first week of the year
|
||||
return 1;
|
||||
}
|
||||
|
||||
int daysInYearBeforeMinSupportedYear = DaysInYearBeforeMinSupportedYear - 1; // Make the day of year to be 0-based, so that 1/1 is day 0.
|
||||
int dayOfWeekOfFirstOfPreviousYear = dayOfWeekOfFirstOfYear - 1 - (daysInYearBeforeMinSupportedYear % 7);
|
||||
|
||||
// starting from first day of the year, how many days do you have to go forward
|
||||
// before getting to the first day of the week?
|
||||
int daysInInitialPartialWeek = (firstDayOfWeek - dayOfWeekOfFirstOfPreviousYear + 14) % 7;
|
||||
int day = daysInYearBeforeMinSupportedYear - daysInInitialPartialWeek;
|
||||
if (daysInInitialPartialWeek >= minimumDaysInFirstWeek)
|
||||
{
|
||||
// If the offset is greater than the minimum Days in the first week, it means that
|
||||
// First of year is part of the first week of the year even though it is only a partial week
|
||||
// add another week
|
||||
day += 7;
|
||||
}
|
||||
|
||||
return (day / 7 + 1);
|
||||
}
|
||||
|
||||
// it would be nice to make this abstract but we can't since that would break previous implementations
|
||||
protected virtual int DaysInYearBeforeMinSupportedYear
|
||||
{
|
||||
get
|
||||
{
|
||||
return 365;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the week of year for the specified DateTime. The returned value is an
|
||||
// integer between 1 and 53.
|
||||
//
|
||||
|
||||
public virtual Result<int> GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
|
||||
{
|
||||
if ((int)firstDayOfWeek < 0 || (int)firstDayOfWeek > 6) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"firstDayOfWeek", Environment.GetResourceString("ArgumentOutOfRange_Range",
|
||||
DayOfWeek.Sunday, DayOfWeek.Saturday));*/
|
||||
return .Err;
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
switch (rule) {
|
||||
case CalendarWeekRule.FirstDay:
|
||||
return (GetFirstDayWeekOfYear(time, (int)firstDayOfWeek));
|
||||
case CalendarWeekRule.FirstFullWeek:
|
||||
return (GetWeekOfYearFullDays(time, (int)firstDayOfWeek, 7));
|
||||
case CalendarWeekRule.FirstFourDayWeek:
|
||||
return (GetWeekOfYearFullDays(time, (int)firstDayOfWeek, 4));
|
||||
}
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"rule", Environment.GetResourceString("ArgumentOutOfRange_Range",
|
||||
CalendarWeekRule.FirstDay, CalendarWeekRule.FirstFourDayWeek));*/
|
||||
//return .Err;
|
||||
|
||||
}
|
||||
|
||||
// Returns the year part of the specified DateTime. The returned value is an
|
||||
// integer between 1 and 9999.
|
||||
//
|
||||
|
||||
public abstract Result<int> GetYear(DateTime time);
|
||||
|
||||
// Checks whether a given day in the current era is a leap day. This method returns true if
|
||||
// the date is a leap day, or false if not.
|
||||
//
|
||||
|
||||
public virtual Result<bool> IsLeapDay(int year, int month, int day)
|
||||
{
|
||||
return (IsLeapDay(year, month, day, CurrentEra));
|
||||
}
|
||||
|
||||
// Checks whether a given day in the specified era is a leap day. This method returns true if
|
||||
// the date is a leap day, or false if not.
|
||||
//
|
||||
|
||||
public abstract Result<bool> IsLeapDay(int year, int month, int day, int era);
|
||||
|
||||
// Checks whether a given month in the current era is a leap month. This method returns true if
|
||||
// month is a leap month, or false if not.
|
||||
//
|
||||
|
||||
public virtual Result<bool> IsLeapMonth(int year, int month) {
|
||||
return (IsLeapMonth(year, month, CurrentEra));
|
||||
}
|
||||
|
||||
// Checks whether a given month in the specified era is a leap month. This method returns true if
|
||||
// month is a leap month, or false if not.
|
||||
//
|
||||
|
||||
public abstract Result<bool> IsLeapMonth(int year, int month, int era);
|
||||
|
||||
// Returns the leap month in a calendar year of the current era. This method returns 0
|
||||
// if this calendar does not have leap month, or this year is not a leap year.
|
||||
//
|
||||
|
||||
public virtual Result<int> GetLeapMonth(int year)
|
||||
{
|
||||
return (GetLeapMonth(year, CurrentEra));
|
||||
}
|
||||
|
||||
// Returns the leap month in a calendar year of the specified era. This method returns 0
|
||||
// if this calendar does not have leap month, or this year is not a leap year.
|
||||
//
|
||||
public virtual Result<int> GetLeapMonth(int year, int era)
|
||||
{
|
||||
if (!IsLeapYear(year, era))
|
||||
return 0;
|
||||
|
||||
int monthsCount = GetMonthsInYear(year, era);
|
||||
for (int month=1; month<=monthsCount; month++)
|
||||
{
|
||||
if (IsLeapMonth(year, month, era))
|
||||
return month;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Checks whether a given year in the current era is a leap year. This method returns true if
|
||||
// year is a leap year, or false if not.
|
||||
//
|
||||
|
||||
public virtual Result<bool> IsLeapYear(int year)
|
||||
{
|
||||
return (IsLeapYear(year, CurrentEra));
|
||||
}
|
||||
|
||||
// Checks whether a given year in the specified era is a leap year. This method returns true if
|
||||
// year is a leap year, or false if not.
|
||||
//
|
||||
|
||||
public abstract Result<bool> IsLeapYear(int year, int era);
|
||||
|
||||
// Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
|
||||
//
|
||||
|
||||
public virtual DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
return (ToDateTime(year, month, day, hour, minute, second, millisecond, CurrentEra));
|
||||
}
|
||||
|
||||
// Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
|
||||
//
|
||||
|
||||
public abstract Result<DateTime> ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era);
|
||||
|
||||
internal virtual bool TryToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era, out DateTime result) {
|
||||
result = DateTime.MinValue;
|
||||
switch (ToDateTime(year, month, day, hour, minute, second, millisecond, era))
|
||||
{
|
||||
case .Ok(out result):
|
||||
return true;
|
||||
case .Err:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual bool IsValidYear(int year, int era) {
|
||||
return (year >= GetYear(MinSupportedDateTime).Get() && year <= GetYear(MaxSupportedDateTime).Get());
|
||||
}
|
||||
|
||||
internal virtual bool IsValidMonth(int year, int month, int era) {
|
||||
return (IsValidYear(year, era) && month >= 1 && month <= GetMonthsInYear(year, era).Get());
|
||||
}
|
||||
|
||||
internal virtual bool IsValidDay(int year, int month, int day, int era)
|
||||
{
|
||||
return (IsValidMonth(year, month, era) && day >= 1 && day <= GetDaysInMonth(year, month, era).Get());
|
||||
}
|
||||
|
||||
// Returns and assigns the maximum value to represent a two digit year. This
|
||||
// value is the upper boundary of a 100 year range that allows a two digit year
|
||||
// to be properly translated to a four digit year. For example, if 2029 is the
|
||||
// upper boundary, then a two digit value of 30 should be interpreted as 1930
|
||||
// while a two digit value of 29 should be interpreted as 2029. In this example
|
||||
// , the 100 year range would be from 1930-2029. See ToFourDigitYear().
|
||||
|
||||
public virtual int TwoDigitYearMax
|
||||
{
|
||||
get
|
||||
{
|
||||
return (twoDigitYearMax);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
VerifyWritable();
|
||||
twoDigitYearMax = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts the year value to the appropriate century by using the
|
||||
// TwoDigitYearMax property. For example, if the TwoDigitYearMax value is 2029,
|
||||
// then a two digit value of 30 will get converted to 1930 while a two digit
|
||||
// value of 29 will get converted to 2029.
|
||||
|
||||
public virtual Result<int> ToFourDigitYear(int year) {
|
||||
if (year < 0) {
|
||||
/*throw new ArgumentOutOfRangeException("year",
|
||||
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));*/
|
||||
return .Err;
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
if (year < 100) {
|
||||
return ((TwoDigitYearMax/100 - ( year > TwoDigitYearMax % 100 ? 1 : 0))*100 + year);
|
||||
}
|
||||
// If the year value is above 100, just return the year value. Don't have to do
|
||||
// the TwoDigitYearMax comparison.
|
||||
return (year);
|
||||
}
|
||||
|
||||
// Return the tick count corresponding to the given hour, minute, second.
|
||||
// Will check the if the parameters are valid.
|
||||
internal static Result<int64> TimeToTicks(int hour, int minute, int second, int millisecond)
|
||||
{
|
||||
if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >=0 && second < 60)
|
||||
{
|
||||
if (millisecond < 0 || millisecond >= MillisPerSecond) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"millisecond",
|
||||
String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"), 0, MillisPerSecond - 1));*/
|
||||
return .Err;
|
||||
}
|
||||
return Try!(TimeSpan.TimeToTicks(hour, minute, second)) + millisecond * TicksPerMillisecond;
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadHourMinuteSecond"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
internal static int GetSystemTwoDigitYearSetting(int CalID, int defaultYearValue)
|
||||
{
|
||||
// Call nativeGetTwoDigitYearMax
|
||||
int twoDigitYearMax = CalendarData.nativeGetTwoDigitYearMax(CalID);
|
||||
if (twoDigitYearMax < 0)
|
||||
{
|
||||
twoDigitYearMax = defaultYearValue;
|
||||
}
|
||||
return (twoDigitYearMax);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization {
|
||||
using System;
|
||||
|
||||
public enum CalendarAlgorithmType
|
||||
{
|
||||
Unknown = 0, // This is the default value to return in the Calendar base class.
|
||||
SolarCalendar = 1, // Solar-base calendar, such as GregorianCalendar, jaoaneseCalendar, JulianCalendar, etc.
|
||||
// Solar calendars are based on the solar year and seasons.
|
||||
LunarCalendar = 2, // Lunar-based calendar, such as Hijri and UmAlQuraCalendar.
|
||||
// Lunar calendars are based on the path of the moon. The seasons are not accurately represented.
|
||||
LunisolarCalendar = 3 // Lunisolar-based calendar which use leap month rule, such as HebrewCalendar and Asian Lunisolar calendars.
|
||||
// Lunisolar calendars are based on the cycle of the moon, but consider the seasons as a secondary consideration,
|
||||
// so they align with the seasons as well as lunar events.
|
||||
|
||||
}
|
||||
}
|
506
BeefLibs/corlib/src/System/Globalization/CalendarData.bf
Normal file
506
BeefLibs/corlib/src/System/Globalization/CalendarData.bf
Normal file
|
@ -0,0 +1,506 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization
|
||||
{
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Diagnostics;
|
||||
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// List of calendar data
|
||||
// Note the we cache overrides.
|
||||
// Note that localized names (resource names) aren't available from here.
|
||||
//
|
||||
// NOTE: Calendars depend on the locale name that creates it. Only a few
|
||||
// properties are available without locales using CalendarData.GetCalendar(int)
|
||||
|
||||
// StructLayout is needed here otherwise compiler can re-arrange the fields.
|
||||
// We have to keep this in-[....] with the definition in calendardata.h
|
||||
//
|
||||
// WARNING WARNING WARNING
|
||||
//
|
||||
// WARNING: Anything changed here also needs to be updated on the native side (object.h see type CalendarDataBaseObject)
|
||||
// WARNING: The type loader will rearrange class member offsets so the mscorwks!CalendarDataBaseObject
|
||||
// WARNING: must be manually structured to match the true loaded class layout
|
||||
//
|
||||
internal class CalendarData
|
||||
{
|
||||
// Max calendars
|
||||
internal const int MAX_CALENDARS = 23;
|
||||
|
||||
// Identity
|
||||
internal String sNativeName ~ delete _; // Calendar Name for the locale
|
||||
|
||||
// Formats
|
||||
internal String[] saShortDates ~ DeleteContainerAndItems!(_); // Short Data format, default first
|
||||
internal String[] saYearMonths ~ DeleteContainerAndItems!(_); // Year/Month Data format, default first
|
||||
internal String[] saLongDates ~ DeleteContainerAndItems!(_); // Long Data format, default first
|
||||
internal String sMonthDay ~ delete _; // Month/Day format
|
||||
|
||||
// Calendar Parts Names
|
||||
internal String[] saEraNames ~ DeleteContainerAndItems!(_); // Names of Eras
|
||||
internal String[] saAbbrevEraNames ~ DeleteContainerAndItems!(_); // Abbreviated Era Names
|
||||
internal String[] saAbbrevEnglishEraNames ~ DeleteContainerAndItems!(_); // Abbreviated Era Names in English
|
||||
internal String[] saDayNames ~ DeleteContainerAndItems!(_); // Day Names, null to use locale data, starts on Sunday
|
||||
internal String[] saAbbrevDayNames ~ DeleteContainerAndItems!(_); // Abbrev Day Names, null to use locale data, starts on Sunday
|
||||
internal String[] saSuperShortDayNames ~ DeleteContainerAndItems!(_); // Super short Day of week names
|
||||
internal String[] saMonthNames ~ DeleteContainerAndItems!(_); // Month Names (13)
|
||||
internal String[] saAbbrevMonthNames ~ DeleteContainerAndItems!(_); // Abbrev Month Names (13)
|
||||
internal String[] saMonthGenitiveNames ~ DeleteContainerAndItems!(_); // Genitive Month Names (13)
|
||||
internal String[] saAbbrevMonthGenitiveNames~ DeleteContainerAndItems!(_); // Genitive Abbrev Month Names (13)
|
||||
internal String[] saLeapYearMonthNames ~ DeleteContainerAndItems!(_); // Multiple strings for the month names in a leap year.
|
||||
|
||||
// Integers at end to make marshaller happier
|
||||
internal int iTwoDigitYearMax=2029 ; // Max 2 digit year (for Y2K bug data entry)
|
||||
internal int iCurrentEra=0 ; // current era # (usually 1)
|
||||
|
||||
// Use overrides?
|
||||
internal bool bUseUserOverrides ; // True if we want user overrides.
|
||||
|
||||
// Static invariant for the invariant locale
|
||||
internal static CalendarData Invariant ~ delete _;
|
||||
|
||||
// Private constructor
|
||||
private this() {}
|
||||
|
||||
// Invariant constructor
|
||||
static this()
|
||||
{
|
||||
|
||||
// Set our default/gregorian US calendar data
|
||||
// Calendar IDs are 1-based, arrays are 0 based.
|
||||
CalendarData invariant = new CalendarData();
|
||||
|
||||
invariant.SetupDefaults();
|
||||
|
||||
// Calendar was built, go ahead and assign it...
|
||||
Invariant = invariant;
|
||||
}
|
||||
|
||||
void SetupDefaults()
|
||||
{
|
||||
// Set default data for calendar
|
||||
// Note that we don't load resources since this IS NOT supposed to change (by definition)
|
||||
sNativeName = new String("Gregorian Calendar"); // Calendar Name
|
||||
|
||||
// Year
|
||||
iTwoDigitYearMax = 2029; // Max 2 digit year (for Y2K bug data entry)
|
||||
iCurrentEra = 1; // Current era #
|
||||
|
||||
// Formats
|
||||
saShortDates = AllocStrings("MM/dd/yyyy", "yyyy-MM-dd"); // short date format
|
||||
saLongDates = AllocStrings("dddd, dd MMMM yyyy"); // long date format
|
||||
saYearMonths = AllocStrings("yyyy MMMM"); // year month format
|
||||
sMonthDay = new String("MMMM dd"); // Month day pattern
|
||||
|
||||
// Calendar Parts Names
|
||||
saEraNames = AllocStrings("A.D."); // Era names
|
||||
saAbbrevEraNames = AllocStrings("AD"); // Abbreviated Era names
|
||||
saAbbrevEnglishEraNames=AllocStrings("AD"); // Abbreviated era names in English
|
||||
saDayNames = AllocStrings("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");// day names
|
||||
saAbbrevDayNames = AllocStrings("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"); // abbreviated day names
|
||||
saSuperShortDayNames = AllocStrings("Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"); // The super short day names
|
||||
saMonthNames = AllocStrings("January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December", String.Empty); // month names
|
||||
saAbbrevMonthNames = AllocStrings("Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", String.Empty); // abbreviated month names
|
||||
saMonthGenitiveNames = AllocStrings(params saMonthNames); // Genitive month names (same as month names for invariant)
|
||||
saAbbrevMonthGenitiveNames=AllocStrings(params saAbbrevMonthNames); // Abbreviated genitive month names (same as abbrev month names for invariant)
|
||||
saLeapYearMonthNames = AllocStrings(params saMonthNames); // leap year month names are unused in Gregorian English (invariant)
|
||||
|
||||
bUseUserOverrides = false;
|
||||
}
|
||||
|
||||
static String[] AllocStrings(params String[] strs)
|
||||
{
|
||||
String[] newStrs = new String[strs.Count];
|
||||
for (var str in strs)
|
||||
newStrs[@str] = new String(str);
|
||||
return newStrs;
|
||||
}
|
||||
|
||||
//
|
||||
// Get a bunch of data for a calendar
|
||||
//
|
||||
internal this(String localeName, int calendarId, bool bUseUserOverrides)
|
||||
{
|
||||
String[] Clone(String[] strs)
|
||||
{
|
||||
var newStrs = new String[strs.Count];
|
||||
for (var str in strs)
|
||||
newStrs[@str] = new String(str);
|
||||
return newStrs;
|
||||
}
|
||||
|
||||
// Call nativeGetCalendarData to populate the data
|
||||
this.bUseUserOverrides = bUseUserOverrides;
|
||||
if (!nativeGetCalendarData(this, localeName, calendarId))
|
||||
{
|
||||
//Contract.Assert(false, "[CalendarData] nativeGetCalendarData call isn't expected to fail for calendar " + calendarId + " locale " +localeName);
|
||||
Debug.FatalError("[CalendarData] nativeGetCalendarData call isn't expected to fail for calendar");
|
||||
// Something failed, try invariant for missing parts
|
||||
// This is really not good, but we don't want the callers to crash.
|
||||
#unwarn
|
||||
if (this.sNativeName == null) this.sNativeName = String.Empty; // Calendar Name for the locale.
|
||||
|
||||
// Formats
|
||||
if (this.saShortDates == null) this.saShortDates = Clone(Invariant.saShortDates); // Short Data format, default first
|
||||
if (this.saYearMonths == null) this.saYearMonths = Clone(Invariant.saYearMonths); // Year/Month Data format, default first
|
||||
if (this.saLongDates == null) this.saLongDates = Clone(Invariant.saLongDates); // Long Data format, default first
|
||||
if (this.sMonthDay == null) this.sMonthDay = Invariant.sMonthDay; // Month/Day format
|
||||
|
||||
// Calendar Parts Names
|
||||
if (this.saEraNames == null) this.saEraNames = Invariant.saEraNames; // Names of Eras
|
||||
if (this.saAbbrevEraNames == null) this.saAbbrevEraNames = Invariant.saAbbrevEraNames; // Abbreviated Era Names
|
||||
if (this.saAbbrevEnglishEraNames == null) this.saAbbrevEnglishEraNames = Invariant.saAbbrevEnglishEraNames; // Abbreviated Era Names in English
|
||||
if (this.saDayNames == null) this.saDayNames = Invariant.saDayNames; // Day Names, null to use locale data, starts on Sunday
|
||||
if (this.saAbbrevDayNames == null) this.saAbbrevDayNames = Invariant.saAbbrevDayNames; // Abbrev Day Names, null to use locale data, starts on Sunday
|
||||
if (this.saSuperShortDayNames == null) this.saSuperShortDayNames = Invariant.saSuperShortDayNames; // Super short Day of week names
|
||||
if (this.saMonthNames == null) this.saMonthNames = Invariant.saMonthNames; // Month Names (13)
|
||||
if (this.saAbbrevMonthNames == null) this.saAbbrevMonthNames = Invariant.saAbbrevMonthNames; // Abbrev Month Names (13)
|
||||
// Genitive and Leap names can follow the fallback below
|
||||
}
|
||||
|
||||
// Clean up the escaping of the formats
|
||||
|
||||
CultureData.ReescapeWin32Strings(this.saShortDates);
|
||||
CultureData.ReescapeWin32Strings(this.saLongDates);
|
||||
CultureData.ReescapeWin32Strings(this.saYearMonths);
|
||||
CultureData.ReescapeWin32String(this.sMonthDay);
|
||||
|
||||
if ((CalendarId)calendarId == CalendarId.TAIWAN)
|
||||
{
|
||||
// for Geo----al reasons, the ----ese native name should only be returned when
|
||||
// for ----ese SKU
|
||||
if (CultureInfo.IsTaiwanSku)
|
||||
{
|
||||
// We got the month/day names from the OS (same as gregorian), but the native name is wrong
|
||||
this.sNativeName = "\x4e2d\x83ef\x6c11\x570b\x66c6";
|
||||
}
|
||||
else
|
||||
{
|
||||
this.sNativeName = String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for null genitive names (in case unmanaged side skips it for non-gregorian calendars, etc)
|
||||
if (this.saMonthGenitiveNames == null || String.IsNullOrEmpty(this.saMonthGenitiveNames[0]))
|
||||
this.saMonthGenitiveNames = this.saMonthNames; // Genitive month names (same as month names for invariant)
|
||||
if (this.saAbbrevMonthGenitiveNames == null || String.IsNullOrEmpty(this.saAbbrevMonthGenitiveNames[0]))
|
||||
this.saAbbrevMonthGenitiveNames = this.saAbbrevMonthNames; // Abbreviated genitive month names (same as abbrev month names for invariant)
|
||||
if (this.saLeapYearMonthNames == null || String.IsNullOrEmpty(this.saLeapYearMonthNames[0]))
|
||||
this.saLeapYearMonthNames = this.saMonthNames;
|
||||
|
||||
InitializeEraNames(localeName, calendarId);
|
||||
|
||||
InitializeAbbreviatedEraNames(localeName, calendarId);
|
||||
|
||||
// Abbreviated English Era Names are only used for the Japanese calendar.
|
||||
if (calendarId == (int)CalendarId.JAPAN)
|
||||
{
|
||||
//this.saAbbrevEnglishEraNames = JapaneseCalendar.EnglishEraNames();
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
else
|
||||
{
|
||||
// For all others just use the an empty string (doesn't matter we'll never ask for it for other calendars)
|
||||
this.saAbbrevEnglishEraNames = new String[] { "" };
|
||||
}
|
||||
|
||||
// Japanese is the only thing with > 1 era. Its current era # is how many ever
|
||||
// eras are in the array. (And the others all have 1 string in the array)
|
||||
this.iCurrentEra = this.saEraNames.Count;
|
||||
}
|
||||
|
||||
private void InitializeEraNames(String localeName, int calendarId)
|
||||
{
|
||||
// Note that the saEraNames only include "A.D." We don't have localized names for other calendars available from windows
|
||||
switch ((CalendarId)calendarId)
|
||||
{
|
||||
// For Localized Gregorian we really expect the data from the OS.
|
||||
case CalendarId.GREGORIAN:
|
||||
// Fallback for CoreCLR < Win7 or culture.dll missing
|
||||
if (this.saEraNames == null || this.saEraNames.Count == 0 || String.IsNullOrEmpty(this.saEraNames[0]))
|
||||
{
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("A.D.");
|
||||
}
|
||||
break;
|
||||
|
||||
// The rest of the calendars have constant data, so we'll just use that
|
||||
case CalendarId.GREGORIAN_US:
|
||||
case CalendarId.JULIAN:
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("A.D.");
|
||||
break;
|
||||
case CalendarId.HEBREW:
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("C.E.");
|
||||
break;
|
||||
case CalendarId.HIJRI:
|
||||
case CalendarId.UMALQURA:
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
if (localeName == "dv-MV")
|
||||
{
|
||||
// Special case for Divehi
|
||||
this.saEraNames = AllocStrings("\x0780\x07a8\x0796\x07b0\x0783\x07a9");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.saEraNames = AllocStrings("\x0628\x0639\x062F \x0627\x0644\x0647\x062C\x0631\x0629");
|
||||
}
|
||||
break;
|
||||
case CalendarId.GREGORIAN_ARABIC:
|
||||
case CalendarId.GREGORIAN_XLIT_ENGLISH:
|
||||
case CalendarId.GREGORIAN_XLIT_FRENCH:
|
||||
// These are all the same:
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("\x0645");
|
||||
break;
|
||||
|
||||
case CalendarId.GREGORIAN_ME_FRENCH:
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("ap. J.-C.");
|
||||
break;
|
||||
|
||||
case CalendarId.TAIWAN:
|
||||
// for Geo----al reasons, the ----ese native name should only be returned when
|
||||
// for ----ese SKU
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
if (CultureInfo.IsTaiwanSku)
|
||||
{
|
||||
//
|
||||
this.saEraNames = AllocStrings("\x4e2d\x83ef\x6c11\x570b");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.saEraNames = AllocStrings(String.Empty);
|
||||
}
|
||||
break;
|
||||
|
||||
case CalendarId.KOREA:
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("\xb2e8\xae30");
|
||||
break;
|
||||
|
||||
case CalendarId.THAI:
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("\x0e1e\x002e\x0e28\x002e");
|
||||
break;
|
||||
|
||||
case CalendarId.JAPAN:
|
||||
case CalendarId.JAPANESELUNISOLAR:
|
||||
//this.saEraNames = JapaneseCalendar.EraNames();
|
||||
Runtime.NotImplemented();
|
||||
//break;
|
||||
|
||||
case CalendarId.PERSIAN:
|
||||
if (this.saEraNames == null || this.saEraNames.Count == 0 || String.IsNullOrEmpty(this.saEraNames[0]))
|
||||
{
|
||||
DeleteContainerAndItems!(this.saEraNames);
|
||||
this.saEraNames = AllocStrings("\x0647\x002e\x0634");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Most calendars are just "A.D."
|
||||
this.saEraNames = Invariant.saEraNames;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAbbreviatedEraNames(StringView localeName, int calendarId)
|
||||
{
|
||||
// Note that the saAbbrevEraNames only include "AD" We don't have localized names for other calendars available from windows
|
||||
switch ((CalendarId)calendarId)
|
||||
{
|
||||
// For Localized Gregorian we really expect the data from the OS.
|
||||
case CalendarId.GREGORIAN:
|
||||
// Fallback for CoreCLR < Win7 or culture.dll missing
|
||||
if (this.saAbbrevEraNames == null || this.saAbbrevEraNames.Count == 0 || String.IsNullOrEmpty(this.saAbbrevEraNames[0]))
|
||||
{
|
||||
DeleteContainerAndItems!(this.saAbbrevEraNames);
|
||||
this.saAbbrevEraNames = AllocStrings("AD");
|
||||
}
|
||||
|
||||
// The rest of the calendars have constant data, so we'll just use that
|
||||
case CalendarId.GREGORIAN_US:
|
||||
case CalendarId.JULIAN:
|
||||
DeleteContainerAndItems!(this.saAbbrevEraNames);
|
||||
this.saAbbrevEraNames = AllocStrings("AD");
|
||||
break;
|
||||
case CalendarId.JAPAN:
|
||||
case CalendarId.JAPANESELUNISOLAR:
|
||||
//this.saAbbrevEraNames = JapaneseCalendar.AbbrevEraNames();
|
||||
Runtime.NotImplemented();
|
||||
case CalendarId.HIJRI:
|
||||
case CalendarId.UMALQURA:
|
||||
DeleteContainerAndItems!(this.saAbbrevEraNames);
|
||||
if (localeName == "dv-MV")
|
||||
{
|
||||
// Special case for Divehi
|
||||
this.saAbbrevEraNames = AllocStrings("\x0780\x002e");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.saAbbrevEraNames = AllocStrings("\x0647\x0640");
|
||||
}
|
||||
break;
|
||||
case CalendarId.TAIWAN:
|
||||
// Get era name and abbreviate it
|
||||
DeleteContainerAndItems!(this.saAbbrevEraNames);
|
||||
this.saAbbrevEraNames = new String[1];
|
||||
if (this.saEraNames[0].Length == 4)
|
||||
{
|
||||
this.saAbbrevEraNames[0] = new String(this.saEraNames[0], 2, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.saAbbrevEraNames[0] = new String(this.saEraNames[0]);
|
||||
}
|
||||
break;
|
||||
|
||||
case CalendarId.PERSIAN:
|
||||
if (this.saAbbrevEraNames == null || this.saAbbrevEraNames.Count == 0 || String.IsNullOrEmpty(this.saAbbrevEraNames[0]))
|
||||
{
|
||||
DeleteContainerAndItems!(this.saAbbrevEraNames);
|
||||
this.saAbbrevEraNames = AllocStrings(params this.saEraNames);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Most calendars just use the full name
|
||||
this.saAbbrevEraNames = AllocStrings(params this.saEraNames);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal static CalendarData GetCalendarData(int calendarId)
|
||||
{
|
||||
/*//
|
||||
// Get a calendar.
|
||||
// Unfortunately we depend on the locale in the OS, so we need a locale
|
||||
// no matter what. So just get the appropriate calendar from the
|
||||
// appropriate locale here
|
||||
//
|
||||
|
||||
// Get a culture name
|
||||
//
|
||||
String culture = CalendarIdToCultureName(calendarId);
|
||||
|
||||
// Return our calendar
|
||||
return CultureInfo.GetCultureInfo(culture).m_cultureData.GetCalendar(calendarId);*/
|
||||
Runtime.FatalError();
|
||||
}
|
||||
|
||||
//
|
||||
// Helper methods
|
||||
//
|
||||
private static String CalendarIdToCultureName(int calendarId)
|
||||
{
|
||||
switch (calendarId)
|
||||
{
|
||||
case Calendar.CAL_GREGORIAN_US:
|
||||
return "fa-IR"; // "fa-IR" Iran
|
||||
|
||||
case Calendar.CAL_JAPAN:
|
||||
return "ja-JP"; // "ja-JP" Japan
|
||||
|
||||
case Calendar.CAL_TAIWAN:
|
||||
return "zh-TW"; // zh-TW Taiwan
|
||||
|
||||
case Calendar.CAL_KOREA:
|
||||
return "ko-KR"; // "ko-KR" Korea
|
||||
|
||||
case Calendar.CAL_HIJRI:
|
||||
case Calendar.CAL_GREGORIAN_ARABIC:
|
||||
case Calendar.CAL_UMALQURA:
|
||||
return "ar-SA"; // "ar-SA" Saudi Arabia
|
||||
|
||||
case Calendar.CAL_THAI:
|
||||
return "th-TH"; // "th-TH" Thailand
|
||||
|
||||
case Calendar.CAL_HEBREW:
|
||||
return "he-IL"; // "he-IL" Israel
|
||||
|
||||
case Calendar.CAL_GREGORIAN_ME_FRENCH:
|
||||
return "ar-DZ"; // "ar-DZ" Algeria
|
||||
|
||||
case Calendar.CAL_GREGORIAN_XLIT_ENGLISH:
|
||||
case Calendar.CAL_GREGORIAN_XLIT_FRENCH:
|
||||
return "ar-IQ"; // "ar-IQ"; Iraq
|
||||
|
||||
default:
|
||||
// Default to gregorian en-US
|
||||
break;
|
||||
}
|
||||
|
||||
return "en-US";
|
||||
}
|
||||
|
||||
/*internal void FixupWin7MonthDaySemicolonBug()
|
||||
{
|
||||
int unescapedCharacterIndex = FindUnescapedCharacter(sMonthDay, ';');
|
||||
if (unescapedCharacterIndex > 0)
|
||||
{
|
||||
sMonthDay = sMonthDay.Substring(0, unescapedCharacterIndex);
|
||||
}
|
||||
}*/
|
||||
|
||||
private static int FindUnescapedCharacter(String s, char8 charToFind)
|
||||
{
|
||||
bool inComment = false;
|
||||
int length = s.Length;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
char8 c = s[i];
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\'':
|
||||
inComment = !inComment;
|
||||
break;
|
||||
case '\\':
|
||||
i++; // escape sequence -- skip next character
|
||||
break;
|
||||
default:
|
||||
if (!inComment && charToFind == c)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal static int nativeGetTwoDigitYearMax(int calID)
|
||||
{
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
|
||||
private static bool nativeGetCalendarData(CalendarData data, String localeName, int calendar)
|
||||
{
|
||||
data.SetupDefaults();
|
||||
//NotImplemented();
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static int nativeGetCalendars(String localeName, bool useUserOverride, int[] calendars)
|
||||
{
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
17
BeefLibs/corlib/src/System/Globalization/CalendarWeekRule.bf
Normal file
17
BeefLibs/corlib/src/System/Globalization/CalendarWeekRule.bf
Normal file
|
@ -0,0 +1,17 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization
|
||||
{
|
||||
using System;
|
||||
|
||||
public enum CalendarWeekRule
|
||||
{
|
||||
FirstDay = 0, // Week 1 begins on the first day of the year
|
||||
|
||||
FirstFullWeek = 1, // Week 1 begins on first FirstDayOfWeek not before the first day of the year
|
||||
|
||||
FirstFourDayWeek = 2 // Week 1 begins on first FirstDayOfWeek such that FirstDayOfWeek+3 is not before the first day of the year
|
||||
}
|
||||
}
|
589
BeefLibs/corlib/src/System/Globalization/CultureData.bf
Normal file
589
BeefLibs/corlib/src/System/Globalization/CultureData.bf
Normal file
|
@ -0,0 +1,589 @@
|
|||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization
|
||||
{
|
||||
class CultureData
|
||||
{
|
||||
private const uint LOCALE_NOUSEROVERRIDE = 0x80000000; // do not use user overrides
|
||||
|
||||
private const uint LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002; // localized name of locale, eg "German (Germany)" in UI language
|
||||
private const uint LOCALE_SENGLISHDISPLAYNAME = 0x00000072; // Display name (language + country usually) in English, eg "German (Germany)"
|
||||
private const uint LOCALE_SNATIVEDISPLAYNAME = 0x00000073; // Display name in native locale language, eg "Deutsch (Deutschland)
|
||||
|
||||
private const uint LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006f; // Language Display Name for a language, eg "German" in UI language
|
||||
private const uint LOCALE_SENGLISHLANGUAGENAME = 0x00001001; // English name of language, eg "German"
|
||||
private const uint LOCALE_SNATIVELANGUAGENAME = 0x00000004; // native name of language, eg "Deutsch"
|
||||
|
||||
private const uint LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006; // localized name of country, eg "Germany" in UI language
|
||||
private const uint LOCALE_SENGLISHCOUNTRYNAME = 0x00001002; // English name of country, eg "Germany"
|
||||
private const uint LOCALE_SNATIVECOUNTRYNAME = 0x00000008; // native name of country, eg "Deutschland"
|
||||
|
||||
private const uint LOCALE_STIME = 0x0000001E; // time separator (derived from LOCALE_STIMEFORMAT, use that instead)
|
||||
private const uint LOCALE_STIMEFORMAT = 0x00001003; // time format string
|
||||
|
||||
const int32 undef = -1;
|
||||
|
||||
// Override flag
|
||||
private String sRealName ~ delete _; // Name you passed in (ie: en-US, en, or de-DE_phoneb)
|
||||
private String sWindowsName ~ delete _; // Name OS thinks the object is (ie: de-DE_phoneb, or en-US (even if en was passed in))
|
||||
|
||||
// Identity
|
||||
private String sName ~ delete _; // locale name (ie: en-us, NO sort info, but could be neutral)
|
||||
private String sParent; // Parent name (which may be a custom locale/culture)
|
||||
private String sLocalizedDisplayName; // Localized pretty name for this locale
|
||||
private String sEnglishDisplayName; // English pretty name for this locale
|
||||
private String sNativeDisplayName; // Native pretty name for this locale
|
||||
private String sSpecificCulture ~ delete _; // The culture name to be used in CultureInfo.CreateSpecificCulture(), en-US form if neutral, sort name if sort
|
||||
|
||||
// Language
|
||||
private String sISO639Language; // ISO 639 Language Name
|
||||
private String sLocalizedLanguage; // Localized name for this language
|
||||
private String sEnglishLanguage; // English name for this language
|
||||
private String sNativeLanguage; // Native name of this language
|
||||
|
||||
// Region
|
||||
private String sRegionName; // (RegionInfo)
|
||||
// private int iCountry=undef ; // (user can override) ---- code (RegionInfo)
|
||||
private int iGeoId = undef; // GeoId
|
||||
private String sLocalizedCountry; // localized country name
|
||||
private String sEnglishCountry; // english country name (RegionInfo)
|
||||
private String sNativeCountry; // native country name
|
||||
private String sISO3166CountryName; // ISO 3166 (RegionInfo), ie: US
|
||||
|
||||
// Numbers
|
||||
private String sPositiveSign; // (user can override) positive sign
|
||||
private String sNegativeSign; // (user can override) negative sign
|
||||
private String[] saNativeDigits; // (user can override) native characters for digits 0-9
|
||||
// (nfi populates these 5, don't have to be = undef)
|
||||
private int32 iDigitSubstitution; // (user can override) Digit substitution 0=context, 1=none/arabic, 2=Native/national (2 seems to be unused)
|
||||
private int32 iLeadingZeros; // (user can override) leading zeros 0 = no leading zeros, 1 = leading zeros
|
||||
private int32 iDigits; // (user can override) number of fractional digits
|
||||
private int32 iNegativeNumber; // (user can override) negative number format
|
||||
private int32[] waGrouping; // (user can override) grouping of digits
|
||||
private String sDecimalSeparator; // (user can override) decimal separator
|
||||
private String sThousandSeparator; // (user can override) thousands separator
|
||||
private String sNaN; // Not a Number
|
||||
private String sPositiveInfinity; // + Infinity
|
||||
private String sNegativeInfinity; // - Infinity
|
||||
|
||||
// Percent
|
||||
private int iNegativePercent = undef; // Negative Percent (0-3)
|
||||
private int iPositivePercent = undef; // Positive Percent (0-11)
|
||||
private String sPercent; // Percent (%) symbol
|
||||
private String sPerMille; // PerMille (‰) symbol
|
||||
|
||||
// Currency
|
||||
private String sCurrency; // (user can override) local monetary symbol
|
||||
private String sIntlMonetarySymbol; // international monetary symbol (RegionInfo)
|
||||
private String sEnglishCurrency; // English name for this currency
|
||||
private String sNativeCurrency; // Native name for this currency
|
||||
// (nfi populates these 4, don't have to be = undef)
|
||||
private int iCurrencyDigits; // (user can override) # local monetary fractional digits
|
||||
private int iCurrency; // (user can override) positive currency format
|
||||
private int iNegativeCurrency; // (user can override) negative currency format
|
||||
private int[] waMonetaryGrouping; // (user can override) monetary grouping of digits
|
||||
private String sMonetaryDecimal; // (user can override) monetary decimal separator
|
||||
private String sMonetaryThousand; // (user can override) monetary thousands separator
|
||||
|
||||
// Misc
|
||||
private int32 iMeasure = undef; // (user can override) system of measurement 0=metric, 1=US (RegionInfo)
|
||||
private String sListSeparator; // (user can override) list separator
|
||||
// private int iPaperSize ; // default paper size (RegionInfo)
|
||||
|
||||
// Time
|
||||
private String sAM1159; // (user can override) AM designator
|
||||
private String sPM2359; // (user can override) PM designator
|
||||
private String sTimeSeparator;
|
||||
private volatile String[] saLongTimes; // (user can override) time format
|
||||
private volatile String[] saShortTimes; // short time format
|
||||
private volatile String[] saDurationFormats; // time duration format
|
||||
|
||||
// Calendar specific data
|
||||
private int iFirstDayOfWeek = undef; // (user can override) first day of week (gregorian really)
|
||||
private int iFirstWeekOfYear = undef; // (user can override) first week of year (gregorian really)
|
||||
private volatile int[] waCalendars; // all available calendar type(s). The first one is the default calendar
|
||||
|
||||
// Store for specific data about each calendar
|
||||
private CalendarData[] calendars; // Store for specific calendar data
|
||||
|
||||
// Text information
|
||||
private int iReadingLayout = undef; // Reading layout data
|
||||
// 0 - Left to right (eg en-US)
|
||||
// 1 - Right to left (eg arabic locales)
|
||||
// 2 - Vertical top to bottom with columns to the left and also left to right (ja-JP locales)
|
||||
// 3 - Vertical top to bottom with columns proceeding to the right
|
||||
|
||||
private String sTextInfo; // Text info name to use for custom
|
||||
private String sCompareInfo; // Compare info name (including sorting key) to use if custom
|
||||
private String sScripts; // Typical Scripts for this locale (latn;cyrl; etc)
|
||||
|
||||
// The bools all need to be in one spot
|
||||
private bool bUseOverrides; // use user overrides?
|
||||
private bool bNeutral; // Flags for the culture (ie: neutral or not right now)
|
||||
|
||||
List<Object> ownedObjects = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal void GetNFIValues(NumberFormatInfo nfi)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool IsInvariantCulture
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal Calendar DefaultCalendar
|
||||
{
|
||||
get
|
||||
{
|
||||
/*int defaultCalId = DoGetLocaleInfoInt(LOCALE_ICALENDARTYPE);
|
||||
if (defaultCalId == 0)
|
||||
{
|
||||
defaultCalId = this.CalendarIds[0];
|
||||
}
|
||||
|
||||
return CultureInfo.GetCalendarInstance(defaultCalId);*/
|
||||
//Runtime.NotImplemented();
|
||||
// NotImplemented
|
||||
return CultureInfo.GetCalendarInstance(Calendar.CAL_GREGORIAN);
|
||||
}
|
||||
}
|
||||
|
||||
internal StringView CultureName
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (this.sName)
|
||||
{
|
||||
case "zh-CHS":
|
||||
case "zh-CHT":
|
||||
return this.sName;
|
||||
}
|
||||
return this.sRealName;
|
||||
}
|
||||
}
|
||||
|
||||
internal String[] LongTimes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.saLongTimes == null
|
||||
#if !FEATURE_CORECLR
|
||||
|| UseUserOverride
|
||||
#endif
|
||||
)
|
||||
{
|
||||
/*String[] longTimes = DoEnumTimeFormats();
|
||||
if (longTimes == null || longTimes.Length == 0)
|
||||
{
|
||||
this.saLongTimes = Invariant.saLongTimes;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.saLongTimes = longTimes;
|
||||
}*/
|
||||
//NotImpl
|
||||
saLongTimes = new String[](new .("h:mm:ss tt"));
|
||||
ownedObjects.Add(saLongTimes);
|
||||
ownedObjects.Add(saLongTimes[0]);
|
||||
|
||||
}
|
||||
return this.saLongTimes;
|
||||
}
|
||||
}
|
||||
|
||||
internal String[] ShortTimes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.saShortTimes == null
|
||||
#if !FEATURE_CORECLR
|
||||
|| UseUserOverride
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// Try to get the short times from the OS/culture.dll
|
||||
/*String[] shortTimes = DoEnumShortTimeFormats();
|
||||
|
||||
if (shortTimes == null || shortTimes.Length == 0)
|
||||
{
|
||||
//
|
||||
// If we couldn't find short times, then compute them from long times
|
||||
// (eg: CORECLR on < Win7 OS & fallback for missing culture.dll)
|
||||
//
|
||||
shortTimes = DeriveShortTimesFromLong();
|
||||
}
|
||||
|
||||
// Found short times, use them
|
||||
this.saShortTimes = shortTimes;*/
|
||||
saShortTimes = new String[](new .("h:mm tt"));
|
||||
ownedObjects.Add(saShortTimes);
|
||||
ownedObjects.Add(saShortTimes[0]);
|
||||
}
|
||||
return this.saShortTimes;
|
||||
}
|
||||
}
|
||||
|
||||
internal static CultureData GetCultureData(StringView cultureName, bool useUserOverride)
|
||||
{
|
||||
CultureData culture = CreateCultureData(cultureName, useUserOverride);
|
||||
return culture;
|
||||
|
||||
}
|
||||
|
||||
private static CultureData CreateCultureData(StringView cultureName, bool useUserOverride)
|
||||
{
|
||||
CultureData culture = new CultureData();
|
||||
//culture.bUseOverrides = useUserOverride;
|
||||
//culture.sRealName = cultureName;
|
||||
|
||||
// Ask native code if that one's real
|
||||
if (culture.InitCultureData() == false)
|
||||
{
|
||||
/*#if !FEATURE_CORECLR
|
||||
if (culture.InitCompatibilityCultureData() == false
|
||||
&& culture.InitLegacyAlternateSortData() == false)
|
||||
#endif*/
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return culture;
|
||||
}
|
||||
|
||||
private bool InitCultureData()
|
||||
{
|
||||
sWindowsName = new String("en-US");
|
||||
sRealName = new String("en-US");
|
||||
sSpecificCulture = new String("en-US");
|
||||
sName = new String("en-US");
|
||||
|
||||
//NotImplemented
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Reescape a Win32 style quote string as a NLS+ style quoted string
|
||||
//
|
||||
// This is also the escaping style used by custom culture data files
|
||||
//
|
||||
// NLS+ uses \ to escape the next character, whether in a quoted string or
|
||||
// not, so we always have to change \ to \\.
|
||||
//
|
||||
// NLS+ uses \' to escape a quote inside a quoted string so we have to change
|
||||
// '' to \' (if inside a quoted string)
|
||||
//
|
||||
// We don't build the stringbuilder unless we find something to change
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
static internal void ReescapeWin32String(String inStr)
|
||||
{
|
||||
// If we don't have data, then don't try anything
|
||||
if (inStr == null)
|
||||
return;
|
||||
|
||||
var inStr;
|
||||
|
||||
|
||||
bool inQuote = false;
|
||||
for (int i = 0; i < inStr.Length; i++)
|
||||
{
|
||||
// Look for quote
|
||||
if (inStr[i] == '\'')
|
||||
{
|
||||
// Already in quote?
|
||||
if (inQuote)
|
||||
{
|
||||
// See another single quote. Is this '' of 'fred''s' or '''', or is it an ending quote?
|
||||
if (i + 1 < inStr.Length && inStr[i + 1] == '\'')
|
||||
{
|
||||
// Found another ', so we have ''. Need to add \' instead.
|
||||
// 1st make sure we have our stringbuilder
|
||||
|
||||
// Append a \' and keep going (so we don't turn off quote mode)
|
||||
inStr.Insert(i, "\\");
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Turning off quote mode, fall through to add it
|
||||
inQuote = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found beginning quote, fall through to add it
|
||||
inQuote = true;
|
||||
}
|
||||
}
|
||||
// Is there a single \ character?
|
||||
else if (inStr[i] == '\\')
|
||||
{
|
||||
// Found a \, need to change it to \\
|
||||
// 1st make sure we have our stringbuilder
|
||||
|
||||
// Append our \\ to the string & continue
|
||||
inStr.Insert(i, "\\\\");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static internal void ReescapeWin32Strings(String[] inArray)
|
||||
{
|
||||
if (inArray != null)
|
||||
{
|
||||
for (int i = 0; i < inArray.Count; i++)
|
||||
{
|
||||
ReescapeWin32String(inArray[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool UseUserOverride
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.bUseOverrides;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsSupplementalCustomCulture
|
||||
{
|
||||
get
|
||||
{
|
||||
//return IsCustomCultureId(this.ILANGUAGE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static private bool IsOsWin7OrPrior()
|
||||
{
|
||||
return Environment.OSVersion.Platform == PlatformID.Win32NT &&
|
||||
Environment.OSVersion.Version < Version(6, 2); // Win7 is 6.1.Build.Revision so we have to check for anything less than 6.2
|
||||
}
|
||||
|
||||
internal CalendarData GetCalendar(int calendarId)
|
||||
{
|
||||
Debug.Assert(calendarId > 0 && calendarId <= CalendarData.MAX_CALENDARS,
|
||||
"[CultureData.GetCalendar] Expect calendarId to be in a valid range");
|
||||
|
||||
// arrays are 0 based, calendarIds are 1 based
|
||||
int calendarIndex = calendarId - 1;
|
||||
|
||||
// Have to have calendars
|
||||
if (calendars == null)
|
||||
{
|
||||
calendars = new CalendarData[CalendarData.MAX_CALENDARS];
|
||||
}
|
||||
|
||||
// we need the following local variable to avoid returning null
|
||||
// when another thread creates a new array of CalendarData (above)
|
||||
// right after we insert the newly created CalendarData (below)
|
||||
CalendarData calendarData = calendars[calendarIndex];
|
||||
// Make sure that calendar has data
|
||||
if (calendarData == null
|
||||
#if !FEATURE_CORECLR
|
||||
|| UseUserOverride
|
||||
#endif
|
||||
)
|
||||
{
|
||||
//Contract.Assert(this.sWindowsName != null, "[CultureData.GetCalendar] Expected this.sWindowsName to be populated by COMNlsInfo::nativeInitCultureData already");
|
||||
calendarData = new CalendarData(this.sWindowsName, calendarId, this.UseUserOverride);
|
||||
/*#if !FEATURE_CORECLR
|
||||
//Work around issue where Win7 data for MonthDay contains invalid two sets of data separated by semicolon
|
||||
//even though MonthDay is not enumerated
|
||||
if (IsOsWin7OrPrior() && !IsSupplementalCustomCulture && !IsReplacementCulture)
|
||||
{
|
||||
calendarData.FixupWin7MonthDaySemicolonBug();
|
||||
}
|
||||
#endif*/
|
||||
calendars[calendarIndex] = calendarData;
|
||||
}
|
||||
|
||||
return calendarData;
|
||||
}
|
||||
|
||||
internal String[] ShortDates(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saShortDates;
|
||||
}
|
||||
|
||||
internal String[] LongDates(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saLongDates;
|
||||
}
|
||||
|
||||
// (user can override) date year/month format.
|
||||
internal String[] YearMonths(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saYearMonths;
|
||||
}
|
||||
|
||||
// day names
|
||||
internal String[] DayNames(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saDayNames;
|
||||
}
|
||||
|
||||
// abbreviated day names
|
||||
internal String[] AbbreviatedDayNames(int calendarId)
|
||||
{
|
||||
// Get abbreviated day names for this calendar from the OS if necessary
|
||||
return GetCalendar(calendarId).saAbbrevDayNames;
|
||||
}
|
||||
|
||||
// The super short day names
|
||||
internal String[] SuperShortDayNames(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saSuperShortDayNames;
|
||||
}
|
||||
|
||||
// month names
|
||||
internal String[] MonthNames(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saMonthNames;
|
||||
}
|
||||
|
||||
// Genitive month names
|
||||
internal String[] GenitiveMonthNames(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saMonthGenitiveNames;
|
||||
}
|
||||
|
||||
// month names
|
||||
internal String[] AbbreviatedMonthNames(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saAbbrevMonthNames;
|
||||
}
|
||||
|
||||
// Genitive month names
|
||||
internal String[] AbbreviatedGenitiveMonthNames(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saAbbrevMonthGenitiveNames;
|
||||
}
|
||||
|
||||
// Leap year month names
|
||||
// Note: This only applies to Hebrew, and it basically adds a "1" to the 6th month name
|
||||
// the non-leap names skip the 7th name in the normal month name array
|
||||
internal String[] LeapYearMonthNames(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).saLeapYearMonthNames;
|
||||
}
|
||||
|
||||
// month/day format (single string, no override)
|
||||
internal String MonthDay(int calendarId)
|
||||
{
|
||||
return GetCalendar(calendarId).sMonthDay;
|
||||
}
|
||||
|
||||
void DoGetLocaleInfo(uint lctype, String outStr)
|
||||
{
|
||||
DoGetLocaleInfo(this.sWindowsName, lctype, outStr);
|
||||
}
|
||||
|
||||
void DoGetLocaleInfo(StringView localeName, uint lctype, String outStr)
|
||||
{
|
||||
var lctype;
|
||||
|
||||
// Fix lctype if we don't want overrides
|
||||
if (!UseUserOverride)
|
||||
{
|
||||
lctype |= LOCALE_NOUSEROVERRIDE;
|
||||
}
|
||||
|
||||
// Ask OS for data
|
||||
/*String result = CultureInfo.nativeGetLocaleInfoEx(localeName, lctype);
|
||||
if (result == null)
|
||||
{
|
||||
// Failed, just use empty string
|
||||
result = String.Empty;
|
||||
}
|
||||
|
||||
return result;*/
|
||||
}
|
||||
|
||||
private static void GetSeparator(StringView format, StringView timeParts, String outStr)
|
||||
{
|
||||
/*int index = IndexOfTimePart(format, 0, timeParts);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
// Found a time part, find out when it changes
|
||||
char8 cTimePart = format[index];
|
||||
|
||||
do
|
||||
{
|
||||
index++;
|
||||
} while (index < format.Length && format[index] == cTimePart);
|
||||
|
||||
int separatorStart = index;
|
||||
|
||||
// Now we need to find the end of the separator
|
||||
if (separatorStart < format.Length)
|
||||
{
|
||||
int separatorEnd = IndexOfTimePart(format, separatorStart, timeParts);
|
||||
if (separatorEnd != -1)
|
||||
{
|
||||
// From [separatorStart, count) is our string, except we need to unescape
|
||||
return UnescapeNlsString(format, separatorStart, separatorEnd - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return String.Empty;*/
|
||||
}
|
||||
|
||||
static private void GetTimeSeparator(StringView format, String outStr)
|
||||
{
|
||||
// Time format separator (ie: : in 12:39:00)
|
||||
//
|
||||
// We calculate this from the provided time format
|
||||
//
|
||||
|
||||
//
|
||||
// Find the time separator so that we can pretend we know STIME.
|
||||
//
|
||||
GetSeparator(format, "Hhms", outStr);
|
||||
}
|
||||
|
||||
internal String TimeSeparator
|
||||
{
|
||||
get
|
||||
{
|
||||
if (sTimeSeparator == null
|
||||
#if !FEATURE_CORECLR
|
||||
|| UseUserOverride
|
||||
#endif
|
||||
)
|
||||
{
|
||||
String longTimeFormat = scope String();
|
||||
DoGetLocaleInfo(LOCALE_STIMEFORMAT, longTimeFormat);
|
||||
ReescapeWin32String(longTimeFormat);
|
||||
if (String.IsNullOrEmpty(longTimeFormat))
|
||||
{
|
||||
longTimeFormat = LongTimes[0];
|
||||
}
|
||||
|
||||
// Compute STIME from time format
|
||||
sTimeSeparator = new String();
|
||||
GetTimeSeparator(longTimeFormat, sTimeSeparator);
|
||||
}
|
||||
return sTimeSeparator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
343
BeefLibs/corlib/src/System/Globalization/CultureInfo.bf
Normal file
343
BeefLibs/corlib/src/System/Globalization/CultureInfo.bf
Normal file
|
@ -0,0 +1,343 @@
|
|||
using System.Diagnostics.Contracts;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Globalization
|
||||
{
|
||||
class CultureInfo : IFormatProvider
|
||||
{
|
||||
//public static readonly CultureInfo CurrentCulture = null;
|
||||
|
||||
public CultureData mCultureData = new CultureData() ~ delete _;
|
||||
public NumberFormatInfo mNumInfo ~ delete _;
|
||||
|
||||
static CultureInfo sInvariantCultureInfo ~ delete _; // Volatile?
|
||||
static CultureInfo sUserDefaultUICulture ~ delete _; // Volatile?
|
||||
static CultureInfo sUserDefaultCulture ~ delete _; // Volatile?
|
||||
public static CultureInfo mDefaultCultureInfo /*= new CultureInfo()*/ ~ delete _;
|
||||
|
||||
[ThreadStatic]
|
||||
private static CultureInfo tlCurrentCulture;
|
||||
[ThreadStatic]
|
||||
private static CultureInfo tlCurrentUICulture;
|
||||
|
||||
String m_name ~ delete _;
|
||||
internal bool m_isInherited;
|
||||
DateTimeFormatInfo dateTimeInfo ~ delete _;
|
||||
CultureData m_cultureData ~ delete _;
|
||||
Calendar calendar ~ delete _;
|
||||
bool m_isReadOnly;
|
||||
|
||||
// LOCALE constants of interest to us internally and privately for LCID functions
|
||||
// (ie: avoid using these and use names if possible)
|
||||
internal const int LOCALE_NEUTRAL = 0x0000;
|
||||
private const int LOCALE_USER_DEFAULT = 0x0400;
|
||||
private const int LOCALE_SYSTEM_DEFAULT = 0x0800;
|
||||
internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00;
|
||||
internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000;
|
||||
internal const int LOCALE_INVARIANT = 0x007F;
|
||||
private const int LOCALE_TRADITIONAL_SPANISH = 0x040a;
|
||||
|
||||
public static CultureInfo DefaultThreadCurrentCulture
|
||||
{
|
||||
get
|
||||
{
|
||||
return mDefaultCultureInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static CultureInfo UserDefaultCulture
|
||||
{
|
||||
get
|
||||
{
|
||||
return sUserDefaultCulture;
|
||||
}
|
||||
}
|
||||
|
||||
public static CultureInfo InvariantCulture
|
||||
{
|
||||
get
|
||||
{
|
||||
return sInvariantCultureInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
}
|
||||
|
||||
public NumberFormatInfo NumberFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mNumInfo == null)
|
||||
mNumInfo = new NumberFormatInfo(mCultureData);
|
||||
return mNumInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static CultureInfo CurrentCulture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (tlCurrentCulture == null)
|
||||
tlCurrentCulture = CultureInfo.DefaultThreadCurrentCulture ?? CultureInfo.UserDefaultCulture;
|
||||
return tlCurrentCulture;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Contract.Assert(value != null);
|
||||
Contract.EndContractBlock();
|
||||
tlCurrentCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static CultureInfo CurrentUICulture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (tlCurrentUICulture == null)
|
||||
tlCurrentUICulture = CultureInfo.DefaultThreadCurrentCulture ?? CultureInfo.UserDefaultCulture;
|
||||
return tlCurrentUICulture;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Contract.Assert(value != null);
|
||||
Contract.EndContractBlock();
|
||||
tlCurrentUICulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Calendar Calendar {
|
||||
get {
|
||||
|
||||
if (calendar == null)
|
||||
{
|
||||
//Contract.Assert(this.m_cultureData.CalendarIds.Length > 0, "this.m_cultureData.CalendarIds.Length > 0");
|
||||
// Get the default calendar for this culture. Note that the value can be
|
||||
// from registry if this is a user default culture.
|
||||
Calendar newObj = this.m_cultureData.DefaultCalendar;
|
||||
|
||||
Interlocked.Fence();
|
||||
newObj.SetReadOnlyState(m_isReadOnly);
|
||||
calendar = newObj;
|
||||
}
|
||||
return (calendar);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual DateTimeFormatInfo DateTimeFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
if (dateTimeInfo == null)
|
||||
{
|
||||
// Change the calendar of DTFI to the specified calendar of this CultureInfo.
|
||||
DateTimeFormatInfo temp = new DateTimeFormatInfo(
|
||||
this.m_cultureData, this.Calendar);
|
||||
temp.[Friend]m_isReadOnly = m_isReadOnly;
|
||||
Interlocked.Fence();
|
||||
dateTimeInfo = temp;
|
||||
}
|
||||
return (dateTimeInfo);
|
||||
}
|
||||
|
||||
set {
|
||||
/*if (value == null) {
|
||||
throw new ArgumentNullException("value",
|
||||
Environment.GetResourceString("ArgumentNull_Obj"));
|
||||
}
|
||||
Contract.EndContractBlock();*/
|
||||
VerifyWritable();
|
||||
dateTimeInfo = value;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The CultureData instance that reads the data provided by our CultureData class.
|
||||
//
|
||||
//Using a field initializer rather than a static constructor so that the whole class can be lazy
|
||||
//init.
|
||||
private static readonly bool init = Init();
|
||||
private static bool Init()
|
||||
{
|
||||
|
||||
if (sInvariantCultureInfo == null)
|
||||
{
|
||||
CultureInfo temp = new CultureInfo("", false);
|
||||
temp.m_isReadOnly = true;
|
||||
sInvariantCultureInfo = temp;
|
||||
}
|
||||
// First we set it to Invariant in case someone needs it before we're done finding it.
|
||||
// For example, if we throw an exception in InitUserDefaultCulture, we will still need an valid
|
||||
// s_userDefaultCulture to be used in Thread.CurrentCulture.
|
||||
sUserDefaultCulture = sUserDefaultUICulture = sInvariantCultureInfo;
|
||||
|
||||
sUserDefaultCulture = InitUserDefaultCulture();
|
||||
sUserDefaultUICulture = InitUserDefaultUICulture();
|
||||
return true;
|
||||
}
|
||||
|
||||
static CultureInfo InitUserDefaultCulture()
|
||||
{
|
||||
String strDefault = scope String();
|
||||
GetDefaultLocaleName(LOCALE_USER_DEFAULT, strDefault);
|
||||
if (strDefault.IsEmpty)
|
||||
{
|
||||
GetDefaultLocaleName(LOCALE_SYSTEM_DEFAULT, strDefault);
|
||||
|
||||
if (strDefault.IsEmpty)
|
||||
{
|
||||
// If system default doesn't work, keep using the invariant
|
||||
return (CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
CultureInfo temp = GetCultureByName(strDefault, true);
|
||||
temp.m_isReadOnly = true;
|
||||
|
||||
return (temp);
|
||||
}
|
||||
|
||||
static CultureInfo InitUserDefaultUICulture()
|
||||
{
|
||||
String strDefault = scope .();
|
||||
GetUserDefaultUILanguage(strDefault);
|
||||
|
||||
// In most of cases, UserDefaultCulture == UserDefaultUICulture, so we should use the same instance if possible.
|
||||
if (strDefault == UserDefaultCulture.Name)
|
||||
{
|
||||
return (UserDefaultCulture);
|
||||
}
|
||||
|
||||
CultureInfo temp = GetCultureByName( strDefault, true);
|
||||
|
||||
if (temp == null)
|
||||
{
|
||||
return (CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
temp.m_isReadOnly = true;
|
||||
|
||||
return (temp);
|
||||
}
|
||||
|
||||
public this(String name) : this(name, true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public this(String name, bool useUserOverride)
|
||||
{
|
||||
// Get our data providing record
|
||||
this.m_cultureData = CultureData.GetCultureData(name, useUserOverride);
|
||||
|
||||
if (this.m_cultureData == null) {
|
||||
//throw new CultureNotFoundException("name", name, Environment.GetResourceString("Argument_CultureNotSupported"));
|
||||
Runtime.FatalError();
|
||||
}
|
||||
|
||||
this.m_name = new String(this.m_cultureData.CultureName);
|
||||
this.m_isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo));
|
||||
}
|
||||
|
||||
private void VerifyWritable()
|
||||
{
|
||||
Runtime.Assert(!m_isReadOnly);
|
||||
}
|
||||
|
||||
public static this()
|
||||
{
|
||||
//sInvariantCultureInfo = new CultureInfo();
|
||||
}
|
||||
|
||||
private static volatile bool s_isTaiwanSku;
|
||||
private static volatile bool s_haveIsTaiwanSku;
|
||||
internal static bool IsTaiwanSku
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_haveIsTaiwanSku)
|
||||
{
|
||||
var language = scope String();
|
||||
GetSystemDefaultUILanguage(language);
|
||||
s_isTaiwanSku = (language == "zh-TW");
|
||||
s_haveIsTaiwanSku = true;
|
||||
}
|
||||
return (bool)s_isTaiwanSku;
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetSystemDefaultUILanguage(String outStr)
|
||||
{
|
||||
outStr.Append("EN-us");
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual Object GetFormat(Type formatType)
|
||||
{
|
||||
if (formatType == typeof(NumberFormatInfo))
|
||||
{
|
||||
return (NumberFormat);
|
||||
}
|
||||
if (formatType == typeof(DateTimeFormatInfo))
|
||||
{
|
||||
return (DateTimeFormat);
|
||||
}
|
||||
return (null);
|
||||
}
|
||||
|
||||
private static void GetDefaultLocaleName(int localeType, String outName)
|
||||
{
|
||||
/*Contract.Assert(localeType == LOCALE_USER_DEFAULT || localeType == LOCALE_SYSTEM_DEFAULT, "[CultureInfo.GetDefaultLocaleName] localeType must be LOCALE_USER_DEFAULT or LOCALE_SYSTEM_DEFAULT");
|
||||
|
||||
string localeName = null;
|
||||
if (InternalGetDefaultLocaleName(localeType, JitHelpers.GetStringHandleOnStack(ref localeName)))
|
||||
{
|
||||
return localeName;
|
||||
}
|
||||
return string.Empty;*/
|
||||
outName.Append("EN-us");
|
||||
}
|
||||
|
||||
// Gets a cached copy of the specified culture from an internal hashtable (or creates it
|
||||
// if not found). (Named version)
|
||||
public static CultureInfo GetCultureInfo(StringView name)
|
||||
{
|
||||
// Make sure we have a valid, non-zero length string as name
|
||||
CultureInfo retval = GetCultureInfoHelper(0, name, .());
|
||||
return retval;
|
||||
}
|
||||
|
||||
private static CultureInfo GetCultureByName(String name, bool userOverride)
|
||||
{
|
||||
// Try to get our culture
|
||||
return userOverride ? new CultureInfo(name) : CultureInfo.GetCultureInfo(name);
|
||||
}
|
||||
|
||||
// Helper function both both overloads of GetCachedReadOnlyCulture. If lcid is 0, we use the name.
|
||||
// If lcid is -1, use the altName and create one of those special SQL cultures.
|
||||
internal static CultureInfo GetCultureInfoHelper(int lcid, StringView name, StringView altName)
|
||||
{
|
||||
return new CultureInfo();
|
||||
}
|
||||
|
||||
private static void GetUserDefaultUILanguage(String langName)
|
||||
{
|
||||
//NotImplemented
|
||||
}
|
||||
|
||||
internal static Calendar GetCalendarInstance(int calType)
|
||||
{
|
||||
if (calType==Calendar.CAL_GREGORIAN) {
|
||||
return new GregorianCalendar();
|
||||
}
|
||||
Runtime.NotImplemented();
|
||||
//return GetCalendarInstanceRare(calType);
|
||||
}
|
||||
}
|
||||
}
|
1082
BeefLibs/corlib/src/System/Globalization/DateTimeFormat.bf
Normal file
1082
BeefLibs/corlib/src/System/Globalization/DateTimeFormat.bf
Normal file
File diff suppressed because it is too large
Load diff
590
BeefLibs/corlib/src/System/Globalization/DateTimeFormatInfo.bf
Normal file
590
BeefLibs/corlib/src/System/Globalization/DateTimeFormatInfo.bf
Normal file
|
@ -0,0 +1,590 @@
|
|||
using System.Collections.Generic;
|
||||
namespace System.Globalization
|
||||
{
|
||||
internal enum MonthNameStyles {
|
||||
Regular = 0x00000000,
|
||||
Genitive = 0x00000001,
|
||||
LeapYear = 0x00000002,
|
||||
}
|
||||
|
||||
internal enum DateTimeFormatFlags {
|
||||
None = 0x00000000,
|
||||
UseGenitiveMonth = 0x00000001,
|
||||
UseLeapYearMonth = 0x00000002,
|
||||
UseSpacesInMonthNames = 0x00000004, // Has spaces or non-breaking space in the month names.
|
||||
UseHebrewRule = 0x00000008, // Format/Parse using the Hebrew calendar rule.
|
||||
UseSpacesInDayNames = 0x00000010, // Has spaces or non-breaking space in the day names.
|
||||
UseDigitPrefixInTokens = 0x00000020, // Has token starting with numbers.
|
||||
|
||||
NotInitialized = -1,
|
||||
}
|
||||
|
||||
class DateTimeFormatInfo : IFormatProvider
|
||||
{
|
||||
//
|
||||
// Note, some fields are derived so don't really need to be serialized, but we can't mark as
|
||||
// optional because Whidbey was expecting them. Post-Arrowhead we could fix that
|
||||
// once Whidbey serialization is no longer necessary.
|
||||
//
|
||||
|
||||
// cache for the invariant culture.
|
||||
// invariantInfo is constant irrespective of your current culture.
|
||||
private static volatile DateTimeFormatInfo invariantInfo;
|
||||
|
||||
// an index which points to a record in Culture Data Table.
|
||||
private CultureData m_cultureData;
|
||||
|
||||
// The culture name used to create this DTFI.
|
||||
internal String m_name = null;
|
||||
|
||||
// The language name of the culture used to create this DTFI.
|
||||
private String m_langName = null;
|
||||
|
||||
// CompareInfo usually used by the parser.
|
||||
//private CompareInfo m_compareInfo = null;
|
||||
|
||||
// Culture matches current DTFI. mainly used for string comparisons during parsing.
|
||||
private CultureInfo m_cultureInfo = null;
|
||||
|
||||
//
|
||||
// Caches for various properties.
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
//NotImpl: Shouldn't be initialized
|
||||
internal String amDesignator = "AM";
|
||||
internal String pmDesignator = "PM";
|
||||
internal String dateSeparator = "/"; // derived from short date (whidbey expects, arrowhead doesn't)
|
||||
internal String generalShortTimePattern = null; // short date + short time (whidbey expects, arrowhead doesn't)
|
||||
internal String generalLongTimePattern = null; // short date + long time (whidbey expects, arrowhead doesn't)
|
||||
internal String timeSeparator = ":"; // derived from long time (whidbey expects, arrowhead doesn't)
|
||||
internal String monthDayPattern = null;
|
||||
internal String dateTimeOffsetPattern = null;
|
||||
|
||||
//
|
||||
// The following are constant values.
|
||||
//
|
||||
internal const String rfc1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
|
||||
|
||||
// The sortable pattern is based on ISO 8601.
|
||||
internal const String sortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
|
||||
internal const String universalSortableDateTimePattern = "yyyy'-'MM'-'dd HH':'mm':'ss'Z'";
|
||||
|
||||
//
|
||||
// The following are affected by calendar settings.
|
||||
//
|
||||
internal Calendar calendar = null;
|
||||
|
||||
internal int firstDayOfWeek = -1;
|
||||
internal int calendarWeekRule = -1;
|
||||
|
||||
internal String fullDateTimePattern = null; // long date + long time (whidbey expects, arrowhead doesn't)
|
||||
|
||||
internal String[] abbreviatedDayNames = null;
|
||||
|
||||
internal String[] m_superShortDayNames = null;
|
||||
|
||||
internal String[] dayNames = null;
|
||||
internal String[] abbreviatedMonthNames = null;
|
||||
internal String[] monthNames = null;
|
||||
// Cache the genitive month names that we retrieve from the data table.
|
||||
internal String[] genitiveMonthNames = null;
|
||||
|
||||
// Cache the abbreviated genitive month names that we retrieve from the data table.
|
||||
internal String[] m_genitiveAbbreviatedMonthNames = null;
|
||||
|
||||
// Cache the month names of a leap year that we retrieve from the data table.
|
||||
internal String[] leapYearMonthNames = null;
|
||||
|
||||
// For our "patterns" arrays we have 2 variables, a string and a string[]
|
||||
//
|
||||
// The string[] contains the list of patterns, EXCEPT the default may not be included.
|
||||
// The string contains the default pattern.
|
||||
// When we initially construct our string[], we set the string to string[0]
|
||||
|
||||
// The "default" Date/time patterns
|
||||
internal String longDatePattern = "dddd, MMMM d, yyyy";
|
||||
internal String shortDatePattern = "M/d/yyyy";
|
||||
internal String yearMonthPattern = "MMMM yyyy";
|
||||
internal String longTimePattern = null;
|
||||
internal String shortTimePattern = null;
|
||||
|
||||
// These are Whidbey-serialization compatable arrays (eg: default not included)
|
||||
// "all" is a bit of a misnomer since the "default" pattern stored above isn't
|
||||
// necessarily a member of the list
|
||||
private String[] allYearMonthPatterns = null; // This was wasn't serialized in Whidbey
|
||||
internal String[] allShortDatePatterns = null;
|
||||
internal String[] allLongDatePatterns = null;
|
||||
internal String[] allShortTimePatterns = null;
|
||||
internal String[] allLongTimePatterns = null;
|
||||
|
||||
// Cache the era names for this DateTimeFormatInfo instance.
|
||||
internal String[] m_eraNames = null;
|
||||
internal String[] m_abbrevEraNames = null;
|
||||
internal String[] m_abbrevEnglishEraNames = null;
|
||||
|
||||
internal int[] optionalCalendars = null;
|
||||
|
||||
private const int DEFAULT_ALL_DATETIMES_SIZE = 132;
|
||||
|
||||
// CultureInfo updates this
|
||||
internal bool m_isReadOnly=false;
|
||||
|
||||
// This flag gives hints about if formatting/parsing should perform special code path for things like
|
||||
// genitive form or leap year month names.
|
||||
internal DateTimeFormatFlags formatFlags = DateTimeFormatFlags.NotInitialized;
|
||||
//internal static bool preferExistingTokens = InitPreferExistingTokens();
|
||||
|
||||
List<Object> ownedObjects = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public String AllocString()
|
||||
{
|
||||
var str = new String();
|
||||
ownedObjects.Add(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
public this(CultureData cultureData, Calendar calendar)
|
||||
{
|
||||
this.m_cultureData = cultureData;
|
||||
this.calendar = calendar;
|
||||
}
|
||||
|
||||
public static DateTimeFormatInfo CurrentInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
CultureInfo culture = CultureInfo.CurrentCulture;
|
||||
if (!culture.m_isInherited)
|
||||
{
|
||||
DateTimeFormatInfo info = culture.[Friend]dateTimeInfo;
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo));
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTimeFormatInfo InvariantInfo {
|
||||
get
|
||||
{
|
||||
if (invariantInfo == null)
|
||||
{
|
||||
DateTimeFormatInfo info = new DateTimeFormatInfo();
|
||||
info.Calendar.SetReadOnlyState(true);
|
||||
info.m_isReadOnly = true;
|
||||
invariantInfo = info;
|
||||
}
|
||||
return (invariantInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public Calendar Calendar
|
||||
{
|
||||
get
|
||||
{
|
||||
return calendar;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView AMDesignator
|
||||
{
|
||||
get
|
||||
{
|
||||
return amDesignator;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView PMDesignator
|
||||
{
|
||||
get
|
||||
{
|
||||
return pmDesignator;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView TimeSeparator
|
||||
{
|
||||
get
|
||||
{
|
||||
if (timeSeparator == null)
|
||||
{
|
||||
timeSeparator = this.m_cultureData.TimeSeparator;
|
||||
}
|
||||
return timeSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView DateSeparator
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.dateSeparator == null)
|
||||
{
|
||||
Runtime.NotImplemented();
|
||||
//this.dateSeparator = this.m_cultureData.DateSeparator(Calendar.ID);
|
||||
}
|
||||
return dateSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasForceTwoDigitYears
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTimeFormatFlags FormatFlags
|
||||
{
|
||||
get
|
||||
{
|
||||
if (formatFlags == DateTimeFormatFlags.NotInitialized)
|
||||
{
|
||||
// Build the format flags from the data in this DTFI
|
||||
formatFlags = DateTimeFormatFlags.None;
|
||||
/*formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagGenitiveMonth(
|
||||
MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true));
|
||||
formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInMonthNames(
|
||||
MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true));
|
||||
formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInDayNames(DayNames, AbbreviatedDayNames);
|
||||
formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseHebrewCalendar((int)Calendar.ID);*/
|
||||
}
|
||||
return (formatFlags);
|
||||
}
|
||||
}
|
||||
|
||||
public StringView ShortDatePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
// Initialize our short date pattern from the 1st array value if not set
|
||||
if (this.shortDatePattern == null)
|
||||
{
|
||||
// Initialize our data
|
||||
this.shortDatePattern = this.UnclonedShortDatePatterns[0];
|
||||
}
|
||||
|
||||
return this.shortDatePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView LongDatePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
// Initialize our long date pattern from the 1st array value if not set
|
||||
if (this.longDatePattern == null)
|
||||
{
|
||||
// Initialize our data
|
||||
this.longDatePattern = this.UnclonedLongDatePatterns[0];
|
||||
}
|
||||
|
||||
return this.longDatePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView ShortTimePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
// Initialize our short time pattern from the 1st array value if not set
|
||||
if (this.shortTimePattern == null)
|
||||
{
|
||||
// Initialize our data
|
||||
this.shortTimePattern = this.UnclonedShortTimePatterns[0];
|
||||
}
|
||||
return this.shortTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView FullDateTimePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fullDateTimePattern == null)
|
||||
fullDateTimePattern = AllocString()..Append("dddd, MMMM d, yyyy h:mm:ss tt");
|
||||
return fullDateTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView DateTimeOffsetPattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (dateTimeOffsetPattern == null)
|
||||
dateTimeOffsetPattern = AllocString()..Append("M/d/yyyy h:mm:ss tt zzz");
|
||||
return dateTimeOffsetPattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView GeneralShortTimePattern
|
||||
{
|
||||
get {
|
||||
if (generalShortTimePattern == null) {
|
||||
generalShortTimePattern = AllocString()..AppendF("{0} {1}", ShortDatePattern, ShortTimePattern);
|
||||
}
|
||||
return (generalShortTimePattern);
|
||||
}
|
||||
}
|
||||
|
||||
public StringView GeneralLongTimePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (generalLongTimePattern == null) {
|
||||
generalLongTimePattern = AllocString()..AppendF("{0} {1}", ShortDatePattern, LongTimePattern);
|
||||
}
|
||||
return (generalLongTimePattern);
|
||||
}
|
||||
}
|
||||
|
||||
public StringView MonthDayPattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.monthDayPattern == null)
|
||||
{
|
||||
this.monthDayPattern = this.m_cultureData.MonthDay(Calendar.ID);
|
||||
}
|
||||
//Contract.Assert(this.monthDayPattern != null, "DateTimeFormatInfo.MonthDayPattern, monthDayPattern != null");
|
||||
return (this.monthDayPattern);
|
||||
}
|
||||
|
||||
set {
|
||||
/*if (IsReadOnly)
|
||||
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
|
||||
if (value == null) {
|
||||
throw new ArgumentNullException("value",
|
||||
Environment.GetResourceString("ArgumentNull_String"));
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
|
||||
this.monthDayPattern = value;*/
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
public StringView RFC1123Pattern
|
||||
{
|
||||
get
|
||||
{
|
||||
return (rfc1123Pattern);
|
||||
}
|
||||
}
|
||||
|
||||
public StringView SortableDateTimePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
return sortableDateTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView LongTimePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.longTimePattern == null)
|
||||
{
|
||||
// Initialize our data
|
||||
this.longTimePattern = this.UnclonedLongTimePatterns[0];
|
||||
}
|
||||
|
||||
return this.longTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView UniversalSortableDateTimePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
return universalSortableDateTimePattern;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView YearMonthPattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.yearMonthPattern == null)
|
||||
{
|
||||
// Initialize our data
|
||||
this.yearMonthPattern = this.UnclonedYearMonthPatterns[0];
|
||||
}
|
||||
return this.yearMonthPattern;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetAbbreviatedDayName(DayOfWeek dayofweek, String outStr)
|
||||
{
|
||||
outStr.Append(CalendarData.Invariant.saAbbrevDayNames[(int)dayofweek]);
|
||||
}
|
||||
|
||||
public void GetDayName(DayOfWeek dayofweek, String outStr)
|
||||
{
|
||||
outStr.Append(CalendarData.Invariant.saDayNames[(int)dayofweek]);
|
||||
}
|
||||
|
||||
public void GetAbbreviatedMonthName(int month, String outStr)
|
||||
{
|
||||
outStr.Append(CalendarData.Invariant.saAbbrevMonthNames[month - 1]);
|
||||
}
|
||||
|
||||
public void GetMonthName(int month, String outStr)
|
||||
{
|
||||
outStr.Append(CalendarData.Invariant.saMonthNames[month - 1]);
|
||||
}
|
||||
|
||||
public void GetEraName(int era, String outStr)
|
||||
{
|
||||
outStr.Append(CalendarData.Invariant.saEraNames[era]);
|
||||
}
|
||||
|
||||
internal void internalGetMonthName(int month, MonthNameStyles style, bool abbreviated, String outStr)
|
||||
{
|
||||
GetMonthName(month, outStr);
|
||||
}
|
||||
|
||||
public Object GetFormat(Type formatType)
|
||||
{
|
||||
return (formatType == typeof(DateTimeFormatInfo)? this: null);
|
||||
}
|
||||
|
||||
public static DateTimeFormatInfo GetInstance(IFormatProvider provider)
|
||||
{
|
||||
// Fast case for a regular CultureInfo
|
||||
DateTimeFormatInfo info;
|
||||
CultureInfo cultureProvider = provider as CultureInfo;
|
||||
if (cultureProvider != null && !cultureProvider.m_isInherited)
|
||||
{
|
||||
return cultureProvider.DateTimeFormat;
|
||||
}
|
||||
// Fast case for a DTFI;
|
||||
info = provider as DateTimeFormatInfo;
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
// Wasn't cultureInfo or DTFI, do it the slower way
|
||||
if (provider != null) {
|
||||
info = provider.GetFormat(typeof(DateTimeFormatInfo)) as DateTimeFormatInfo;
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
// Couldn't get anything, just use currentInfo as fallback
|
||||
return CurrentInfo;
|
||||
}
|
||||
|
||||
private String[] UnclonedYearMonthPatterns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.allYearMonthPatterns == null)
|
||||
{
|
||||
this.allYearMonthPatterns = this.m_cultureData.YearMonths(this.Calendar.ID);
|
||||
}
|
||||
|
||||
return this.allYearMonthPatterns;
|
||||
}
|
||||
}
|
||||
|
||||
private String [] UnclonedShortDatePatterns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (allShortDatePatterns == null)
|
||||
{
|
||||
this.allShortDatePatterns = this.m_cultureData.ShortDates(this.Calendar.ID);
|
||||
}
|
||||
|
||||
return this.allShortDatePatterns;
|
||||
}
|
||||
}
|
||||
|
||||
private String[] UnclonedLongDatePatterns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (allLongDatePatterns == null)
|
||||
{
|
||||
this.allLongDatePatterns = this.m_cultureData.LongDates(this.Calendar.ID);
|
||||
}
|
||||
|
||||
return this.allLongDatePatterns;
|
||||
}
|
||||
}
|
||||
|
||||
private String[] UnclonedShortTimePatterns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.allShortTimePatterns == null)
|
||||
{
|
||||
this.allShortTimePatterns = this.m_cultureData.ShortTimes;
|
||||
}
|
||||
|
||||
return this.allShortTimePatterns;
|
||||
}
|
||||
}
|
||||
|
||||
private String[] UnclonedLongTimePatterns
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.allLongTimePatterns == null)
|
||||
{
|
||||
this.allLongTimePatterns = this.m_cultureData.LongTimes;
|
||||
}
|
||||
|
||||
return this.allLongTimePatterns;
|
||||
}
|
||||
}
|
||||
|
||||
private String m_fullTimeSpanPositivePattern ~ delete _;
|
||||
internal String FullTimeSpanPositivePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_fullTimeSpanPositivePattern == null)
|
||||
{
|
||||
CultureData cultureDataWithoutUserOverrides;
|
||||
if (m_cultureData.UseUserOverride)
|
||||
cultureDataWithoutUserOverrides = CultureData.GetCultureData(m_cultureData.CultureName, false);
|
||||
else
|
||||
cultureDataWithoutUserOverrides = m_cultureData;
|
||||
StringView decimalSeparator = scope NumberFormatInfo(cultureDataWithoutUserOverrides).NumberDecimalSeparator;
|
||||
|
||||
m_fullTimeSpanPositivePattern = AllocString()..Append("d':'h':'mm':'ss'");
|
||||
m_fullTimeSpanPositivePattern.Append(decimalSeparator);
|
||||
m_fullTimeSpanPositivePattern.Append("'FFFFFFF");
|
||||
}
|
||||
return m_fullTimeSpanPositivePattern;
|
||||
}
|
||||
}
|
||||
|
||||
private String m_fullTimeSpanNegativePattern ~ delete _;
|
||||
internal String FullTimeSpanNegativePattern
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_fullTimeSpanNegativePattern == null)
|
||||
m_fullTimeSpanNegativePattern = AllocString()..Append("'-'", FullTimeSpanPositivePattern);
|
||||
return m_fullTimeSpanNegativePattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
namespace System.Globalization
|
||||
{
|
||||
internal enum CalendarId : uint16
|
||||
{
|
||||
GREGORIAN = 1 , // Gregorian (localized) calendar
|
||||
GREGORIAN_US = 2 , // Gregorian (U.S.) calendar
|
||||
JAPAN = 3 , // Japanese Emperor Era calendar
|
||||
/* SSS_WARNINGS_OFF */ TAIWAN = 4 , // Taiwan Era calendar /* SSS_WARNINGS_ON */
|
||||
KOREA = 5 , // Korean Tangun Era calendar
|
||||
HIJRI = 6 , // Hijri (Arabic Lunar) calendar
|
||||
THAI = 7 , // Thai calendar
|
||||
HEBREW = 8 , // Hebrew (Lunar) calendar
|
||||
GREGORIAN_ME_FRENCH = 9 , // Gregorian Middle East French calendar
|
||||
GREGORIAN_ARABIC = 10, // Gregorian Arabic calendar
|
||||
GREGORIAN_XLIT_ENGLISH = 11, // Gregorian Transliterated English calendar
|
||||
GREGORIAN_XLIT_FRENCH = 12,
|
||||
// Note that all calendars after this point are MANAGED ONLY for now.
|
||||
JULIAN = 13,
|
||||
JAPANESELUNISOLAR = 14,
|
||||
CHINESELUNISOLAR = 15,
|
||||
SAKA = 16, // reserved to match Office but not implemented in our code
|
||||
LUNAR_ETO_CHN = 17, // reserved to match Office but not implemented in our code
|
||||
LUNAR_ETO_KOR = 18, // reserved to match Office but not implemented in our code
|
||||
LUNAR_ETO_ROKUYOU = 19, // reserved to match Office but not implemented in our code
|
||||
KOREANLUNISOLAR = 20,
|
||||
TAIWANLUNISOLAR = 21,
|
||||
PERSIAN = 22,
|
||||
UMALQURA = 23,
|
||||
LAST_CALENDAR = 23 // Last calendar ID
|
||||
}
|
||||
|
||||
class DateTimeFormatInfoScanner
|
||||
{
|
||||
}
|
||||
}
|
38
BeefLibs/corlib/src/System/Globalization/DateTimeStyles.bf
Normal file
38
BeefLibs/corlib/src/System/Globalization/DateTimeStyles.bf
Normal file
|
@ -0,0 +1,38 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization
|
||||
{
|
||||
public enum DateTimeStyles
|
||||
{
|
||||
// Bit flag indicating that leading whitespace is allowed. Character values
|
||||
// 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be
|
||||
// whitespace.
|
||||
|
||||
|
||||
None = 0x00000000,
|
||||
|
||||
AllowLeadingWhite = 0x00000001,
|
||||
|
||||
AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed.
|
||||
|
||||
AllowInnerWhite = 0x00000004,
|
||||
|
||||
AllowWhiteSpaces = AllowLeadingWhite | AllowInnerWhite | AllowTrailingWhite,
|
||||
// When parsing a date/time string, if all year/month/day are missing, set the default date
|
||||
// to 0001/1/1, instead of the current year/month/day.
|
||||
|
||||
NoCurrentDateDefault = 0x00000008,
|
||||
// When parsing a date/time string, if a timezone specifier ("GMT","Z","+xxxx", "-xxxx" exists), we will
|
||||
// ajdust the parsed time based to GMT.
|
||||
|
||||
AdjustToUniversal = 0x00000010,
|
||||
|
||||
AssumeLocal = 0x00000020,
|
||||
|
||||
AssumeUniversal = 0x00000040,
|
||||
// Attempt to preserve whether the input is unspecified, local or UTC
|
||||
RoundtripKind = 0x00000080,
|
||||
}
|
||||
}
|
56
BeefLibs/corlib/src/System/Globalization/DaylightTime.bf
Normal file
56
BeefLibs/corlib/src/System/Globalization/DaylightTime.bf
Normal file
|
@ -0,0 +1,56 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization
|
||||
{
|
||||
using System;
|
||||
// This class represents a starting/ending time for a period of daylight saving time.
|
||||
public class DaylightTime
|
||||
{
|
||||
internal DateTime m_start;
|
||||
internal DateTime m_end;
|
||||
internal TimeSpan m_delta;
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public this(DateTime start, DateTime end, TimeSpan delta)
|
||||
{
|
||||
m_start = start;
|
||||
m_end = end;
|
||||
m_delta = delta;
|
||||
}
|
||||
|
||||
public void Set(DateTime start, DateTime end, TimeSpan delta)
|
||||
{
|
||||
m_start = start;
|
||||
m_end = end;
|
||||
m_delta = delta;
|
||||
}
|
||||
|
||||
// The start date of a daylight saving period.
|
||||
public DateTime Start {
|
||||
get {
|
||||
return m_start;
|
||||
}
|
||||
}
|
||||
|
||||
// The end date of a daylight saving period.
|
||||
public DateTime End {
|
||||
get {
|
||||
return m_end;
|
||||
}
|
||||
}
|
||||
|
||||
// Delta to stardard offset in ticks.
|
||||
public TimeSpan Delta {
|
||||
get {
|
||||
return m_delta;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
661
BeefLibs/corlib/src/System/Globalization/GregorianCalendar.bf
Normal file
661
BeefLibs/corlib/src/System/Globalization/GregorianCalendar.bf
Normal file
|
@ -0,0 +1,661 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
namespace System.Globalization {
|
||||
//
|
||||
// N.B.:
|
||||
// A lot of this code is directly from DateTime.cs. If you update that class,
|
||||
// update this one as well.
|
||||
// However, we still need these duplicated code because we will add era support
|
||||
// in this class.
|
||||
//
|
||||
//
|
||||
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
public enum GregorianCalendarTypes
|
||||
{
|
||||
Localized = Calendar.CAL_GREGORIAN,
|
||||
USEnglish = Calendar.CAL_GREGORIAN_US,
|
||||
MiddleEastFrench = Calendar.CAL_GREGORIAN_ME_FRENCH,
|
||||
Arabic = Calendar.CAL_GREGORIAN_ARABIC,
|
||||
TransliteratedEnglish = Calendar.CAL_GREGORIAN_XLIT_ENGLISH,
|
||||
TransliteratedFrench = Calendar.CAL_GREGORIAN_XLIT_FRENCH,
|
||||
}
|
||||
|
||||
// This calendar recognizes two era values:
|
||||
// 0 CurrentEra (AD)
|
||||
// 1 BeforeCurrentEra (BC)
|
||||
|
||||
public class GregorianCalendar : Calendar
|
||||
{
|
||||
/*
|
||||
A.D. = anno Domini
|
||||
*/
|
||||
|
||||
public const int ADEra = 1;
|
||||
|
||||
|
||||
internal const int DatePartYear = 0;
|
||||
internal const int DatePartDayOfYear = 1;
|
||||
internal const int DatePartMonth = 2;
|
||||
internal const int DatePartDay = 3;
|
||||
|
||||
//
|
||||
// This is the max Gregorian year can be represented by DateTime class. The limitation
|
||||
// is derived from DateTime class.
|
||||
//
|
||||
internal const int MaxYear = 9999;
|
||||
|
||||
internal GregorianCalendarTypes m_type;
|
||||
|
||||
internal static readonly int[] DaysToMonth365 = new int[]
|
||||
{
|
||||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
|
||||
} ~ delete _;
|
||||
|
||||
internal static readonly int[] DaysToMonth366 = new int[]
|
||||
{
|
||||
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
|
||||
} ~ delete _;
|
||||
|
||||
private static volatile Calendar s_defaultInstance;
|
||||
|
||||
|
||||
#region Serialization
|
||||
/*private void OnDeserialized(StreamingContext ctx)
|
||||
{
|
||||
if (m_type < GregorianCalendarTypes.Localized ||
|
||||
m_type > GregorianCalendarTypes.TransliteratedFrench)
|
||||
{
|
||||
throw new SerializationException(
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString(
|
||||
"Serialization_MemberOutOfRange"),
|
||||
"type",
|
||||
"GregorianCalendar"));
|
||||
}
|
||||
}*/
|
||||
#endregion Serialization
|
||||
|
||||
public override DateTime MinSupportedDateTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DateTime.MinValue);
|
||||
}
|
||||
}
|
||||
|
||||
public override DateTime MaxSupportedDateTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DateTime.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
public override CalendarAlgorithmType AlgorithmType
|
||||
{
|
||||
get
|
||||
{
|
||||
return CalendarAlgorithmType.SolarCalendar;
|
||||
}
|
||||
}
|
||||
|
||||
/*=================================GetDefaultInstance==========================
|
||||
**Action: Internal method to provide a default intance of GregorianCalendar. Used by NLS+ implementation
|
||||
** and other calendars.
|
||||
**Returns:
|
||||
**Arguments:
|
||||
**Exceptions:
|
||||
============================================================================*/
|
||||
|
||||
internal static Calendar GetDefaultInstance() {
|
||||
if (s_defaultInstance == null) {
|
||||
s_defaultInstance = new GregorianCalendar();
|
||||
}
|
||||
return (s_defaultInstance);
|
||||
}
|
||||
|
||||
// Construct an instance of gregorian calendar.
|
||||
|
||||
public this() :
|
||||
this(GregorianCalendarTypes.Localized)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public this(GregorianCalendarTypes type) {
|
||||
if ((int)type < (int)GregorianCalendarTypes.Localized || (int)type > (int)GregorianCalendarTypes.TransliteratedFrench) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"type",
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range",
|
||||
GregorianCalendarTypes.Localized, GregorianCalendarTypes.TransliteratedFrench));*/
|
||||
Runtime.FatalError();
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
this.m_type = type;
|
||||
}
|
||||
|
||||
public virtual GregorianCalendarTypes CalendarType {
|
||||
get {
|
||||
return (m_type);
|
||||
}
|
||||
|
||||
set {
|
||||
VerifyWritable();
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case GregorianCalendarTypes.Localized:
|
||||
case GregorianCalendarTypes.USEnglish:
|
||||
case GregorianCalendarTypes.MiddleEastFrench:
|
||||
case GregorianCalendarTypes.Arabic:
|
||||
case GregorianCalendarTypes.TransliteratedEnglish:
|
||||
case GregorianCalendarTypes.TransliteratedFrench:
|
||||
m_type = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
Runtime.FatalError();
|
||||
//throw new ArgumentOutOfRangeException("m_type", Environment.GetResourceString("ArgumentOutOfRange_Enum"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override int ID {
|
||||
get {
|
||||
// By returning different ID for different variations of GregorianCalendar,
|
||||
// we can support the Transliterated Gregorian calendar.
|
||||
// DateTimeFormatInfo will use this ID to get formatting information about
|
||||
// the calendar.
|
||||
return ((int)m_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a given date part of this DateTime. This method is used
|
||||
// to compute the year, day-of-year, month, or day part.
|
||||
internal virtual int GetDatePart(int64 ticks, int part)
|
||||
{
|
||||
// n = number of days since 1/1/0001
|
||||
int n = (int)(ticks / TicksPerDay);
|
||||
// y400 = number of whole 400-year periods since 1/1/0001
|
||||
int y400 = n / DaysPer400Years;
|
||||
// n = day number within 400-year period
|
||||
n -= y400 * DaysPer400Years;
|
||||
// y100 = number of whole 100-year periods within 400-year period
|
||||
int y100 = n / DaysPer100Years;
|
||||
// Last 100-year period has an extra day, so decrement result if 4
|
||||
if (y100 == 4) y100 = 3;
|
||||
// n = day number within 100-year period
|
||||
n -= y100 * DaysPer100Years;
|
||||
// y4 = number of whole 4-year periods within 100-year period
|
||||
int y4 = n / DaysPer4Years;
|
||||
// n = day number within 4-year period
|
||||
n -= y4 * DaysPer4Years;
|
||||
// y1 = number of whole years within 4-year period
|
||||
int y1 = n / DaysPerYear;
|
||||
// Last year has an extra day, so decrement result if 4
|
||||
if (y1 == 4) y1 = 3;
|
||||
// If year was requested, compute and return it
|
||||
if (part == DatePartYear)
|
||||
{
|
||||
return (y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1);
|
||||
}
|
||||
// n = day number within year
|
||||
n -= y1 * DaysPerYear;
|
||||
// If day-of-year was requested, return it
|
||||
if (part == DatePartDayOfYear)
|
||||
{
|
||||
return (n + 1);
|
||||
}
|
||||
// Leap year calculation looks different from IsLeapYear since y1, y4,
|
||||
// and y100 are relative to year 1, not year 0
|
||||
bool leapYear = (y1 == 3 && (y4 != 24 || y100 == 3));
|
||||
int[] days = leapYear? DaysToMonth366: DaysToMonth365;
|
||||
// All months have less than 32 days, so n >> 5 is a good conservative
|
||||
// estimate for the month
|
||||
int m = n >> 5 + 1;
|
||||
// m = 1-based month number
|
||||
while (n >= days[m]) m++;
|
||||
// If month was requested, return it
|
||||
if (part == DatePartMonth) return (m);
|
||||
// Return 1-based day-of-month
|
||||
return (n - days[m - 1] + 1);
|
||||
}
|
||||
|
||||
/*=================================GetAbsoluteDate==========================
|
||||
**Action: Gets the absolute date for the given Gregorian date. The absolute date means
|
||||
** the number of days from January 1st, 1 A.D.
|
||||
**Returns: the absolute date
|
||||
**Arguments:
|
||||
** year the Gregorian year
|
||||
** month the Gregorian month
|
||||
** day the day
|
||||
**Exceptions:
|
||||
** ArgumentOutOfRangException if year, month, day value is valid.
|
||||
**Note:
|
||||
** This is an internal method used by DateToTicks() and the calculations of Hijri and Hebrew calendars.
|
||||
** Number of Days in Prior Years (both common and leap years) +
|
||||
** Number of Days in Prior Months of Current Year +
|
||||
** Number of Days in Current Month
|
||||
**
|
||||
============================================================================*/
|
||||
|
||||
internal static Result<int64> GetAbsoluteDate(int year, int month, int day) {
|
||||
if (year >= 1 && year <= MaxYear && month >= 1 && month <= 12)
|
||||
{
|
||||
int[] days = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) ? DaysToMonth366: DaysToMonth365;
|
||||
if (day >= 1 && (day <= days[month] - days[month - 1])) {
|
||||
int y = year - 1;
|
||||
int absoluteDate = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
|
||||
return (absoluteDate);
|
||||
}
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadYearMonthDay"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Returns the tick count corresponding to the given year, month, and day.
|
||||
// Will check the if the parameters are valid.
|
||||
internal virtual Result<int64> DateToTicks(int year, int month, int day) {
|
||||
return (Try!(GetAbsoluteDate(year, month, day)) * TicksPerDay);
|
||||
}
|
||||
|
||||
// Returns the DateTime resulting from adding the given number of
|
||||
// months to the specified DateTime. The result is computed by incrementing
|
||||
// (or decrementing) the year and month parts of the specified DateTime by
|
||||
// value months, and, if required, adjusting the day part of the
|
||||
// resulting date downwards to the last day of the resulting month in the
|
||||
// resulting year. The time-of-day part of the result is the same as the
|
||||
// time-of-day part of the specified DateTime.
|
||||
//
|
||||
// In more precise terms, considering the specified DateTime to be of the
|
||||
// form y / m / d + t, where y is the
|
||||
// year, m is the month, d is the day, and t is the
|
||||
// time-of-day, the result is y1 / m1 / d1 + t,
|
||||
// where y1 and m1 are computed by adding value months
|
||||
// to y and m, and d1 is the largest value less than
|
||||
// or equal to d that denotes a valid day in month m1 of year
|
||||
// y1.
|
||||
//
|
||||
|
||||
public override Result<DateTime> AddMonths(DateTime time, int months)
|
||||
{
|
||||
if (months < -120000 || months > 120000) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"months",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"),
|
||||
-120000,
|
||||
120000));*/
|
||||
return .Err;
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
int y = GetDatePart(time.Ticks, DatePartYear);
|
||||
int m = GetDatePart(time.Ticks, DatePartMonth);
|
||||
int d = GetDatePart(time.Ticks, DatePartDay);
|
||||
int i = m - 1 + months;
|
||||
if (i >= 0)
|
||||
{
|
||||
m = i % 12 + 1;
|
||||
y = y + i / 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = 12 + (i + 1) % 12;
|
||||
y = y + (i - 11) / 12;
|
||||
}
|
||||
int[] daysArray = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? DaysToMonth366: DaysToMonth365;
|
||||
int days = (daysArray[m] - daysArray[m - 1]);
|
||||
|
||||
if (d > days)
|
||||
{
|
||||
d = days;
|
||||
}
|
||||
int64 ticks = Try!(DateToTicks(y, m, d)) + time.Ticks % TicksPerDay;
|
||||
Try!(Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime));
|
||||
|
||||
return (DateTime(ticks));
|
||||
}
|
||||
|
||||
|
||||
// Returns the DateTime resulting from adding the given number of
|
||||
// years to the specified DateTime. The result is computed by incrementing
|
||||
// (or decrementing) the year part of the specified DateTime by value
|
||||
// years. If the month and day of the specified DateTime is 2/29, and if the
|
||||
// resulting year is not a leap year, the month and day of the resulting
|
||||
// DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
|
||||
// parts of the result are the same as those of the specified DateTime.
|
||||
//
|
||||
|
||||
public override Result<DateTime> AddYears(DateTime time, int years)
|
||||
{
|
||||
return (AddMonths(time, years * 12));
|
||||
}
|
||||
|
||||
// Returns the day-of-month part of the specified DateTime. The returned
|
||||
// value is an integer between 1 and 31.
|
||||
//
|
||||
|
||||
public override Result<int> GetDayOfMonth(DateTime time)
|
||||
{
|
||||
return (GetDatePart(time.Ticks, DatePartDay));
|
||||
}
|
||||
|
||||
// Returns the day-of-week part of the specified DateTime. The returned value
|
||||
// is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
|
||||
// Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
|
||||
// Thursday, 5 indicates Friday, and 6 indicates Saturday.
|
||||
//
|
||||
|
||||
public override Result<DayOfWeek> GetDayOfWeek(DateTime time)
|
||||
{
|
||||
return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
|
||||
}
|
||||
|
||||
// Returns the day-of-year part of the specified DateTime. The returned value
|
||||
// is an integer between 1 and 366.
|
||||
//
|
||||
|
||||
public override Result<int> GetDayOfYear(DateTime time)
|
||||
{
|
||||
return (GetDatePart(time.Ticks, DatePartDayOfYear));
|
||||
}
|
||||
|
||||
// Returns the number of days in the month given by the year and
|
||||
// month arguments.
|
||||
//
|
||||
|
||||
public override Result<int> GetDaysInMonth(int year, int month, int era)
|
||||
{
|
||||
if (era == CurrentEra || era == ADEra) {
|
||||
if (year < 1 || year > MaxYear) {
|
||||
/*throw new ArgumentOutOfRangeException("year", Environment.GetResourceString("ArgumentOutOfRange_Range",
|
||||
1, MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
if (month < 1 || month > 12) {
|
||||
//throw new ArgumentOutOfRangeException("month", Environment.GetResourceString("ArgumentOutOfRange_Month"));
|
||||
return .Err;
|
||||
}
|
||||
int[] days = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? DaysToMonth366: DaysToMonth365);
|
||||
return (days[month] - days[month - 1]);
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Returns the number of days in the year given by the year argument for the current era.
|
||||
//
|
||||
|
||||
public override Result<int> GetDaysInYear(int year, int era)
|
||||
{
|
||||
if (era == CurrentEra || era == ADEra) {
|
||||
if (year >= 1 && year <= MaxYear) {
|
||||
return ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 366:365);
|
||||
}
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"),
|
||||
1,
|
||||
MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Returns the era for the specified DateTime value.
|
||||
|
||||
public override Result<int> GetEra(DateTime time)
|
||||
{
|
||||
return (ADEra);
|
||||
}
|
||||
|
||||
|
||||
public override int[] Eras
|
||||
{
|
||||
get
|
||||
{
|
||||
return (new int[] {ADEra} );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the month part of the specified DateTime. The returned value is an
|
||||
// integer between 1 and 12.
|
||||
//
|
||||
|
||||
public override Result<int> GetMonth(DateTime time)
|
||||
{
|
||||
return (GetDatePart(time.Ticks, DatePartMonth));
|
||||
}
|
||||
|
||||
// Returns the number of months in the specified year and era.
|
||||
|
||||
public override Result<int> GetMonthsInYear(int year, int era)
|
||||
{
|
||||
if (era == CurrentEra || era == ADEra) {
|
||||
if (year >= 1 && year <= MaxYear)
|
||||
{
|
||||
return (12);
|
||||
}
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"),
|
||||
1,
|
||||
MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Returns the year part of the specified DateTime. The returned value is an
|
||||
// integer between 1 and 9999.
|
||||
//
|
||||
|
||||
public override Result<int> GetYear(DateTime time)
|
||||
{
|
||||
return (GetDatePart(time.Ticks, DatePartYear));
|
||||
}
|
||||
|
||||
// Checks whether a given day in the specified era is a leap day. This method returns true if
|
||||
// the date is a leap day, or false if not.
|
||||
//
|
||||
|
||||
public override Result<bool> IsLeapDay(int year, int month, int day, int era)
|
||||
{
|
||||
if (month < 1 || month > 12) {
|
||||
/*throw new ArgumentOutOfRangeException("month", Environment.GetResourceString("ArgumentOutOfRange_Range",
|
||||
1, 12));*/
|
||||
return .Err;
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
|
||||
if (era != CurrentEra && era != ADEra)
|
||||
{
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
if (year < 1 || year > MaxYear) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range", 1, MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
|
||||
if (day < 1 || day > Try!(GetDaysInMonth(year, month))) {
|
||||
/*throw new ArgumentOutOfRangeException("day", Environment.GetResourceString("ArgumentOutOfRange_Range",
|
||||
1, GetDaysInMonth(year, month)));*/
|
||||
return .Err;
|
||||
}
|
||||
if (!Try!(IsLeapYear(year))) {
|
||||
return (false);
|
||||
}
|
||||
if (month == 2 && day == 29) {
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Returns the leap month in a calendar year of the specified era. This method returns 0
|
||||
// if this calendar does not have leap month, or this year is not a leap year.
|
||||
//
|
||||
|
||||
public override Result<int> GetLeapMonth(int year, int era)
|
||||
{
|
||||
if (era != CurrentEra && era != ADEra)
|
||||
{
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
if (year < 1 || year > MaxYear) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
return (0);
|
||||
}
|
||||
|
||||
// Checks whether a given month in the specified era is a leap month. This method returns true if
|
||||
// month is a leap month, or false if not.
|
||||
//
|
||||
|
||||
public override Result<bool> IsLeapMonth(int year, int month, int era)
|
||||
{
|
||||
if (era != CurrentEra && era != ADEra) {
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
if (year < 1 || year > MaxYear) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
|
||||
if (month < 1 || month > 12) {
|
||||
/*throw new ArgumentOutOfRangeException("month", Environment.GetResourceString("ArgumentOutOfRange_Range",
|
||||
1, 12));*/
|
||||
return .Err;
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
return (false);
|
||||
|
||||
}
|
||||
|
||||
// Checks whether a given year in the specified era is a leap year. This method returns true if
|
||||
// year is a leap year, or false if not.
|
||||
//
|
||||
|
||||
public override Result<bool> IsLeapYear(int year, int era) {
|
||||
if (era == CurrentEra || era == ADEra) {
|
||||
if (year >= 1 && year <= MaxYear) {
|
||||
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
|
||||
}
|
||||
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
|
||||
//
|
||||
|
||||
public override Result<DateTime> ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
|
||||
{
|
||||
if (era == CurrentEra || era == ADEra) {
|
||||
//return new DateTime(year, month, day, hour, minute, second, millisecond);
|
||||
return .Err;
|
||||
}
|
||||
//throw new ArgumentOutOfRangeException("era", Environment.GetResourceString("ArgumentOutOfRange_InvalidEraValue"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
internal override bool TryToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era, out DateTime result) {
|
||||
if (era == CurrentEra || era == ADEra) {
|
||||
switch (DateTime.TryCreate(year, month, day, hour, minute, second, millisecond))
|
||||
{
|
||||
case .Ok(out result):
|
||||
return true;
|
||||
case .Err:
|
||||
}
|
||||
}
|
||||
result = DateTime.MinValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 2029;
|
||||
|
||||
|
||||
public override int TwoDigitYearMax
|
||||
{
|
||||
get {
|
||||
if (twoDigitYearMax == -1) {
|
||||
twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
|
||||
}
|
||||
return (twoDigitYearMax);
|
||||
}
|
||||
|
||||
set {
|
||||
VerifyWritable();
|
||||
if (value < 99 || value > MaxYear) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"),
|
||||
99,
|
||||
MaxYear));*/
|
||||
Runtime.FatalError();
|
||||
}
|
||||
twoDigitYearMax = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override Result<int> ToFourDigitYear(int year) {
|
||||
if (year < 0) {
|
||||
/*throw new ArgumentOutOfRangeException("year",
|
||||
Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));*/
|
||||
return .Err;
|
||||
}
|
||||
//Contract.EndContractBlock();
|
||||
|
||||
if (year > MaxYear) {
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"year",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"), 1, MaxYear));*/
|
||||
return .Err;
|
||||
}
|
||||
return (base.ToFourDigitYear(year));
|
||||
}
|
||||
}
|
||||
}
|
736
BeefLibs/corlib/src/System/Globalization/NumberFormatInfo.bf
Normal file
736
BeefLibs/corlib/src/System/Globalization/NumberFormatInfo.bf
Normal file
|
@ -0,0 +1,736 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
namespace System.Globalization {
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
//
|
||||
// Property Default Description
|
||||
// PositiveSign '+' Character used to indicate positive values.
|
||||
// NegativeSign '-' Character used to indicate negative values.
|
||||
// NumberDecimalSeparator '.' The character used as the decimal separator.
|
||||
// NumberGroupSeparator ',' The character used to separate groups of
|
||||
// digits to the left of the decimal point.
|
||||
// NumberDecimalDigits 2 The default number of decimal places.
|
||||
// NumberGroupSizes 3 The number of digits in each group to the
|
||||
// left of the decimal point.
|
||||
// NaNSymbol "NaN" The string used to represent NaN values.
|
||||
// PositiveInfinitySymbol"Infinity" The string used to represent positive
|
||||
// infinities.
|
||||
// NegativeInfinitySymbol"-Infinity" The string used to represent negative
|
||||
// infinities.
|
||||
//
|
||||
//
|
||||
//
|
||||
// Property Default Description
|
||||
// CurrencyDecimalSeparator '.' The character used as the decimal
|
||||
// separator.
|
||||
// CurrencyGroupSeparator ',' The character used to separate groups
|
||||
// of digits to the left of the decimal
|
||||
// point.
|
||||
// CurrencyDecimalDigits 2 The default number of decimal places.
|
||||
// CurrencyGroupSizes 3 The number of digits in each group to
|
||||
// the left of the decimal point.
|
||||
// CurrencyPositivePattern 0 The format of positive values.
|
||||
// CurrencyNegativePattern 0 The format of negative values.
|
||||
// CurrencySymbol "$" String used as local monetary symbol.
|
||||
//
|
||||
|
||||
struct OwnedString
|
||||
{
|
||||
public String mString;
|
||||
public bool mOwned;
|
||||
|
||||
public this(String unownedString)
|
||||
{
|
||||
mString = unownedString;
|
||||
mOwned = false;
|
||||
}
|
||||
|
||||
public this(String str, bool owned)
|
||||
{
|
||||
mString = str;
|
||||
mOwned = owned;
|
||||
}
|
||||
|
||||
public void Dispose() mut
|
||||
{
|
||||
if (mOwned)
|
||||
delete mString;
|
||||
}
|
||||
|
||||
public void Set(StringView value) mut
|
||||
{
|
||||
if (!mOwned)
|
||||
{
|
||||
mString = new String(value);
|
||||
mOwned = true;
|
||||
}
|
||||
else
|
||||
mString.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
public class NumberFormatInfo : /*ICloneable,*/ IFormatProvider
|
||||
{
|
||||
// invariantInfo is constant irrespective of your current culture.
|
||||
private static volatile NumberFormatInfo invariantInfo;
|
||||
|
||||
// READTHIS READTHIS READTHIS
|
||||
// This class has an exact mapping onto a native structure defined in COMNumber.cpp
|
||||
// DO NOT UPDATE THIS WITHOUT UPDATING THAT STRUCTURE. IF YOU ADD BOOL, ADD THEM AT THE END.
|
||||
// ALSO MAKE SURE TO UPDATE mscorlib.h in the VM directory to check field offsets.
|
||||
// READTHIS READTHIS READTHIS
|
||||
internal int32[] numberGroupSizes = new int32[] {3} ~ delete _;
|
||||
internal int32[] currencyGroupSizes = new int32[] {3} ~ delete _;
|
||||
internal int32[] percentGroupSizes = new int32[] {3} ~ delete _;
|
||||
internal OwnedString positiveSign = .("+") ~ _.Dispose();
|
||||
internal OwnedString negativeSign = .("-") ~ _.Dispose();
|
||||
internal OwnedString numberDecimalSeparator = .(".") ~ _.Dispose();
|
||||
internal OwnedString numberGroupSeparator = .(",") ~ _.Dispose();
|
||||
internal OwnedString currencyGroupSeparator = .(",") ~ _.Dispose();;
|
||||
internal OwnedString currencyDecimalSeparator = .(".") ~ _.Dispose();
|
||||
internal OwnedString currencySymbol = .("\u{00a4}") ~ _.Dispose(); // U+00a4 is the symbol for International Monetary Fund.
|
||||
// The alternative currency symbol used in Win9x ANSI codepage, that can not roundtrip between ANSI and Unicode.
|
||||
// Currently, only ja-JP and ko-KR has non-null values (which is U+005c, backslash)
|
||||
// NOTE: The only legal values for this string are null and "\u005c"
|
||||
internal String ansiCurrencySymbol = null;
|
||||
internal OwnedString nanSymbol = .("NaN") ~ _.Dispose();
|
||||
internal OwnedString positiveInfinitySymbol = .("Infinity") ~ _.Dispose();
|
||||
internal OwnedString negativeInfinitySymbol = .("-Infinity") ~ _.Dispose();
|
||||
internal OwnedString percentDecimalSeparator = .(".") ~ _.Dispose();
|
||||
internal OwnedString percentGroupSeparator = .(",") ~ _.Dispose();
|
||||
internal OwnedString percentSymbol = .("%") ~ _.Dispose();
|
||||
internal OwnedString perMilleSymbol = .("\u{2030}") ~ _.Dispose();
|
||||
|
||||
internal String[] nativeDigits = new .[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} ~ DeleteNativeDigits();
|
||||
|
||||
internal int32 numberDecimalDigits = 2;
|
||||
internal int32 currencyDecimalDigits = 2;
|
||||
internal int32 currencyPositivePattern = 0;
|
||||
internal int32 currencyNegativePattern = 0;
|
||||
internal int32 numberNegativePattern = 1;
|
||||
internal int32 percentPositivePattern = 0;
|
||||
internal int32 percentNegativePattern = 0;
|
||||
internal int32 percentDecimalDigits = 2;
|
||||
|
||||
|
||||
internal int32 digitSubstitution = 1; // DigitShapes.None
|
||||
|
||||
internal bool isReadOnly=false;
|
||||
|
||||
// Is this NumberFormatInfo for invariant culture?
|
||||
internal bool m_isInvariant=false;
|
||||
|
||||
void DeleteNativeDigits()
|
||||
{
|
||||
if ((!nativeDigits.IsEmpty) && ((Object)nativeDigits[0] != "0"))
|
||||
{
|
||||
for (var str in nativeDigits)
|
||||
delete str;
|
||||
}
|
||||
delete nativeDigits;
|
||||
}
|
||||
|
||||
public this() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
static private void VerifyDecimalSeparator(String decSep, String propertyName)
|
||||
{
|
||||
if (decSep==null) {
|
||||
/*throw new ArgumentNullException(propertyName,
|
||||
Environment.GetResourceString("ArgumentNull_String"));*/
|
||||
}
|
||||
|
||||
if (decSep.Length==0) {
|
||||
/*throw new ArgumentException(Environment.GetResourceString("Argument_EmptyDecString"));*/
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
|
||||
}
|
||||
|
||||
static private void VerifyGroupSeparator(String groupSep, String propertyName) {
|
||||
if (groupSep==null) {
|
||||
/*throw new ArgumentNullException(propertyName,
|
||||
Environment.GetResourceString("ArgumentNull_String"));*/
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
}
|
||||
|
||||
static private void VerifyNativeDigits(String [] nativeDig, String propertyName) {
|
||||
if (nativeDig==null) {
|
||||
/*throw new ArgumentNullException(propertyName,
|
||||
Environment.GetResourceString("ArgumentNull_Array"));*/
|
||||
}
|
||||
|
||||
if (nativeDig.Count != 10)
|
||||
{
|
||||
//throw new ArgumentException(Environment.GetResourceString("Argument_InvalidNativeDigitCount"), propertyName);
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
|
||||
for(int i = 0; i < nativeDig.Count; i++)
|
||||
{
|
||||
if (nativeDig[i] == null)
|
||||
{
|
||||
/*throw new ArgumentNullException(propertyName,
|
||||
Environment.GetResourceString("ArgumentNull_ArrayValue"));*/
|
||||
}
|
||||
|
||||
|
||||
if (nativeDig[i].Length != 1) {
|
||||
if(nativeDig[i].Length != 2) {
|
||||
// Not 1 or 2 UTF-16 code points
|
||||
/*throw new ArgumentException(Environment.GetResourceString("Argument_InvalidNativeDigitValue"), propertyName);*/
|
||||
}
|
||||
/*else if (!char.IsSurrogatePair(nativeDig[i][0], nativeDig[i][1])) {
|
||||
// 2 UTF-6 code points, but not a surrogate pair
|
||||
/*throw new ArgumentException(Environment.GetResourceString("Argument_InvalidNativeDigitValue"), propertyName);*/
|
||||
}*/
|
||||
}
|
||||
|
||||
/*if (CharUnicodeInfo.GetDecimalDigitValue(nativeDig[i], 0) != i &&
|
||||
CharUnicodeInfo.GetUnicodeCategory(nativeDig[i], 0) != UnicodeCategory.PrivateUse) {
|
||||
// Not the appropriate digit according to the Unicode data properties
|
||||
// (Digit 0 must be a 0, etc.).
|
||||
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidNativeDigitValue"), propertyName);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We aren't persisting dataItem any more (since its useless & we weren't using it),
|
||||
// Ditto with m_useUserOverride. Don't use them, we use a local copy of everything.
|
||||
internal this(CultureData cultureData)
|
||||
{
|
||||
if (cultureData != null)
|
||||
{
|
||||
// We directly use fields here since these data is coming from data table or Win32, so we
|
||||
// don't need to verify their values (except for invalid parsing situations).
|
||||
cultureData.GetNFIValues(this);
|
||||
|
||||
if (cultureData.IsInvariantCulture)
|
||||
{
|
||||
// For invariant culture
|
||||
this.m_isInvariant = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyWritable()
|
||||
{
|
||||
if (isReadOnly)
|
||||
{
|
||||
Runtime.FatalError("Read only");
|
||||
//throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
}
|
||||
|
||||
// Returns a default NumberFormatInfo that will be universally
|
||||
// supported and constant irrespective of the current culture.
|
||||
// Used by FromString methods.
|
||||
//
|
||||
|
||||
/*public static NumberFormatInfo InvariantInfo {
|
||||
get {
|
||||
/*if (invariantInfo == null) {
|
||||
// Lazy create the invariant info. This cannot be done in a .cctor because exceptions can
|
||||
// be thrown out of a .cctor stack that will need this.
|
||||
NumberFormatInfo nfi = new NumberFormatInfo();
|
||||
nfi.m_isInvariant = true;
|
||||
invariantInfo = ReadOnly(nfi);
|
||||
}
|
||||
return invariantInfo;*/
|
||||
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/*public static NumberFormatInfo GetInstance(IFormatProvider formatProvider)
|
||||
{
|
||||
// Fast case for a regular CultureInfo
|
||||
/*NumberFormatInfo info;
|
||||
CultureInfo cultureProvider = formatProvider as CultureInfo;
|
||||
if (cultureProvider != null && !cultureProvider.m_isInherited) {
|
||||
info = cultureProvider.numInfo;
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
else {
|
||||
return cultureProvider.NumberFormat;
|
||||
}
|
||||
}
|
||||
// Fast case for an NFI;
|
||||
info = formatProvider as NumberFormatInfo;
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
if (formatProvider != null) {
|
||||
info = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo;
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return CurrentInfo;*/
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
/*public Object Clone()
|
||||
{
|
||||
/*NumberFormatInfo n = (NumberFormatInfo)MemberwiseClone();
|
||||
n.isReadOnly = false;
|
||||
return n;*/
|
||||
return null;
|
||||
}*/
|
||||
|
||||
|
||||
public int32 CurrencyDecimalDigits
|
||||
{
|
||||
get { return currencyDecimalDigits; }
|
||||
set {
|
||||
if (value < 0 || value > 99)
|
||||
{
|
||||
/*throw new ArgumentOutOfRangeException(
|
||||
"CurrencyDecimalDigits",
|
||||
String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Environment.GetResourceString("ArgumentOutOfRange_Range"),
|
||||
0,
|
||||
99));*/
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
VerifyWritable();
|
||||
currencyDecimalDigits = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String CurrencyDecimalSeparator {
|
||||
get { return currencyDecimalSeparator.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
VerifyDecimalSeparator(value, "CurrencyDecimalSeparator");
|
||||
currencyDecimalSeparator.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool IsReadOnly {
|
||||
get {
|
||||
return isReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Check the values of the groupSize array.
|
||||
//
|
||||
// Every element in the groupSize array should be between 1 and 9
|
||||
// excpet the last element could be zero.
|
||||
//
|
||||
static internal void CheckGroupSize(String propName, int[] groupSize)
|
||||
{
|
||||
for (int i = 0; i < groupSize.Count; i++)
|
||||
{
|
||||
if (groupSize[i] < 1)
|
||||
{
|
||||
if (i == groupSize.Count - 1 && groupSize[i] == 0)
|
||||
return;
|
||||
/*throw new ArgumentException(Environment.GetResourceString("Argument_InvalidGroupSize"), propName);*/
|
||||
}
|
||||
else if (groupSize[i] > 9)
|
||||
{
|
||||
/*throw new ArgumentException(Environment.GetResourceString("Argument_InvalidGroupSize"), propName);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Span<int32> CurrencyGroupSizes
|
||||
{
|
||||
get
|
||||
{
|
||||
//return ((int[])currencyGroupSizes.Clone());
|
||||
return currencyGroupSizes;
|
||||
}
|
||||
set
|
||||
{
|
||||
VerifyWritable();
|
||||
|
||||
delete currencyGroupSizes;
|
||||
currencyGroupSizes = new int32[value.Length];
|
||||
for (int i < value.Length)
|
||||
currencyGroupSizes[i] = value[i];
|
||||
|
||||
/*Int32[] inputSizes = (Int32[])value.Clone();
|
||||
CheckGroupSize("CurrencyGroupSizes", inputSizes);
|
||||
currencyGroupSizes = inputSizes;*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Span<int32> NumberGroupSizes
|
||||
{
|
||||
get
|
||||
{
|
||||
return numberGroupSizes;
|
||||
}
|
||||
set
|
||||
{
|
||||
VerifyWritable();
|
||||
|
||||
delete numberGroupSizes;
|
||||
numberGroupSizes = new int32[value.Length];
|
||||
for (int i < value.Length)
|
||||
numberGroupSizes[i] = value[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Span<int32> PercentGroupSizes
|
||||
{
|
||||
get
|
||||
{
|
||||
return percentGroupSizes;
|
||||
}
|
||||
set
|
||||
{
|
||||
VerifyWritable();
|
||||
|
||||
delete percentGroupSizes;
|
||||
percentGroupSizes = new int32[value.Length];
|
||||
for (int i < value.Length)
|
||||
percentGroupSizes[i] = value[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public StringView CurrencyGroupSeparator {
|
||||
get { return currencyGroupSeparator.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
//VerifyGroupSeparator(value, "CurrencyGroupSeparator");
|
||||
currencyGroupSeparator.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView CurrencySymbol {
|
||||
get { return currencySymbol.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
currencySymbol.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the current culture's NumberFormatInfo. Used by Parse methods.
|
||||
//
|
||||
|
||||
public static NumberFormatInfo CurrentInfo {
|
||||
get {
|
||||
/*System.Globalization.CultureInfo culture = System.Threading.Thread.CurrentThread.CurrentCulture;
|
||||
if (!culture.m_isInherited) {
|
||||
NumberFormatInfo info = culture.numInfo;
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return ((NumberFormatInfo)culture.GetFormat(typeof(NumberFormatInfo)));*/
|
||||
return CultureInfo.CurrentCulture.NumberFormat;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView NaNSymbol
|
||||
{
|
||||
get
|
||||
{
|
||||
return nanSymbol.mString;
|
||||
}
|
||||
set {
|
||||
/*if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("NaNSymbol",
|
||||
Environment.GetResourceString("ArgumentNull_String"));
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
VerifyWritable();*/
|
||||
//Contract.Assert(value != null);
|
||||
|
||||
nanSymbol.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int CurrencyNegativePattern {
|
||||
get { return currencyNegativePattern; }
|
||||
set {
|
||||
Debug.Assert((value >= 0) && (value <= 15));
|
||||
VerifyWritable();
|
||||
currencyNegativePattern = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int NumberNegativePattern {
|
||||
get { return numberNegativePattern; }
|
||||
set {
|
||||
Debug.Assert((value >= 0) && (value <= 4));
|
||||
VerifyWritable();
|
||||
numberNegativePattern = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int PercentPositivePattern {
|
||||
get { return percentPositivePattern; }
|
||||
set {
|
||||
Debug.Assert((value >= 0) && (value <= 3));
|
||||
VerifyWritable();
|
||||
percentPositivePattern = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int PercentNegativePattern {
|
||||
get { return percentNegativePattern; }
|
||||
set {
|
||||
Debug.Assert((value >= 0) && (value <= 11));
|
||||
VerifyWritable();
|
||||
percentNegativePattern = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView NegativeInfinitySymbol {
|
||||
get {
|
||||
return negativeInfinitySymbol.mString;
|
||||
}
|
||||
set {
|
||||
VerifyWritable();
|
||||
negativeInfinitySymbol.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView NegativeSign {
|
||||
get { return negativeSign.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
negativeSign.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int32 NumberDecimalDigits {
|
||||
get { return numberDecimalDigits; }
|
||||
set {
|
||||
Debug.Assert((value >= 0) && (value <= 99));
|
||||
VerifyWritable();
|
||||
numberDecimalDigits = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView NumberDecimalSeparator {
|
||||
get { return numberDecimalSeparator.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
numberDecimalSeparator.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView NumberGroupSeparator {
|
||||
get { return numberGroupSeparator.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
numberGroupSeparator.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int CurrencyPositivePattern {
|
||||
get { return currencyPositivePattern; }
|
||||
set {
|
||||
Debug.Assert((value >= 0) && (value <= 3));
|
||||
VerifyWritable();
|
||||
currencyPositivePattern = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView PositiveInfinitySymbol {
|
||||
get {
|
||||
return positiveInfinitySymbol.mString;
|
||||
}
|
||||
set {
|
||||
VerifyWritable();
|
||||
positiveInfinitySymbol.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView PositiveSign {
|
||||
get { return positiveSign.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
positiveSign.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int32 PercentDecimalDigits {
|
||||
get { return percentDecimalDigits; }
|
||||
set {
|
||||
Debug.Assert((value >= 0) && (value <= 99));
|
||||
VerifyWritable();
|
||||
percentDecimalDigits = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView PercentDecimalSeparator
|
||||
{
|
||||
get { return percentDecimalSeparator.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
percentDecimalSeparator.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView PercentGroupSeparator
|
||||
{
|
||||
get { return percentGroupSeparator.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
percentGroupSeparator.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView PercentSymbol
|
||||
{
|
||||
get {
|
||||
return percentSymbol.mString;
|
||||
}
|
||||
set {
|
||||
VerifyWritable();
|
||||
percentSymbol.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public StringView PerMilleSymbol {
|
||||
get { return perMilleSymbol.mString; }
|
||||
set {
|
||||
VerifyWritable();
|
||||
perMilleSymbol.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
public Span<String> NativeDigits
|
||||
{
|
||||
get { return nativeDigits; }
|
||||
set
|
||||
{
|
||||
VerifyWritable();
|
||||
|
||||
DeleteNativeDigits();
|
||||
nativeDigits = new String[value.Length];
|
||||
for (int i < value.Length)
|
||||
nativeDigits[i] = new String(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*#if !FEATURE_CORECLR
|
||||
[System.Runtime.InteropServices.ComVisible(false)]
|
||||
public DigitShapes DigitSubstitution
|
||||
{
|
||||
get { return (DigitShapes)digitSubstitution; }
|
||||
set
|
||||
{
|
||||
VerifyWritable();
|
||||
VerifyDigitSubstitution(value, "DigitSubstitution");
|
||||
digitSubstitution = (int)value;
|
||||
}
|
||||
}
|
||||
#endif // !FEATURE_CORECLR*/
|
||||
|
||||
public Object GetFormat(Type formatType) {
|
||||
return formatType == typeof(NumberFormatInfo)? this: null;
|
||||
}
|
||||
|
||||
public static NumberFormatInfo ReadOnly(NumberFormatInfo nfi)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
|
||||
/*if (nfi == null) {
|
||||
throw new ArgumentNullException("nfi");
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
if (nfi.IsReadOnly) {
|
||||
return (nfi);
|
||||
}
|
||||
NumberFormatInfo info = (NumberFormatInfo)(nfi.MemberwiseClone());
|
||||
info.isReadOnly = true;
|
||||
return info;*/
|
||||
}
|
||||
|
||||
// private const NumberStyles InvalidNumberStyles = unchecked((NumberStyles) 0xFFFFFC00);
|
||||
private const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite
|
||||
| NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign
|
||||
| NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPoint
|
||||
| NumberStyles.AllowThousands | NumberStyles.AllowExponent
|
||||
| NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier);
|
||||
|
||||
/*internal static void ValidateParseStyleInteger(NumberStyles style) {
|
||||
// Check for undefined flags
|
||||
if ((style & InvalidNumberStyles) != 0) {
|
||||
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidNumberStyles"), "style");
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
if ((style & NumberStyles.AllowHexSpecifier) != 0) { // Check for hex number
|
||||
if ((style & ~NumberStyles.HexNumber) != 0) {
|
||||
throw new ArgumentException(Environment.GetResourceString("Arg_InvalidHexStyle"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ValidateParseStyleFloatingPoint(NumberStyles style) {
|
||||
// Check for undefined flags
|
||||
if ((style & InvalidNumberStyles) != 0) {
|
||||
throw new ArgumentException(Environment.GetResourceString("Argument_InvalidNumberStyles"), "style");
|
||||
}
|
||||
Contract.EndContractBlock();
|
||||
if ((style & NumberStyles.AllowHexSpecifier) != 0) { // Check for hex number
|
||||
throw new ArgumentException(Environment.GetResourceString("Arg_HexStyleNotSupported"));
|
||||
}
|
||||
}*/
|
||||
} // NumberFormatInfo
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
66
BeefLibs/corlib/src/System/Globalization/NumberStyles.bf
Normal file
66
BeefLibs/corlib/src/System/Globalization/NumberStyles.bf
Normal file
|
@ -0,0 +1,66 @@
|
|||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
/*============================================================
|
||||
**
|
||||
** Enum: NumberStyles.cs
|
||||
**
|
||||
**
|
||||
** Purpose: Contains valid formats for Numbers recognized by
|
||||
** the Number class' parsing code.
|
||||
**
|
||||
**
|
||||
===========================================================*/
|
||||
namespace System.Globalization
|
||||
{
|
||||
using System;
|
||||
public enum NumberStyles
|
||||
{
|
||||
// Bit flag indicating that leading whitespace is allowed. Character values
|
||||
// 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be
|
||||
// whitespace.
|
||||
None = 0x00000000,
|
||||
|
||||
AllowLeadingWhite = 0x00000001,
|
||||
|
||||
AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed.
|
||||
|
||||
AllowLeadingSign = 0x00000004, //Can the number start with a sign char.
|
||||
//Specified by NumberFormatInfo.PositiveSign and NumberFormatInfo.NegativeSign
|
||||
|
||||
AllowTrailingSign = 0x00000008, //Allow the number to end with a sign char
|
||||
|
||||
AllowParentheses = 0x00000010, //Allow the number to be enclosed in parens
|
||||
|
||||
AllowDecimalPoint = 0x00000020, //Allow a decimal point
|
||||
|
||||
AllowThousands = 0x00000040, //Allow thousands separators (more properly, allow group separators)
|
||||
|
||||
AllowExponent = 0x00000080, //Allow an exponent
|
||||
|
||||
AllowCurrencySymbol = 0x00000100, //Allow a currency symbol.
|
||||
|
||||
AllowHexSpecifier = 0x00000200, //Allow specifiying hexadecimal.
|
||||
//Common uses. These represent some of the most common combinations of these flags.
|
||||
|
||||
|
||||
Integer = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign,
|
||||
|
||||
HexNumber = AllowLeadingWhite | AllowTrailingWhite | AllowHexSpecifier,
|
||||
|
||||
Number = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign |
|
||||
AllowDecimalPoint | AllowThousands,
|
||||
|
||||
Float = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign |
|
||||
AllowDecimalPoint | AllowExponent,
|
||||
|
||||
Currency = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign |
|
||||
AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol,
|
||||
|
||||
Any = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign |
|
||||
AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol | AllowExponent,
|
||||
|
||||
}
|
||||
}
|
513
BeefLibs/corlib/src/System/Globalization/TimeSpanFormat.bf
Normal file
513
BeefLibs/corlib/src/System/Globalization/TimeSpanFormat.bf
Normal file
|
@ -0,0 +1,513 @@
|
|||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
// <OWNER>[....]</OWNER>
|
||||
//
|
||||
namespace System.Globalization {
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
|
||||
internal static class TimeSpanFormat
|
||||
{
|
||||
private static void IntToString(int n, int digits, String outStr)
|
||||
{
|
||||
((int32)n).[Friend]ToString(outStr, digits);
|
||||
}
|
||||
|
||||
internal static readonly FormatLiterals PositiveInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(false /*isNegative*/) ~ _.Dispose();
|
||||
internal static readonly FormatLiterals NegativeInvariantFormatLiterals = TimeSpanFormat.FormatLiterals.InitInvariant(true /*isNegative*/) ~ _.Dispose();
|
||||
|
||||
internal enum Pattern {
|
||||
None = 0,
|
||||
Minimum = 1,
|
||||
Full = 2,
|
||||
}
|
||||
|
||||
//
|
||||
// Format
|
||||
//
|
||||
// Actions: Main method called from TimeSpan.ToString
|
||||
//
|
||||
internal static Result<void> Format(TimeSpan value, StringView format, IFormatProvider formatProvider, String outStr)
|
||||
{
|
||||
var format;
|
||||
|
||||
if (format.IsNull || format.Length == 0)
|
||||
format = "c";
|
||||
|
||||
// standard formats
|
||||
if (format.Length == 1) {
|
||||
char8 f = format[0];
|
||||
|
||||
if (f == 'c' || f == 't' || f == 'T')
|
||||
{
|
||||
FormatStandard(value, true, format, Pattern.Minimum, outStr);
|
||||
return .Ok;
|
||||
}
|
||||
if (f == 'g' || f == 'G') {
|
||||
Pattern pattern;
|
||||
DateTimeFormatInfo dtfi = DateTimeFormatInfo.GetInstance(formatProvider);
|
||||
|
||||
if ((int64)value < 0)
|
||||
format = dtfi.FullTimeSpanNegativePattern;
|
||||
else
|
||||
format = dtfi.FullTimeSpanPositivePattern;
|
||||
if (f == 'g')
|
||||
pattern = Pattern.Minimum;
|
||||
else
|
||||
pattern = Pattern.Full;
|
||||
|
||||
return FormatStandard(value, false, format, pattern, outStr);
|
||||
}
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
return .Err;
|
||||
}
|
||||
|
||||
return FormatCustomized(value, format, DateTimeFormatInfo.GetInstance(formatProvider), outStr);
|
||||
}
|
||||
|
||||
//
|
||||
// FormatStandard
|
||||
//
|
||||
// Actions: Format the TimeSpan instance using the specified format.
|
||||
//
|
||||
private static Result<void> FormatStandard(TimeSpan value, bool isInvariant, StringView format, Pattern pattern, String outStr)
|
||||
{
|
||||
int32 day = (int32)((int64)value / TimeSpan.TicksPerDay);
|
||||
int64 time = (int64)value % TimeSpan.TicksPerDay;
|
||||
|
||||
if ((int64)value < 0) {
|
||||
day = -day;
|
||||
time = -time;
|
||||
}
|
||||
int hours = (int)(time / TimeSpan.TicksPerHour % 24);
|
||||
int minutes = (int)(time / TimeSpan.TicksPerMinute % 60);
|
||||
int seconds = (int)(time / TimeSpan.TicksPerSecond % 60);
|
||||
int fraction = (int)(time % TimeSpan.TicksPerSecond);
|
||||
|
||||
FormatLiterals literal;
|
||||
if (isInvariant) {
|
||||
if ((int64)value < 0)
|
||||
literal = NegativeInvariantFormatLiterals;
|
||||
else
|
||||
literal = PositiveInvariantFormatLiterals;
|
||||
}
|
||||
else {
|
||||
literal = FormatLiterals();
|
||||
literal.Init(format, pattern == Pattern.Full);
|
||||
}
|
||||
if (fraction != 0) { // truncate the partial second to the specified length
|
||||
fraction = (int)((int64)fraction / (int64)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - literal.ff));
|
||||
}
|
||||
|
||||
// Pattern.Full: [-]dd.hh:mm:ss.fffffff
|
||||
// Pattern.Minimum: [-][d.]hh:mm:ss[.fffffff]
|
||||
|
||||
outStr.Append(literal.Start); // [-]
|
||||
if (pattern == Pattern.Full || day != 0) { //
|
||||
day.ToString(outStr); // [dd]
|
||||
outStr.Append(literal.DayHourSep); // [.]
|
||||
} //
|
||||
IntToString(hours, literal.hh, outStr); // hh
|
||||
outStr.Append(literal.HourMinuteSep); // :
|
||||
IntToString(minutes, literal.mm, outStr); // mm
|
||||
outStr.Append(literal.MinuteSecondSep); // :
|
||||
IntToString(seconds, literal.ss, outStr); // ss
|
||||
if (!isInvariant && pattern == Pattern.Minimum) {
|
||||
int effectiveDigits = literal.ff;
|
||||
while (effectiveDigits > 0) {
|
||||
if (fraction % 10 == 0) {
|
||||
fraction = fraction / 10;
|
||||
effectiveDigits--;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (effectiveDigits > 0) {
|
||||
outStr.Append(literal.SecondFractionSep); // [.FFFFFFF]
|
||||
(fraction).ToString(outStr, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
else if (pattern == Pattern.Full || fraction != 0) {
|
||||
outStr.Append(literal.SecondFractionSep); // [.]
|
||||
IntToString(fraction, literal.ff, outStr); // [fffffff]
|
||||
} //
|
||||
outStr.Append(literal.End); //
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FormatCustomized
|
||||
//
|
||||
// Actions: Format the TimeSpan instance using the specified format.
|
||||
//
|
||||
internal static Result<void> FormatCustomized(TimeSpan value, StringView format, DateTimeFormatInfo dtfi, String result)
|
||||
{
|
||||
|
||||
Contract.Assert(dtfi != null, "dtfi == null");
|
||||
|
||||
int day = (int)((int64)value / TimeSpan.TicksPerDay);
|
||||
int64 time = (int64)value % TimeSpan.TicksPerDay;
|
||||
|
||||
if ((int64)value < 0) {
|
||||
day = -day;
|
||||
time = -time;
|
||||
}
|
||||
int hours = (int)(time / TimeSpan.TicksPerHour % 24);
|
||||
int minutes = (int)(time / TimeSpan.TicksPerMinute % 60);
|
||||
int seconds = (int)(time / TimeSpan.TicksPerSecond % 60);
|
||||
int fraction = (int)(time % TimeSpan.TicksPerSecond);
|
||||
|
||||
int64 tmp = 0;
|
||||
int i = 0;
|
||||
int tokenLen = 0;
|
||||
|
||||
while (i < format.Length) {
|
||||
char8 ch = format[i];
|
||||
int nextChar;
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
|
||||
if (tokenLen > 2)
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
return .Err;
|
||||
DateTimeFormat.FormatDigits(result, hours, tokenLen);
|
||||
break;
|
||||
case 'm':
|
||||
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
|
||||
if (tokenLen > 2)
|
||||
return .Err;
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
DateTimeFormat.FormatDigits(result, minutes, tokenLen);
|
||||
break;
|
||||
case 's':
|
||||
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
|
||||
if (tokenLen > 2)
|
||||
return .Err;
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
DateTimeFormat.FormatDigits(result, seconds, tokenLen);
|
||||
break;
|
||||
case 'f':
|
||||
//
|
||||
// The fraction of a second in single-digit precision. The remaining digits are truncated.
|
||||
//
|
||||
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
|
||||
if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits)
|
||||
return .Err;
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
|
||||
tmp = (int64)fraction;
|
||||
tmp /= (int64)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
|
||||
(tmp).ToString(result, DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture);
|
||||
break;
|
||||
case 'F':
|
||||
//
|
||||
// Displays the most significant digit of the seconds fraction. Nothing is displayed if the digit is zero.
|
||||
//
|
||||
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
|
||||
if (tokenLen > DateTimeFormat.MaxSecondsFractionDigits)
|
||||
return .Err;
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
|
||||
tmp = (int64)fraction;
|
||||
tmp /= (int64)Math.Pow(10, DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
|
||||
int effectiveDigits = tokenLen;
|
||||
while (effectiveDigits > 0) {
|
||||
if (tmp % 10 == 0) {
|
||||
tmp = tmp / 10;
|
||||
effectiveDigits--;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (effectiveDigits > 0) {
|
||||
(tmp).ToString(result, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
//
|
||||
// tokenLen == 1 : Day as digits with no leading zero.
|
||||
// tokenLen == 2+: Day as digits with leading zero for single-digit days.
|
||||
//
|
||||
tokenLen = DateTimeFormat.ParseRepeatPattern(format, i, ch);
|
||||
if (tokenLen > 8)
|
||||
return .Err;
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
DateTimeFormat.FormatDigits(result, day, tokenLen, true);
|
||||
break;
|
||||
case '\'':
|
||||
case '\"':
|
||||
//StringBuilder enquotedString = new StringBuilder();
|
||||
tokenLen = DateTimeFormat.ParseQuoteString(format, i, result);
|
||||
//result.Append(enquotedString);
|
||||
break;
|
||||
case '%':
|
||||
// Optional format character.
|
||||
// For example, format string "%d" will print day
|
||||
// Most of the cases, "%" can be ignored.
|
||||
nextChar = DateTimeFormat.ParseNextChar(format, i);
|
||||
// nextChar will be -1 if we already reach the end of the format string.
|
||||
// Besides, we will not allow "%%" appear in the pattern.
|
||||
if (nextChar >= 0 && nextChar != (int)'%')
|
||||
{
|
||||
TimeSpanFormat.FormatCustomized(value, StringView((char8*)&nextChar, 1), dtfi, result);
|
||||
tokenLen = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// This means that '%' is at the end of the format string or
|
||||
// "%%" appears in the format string.
|
||||
//
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
return .Err;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
// Escaped character. Can be used to insert character into the format string.
|
||||
// For example, "\d" will insert the character 'd' into the string.
|
||||
//
|
||||
nextChar = DateTimeFormat.ParseNextChar(format, i);
|
||||
if (nextChar >= 0)
|
||||
{
|
||||
result.Append(((char8)nextChar));
|
||||
tokenLen = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// This means that '\' is at the end of the formatting string.
|
||||
//
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
return .Err;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
|
||||
return .Err;
|
||||
}
|
||||
i += tokenLen;
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
internal struct FormatLiterals {
|
||||
internal String Start {
|
||||
get {
|
||||
return literals[0];
|
||||
}
|
||||
}
|
||||
internal String DayHourSep {
|
||||
get {
|
||||
return literals[1];
|
||||
}
|
||||
}
|
||||
internal String HourMinuteSep {
|
||||
get {
|
||||
return literals[2];
|
||||
}
|
||||
}
|
||||
internal String MinuteSecondSep {
|
||||
get {
|
||||
return literals[3];
|
||||
}
|
||||
}
|
||||
internal String SecondFractionSep {
|
||||
get {
|
||||
return literals[4];
|
||||
}
|
||||
}
|
||||
internal String End {
|
||||
get {
|
||||
return literals[5];
|
||||
}
|
||||
}
|
||||
internal String AppCompatLiteral;
|
||||
internal int dd;
|
||||
internal int hh;
|
||||
internal int mm;
|
||||
internal int ss;
|
||||
internal int ff;
|
||||
|
||||
private String[] literals;
|
||||
private List<String> ownedStrs;
|
||||
|
||||
public void Dispose() mut
|
||||
{
|
||||
DeleteAndNullify!(AppCompatLiteral);
|
||||
DeleteAndNullify!(literals);
|
||||
if (ownedStrs != null)
|
||||
{
|
||||
DeleteContainerAndItems!(ownedStrs);
|
||||
ownedStrs = null;
|
||||
}
|
||||
}
|
||||
|
||||
String AddOwnedStr(String str) mut
|
||||
{
|
||||
if (ownedStrs == null)
|
||||
ownedStrs = new List<String>();
|
||||
ownedStrs.Add(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
/* factory method for static invariant FormatLiterals */
|
||||
internal static FormatLiterals InitInvariant(bool isNegative) {
|
||||
FormatLiterals x = FormatLiterals();
|
||||
x.literals = new String[6];
|
||||
x.literals[0] = isNegative ? "-" : String.Empty;
|
||||
x.literals[1] = ".";
|
||||
x.literals[2] = ":";
|
||||
x.literals[3] = ":";
|
||||
x.literals[4] = ".";
|
||||
x.literals[5] = String.Empty;
|
||||
x.AppCompatLiteral = ":."; // MinuteSecondSep+SecondFractionSep;
|
||||
x.dd = 2;
|
||||
x.hh = 2;
|
||||
x.mm = 2;
|
||||
x.ss = 2;
|
||||
x.ff = DateTimeFormat.MaxSecondsFractionDigits;
|
||||
return x;
|
||||
}
|
||||
|
||||
// For the "v1" TimeSpan localized patterns, the data is simply literal field separators with
|
||||
// the constants guaranteed to include DHMSF ordered greatest to least significant.
|
||||
// Once the data becomes more complex than this we will need to write a proper tokenizer for
|
||||
// parsing and formatting
|
||||
internal void Init(StringView format, bool useInvariantFieldLengths) mut
|
||||
{
|
||||
literals = new String[6];
|
||||
for (int i = 0; i < literals.Count; i++)
|
||||
literals[i] = "";
|
||||
dd = 0;
|
||||
hh = 0;
|
||||
mm = 0;
|
||||
ss = 0;
|
||||
ff = 0;
|
||||
|
||||
String sb = scope String();
|
||||
bool inQuote = false;
|
||||
char8 quote = '\'';
|
||||
int field = 0;
|
||||
|
||||
for (int i = 0; i < format.Length; i++) {
|
||||
switch (format[i]) {
|
||||
case '\'':
|
||||
case '\"':
|
||||
if (inQuote && (quote == format[i])) {
|
||||
/* we were in a quote and found a matching exit quote, so we are outside a quote now */
|
||||
Contract.Assert(field >= 0 && field <= 5, "field >= 0 && field <= 5");
|
||||
if (field >= 0 && field <= 5) {
|
||||
literals[field] = AddOwnedStr(new String(sb));
|
||||
sb.Length = 0;
|
||||
inQuote = false;
|
||||
}
|
||||
else {
|
||||
return; // how did we get here?
|
||||
}
|
||||
}
|
||||
else if (!inQuote) {
|
||||
/* we are at the start of a new quote block */
|
||||
quote = format[i];
|
||||
inQuote = true;
|
||||
}
|
||||
else {
|
||||
/* we were in a quote and saw the other type of quote character, so we are still in a quote */
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
Contract.Assert(false, "Unexpected special token '%', Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
sb.Append(format[i]);
|
||||
case '\\':
|
||||
if (!inQuote) {
|
||||
i++; /* skip next character that is escaped by this backslash or percent sign */
|
||||
break;
|
||||
}
|
||||
sb.Append(format[i]);
|
||||
case 'd':
|
||||
if (!inQuote) {
|
||||
Contract.Assert((field == 0 && sb.Length == 0) || field == 1,
|
||||
"field == 0 || field == 1, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
field = 1; // DayHourSep
|
||||
dd++;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (!inQuote) {
|
||||
Contract.Assert((field == 1 && sb.Length == 0) || field == 2,
|
||||
"field == 1 || field == 2, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
field = 2; // HourMinuteSep
|
||||
hh++;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (!inQuote) {
|
||||
Contract.Assert((field == 2 && sb.Length == 0) || field == 3,
|
||||
"field == 2 || field == 3, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
field = 3; // MinuteSecondSep
|
||||
mm++;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (!inQuote) {
|
||||
Contract.Assert((field == 3 && sb.Length == 0) || field == 4,
|
||||
"field == 3 || field == 4, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
field = 4; // SecondFractionSep
|
||||
ss++;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
if (!inQuote) {
|
||||
Contract.Assert((field == 4 && sb.Length == 0) || field == 5,
|
||||
"field == 4 || field == 5, Bug in DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
field = 5; // End
|
||||
ff++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sb.Append(format[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Contract.Assert(field == 5);
|
||||
AppCompatLiteral = new String(MinuteSecondSep, SecondFractionSep);
|
||||
|
||||
Contract.Assert(0 < dd && dd < 3, "0 < dd && dd < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
Contract.Assert(0 < hh && hh < 3, "0 < hh && hh < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
Contract.Assert(0 < mm && mm < 3, "0 < mm && mm < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
Contract.Assert(0 < ss && ss < 3, "0 < ss && ss < 3, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
Contract.Assert(0 < ff && ff < 8, "0 < ff && ff < 8, Bug in System.Globalization.DateTimeFormatInfo.FullTimeSpan[Positive|Negative]Pattern");
|
||||
|
||||
if (useInvariantFieldLengths) {
|
||||
dd = 2;
|
||||
hh = 2;
|
||||
mm = 2;
|
||||
ss = 2;
|
||||
ff = DateTimeFormat.MaxSecondsFractionDigits;
|
||||
}
|
||||
else {
|
||||
if (dd < 1 || dd > 2) dd = 2; // The DTFI property has a problem. let's try to make the best of the situation.
|
||||
if (hh < 1 || hh > 2) hh = 2;
|
||||
if (mm < 1 || mm > 2) mm = 2;
|
||||
if (ss < 1 || ss > 2) ss = 2;
|
||||
if (ff < 1 || ff > 7) ff = 7;
|
||||
}
|
||||
}
|
||||
} //end of struct FormatLiterals
|
||||
}
|
||||
}
|
72
BeefLibs/corlib/src/System/Guid.bf
Normal file
72
BeefLibs/corlib/src/System/Guid.bf
Normal file
|
@ -0,0 +1,72 @@
|
|||
namespace System
|
||||
{
|
||||
struct Guid : IHashable
|
||||
{
|
||||
public static readonly Guid Empty = Guid();
|
||||
|
||||
private uint32 mA;
|
||||
private uint16 mB;
|
||||
private uint16 mC;
|
||||
private uint8 mD;
|
||||
private uint8 mE;
|
||||
private uint8 mF;
|
||||
private uint8 mG;
|
||||
private uint8 mH;
|
||||
private uint8 mI;
|
||||
private uint8 mJ;
|
||||
private uint8 mK;
|
||||
|
||||
public this()
|
||||
{
|
||||
this = default;
|
||||
}
|
||||
|
||||
public this(uint32 a, uint16 b, uint16 c, uint8 d, uint8 e, uint8 f, uint8 g, uint8 h, uint8 i, uint8 j, uint8 k)
|
||||
{
|
||||
mA = a;
|
||||
mB = b;
|
||||
mC = c;
|
||||
mD = d;
|
||||
mE = e;
|
||||
mF = f;
|
||||
mG = g;
|
||||
mH = h;
|
||||
mI = i;
|
||||
mJ = j;
|
||||
mK = k;
|
||||
}
|
||||
|
||||
public int GetHashCode()
|
||||
{
|
||||
return (int)mA ^ (int)mK;
|
||||
}
|
||||
|
||||
public static bool operator==(Guid val1, Guid val2)
|
||||
{
|
||||
return
|
||||
(val1.mA == val2.mA) &&
|
||||
(val1.mB == val2.mB) &&
|
||||
(val1.mC == val2.mC) &&
|
||||
(val1.mD == val2.mD) &&
|
||||
(val1.mE == val2.mE) &&
|
||||
(val1.mF == val2.mF) &&
|
||||
(val1.mG == val2.mG) &&
|
||||
(val1.mH == val2.mH) &&
|
||||
(val1.mI == val2.mI) &&
|
||||
(val1.mJ == val2.mJ) &&
|
||||
(val1.mK == val2.mK);
|
||||
}
|
||||
|
||||
public static Result<Guid> Parse(String str)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
|
||||
public static Guid Create()
|
||||
{
|
||||
Guid guid = ?;
|
||||
Platform.BfpSystem_CreateGUID(&guid);
|
||||
return guid;
|
||||
}
|
||||
}
|
||||
}
|
25
BeefLibs/corlib/src/System/IAsyncResult.bf
Normal file
25
BeefLibs/corlib/src/System/IAsyncResult.bf
Normal file
|
@ -0,0 +1,25 @@
|
|||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
/*============================================================
|
||||
**
|
||||
** Interface: IAsyncResult
|
||||
**
|
||||
** Purpose: Interface to encapsulate the results of an async
|
||||
** operation
|
||||
**
|
||||
===========================================================*/
|
||||
namespace System {
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
public interface IAsyncResult
|
||||
{
|
||||
bool IsCompleted { get; }
|
||||
WaitEvent AsyncWaitHandle { get; }
|
||||
Object AsyncState { get; }
|
||||
bool CompletedSynchronously { get; }
|
||||
}
|
||||
}
|
83
BeefLibs/corlib/src/System/IComparable.bf
Normal file
83
BeefLibs/corlib/src/System/IComparable.bf
Normal file
|
@ -0,0 +1,83 @@
|
|||
namespace System
|
||||
{
|
||||
interface IComparable<T>
|
||||
{
|
||||
int32 CompareTo(T other);
|
||||
}
|
||||
|
||||
public interface IComparer<T>
|
||||
{
|
||||
int Compare(T x, T y);
|
||||
}
|
||||
|
||||
public class CompareWrapper<T> : IComparer<T> where T : IOpComparable
|
||||
{
|
||||
public int Compare(T x, T y)
|
||||
{
|
||||
return x <=> y;
|
||||
}
|
||||
}
|
||||
|
||||
interface IInteger
|
||||
{
|
||||
}
|
||||
|
||||
interface IUnsigned
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
interface ISigned
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
interface IFloating
|
||||
{
|
||||
}
|
||||
|
||||
interface IOpEquals
|
||||
{
|
||||
public static bool operator==(Self val1, Self val2);
|
||||
}
|
||||
|
||||
interface IOpEquals<T>
|
||||
{
|
||||
public static bool operator==(Self val1, T val2);
|
||||
}
|
||||
|
||||
interface IOpComparable
|
||||
{
|
||||
static int operator<=>(Self lhs, Self rhs);
|
||||
}
|
||||
|
||||
interface IOpAddable
|
||||
{
|
||||
static Self operator+(Self lhs, Self rhs);
|
||||
}
|
||||
|
||||
interface IOpNegatable
|
||||
{
|
||||
static Self operator-(Self value);
|
||||
}
|
||||
|
||||
interface IOpConvertibleTo<T>
|
||||
{
|
||||
static operator T(Self value);
|
||||
}
|
||||
|
||||
interface IOpConvertibleFrom<T>
|
||||
{
|
||||
static operator Self(T value);
|
||||
}
|
||||
|
||||
interface IIsNaN
|
||||
{
|
||||
bool IsNaN { get; }
|
||||
}
|
||||
|
||||
interface ICanBeNaN : IIsNaN
|
||||
{
|
||||
|
||||
}
|
||||
}
|
12
BeefLibs/corlib/src/System/IEquatable.bf
Normal file
12
BeefLibs/corlib/src/System/IEquatable.bf
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace System
|
||||
{
|
||||
interface IEquatable
|
||||
{
|
||||
bool Equals(Object val);
|
||||
}
|
||||
|
||||
interface IEquatable<T>
|
||||
{
|
||||
bool Equals(T val2);
|
||||
}
|
||||
}
|
7
BeefLibs/corlib/src/System/IFormatProvider.bf
Normal file
7
BeefLibs/corlib/src/System/IFormatProvider.bf
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace System
|
||||
{
|
||||
interface IFormatProvider
|
||||
{
|
||||
Object GetFormat(Type formatType);
|
||||
}
|
||||
}
|
7
BeefLibs/corlib/src/System/IFormattable.bf
Normal file
7
BeefLibs/corlib/src/System/IFormattable.bf
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace System
|
||||
{
|
||||
interface IFormattable
|
||||
{
|
||||
void ToString(String outString, String format, IFormatProvider formatProvider);
|
||||
}
|
||||
}
|
7
BeefLibs/corlib/src/System/IHashable.bf
Normal file
7
BeefLibs/corlib/src/System/IHashable.bf
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace System
|
||||
{
|
||||
interface IHashable
|
||||
{
|
||||
int GetHashCode();
|
||||
}
|
||||
}
|
284
BeefLibs/corlib/src/System/IO/Directory.bf
Normal file
284
BeefLibs/corlib/src/System/IO/Directory.bf
Normal file
|
@ -0,0 +1,284 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public static class Directory
|
||||
{
|
||||
static extern bool Exists(char8* fileName);
|
||||
|
||||
public static bool Exists(StringView fileName)
|
||||
{
|
||||
return Exists(fileName.ToScopeCStr!());
|
||||
}
|
||||
|
||||
public static Result<void> CreateDirectory(StringView fullPath)
|
||||
{
|
||||
for (int32 pass = 0; pass < 2; pass++)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpDirectory_Create(fullPath.ToScopeCStr!(), &result);
|
||||
|
||||
if (result == .Ok)
|
||||
break;
|
||||
|
||||
if (result == .AlreadyExists)
|
||||
return .Ok;
|
||||
if ((pass == 0) && (result == .NotFound))
|
||||
{
|
||||
int32 prevSlash = Math.Max((int32)fullPath.LastIndexOf('/'), (int32)fullPath.LastIndexOf('\\'));
|
||||
if (prevSlash != -1)
|
||||
{
|
||||
StringView prevDir = StringView(fullPath, 0, prevSlash);
|
||||
Try!(CreateDirectory(prevDir));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return .Err;
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> Delete(StringView path)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpDirectory_Delete(path.ToScopeCStr!(), &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void> DelTree(String path)
|
||||
{
|
||||
if (path.Length <= 2)
|
||||
return .Err;
|
||||
if ((path[0] != '/') && (path[0] != '\\'))
|
||||
{
|
||||
if (path[1] == ':')
|
||||
{
|
||||
if (path.Length < 3)
|
||||
return .Err;
|
||||
}
|
||||
else
|
||||
return .Err;
|
||||
}
|
||||
|
||||
for (var fileEntry in Directory.EnumerateDirectories(path))
|
||||
{
|
||||
let fileName = scope String();
|
||||
fileEntry.GetFilePath(fileName);
|
||||
Try!(DelTree(fileName));
|
||||
}
|
||||
|
||||
for (var fileEntry in Directory.EnumerateFiles(path))
|
||||
{
|
||||
let fileName = scope String();
|
||||
fileEntry.GetFilePath(fileName);
|
||||
|
||||
Try!(File.SetAttributes(fileName, FileAttributes.Archive));
|
||||
Try!(File.Delete(fileName));
|
||||
}
|
||||
|
||||
// Allow failure for the directory, this can often be locked for various reasons
|
||||
// but we only consider a file failure to be an "actual" failure
|
||||
Directory.Delete(path).IgnoreError();
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> Move(StringView oldName, StringView newName)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpDirectory_Rename(oldName.ToScopeCStr!(), newName.ToScopeCStr!(), &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static void GetCurrentDirectory(String outPath)
|
||||
{
|
||||
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpDirectory_GetCurrent(outPtr, outSize, (Platform.BfpFileResult*)outResult);
|
||||
});
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> SetCurrentDirectory(StringView path)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpDirectory_SetCurrent(path.ToScopeCStr!(), &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
enum EnumerateFlags
|
||||
{
|
||||
Files = 1,
|
||||
Directories = 2
|
||||
}
|
||||
|
||||
public static FileEnumerator Enumerate(StringView searchStr, EnumerateFlags flags)
|
||||
{
|
||||
String useStr = new String(searchStr.Length + 1);
|
||||
useStr.Append(searchStr);
|
||||
useStr.EnsureNullTerminator();
|
||||
|
||||
Platform.BfpFindFileFlags bfpFlags = .None;
|
||||
if (flags.HasFlag(.Directories))
|
||||
bfpFlags |= .Directories;
|
||||
if (flags.HasFlag(.Files))
|
||||
bfpFlags |= .Files;
|
||||
let findFileData = Platform.BfpFindFileData_FindFirstFile(useStr, bfpFlags, null);
|
||||
return FileEnumerator(useStr, findFileData);
|
||||
}
|
||||
|
||||
public static FileEnumerator EnumerateDirectories(StringView dirPath)
|
||||
{
|
||||
let searchStr = scope String();
|
||||
searchStr.Append(dirPath);
|
||||
searchStr.Append("/*");
|
||||
return Enumerate(searchStr, .Directories);
|
||||
}
|
||||
|
||||
public static FileEnumerator EnumerateDirectories(StringView dirPath, StringView wildcard)
|
||||
{
|
||||
let searchStr = scope String();
|
||||
searchStr.Append(dirPath);
|
||||
searchStr.Append("/");
|
||||
searchStr.Append(wildcard);
|
||||
return Enumerate(searchStr, .Directories);
|
||||
}
|
||||
|
||||
public static FileEnumerator EnumerateFiles(StringView dirPath)
|
||||
{
|
||||
let searchStr = scope String();
|
||||
searchStr.Append(dirPath);
|
||||
searchStr.Append("/*");
|
||||
return Enumerate(searchStr, .Files);
|
||||
}
|
||||
|
||||
public static FileEnumerator EnumerateFiles(StringView dirPath, StringView wildcard)
|
||||
{
|
||||
let searchStr = scope String();
|
||||
searchStr.Append(dirPath);
|
||||
searchStr.Append("/");
|
||||
searchStr.Append(wildcard);
|
||||
return Enumerate(searchStr, .Directories);
|
||||
}
|
||||
}
|
||||
|
||||
struct FileFindEntry
|
||||
{
|
||||
String mSearchStr;
|
||||
Platform.BfpFindFileData* mFindFileData;
|
||||
|
||||
public this(String searchStr, Platform.BfpFindFileData* findFileData)
|
||||
{
|
||||
mSearchStr = searchStr;
|
||||
mFindFileData = findFileData;
|
||||
}
|
||||
|
||||
public bool IsDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return Platform.BfpFindFileData_GetFileAttributes(mFindFileData).HasFlag(.Directory);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetFileName(String outFileName)
|
||||
{
|
||||
Platform.GetStrHelper(outFileName, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpFindFileData_GetFileName(mFindFileData, outPtr, outSize, (Platform.BfpFileResult*)outResult);
|
||||
});
|
||||
}
|
||||
|
||||
public void GetFilePath(String outPath)
|
||||
{
|
||||
Path.GetDirectoryPath(mSearchStr, outPath);
|
||||
outPath.Append(Path.DirectorySeparatorChar);
|
||||
GetFileName(outPath);
|
||||
}
|
||||
|
||||
public DateTime GetLastWriteTime()
|
||||
{
|
||||
return DateTime.FromFileTime((int64)Platform.BfpFindFileData_GetTime_LastWrite(mFindFileData));
|
||||
}
|
||||
|
||||
public DateTime GetLastWriteTimeUtc()
|
||||
{
|
||||
return DateTime.FromFileTimeUtc((int64)Platform.BfpFindFileData_GetTime_LastWrite(mFindFileData));
|
||||
}
|
||||
|
||||
public DateTime GetCreatedTime()
|
||||
{
|
||||
return DateTime.FromFileTime((int64)Platform.BfpFindFileData_GetTime_Created(mFindFileData));
|
||||
}
|
||||
|
||||
public DateTime GetCreatedTimeUtc()
|
||||
{
|
||||
return DateTime.FromFileTimeUtc((int64)Platform.BfpFindFileData_GetTime_Created(mFindFileData));
|
||||
}
|
||||
|
||||
public DateTime GetAccessedTime()
|
||||
{
|
||||
return DateTime.FromFileTime((int64)Platform.BfpFindFileData_GetTime_Access(mFindFileData));
|
||||
}
|
||||
|
||||
public DateTime GetAccessedTimeUtc()
|
||||
{
|
||||
return DateTime.FromFileTimeUtc((int64)Platform.BfpFindFileData_GetTime_Access(mFindFileData));
|
||||
}
|
||||
|
||||
public Platform.BfpFileAttributes GetFileAttributes()
|
||||
{
|
||||
return Platform.BfpFindFileData_GetFileAttributes(mFindFileData);
|
||||
}
|
||||
}
|
||||
|
||||
struct FileEnumerator : IEnumerator<FileFindEntry>
|
||||
{
|
||||
String mSearchStr;
|
||||
Platform.BfpFindFileData* mFindFileData;
|
||||
int mIdx;
|
||||
|
||||
public this(String searchStr, Platform.BfpFindFileData* findFileData)
|
||||
{
|
||||
mSearchStr = searchStr;
|
||||
mFindFileData = findFileData;
|
||||
mIdx = -1;
|
||||
}
|
||||
|
||||
public FileFindEntry Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return FileFindEntry(mSearchStr, mFindFileData);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
delete mSearchStr;
|
||||
if (mFindFileData != null)
|
||||
Platform.BfpFindFileData_Release(mFindFileData);
|
||||
}
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
mIdx++;
|
||||
if (mIdx == 0)
|
||||
return mFindFileData != null;
|
||||
|
||||
return Platform.BfpFindFileData_FindNextFile(mFindFileData);
|
||||
}
|
||||
|
||||
public Result<FileFindEntry> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
}
|
115
BeefLibs/corlib/src/System/IO/DynMemStream.bf
Normal file
115
BeefLibs/corlib/src/System/IO/DynMemStream.bf
Normal file
|
@ -0,0 +1,115 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
class DynMemStream : Stream
|
||||
{
|
||||
List<uint8> mData ~ { if (mOwnsData) delete _; };
|
||||
int mPosition = 0;
|
||||
bool mOwnsData;
|
||||
|
||||
public this()
|
||||
{
|
||||
mData = new .();
|
||||
mOwnsData = true;
|
||||
}
|
||||
|
||||
public uint8* Ptr
|
||||
{
|
||||
get
|
||||
{
|
||||
return mData.Ptr;
|
||||
}
|
||||
}
|
||||
|
||||
public Span<uint8> Content
|
||||
{
|
||||
get
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
mPosition = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return mData.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public List<uint8> TakeOwnership()
|
||||
{
|
||||
Debug.Assert(mOwnsData);
|
||||
mOwnsData = false;
|
||||
return mData;
|
||||
}
|
||||
|
||||
public override Result<int> TryRead(Span<uint8> data)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
return .Ok(0);
|
||||
int readBytes = Math.Min(data.Length, mData.Count - mPosition);
|
||||
if (readBytes <= 0)
|
||||
return .Ok(readBytes);
|
||||
|
||||
Internal.MemCpy(data.Ptr, &mData[mPosition], readBytes);
|
||||
mPosition += readBytes;
|
||||
return .Ok(readBytes);
|
||||
}
|
||||
|
||||
public override Result<int> TryWrite(Span<uint8> data)
|
||||
{
|
||||
let count = data.Length;
|
||||
if (count == 0)
|
||||
return .Ok(0);
|
||||
int growSize = mPosition + count - mData.Count;
|
||||
if (growSize > 0)
|
||||
mData.GrowUnitialized(growSize);
|
||||
Internal.MemCpy(&mData[mPosition], data.Ptr, count);
|
||||
mPosition += count;
|
||||
return .Ok(count);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void RemoveFromStart(int count)
|
||||
{
|
||||
mPosition = Math.Max(mPosition - count, 0);
|
||||
mData.RemoveRange(0, count);
|
||||
}
|
||||
}
|
||||
}
|
173
BeefLibs/corlib/src/System/IO/File.bf
Normal file
173
BeefLibs/corlib/src/System/IO/File.bf
Normal file
|
@ -0,0 +1,173 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public enum FileOpenError
|
||||
{
|
||||
NotFound,
|
||||
NotFile,
|
||||
Unknown,
|
||||
SharingViolation
|
||||
}
|
||||
|
||||
public enum FileReadError
|
||||
{
|
||||
Unknown
|
||||
}
|
||||
|
||||
public enum FileError
|
||||
{
|
||||
case FileOpenError(FileOpenError);
|
||||
case FileReadError(FileReadError);
|
||||
}
|
||||
|
||||
class File
|
||||
{
|
||||
public static Result<void, FileError> ReadAllText(StringView path, String outText, bool preserveLineEnding = false)
|
||||
{
|
||||
StreamReader sr = scope StreamReader();
|
||||
if (sr.Open(path) case .Err(let err))
|
||||
return .Err(.FileOpenError(err));
|
||||
if (sr.ReadToEnd(outText) case .Err)
|
||||
return .Err(.FileReadError(.Unknown));
|
||||
|
||||
if (!preserveLineEnding)
|
||||
{
|
||||
if (Environment.NewLine.Length > 1)
|
||||
outText.Replace("\r", "");
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void> WriteAllText(StringView path, StringView text, bool doAppend = false)
|
||||
{
|
||||
FileStream fs = scope FileStream();
|
||||
var result = fs.Open(path, doAppend ? .Append : .Create, .Write);
|
||||
if (result case .Err)
|
||||
return .Err;
|
||||
fs.TryWrite(.((uint8*)text.Ptr, text.Length));
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void> WriteAllText(StringView path, StringView text, Encoding encoding)
|
||||
{
|
||||
FileStream fs = scope FileStream();
|
||||
|
||||
int len = encoding.GetEncodedSize(text);
|
||||
uint8* data = new uint8[len]*;
|
||||
defer delete data;
|
||||
int actualLen = encoding.Encode(text, .(data, len));
|
||||
Debug.Assert(len == actualLen);
|
||||
if (len != actualLen)
|
||||
return .Err;
|
||||
|
||||
var result = fs.Open(path, .Create, .Write);
|
||||
if (result case .Err)
|
||||
return .Err;
|
||||
fs.TryWrite(.(data, len));
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void> WriteAllLines(StringView path, IEnumerator<StringView> enumerator)
|
||||
{
|
||||
String strBuf = scope String();
|
||||
for (var str in enumerator)
|
||||
{
|
||||
strBuf.Append(str);
|
||||
strBuf.Append(Environment.NewLine);
|
||||
}
|
||||
return WriteAllText(path, strBuf);
|
||||
}
|
||||
|
||||
public static Result<void> WriteAllLines(StringView path, IEnumerator<String> enumerator)
|
||||
{
|
||||
String strBuf = scope String();
|
||||
for (var str in enumerator)
|
||||
{
|
||||
strBuf.Append(str);
|
||||
strBuf.Append(Environment.NewLine);
|
||||
}
|
||||
return WriteAllText(path, strBuf);
|
||||
}
|
||||
|
||||
/*public static Result<IEnumerator<Result<String>>> ReadLines(String fileName)
|
||||
{
|
||||
ThrowUnimplemented();
|
||||
return (IEnumerator<Result<String>>)null;
|
||||
}*/
|
||||
|
||||
static extern bool Exists(char8* fileName);
|
||||
|
||||
public static bool Exists(StringView fileName)
|
||||
{
|
||||
return Exists(fileName.ToScopeCStr!());
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> Delete(StringView fileName)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpFile_Delete(fileName.ToScopeCStr!(), &result);
|
||||
if ((result != .Ok) && (result != .NotFound))
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> Move(StringView fromPath, StringView toPath)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpFile_Rename(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> Copy(StringView fromPath, StringView toPath)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), .Always, &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> CopyIfNewer(StringView fromPath, StringView toPath)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), .IfNewer, &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> Copy(StringView fromPath, StringView toPath, bool overwrite)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), overwrite ? .Always : .IfNotExists, &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void, Platform.BfpFileResult> SetAttributes(StringView path, FileAttributes attr)
|
||||
{
|
||||
Platform.BfpFileResult result = default;
|
||||
Platform.BfpFile_SetAttributes(path.ToScopeCStr!(), (Platform.BfpFileAttributes)attr, &result);
|
||||
if (result != .Ok)
|
||||
return .Err(result);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<DateTime> GetLastWriteTime(StringView fullPath)
|
||||
{
|
||||
return DateTime.FromFileTime((int64)Platform.BfpFile_GetTime_LastWrite(fullPath.ToScopeCStr!()));
|
||||
}
|
||||
|
||||
public static Result<DateTime> GetLastWriteTimeUtc(StringView fullPath)
|
||||
{
|
||||
return DateTime.FromFileTimeUtc((int64)Platform.BfpFile_GetTime_LastWrite(fullPath.ToScopeCStr!()));
|
||||
}
|
||||
}
|
||||
}
|
11
BeefLibs/corlib/src/System/IO/FileAccess.bf
Normal file
11
BeefLibs/corlib/src/System/IO/FileAccess.bf
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public enum FileAccess
|
||||
{
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
ReadWrite = 3,
|
||||
}
|
||||
}
|
21
BeefLibs/corlib/src/System/IO/FileAttributes.bf
Normal file
21
BeefLibs/corlib/src/System/IO/FileAttributes.bf
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
// These correspond to Platform.BfpFileFlags
|
||||
public enum FileAttributes
|
||||
{
|
||||
None = 0,
|
||||
Normal = 1,
|
||||
Directory = 2,
|
||||
SymLink = 4,
|
||||
Device = 8,
|
||||
ReadOnly = 0x10,
|
||||
Hidden = 0x20,
|
||||
System = 0x40,
|
||||
Temporary = 0x80,
|
||||
Offline = 0x100,
|
||||
Encrypted = 0x200,
|
||||
Archive = 0x400,
|
||||
}
|
||||
}
|
32
BeefLibs/corlib/src/System/IO/FileMode.bf
Normal file
32
BeefLibs/corlib/src/System/IO/FileMode.bf
Normal file
|
@ -0,0 +1,32 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public enum FileMode
|
||||
{
|
||||
/// Creates a new file. Fails if the file already exists.
|
||||
CreateNew = 1,
|
||||
|
||||
/// Creates a new file. If the file already exists, it is overwritten.
|
||||
Create = 2,
|
||||
|
||||
/// Opens an existing file. Fails if the file does not exist.
|
||||
Open = 3,
|
||||
|
||||
// Opens the file if it exists. Otherwise, creates a new file.
|
||||
OpenOrCreate = 4,
|
||||
|
||||
// Opens an existing file. Once opened, the file is truncated so that its
|
||||
// size is zero bytes. The calling process must open the file with at least
|
||||
// WRITE access. Fails if the file does not exist.
|
||||
Truncate = 5,
|
||||
|
||||
// Opens the file if it exists and seeks to the end. Otherwise,
|
||||
// creates a new file.
|
||||
Append = 6,
|
||||
}
|
||||
}
|
32
BeefLibs/corlib/src/System/IO/FileOptions.bf
Normal file
32
BeefLibs/corlib/src/System/IO/FileOptions.bf
Normal file
|
@ -0,0 +1,32 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
// Maps to FILE_FLAG_DELETE_ON_CLOSE and similar values from winbase.h.
|
||||
// We didn't expose a number of these values because we didn't believe
|
||||
// a number of them made sense in managed code, at least not yet.
|
||||
public enum FileOptions
|
||||
{
|
||||
// NOTE: any change to FileOptions enum needs to be
|
||||
// matched in the FileStream ctor for error validation
|
||||
None = 0,
|
||||
WriteThrough = ((int32)0x80000000),
|
||||
Asynchronous = ((int32)0x40000000), // FILE_FLAG_OVERLAPPED
|
||||
// NoBuffering = 0x20000000,
|
||||
RandomAccess = 0x10000000,
|
||||
DeleteOnClose = 0x04000000,
|
||||
SequentialScan = 0x08000000,
|
||||
// AllowPosix = 0x01000000, // FILE_FLAG_POSIX_SEMANTICS
|
||||
// BackupOrRestore,
|
||||
// DisallowReparsePoint = 0x00200000, // FILE_FLAG_OPEN_REPARSE_POINT
|
||||
// NoRemoteRecall = 0x00100000, // FILE_FLAG_OPEN_NO_RECALL
|
||||
// FirstPipeInstance = 0x00080000, // FILE_FLAG_FIRST_PIPE_INSTANCE
|
||||
Encrypted = 0x00004000, // FILE_ATTRIBUTE_ENCRYPTED
|
||||
}
|
||||
}
|
||||
|
44
BeefLibs/corlib/src/System/IO/FileShare.bf
Normal file
44
BeefLibs/corlib/src/System/IO/FileShare.bf
Normal file
|
@ -0,0 +1,44 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
// Contains constants for controlling file sharing options while
|
||||
// opening files. You can specify what access other processes trying
|
||||
// to open the same file concurrently can have.
|
||||
//
|
||||
// Note these values currently match the values for FILE_SHARE_READ,
|
||||
// FILE_SHARE_WRITE, and FILE_SHARE_DELETE in winnt.h
|
||||
//
|
||||
public enum FileShare
|
||||
{
|
||||
/// No sharing. Any request to open the file (by this process or another
|
||||
/// process) will fail until the file is closed.
|
||||
None = 0,
|
||||
|
||||
/// Allows subsequent opening of the file for reading. If this flag is not
|
||||
/// specified, any request to open the file for reading (by this process or
|
||||
/// another process) will fail until the file is closed.
|
||||
Read = 1,
|
||||
|
||||
/// Allows subsequent opening of the file for writing. If this flag is not
|
||||
/// specified, any request to open the file for writing (by this process or
|
||||
/// another process) will fail until the file is closed.
|
||||
Write = 2,
|
||||
|
||||
/// Allows subsequent opening of the file for writing or reading. If this flag
|
||||
/// is not specified, any request to open the file for writing or reading (by
|
||||
/// this process or another process) will fail until the file is closed.
|
||||
ReadWrite = 3,
|
||||
|
||||
/// Open the file, but allow someone else to delete the file.
|
||||
Delete = 4,
|
||||
|
||||
/// Whether the file handle should be inheritable by child processes.
|
||||
/// Note this is not directly supported like this by Win32.
|
||||
Inheritable = 0x10,
|
||||
}
|
||||
}
|
219
BeefLibs/corlib/src/System/IO/FileStream.bf
Normal file
219
BeefLibs/corlib/src/System/IO/FileStream.bf
Normal file
|
@ -0,0 +1,219 @@
|
|||
using System.Threading;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
abstract class FileStreamBase : Stream
|
||||
{
|
||||
protected Platform.BfpFile* mBfpFile;
|
||||
|
||||
public enum SeekKind
|
||||
{
|
||||
Absolute,
|
||||
Relative,
|
||||
FromEnd
|
||||
};
|
||||
|
||||
public override int64 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return Platform.BfpFile_Seek(mBfpFile, 0, .Relative);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Platform.BfpFile_Seek(mBfpFile, value, .Absolute);
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return Platform.BfpFile_GetFileSize(mBfpFile);
|
||||
}
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public Result<void> Seek(int64 pos, SeekKind seekKind = .Absolute)
|
||||
{
|
||||
int64 newPos = Platform.BfpFile_Seek(mBfpFile, pos, (Platform.BfpFileSeekKind)seekKind);
|
||||
// Ensure position is what was requested
|
||||
if ((seekKind == .Absolute) && (newPos != pos))
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public override Result<int> TryRead(Span<uint8> data)
|
||||
{
|
||||
Platform.BfpFileResult result = .Ok;
|
||||
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result);
|
||||
if ((result != .Ok) && (result != .PartialData))
|
||||
return .Err;
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
public Result<int> TryRead(Span<uint8> data, int timeoutMS)
|
||||
{
|
||||
Platform.BfpFileResult result = .Ok;
|
||||
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result);
|
||||
if ((result != .Ok) && (result != .PartialData))
|
||||
return .Err;
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
public override Result<int> TryWrite(Span<uint8> data)
|
||||
{
|
||||
Platform.BfpFileResult result = .Ok;
|
||||
int numBytesWritten = Platform.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result);
|
||||
if ((result != .Ok) && (result != .PartialData))
|
||||
return .Err;
|
||||
return numBytesWritten;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (mBfpFile != null)
|
||||
Platform.BfpFile_Release(mBfpFile);
|
||||
mBfpFile = null;
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if (mBfpFile != null)
|
||||
Platform.BfpFile_Flush(mBfpFile);
|
||||
}
|
||||
}
|
||||
|
||||
class FileStream : FileStreamBase
|
||||
{
|
||||
FileAccess mFileAccess;
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public this(Platform.BfpFile* handle, FileAccess access, int32 bufferSize, bool isAsync)
|
||||
{
|
||||
mBfpFile = handle;
|
||||
mFileAccess = access;
|
||||
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return mFileAccess.HasFlag(FileAccess.Read);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return mFileAccess.HasFlag(FileAccess.Write);
|
||||
}
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Create(StringView path, FileAccess access = .ReadWrite, FileShare share = .None, int bufferSize = 4096, FileOptions options= .None, SecurityAttributes* secAttrs = null)
|
||||
{
|
||||
return Open(path, FileMode.Create, access, share, bufferSize, options, secAttrs);
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Open(StringView path, FileAccess access = .ReadWrite, FileShare share = .None, int bufferSize = 4096, FileOptions options= .None, SecurityAttributes* secAttrs = null)
|
||||
{
|
||||
return Open(path, FileMode.Open, access, share, bufferSize, options, secAttrs);
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> OpenStd(Platform.BfpFileStdKind stdKind)
|
||||
{
|
||||
Platform.BfpFileResult fileResult = .Ok;
|
||||
mBfpFile = Platform.BfpFile_GetStd(stdKind, &fileResult);
|
||||
|
||||
if ((mBfpFile == null) || (fileResult != .Ok))
|
||||
{
|
||||
switch (fileResult)
|
||||
{
|
||||
case .ShareError:
|
||||
return .Err(.SharingViolation);
|
||||
case .NotFound:
|
||||
return .Err(.NotFound);
|
||||
default:
|
||||
return .Err(.Unknown);
|
||||
}
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Open(StringView path, FileMode mode, FileAccess access, FileShare share = .None, int bufferSize = 4096, FileOptions options= .None, SecurityAttributes* secAttrs = null)
|
||||
{
|
||||
Runtime.Assert(mBfpFile == null);
|
||||
|
||||
Platform.BfpFileCreateKind createKind = .CreateAlways;
|
||||
Platform.BfpFileCreateFlags createFlags = .None;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case .CreateNew:
|
||||
createKind = .CreateIfNotExists;
|
||||
case .Create:
|
||||
createKind = .CreateAlways;
|
||||
case .Open:
|
||||
createKind = .OpenExisting;
|
||||
case .OpenOrCreate:
|
||||
createKind = .CreateAlways;
|
||||
case .Truncate:
|
||||
createKind = .CreateAlways;
|
||||
createFlags |= .Truncate;
|
||||
case .Append:
|
||||
createKind = .CreateAlways;
|
||||
createFlags |= .Append;
|
||||
}
|
||||
|
||||
if (access.HasFlag(.Read))
|
||||
createFlags |= .Read;
|
||||
if (access.HasFlag(.Write))
|
||||
createFlags |= .Write;
|
||||
|
||||
if (share.HasFlag(.Read))
|
||||
createFlags |= .ShareRead;
|
||||
if (share.HasFlag(.Write))
|
||||
createFlags |= .ShareWrite;
|
||||
if (share.HasFlag(.Delete))
|
||||
createFlags |= .ShareDelete;
|
||||
|
||||
Platform.BfpFileAttributes fileFlags = .Normal;
|
||||
|
||||
Platform.BfpFileResult fileResult = .Ok;
|
||||
mBfpFile = Platform.BfpFile_Create(path.ToScopeCStr!(128), createKind, createFlags, fileFlags, &fileResult);
|
||||
|
||||
if ((mBfpFile == null) || (fileResult != .Ok))
|
||||
{
|
||||
switch (fileResult)
|
||||
{
|
||||
case .ShareError:
|
||||
return .Err(.SharingViolation);
|
||||
case .NotFound:
|
||||
return .Err(.NotFound);
|
||||
default:
|
||||
return .Err(.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public void Attach(Platform.BfpFile* bfpFile)
|
||||
{
|
||||
Close();
|
||||
mBfpFile = bfpFile;
|
||||
}
|
||||
}
|
||||
}
|
92
BeefLibs/corlib/src/System/IO/FileSystemWatcher.bf
Normal file
92
BeefLibs/corlib/src/System/IO/FileSystemWatcher.bf
Normal file
|
@ -0,0 +1,92 @@
|
|||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public class FileSystemWatcher
|
||||
{
|
||||
String mDirectory ~ delete _;
|
||||
String mFilter ~ delete _;
|
||||
Platform.BfpFileWatcher* mFileWatcher;
|
||||
|
||||
public delegate void CreatedFunc(String fileName);
|
||||
public delegate void DeletedFunc(String fileName);
|
||||
public delegate void ChangedFunc(String fileName);
|
||||
public delegate void RenameFunc(String newName, String oldName);
|
||||
public delegate void ErrorFunc();
|
||||
|
||||
public Event<ChangedFunc> OnChanged ~ _.Dispose();
|
||||
public Event<CreatedFunc> OnCreated ~ _.Dispose();
|
||||
public Event<DeletedFunc> OnDeleted ~ _.Dispose();
|
||||
public Event<RenameFunc> OnRenamed ~ _.Dispose();
|
||||
public Event<ErrorFunc> OnError ~ _.Dispose();
|
||||
public bool IncludeSubdirectories;
|
||||
|
||||
public this()
|
||||
{
|
||||
mDirectory = String.Empty;
|
||||
mFilter = "*.*";
|
||||
}
|
||||
|
||||
public this(StringView path) : this(path, "*.*")
|
||||
{
|
||||
}
|
||||
|
||||
public this(StringView path, StringView filter)
|
||||
{
|
||||
this.mDirectory = new String(path);
|
||||
this.mFilter = new String(filter);
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
StopRaisingEvents().IgnoreError();
|
||||
}
|
||||
|
||||
public String Directory
|
||||
{
|
||||
get
|
||||
{
|
||||
return mDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
static void BfpDirectoryChangeFunc(Platform.BfpFileWatcher* watcher, void* userData, Platform.BfpFileChangeKind changeKind, char8* directory, char8* fileName, char8* newName)
|
||||
{
|
||||
let fileSysWatcher = (FileSystemWatcher)Internal.UnsafeCastToObject(userData);
|
||||
|
||||
switch (changeKind)
|
||||
{
|
||||
case .BfpFileChangeKind_Added:
|
||||
fileSysWatcher.OnCreated(scope String(fileName));
|
||||
case .BfpFileChangeKind_Modified:
|
||||
fileSysWatcher.OnChanged(scope String(fileName));
|
||||
case .BfpFileChangeKind_Removed:
|
||||
fileSysWatcher.OnDeleted(scope String(fileName));
|
||||
case .BfpFileChangeKind_Renamed:
|
||||
fileSysWatcher.OnRenamed(scope String(fileName), scope String(newName));
|
||||
case .BfpFileChangeKind_Failed:
|
||||
fileSysWatcher.OnError();
|
||||
}
|
||||
}
|
||||
|
||||
public Result<void> StartRaisingEvents()
|
||||
{
|
||||
Platform.BfpFileWatcherFlags flags = IncludeSubdirectories ? .IncludeSubdirectories : .None;
|
||||
mFileWatcher = Platform.BfpFileWatcher_WatchDirectory(mDirectory, => BfpDirectoryChangeFunc, flags, Internal.UnsafeCastToPtr(this), null);
|
||||
if (mFileWatcher == null)
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> StopRaisingEvents()
|
||||
{
|
||||
if (mFileWatcher == null)
|
||||
return .Ok;
|
||||
|
||||
Platform.BfpFileWatcher_Release(mFileWatcher);
|
||||
mFileWatcher = null;
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
}
|
273
BeefLibs/corlib/src/System/IO/FolderBrowserDialog.bf
Normal file
273
BeefLibs/corlib/src/System/IO/FolderBrowserDialog.bf
Normal file
|
@ -0,0 +1,273 @@
|
|||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
namespace System.IO
|
||||
{
|
||||
class FolderBrowserDialog : CommonDialog
|
||||
{
|
||||
String mSelectedPath = new String() ~ delete _;
|
||||
public bool ShowNewFolderButton;
|
||||
String mDescriptionText = new String() ~ delete _;
|
||||
bool mSelectedPathNeedsCheck;
|
||||
static FolderBrowserDialog sCurrentThis;
|
||||
|
||||
public this()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public StringView SelectedPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return mSelectedPath;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
mSelectedPath.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
public StringView Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return mDescriptionText;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
mDescriptionText.Set(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
//mRootFolder = .Default;
|
||||
mSelectedPath.Clear();
|
||||
mDescriptionText.Clear();
|
||||
mSelectedPathNeedsCheck = false;
|
||||
ShowNewFolderButton = true;
|
||||
}
|
||||
|
||||
protected Result<DialogResult> RunDialog_New(Windows.HWnd hWndOwner, FolderBrowserDialog.COM_IFileDialog* fileDialog)
|
||||
{
|
||||
fileDialog.VT.SetOptions(fileDialog, .PICKFOLDERS);
|
||||
fileDialog.VT.Show(fileDialog, hWndOwner);
|
||||
|
||||
DialogResult result = .Cancel;
|
||||
|
||||
mSelectedPath.Clear();
|
||||
COM_IShellItem* shellItem = null;
|
||||
fileDialog.VT.GetResult(fileDialog, out shellItem);
|
||||
if (shellItem != null)
|
||||
{
|
||||
char16* cStr = null;
|
||||
if (shellItem.VT.GetDisplayName(shellItem, .FILESYSPATH, out cStr) == .OK)
|
||||
{
|
||||
let str = scope String..Append(cStr);
|
||||
mSelectedPath.Append(str);
|
||||
Windows.COM_IUnknown.CoTaskMemFree(cStr);
|
||||
result = .OK;
|
||||
}
|
||||
shellItem.VT.Release(shellItem);
|
||||
}
|
||||
|
||||
fileDialog.VT.Release(fileDialog);
|
||||
return .Ok(result);
|
||||
}
|
||||
|
||||
protected override Result<DialogResult> RunDialog(Windows.HWnd hWndOwner)
|
||||
{
|
||||
FolderBrowserDialog.COM_IFileDialog* fileDialog = null;
|
||||
let hr = Windows.COM_IUnknown.CoCreateInstance(ref FolderBrowserDialog.COM_IFileDialog.sCLSID, null, .INPROC_SERVER, ref FolderBrowserDialog.COM_IFileDialog.sIID, (void**)&fileDialog);
|
||||
if (hr == 0)
|
||||
return RunDialog_New(hWndOwner, fileDialog);
|
||||
|
||||
int pidlRoot = 0;
|
||||
//Windows.SHGetSpecialFolderLocation(hWndOwner, (int32)mRootFolder, ref pidlRoot);
|
||||
if (pidlRoot == (int)0)
|
||||
{
|
||||
Windows.SHGetSpecialFolderLocation(hWndOwner, Windows.CSIDL_DESKTOP, ref pidlRoot);
|
||||
if (pidlRoot == (int)0)
|
||||
return .Err;
|
||||
}
|
||||
|
||||
int32 mergedOptions = (int32)Windows.BrowseInfos.NewDialogStyle;
|
||||
if (!ShowNewFolderButton)
|
||||
mergedOptions |= (int32)Windows.BrowseInfos.HideNewFolderButton;
|
||||
|
||||
String displayName = scope String(Windows.MAX_PATH);
|
||||
|
||||
Windows.WndProc callback = => FolderBrowserDialog_BrowseCallbackProc;
|
||||
|
||||
Windows.BrowseInfo bi;
|
||||
bi.mHWndOwner = hWndOwner;
|
||||
bi.mIdlRoot = pidlRoot;
|
||||
bi.mDisplayName = displayName;
|
||||
bi.mTitle = mDescriptionText;
|
||||
bi.mFlags = mergedOptions;
|
||||
bi.mCallback = callback;
|
||||
bi.mLParam = 0;
|
||||
bi.mImage = 0;
|
||||
|
||||
sCurrentThis = this;
|
||||
int pidlRet = Windows.SHBrowseForFolder(ref bi);
|
||||
sCurrentThis = null;
|
||||
|
||||
if (pidlRet == (int)0)
|
||||
return DialogResult.Cancel;
|
||||
|
||||
char8* selectedPathCStr = scope char8[Windows.MAX_PATH]*;
|
||||
Windows.SHGetPathFromIDList(pidlRet, selectedPathCStr);
|
||||
mSelectedPath.Clear();
|
||||
mSelectedPath.Append(selectedPathCStr);
|
||||
|
||||
return DialogResult.OK;
|
||||
}
|
||||
|
||||
public static int FolderBrowserDialog_BrowseCallbackProc(Windows.HWnd hWnd, int32 msg, int wParam, int lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case Windows.BFFM_INITIALIZED:
|
||||
// Indicates the browse dialog box has finished initializing. The lpData value is zero.
|
||||
if (sCurrentThis.mSelectedPath.Length != 0)
|
||||
{
|
||||
// Try to select the folder specified by selectedPath
|
||||
Windows.SendMessageW(hWnd, Windows.BFFM_SETSELECTIONA, 1, (int)sCurrentThis.mSelectedPath.ToScopedNativeWChar!());
|
||||
}
|
||||
break;
|
||||
case Windows.BFFM_SELCHANGED:
|
||||
// Indicates the selection has changed. The lpData parameter points to the item identifier list for the newly selected item.
|
||||
int selectedPidl = lParam;
|
||||
if (selectedPidl != (int)0)
|
||||
{
|
||||
char8* pszSelectedPath = scope char8[Windows.MAX_PATH]*;
|
||||
// Try to retrieve the path from the IDList
|
||||
bool isFileSystemFolder = Windows.SHGetPathFromIDList(selectedPidl, pszSelectedPath);
|
||||
Windows.SendMessageW(hWnd, Windows.BFFM_ENABLEOK, 0, (int)(isFileSystemFolder ? 1 : 0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct COM_IFileDialogEvents
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct COM_IShellItem : Windows.COM_IUnknown
|
||||
{
|
||||
public enum SIGDN : uint32
|
||||
{
|
||||
NORMALDISPLAY = 0x00000000, // SHGDN_NORMAL
|
||||
PARENTRELATIVEPARSING = 0x80018001, // SHGDN_INFOLDER | SHGDN_FORPARSING
|
||||
DESKTOPABSOLUTEPARSING = 0x80028000, // SHGDN_FORPARSING
|
||||
PARENTRELATIVEEDITING = 0x80031001, // SHGDN_INFOLDER | SHGDN_FOREDITING
|
||||
DESKTOPABSOLUTEEDITING = 0x8004c000, // SHGDN_FORPARSING | SHGDN_FORADDRESSBAR
|
||||
FILESYSPATH = 0x80058000, // SHGDN_FORPARSING
|
||||
URL = 0x80068000, // SHGDN_FORPARSING
|
||||
PARENTRELATIVEFORADDRESSBAR = 0x8007c001, // SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR
|
||||
PARENTRELATIVE = 0x80080001 // SHGDN_INFOLDER
|
||||
}
|
||||
|
||||
public struct VTable : Windows.COM_IUnknown.VTable
|
||||
{
|
||||
public function HResult(COM_IShellItem* self, void* pbc, ref Guid bhid, ref Guid riid, void** ppv) BindToHandler;
|
||||
public function HResult(COM_IShellItem* self, out COM_IShellItem* ppsi) GetParent;
|
||||
public function HResult(COM_IShellItem* self, SIGDN sigdnName, out char16* ppszName) GetDisplayName;
|
||||
public function HResult(COM_IShellItem* self, uint sfgaoMask, out uint psfgaoAttribs) GetAttributes;
|
||||
public function HResult(COM_IShellItem* self, COM_IShellItem* psi, uint32 hint, out int32 piOrder) Compare;
|
||||
|
||||
}
|
||||
public new VTable* VT
|
||||
{
|
||||
get
|
||||
{
|
||||
return (.)mVT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct COMDLG_FILTERSPEC
|
||||
{
|
||||
internal char16* pszName;
|
||||
internal char16* pszSpec;
|
||||
}
|
||||
|
||||
internal enum FDAP : uint32
|
||||
{
|
||||
FDAP_BOTTOM = 0x00000000,
|
||||
FDAP_TOP = 0x00000001,
|
||||
}
|
||||
|
||||
public struct COM_IFileDialog : Windows.COM_IUnknown
|
||||
{
|
||||
public static Guid sIID = .(0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, 0x5d, 0x13, 0x5f, 0xc8);
|
||||
public static Guid sCLSID = .(0xdc1c5a9c, 0xe88a, 0x4dde, 0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7);
|
||||
|
||||
///s
|
||||
public enum FOS : uint32
|
||||
{
|
||||
OVERWRITEPROMPT = 0x00000002,
|
||||
STRICTFILETYPES = 0x00000004,
|
||||
NOCHANGEDIR = 0x00000008,
|
||||
PICKFOLDERS = 0x00000020,
|
||||
FORCEFILESYSTEM = 0x00000040,
|
||||
ALLNONSTORAGEITEMS = 0x00000080,
|
||||
NOVALIDATE = 0x00000100,
|
||||
ALLOWMULTISELECT = 0x00000200,
|
||||
PATHMUSTEXIST = 0x00000800,
|
||||
FILEMUSTEXIST = 0x00001000,
|
||||
CREATEPROMPT = 0x00002000,
|
||||
SHAREAWARE = 0x00004000,
|
||||
NOREADONLYRETURN = 0x00008000,
|
||||
NOTESTFILECREATE = 0x00010000,
|
||||
HIDEMRUPLACES = 0x00020000,
|
||||
HIDEPINNEDPLACES = 0x00040000,
|
||||
NODEREFERENCELINKS = 0x00100000,
|
||||
DONTADDTORECENT = 0x02000000,
|
||||
FORCESHOWHIDDEN = 0x10000000,
|
||||
DEFAULTNOMINIMODE = 0x20000000
|
||||
}
|
||||
|
||||
public struct VTable : Windows.COM_IUnknown.VTable
|
||||
{
|
||||
public function HResult(COM_IFileDialog* self, Windows.HWnd parent) Show;
|
||||
public function HResult(COM_IFileDialog* self, uint cFileTypes, COMDLG_FILTERSPEC* rgFilterSpec) SetFileTypes;
|
||||
public function HResult(COM_IFileDialog* self, uint iFileType) SetFileTypeIndex;
|
||||
public function HResult(COM_IFileDialog* self, out uint piFileType) GetFileTypeIndex;
|
||||
public function HResult(COM_IFileDialog* self, COM_IFileDialogEvents* pfde, out uint pdwCookie) Advise;
|
||||
public function HResult(COM_IFileDialog* self, uint dwCookie) Unadvise;
|
||||
public function HResult(COM_IFileDialog* self, FOS fos) SetOptions;
|
||||
public function HResult(COM_IFileDialog* self, out FOS pfos) GetOptions;
|
||||
public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetDefaultFolder;
|
||||
public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetFolder;
|
||||
public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetFolder;
|
||||
public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetCurrentSelection;
|
||||
public function HResult(COM_IFileDialog* self, char16* pszName) SetFileName;
|
||||
public function HResult(COM_IFileDialog* self, out char16* pszName) GetFileName;
|
||||
public function HResult(COM_IFileDialog* self, char16* pszTitle) SetTitle;
|
||||
public function HResult(COM_IFileDialog* self, char16* pszText) SetOkButtonLabel;
|
||||
public function HResult(COM_IFileDialog* self, char16* pszLabel) SetFileNameLabel;
|
||||
public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetResult;
|
||||
public function HResult(COM_IFileDialog* self, COM_IShellItem* psi, FDAP fdap) AddPlace;
|
||||
public function HResult(COM_IFileDialog* self, char16* pszDefaultExtension) SetDefaultExtension;
|
||||
public function HResult(COM_IFileDialog* self, int hr) Close;
|
||||
public function HResult(COM_IFileDialog* self, ref Guid guid) SetClientGuid;
|
||||
public function HResult(COM_IFileDialog* self) ClearClientData;
|
||||
public function HResult(COM_IFileDialog* self, void* pFilter) SetFilter;
|
||||
}
|
||||
public new VTable* VT
|
||||
{
|
||||
get
|
||||
{
|
||||
return (.)mVT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
79
BeefLibs/corlib/src/System/IO/MemoryStream.bf
Normal file
79
BeefLibs/corlib/src/System/IO/MemoryStream.bf
Normal file
|
@ -0,0 +1,79 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
class MemoryStream : Stream
|
||||
{
|
||||
List<uint8> mMemory = new List<uint8>() ~ delete _;
|
||||
int mPosition = 0;
|
||||
|
||||
public override int64 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
mPosition = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return mMemory.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override Result<int> TryRead(Span<uint8> data)
|
||||
{
|
||||
let count = data.Length;
|
||||
if (count == 0)
|
||||
return .Ok(0);
|
||||
int readBytes = Math.Min(count, mMemory.Count - mPosition);
|
||||
if (readBytes <= 0)
|
||||
return .Ok(readBytes);
|
||||
|
||||
Internal.MemCpy(data.Ptr, &mMemory[mPosition], readBytes);
|
||||
mPosition += readBytes;
|
||||
return .Ok(readBytes);
|
||||
}
|
||||
|
||||
public override Result<int> TryWrite(Span<uint8> data)
|
||||
{
|
||||
let count = data.Length;
|
||||
if (count == 0)
|
||||
return .Ok(0);
|
||||
int growSize = mPosition + count - mMemory.Count;
|
||||
if (growSize > 0)
|
||||
mMemory.GrowUnitialized(growSize);
|
||||
Internal.MemCpy(&mMemory[mPosition], data.Ptr, count);
|
||||
mPosition += count;
|
||||
return .Ok(count);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
16
BeefLibs/corlib/src/System/IO/NotifyFilters.bf
Normal file
16
BeefLibs/corlib/src/System/IO/NotifyFilters.bf
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace System.IO
|
||||
{
|
||||
public enum NotifyFilters
|
||||
{
|
||||
FileName = 0x00000001,
|
||||
DirectoryName= 0x00000002,
|
||||
Attributes = 0x00000004,
|
||||
Size = 0x00000008,
|
||||
LastWrite = 0x00000010,
|
||||
LastAccess = 0x00000020,
|
||||
CreationTime = 0x00000040,
|
||||
Security = 0x00000100,
|
||||
|
||||
All = FileName | DirectoryName | Attributes | Size | LastWrite | LastAccess | CreationTime | Security
|
||||
}
|
||||
}
|
64
BeefLibs/corlib/src/System/IO/NullStream.bf
Normal file
64
BeefLibs/corlib/src/System/IO/NullStream.bf
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
/// The NullStream will allow ignore all writes (returning success) and return 'no data' on all reads.
|
||||
class NullStream : Stream
|
||||
{
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public override int64 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override Result<int> TryRead(Span<uint8> data)
|
||||
{
|
||||
return .Ok(0);
|
||||
}
|
||||
|
||||
public override Result<int> TryWrite(Span<uint8> data)
|
||||
{
|
||||
return .Ok(data.Length);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
562
BeefLibs/corlib/src/System/IO/OpenFileDialog.bf
Normal file
562
BeefLibs/corlib/src/System/IO/OpenFileDialog.bf
Normal file
|
@ -0,0 +1,562 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
enum DialogResult
|
||||
{
|
||||
None = 0,
|
||||
OK = 1,
|
||||
Cancel = 2
|
||||
}
|
||||
|
||||
abstract class CommonDialog
|
||||
{
|
||||
public Windows.HWnd mHWnd;
|
||||
public Windows.HWnd mDefaultControlHwnd;
|
||||
public int mDefWndProc;
|
||||
|
||||
private const int32 CDM_SETDEFAULTFOCUS = Windows.WM_USER + 0x51;
|
||||
|
||||
public static Dictionary<int, CommonDialog> sHookMap = new Dictionary<int, CommonDialog>() ~
|
||||
{
|
||||
Debug.Assert(sHookMap.Count == 0);
|
||||
delete _;
|
||||
};
|
||||
public static Monitor sMonitor = new Monitor() ~ delete _;
|
||||
|
||||
public Result<DialogResult> ShowDialog(INativeWindow owner = null)
|
||||
{
|
||||
Windows.HWnd hwndOwner = 0;
|
||||
if (owner != null)
|
||||
hwndOwner = (.)owner.Handle;
|
||||
//Native.WndProc wndProc = scope => OwnerWndProc;
|
||||
|
||||
//mDefWndProc = Native.SetWindowLong(mHWnd, Native.GWL_WNDPROC, (intptr)wndProc.GetFuncPtr().Value);
|
||||
|
||||
var result = RunDialog(hwndOwner);
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual int OwnerWndProc(Windows.HWnd hWnd, int32 msg, int wParam, int lParam)
|
||||
{
|
||||
return Windows.CallWindowProcW(mDefWndProc, hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
protected virtual int HookProc(Windows.HWnd hWnd, int32 msg, int wParam, int lparam)
|
||||
{
|
||||
if (msg == Windows.WM_INITDIALOG)
|
||||
{
|
||||
//TODO: MoveToScreenCenter(hWnd);
|
||||
// Under some circumstances, the dialog
|
||||
// does not initially focus on any control. We fix that by explicitly
|
||||
// setting focus ourselves. See ASURT 39435.
|
||||
//
|
||||
mDefaultControlHwnd = (Windows.HWnd)wParam;
|
||||
if (mDefaultControlHwnd != 0)
|
||||
Windows.SetFocus(mDefaultControlHwnd);
|
||||
}
|
||||
else if (msg == Windows.WM_SETFOCUS)
|
||||
{
|
||||
Windows.PostMessageW(hWnd, CDM_SETDEFAULTFOCUS, 0, 0);
|
||||
}
|
||||
else if (msg == CDM_SETDEFAULTFOCUS)
|
||||
{
|
||||
// If the dialog box gets focus, bounce it to the default control.
|
||||
// so we post a message back to ourselves to wait for the focus change then push it to the default
|
||||
// control. See ASURT 84016.
|
||||
//
|
||||
if (mDefaultControlHwnd != 0)
|
||||
Windows.SetFocus(mDefaultControlHwnd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected abstract Result<DialogResult> RunDialog(Windows.HWnd hWndOwner);
|
||||
}
|
||||
|
||||
abstract class FileDialog : CommonDialog
|
||||
{
|
||||
internal abstract Result<DialogResult> RunFileDialog(ref Windows.OpenFileName ofn);
|
||||
|
||||
protected override Result<DialogResult> RunDialog(Windows.HWnd hWndOwner)
|
||||
{
|
||||
return RunDialogOld(hWndOwner);
|
||||
}
|
||||
|
||||
private const int32 FILEBUFSIZE = 8192;
|
||||
internal const int32 OPTION_ADDEXTENSION = (int32)0x80000000;
|
||||
|
||||
internal int32 mOptions;
|
||||
private String mTitle ~ delete _;
|
||||
private String mInitialDir ~ delete _;
|
||||
private String mDefaultExt ~ delete _;
|
||||
protected String[] mFileNames ~ DeleteContainerAndItems!(_);
|
||||
private bool mSecurityCheckFileNames;
|
||||
private String mFilter ~ delete _;
|
||||
private String mFilterBuffer = new String() ~ delete _;
|
||||
private int32 mFilterIndex;
|
||||
private bool mSupportMultiDottedExtensions;
|
||||
private bool mIgnoreSecondFileOkNotification; // Used for VS Whidbey 95342
|
||||
private int32 mOKNotificationCount; // Same
|
||||
//private String char8Buffer = new String(FILEBUFSIZE) ~ delete _;
|
||||
|
||||
public this()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
DeleteAndNullify!(mTitle);
|
||||
DeleteAndNullify!(mInitialDir);
|
||||
DeleteAndNullify!(mDefaultExt);
|
||||
DeleteAndNullify!(mFileNames);
|
||||
DeleteAndNullify!(mFilter);
|
||||
mFilterIndex = 1;
|
||||
mSupportMultiDottedExtensions = false;
|
||||
mOptions = Windows.OFN_HIDEREADONLY | Windows.OFN_PATHMUSTEXIST |
|
||||
OPTION_ADDEXTENSION;
|
||||
}
|
||||
|
||||
protected int32 Options
|
||||
{
|
||||
get
|
||||
{
|
||||
return mOptions & (
|
||||
Windows.OFN_READONLY |
|
||||
Windows.OFN_HIDEREADONLY |
|
||||
Windows.OFN_NOCHANGEDIR |
|
||||
Windows.OFN_SHOWHELP |
|
||||
Windows.OFN_NOVALIDATE |
|
||||
Windows.OFN_ALLOWMULTISELECT |
|
||||
Windows.OFN_PATHMUSTEXIST |
|
||||
Windows.OFN_FILEMUSTEXIST |
|
||||
Windows.OFN_NODEREFERENCELINKS |
|
||||
Windows.OFN_OVERWRITEPROMPT);
|
||||
//return mOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView Title
|
||||
{
|
||||
set
|
||||
{
|
||||
String.NewOrSet!(mTitle, value);
|
||||
}
|
||||
|
||||
get
|
||||
{
|
||||
return mTitle;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView InitialDirectory
|
||||
{
|
||||
set
|
||||
{
|
||||
String.NewOrSet!(mInitialDir, value);
|
||||
}
|
||||
|
||||
get
|
||||
{
|
||||
return mInitialDir;
|
||||
}
|
||||
}
|
||||
|
||||
public String[] FileNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return mFileNames;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView FileName
|
||||
{
|
||||
set
|
||||
{
|
||||
if (mFileNames == null)
|
||||
{
|
||||
mFileNames = new String[](new String(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetOption(OPTION_ADDEXTENSION);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetOption(OPTION_ADDEXTENSION, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CheckFileExists
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetOption(Windows.OFN_FILEMUSTEXIST);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_FILEMUSTEXIST, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool DereferenceLinks
|
||||
{
|
||||
get
|
||||
{
|
||||
return !GetOption(Windows.OFN_NODEREFERENCELINKS);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_NODEREFERENCELINKS, !value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckPathExists
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetOption(Windows.OFN_PATHMUSTEXIST);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_PATHMUSTEXIST, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Multiselect
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetOption(Windows.OFN_ALLOWMULTISELECT);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_ALLOWMULTISELECT, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ValidateNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return !GetOption(Windows.OFN_NOVALIDATE);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_NOVALIDATE, !value);
|
||||
}
|
||||
}
|
||||
|
||||
public StringView DefaultExt
|
||||
{
|
||||
get
|
||||
{
|
||||
return mDefaultExt == null ? "" : mDefaultExt;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
delete mDefaultExt;
|
||||
mDefaultExt = null;
|
||||
|
||||
//if (!String.IsNullOrEmpty(value))
|
||||
if (value.Length > 0)
|
||||
{
|
||||
mDefaultExt = new String(value);
|
||||
if (mDefaultExt.StartsWith("."))
|
||||
mDefaultExt.Remove(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetFilter(String outFilter)
|
||||
{
|
||||
if (mFilter != null)
|
||||
outFilter.Append(mFilter);
|
||||
}
|
||||
|
||||
public Result<void> SetFilter(StringView value)
|
||||
{
|
||||
String useValue = scope String(value);
|
||||
if (useValue != null && useValue.Length > 0)
|
||||
{
|
||||
var formats = String.StackSplit!(useValue, '|');
|
||||
if (formats == null || formats.Count % 2 != 0)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
///
|
||||
/*String[] formats = value.Split('|');
|
||||
if (formats == null || formats.Length % 2 != 0)
|
||||
{
|
||||
throw new ArgumentException(SR.GetString(SR.FileDialogInvalidFilter));
|
||||
}*/
|
||||
String.NewOrSet!(mFilter, useValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
useValue = null;
|
||||
DeleteAndNullify!(mFilter);
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
internal bool GetOption(int32 option)
|
||||
{
|
||||
return (mOptions & option) != 0;
|
||||
}
|
||||
|
||||
internal void SetOption(int32 option, bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
mOptions |= option;
|
||||
}
|
||||
else
|
||||
{
|
||||
mOptions &= ~option;
|
||||
}
|
||||
}
|
||||
|
||||
private static Result<void> MakeFilterString(String s, bool dereferenceLinks, String filterBuffer)
|
||||
{
|
||||
String useStr = s;
|
||||
if (useStr == null || useStr.Length == 0)
|
||||
{
|
||||
// Workaround for Whidbey bug #5165
|
||||
// Apply the workaround only when DereferenceLinks is true and OS is at least WinXP.
|
||||
if (dereferenceLinks && System.Environment.OSVersion.Version.Major >= 5)
|
||||
{
|
||||
useStr = " |*.*";
|
||||
}
|
||||
else if (useStr == null)
|
||||
{
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
filterBuffer.Set(s);
|
||||
for (int32 i = 0; i < filterBuffer.Length; i++)
|
||||
if (filterBuffer[i] == '|')
|
||||
filterBuffer[i] = (char8)0;
|
||||
filterBuffer.Append((char8)0);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static mixin Testie()
|
||||
{
|
||||
int a = 123;
|
||||
char16* buf;
|
||||
if (a == 0)
|
||||
{
|
||||
buf = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf = new char16[123]* { ? };
|
||||
defer:mixin delete buf;
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
private Result<DialogResult> RunDialogOld(Windows.HWnd hWndOwner)
|
||||
{
|
||||
//RunDialogTest(hWndOwner);
|
||||
|
||||
Windows.WndProc hookProcPtr = => StaticHookProc;
|
||||
Windows.OpenFileName ofn = Windows.OpenFileName();
|
||||
|
||||
char16[FILEBUFSIZE] char16Buffer = .(0, ?);
|
||||
|
||||
if (mFileNames != null)
|
||||
{
|
||||
//int len = UTF16.GetEncodedLen(fileNames[0]);
|
||||
//char16Buffer = scope:: char16[len + 1]*;
|
||||
UTF16.Encode(mFileNames[0], (char16*)&char16Buffer, FILEBUFSIZE);
|
||||
}
|
||||
// Degrade to the older style dialog if we're not on Win2K.
|
||||
// We do this by setting the struct size to a different value
|
||||
//
|
||||
|
||||
if (Environment.OSVersion.Platform != System.PlatformID.Win32NT ||
|
||||
Environment.OSVersion.Version.Major < 5) {
|
||||
ofn.mStructSize = 0x4C;
|
||||
}
|
||||
ofn.mHwndOwner = hWndOwner;
|
||||
ofn.mHInstance = (Windows.HInstance)Windows.GetModuleHandleW(null);
|
||||
|
||||
if (mFilter != null)
|
||||
{
|
||||
Try!(MakeFilterString(mFilter, this.DereferenceLinks, mFilterBuffer));
|
||||
ofn.mFilter = mFilterBuffer.ToScopedNativeWChar!::();
|
||||
}
|
||||
ofn.nFilterIndex = mFilterIndex;
|
||||
ofn.mFile = (char16*)&char16Buffer;
|
||||
ofn.nMaxFile = FILEBUFSIZE;
|
||||
if (mInitialDir != null)
|
||||
ofn.mInitialDir = mInitialDir.ToScopedNativeWChar!::();
|
||||
if (mTitle != null)
|
||||
ofn.mTitle = mTitle.ToScopedNativeWChar!();
|
||||
ofn.mFlags = Options | (Windows.OFN_EXPLORER | Windows.OFN_ENABLEHOOK | Windows.OFN_ENABLESIZING);
|
||||
ofn.mHook = hookProcPtr;
|
||||
ofn.mCustData = (int)(void*)this;
|
||||
ofn.mFlagsEx = Windows.OFN_USESHELLITEM;
|
||||
if (mDefaultExt != null && AddExtension)
|
||||
ofn.mDefExt = mDefaultExt;
|
||||
|
||||
DeleteContainerAndItems!(mFileNames);
|
||||
mFileNames = null;
|
||||
//Security checks happen here
|
||||
return RunFileDialog(ref ofn);
|
||||
}
|
||||
|
||||
static int StaticHookProc(Windows.HWnd hWnd, int32 msg, int wParam, int lparam)
|
||||
{
|
||||
if (msg == Windows.WM_INITDIALOG)
|
||||
{
|
||||
using (sMonitor.Enter())
|
||||
{
|
||||
var ofn = (Windows.OpenFileName*)lparam;
|
||||
sHookMap[(int)hWnd] = (CommonDialog)Internal.UnsafeCastToObject((void*)ofn.mCustData);
|
||||
}
|
||||
}
|
||||
|
||||
CommonDialog dlg;
|
||||
using (sMonitor.Enter())
|
||||
{
|
||||
sHookMap.TryGetValue((int)hWnd, out dlg);
|
||||
}
|
||||
if (dlg == null)
|
||||
return 0;
|
||||
|
||||
dlg.[Friend]HookProc(hWnd, msg, wParam, lparam);
|
||||
if (msg == Windows.WM_DESTROY)
|
||||
{
|
||||
using (sMonitor.Enter())
|
||||
{
|
||||
sHookMap.Remove((int)hWnd);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//TODO: Add ProcessFileNames for validation
|
||||
}
|
||||
|
||||
class OpenFileDialog : FileDialog
|
||||
{
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
SetOption(Windows.OFN_FILEMUSTEXIST, true);
|
||||
}
|
||||
|
||||
public bool ReadOnlyChecked
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetOption(Windows.OFN_READONLY);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_READONLY, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return !GetOption(Windows.OFN_HIDEREADONLY);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_HIDEREADONLY, !value);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Result<DialogResult> RunFileDialog(ref Windows.OpenFileName ofn)
|
||||
{
|
||||
bool result = Windows.GetOpenFileNameW(ref ofn);
|
||||
if (!result)
|
||||
return .Err;
|
||||
|
||||
if (!Multiselect)
|
||||
{
|
||||
let pathName = new String();
|
||||
UTF16.Decode(ofn.mFile, pathName);
|
||||
mFileNames = new String[](pathName);
|
||||
return DialogResult.OK;
|
||||
}
|
||||
|
||||
int32 entryCount = 0;
|
||||
int32 prevNull = -1;
|
||||
for (int32 i = 0; true; i++)
|
||||
{
|
||||
if (ofn.mFile[i] == (char8)0)
|
||||
{
|
||||
if (prevNull == i - 1)
|
||||
break;
|
||||
prevNull = i;
|
||||
entryCount++;
|
||||
}
|
||||
}
|
||||
|
||||
String pathName = null;
|
||||
prevNull = -1;
|
||||
mFileNames = new String[Math.Max(1, entryCount - 1)];
|
||||
entryCount = 0;
|
||||
for (int32 i = 0; true; i++)
|
||||
{
|
||||
if (ofn.mFile[i] == (char8)0)
|
||||
{
|
||||
if (prevNull == i - 1)
|
||||
break;
|
||||
if (prevNull == -1)
|
||||
{
|
||||
pathName = scope:: String();
|
||||
UTF16.Decode(ofn.mFile, pathName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var str = new String(pathName.Length + 1 + i - prevNull - 1);
|
||||
str.Append(pathName);
|
||||
str.Append(Path.DirectorySeparatorChar);
|
||||
UTF16.Decode(ofn.mFile + prevNull + 1, str);
|
||||
|
||||
mFileNames[entryCount++] = str;
|
||||
}
|
||||
prevNull = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ((entryCount == 0) && (pathName != null))
|
||||
mFileNames[0] = new String(pathName);
|
||||
|
||||
return DialogResult.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
589
BeefLibs/corlib/src/System/IO/Path.bf
Normal file
589
BeefLibs/corlib/src/System/IO/Path.bf
Normal file
|
@ -0,0 +1,589 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public static class Path
|
||||
{
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
public const char8 DirectorySeparatorChar = '\\';
|
||||
#else
|
||||
public const char8 DirectorySeparatorChar = '/';
|
||||
#endif //BF_PLATFORM_WINDOWS
|
||||
|
||||
// Platform specific alternate directory separator char8acter.
|
||||
// This is backslash ('\') on Unix, and slash ('/') on Windows
|
||||
// and MacOS.
|
||||
//
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
public const char8 AltDirectorySeparatorChar = '/';
|
||||
#else
|
||||
public const char8 AltDirectorySeparatorChar = '\\';
|
||||
#endif //BF_PLATFORM_WINDOWS
|
||||
|
||||
// Platform specific volume separator char8acter. This is colon (':')
|
||||
// on Windows and MacOS, and slash ('/') on Unix. This is mostly
|
||||
// useful for parsing paths like "c:\windows" or "MacVolume:System Folder".
|
||||
//
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
public const char8 VolumeSeparatorChar = ':';
|
||||
#else
|
||||
public const char8 VolumeSeparatorChar = '/';
|
||||
#endif //BF_PLATFORM_WINDOWS
|
||||
|
||||
// Make this public sometime.
|
||||
// The max total path is 260, and the max individual component length is 255.
|
||||
// For example, D:\<256 char8 file name> isn't legal, even though it's under 260 char8s.
|
||||
internal const int32 MaxPath = 260;
|
||||
private const int32 MaxDirectoryLength = 255;
|
||||
|
||||
public static void GetFullPath(String inPartialPath, String outFullPath)
|
||||
{
|
||||
Platform.GetStrHelper(outFullPath, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpFile_GetFullPath(inPartialPath, outPtr, outSize, (Platform.BfpFileResult*)outResult);
|
||||
});
|
||||
}
|
||||
|
||||
internal static void CheckInvalidPathChars(StringView path, bool checkAdditional = false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static void GetFileName(String inPath, String outFileName)
|
||||
{
|
||||
if (inPath == null)
|
||||
return;
|
||||
|
||||
CheckInvalidPathChars(inPath);
|
||||
|
||||
int length = inPath.Length;
|
||||
for (int i = length; --i >= 0; )
|
||||
{
|
||||
char8 ch = inPath[i];
|
||||
if (ch == DirectorySeparatorChar || ch == AltDirectorySeparatorChar || ch == VolumeSeparatorChar)
|
||||
{
|
||||
outFileName.Append(inPath, i + 1, length - i - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
outFileName.Append(inPath);
|
||||
}
|
||||
|
||||
internal static String NormalizePath(String path, bool fullCheck)
|
||||
{
|
||||
return NormalizePath(path, fullCheck, MaxPath);
|
||||
}
|
||||
|
||||
|
||||
internal static String NormalizePath(String path, bool fullCheck, bool expandShortPaths)
|
||||
{
|
||||
return NormalizePath(path, fullCheck, MaxPath, expandShortPaths);
|
||||
}
|
||||
|
||||
|
||||
internal static String NormalizePath(String path, bool fullCheck, int32 maxPathLength)
|
||||
{
|
||||
return NormalizePath(path, fullCheck, maxPathLength, true);
|
||||
}
|
||||
|
||||
internal static String NormalizePath(String path, bool fullCheck, int32 maxPathLength, bool expandShortPaths)
|
||||
{
|
||||
//TODO: Implement
|
||||
return path;
|
||||
}
|
||||
|
||||
internal static String RemoveLongPathPrefix(String path)
|
||||
{
|
||||
//TODO: Implement
|
||||
return path;
|
||||
}
|
||||
|
||||
public static Result<void> GetDirectoryPath(StringView path, String outDir)
|
||||
{
|
||||
String usePath = scope String(Math.Min(MaxPath, path.Length));
|
||||
usePath.Append(path);
|
||||
|
||||
//String origPath = path;
|
||||
//if (usePath != null)
|
||||
{
|
||||
CheckInvalidPathChars(usePath);
|
||||
String normalizedPath = NormalizePath(usePath, false);
|
||||
|
||||
// If there are no permissions for PathDiscovery to this path, we should NOT expand the short paths
|
||||
// as this would leak information about paths to which the user would not have access to.
|
||||
if (usePath.Length > 0)
|
||||
{
|
||||
// If we were passed in a path with \\?\ we need to remove it as FileIOPermission does not like it.
|
||||
String tempPath = Path.RemoveLongPathPrefix(usePath);
|
||||
|
||||
// FileIOPermission cannot handle paths that contain ? or *
|
||||
// So we only pass to FileIOPermission the text up to them.
|
||||
int32 pos = 0;
|
||||
while (pos < tempPath.Length && (tempPath[pos] != '?' && tempPath[pos] != '*'))
|
||||
pos++;
|
||||
// GetFullPath will Demand that we have the PathDiscovery FileIOPermission and thus throw
|
||||
// SecurityException if we don't.
|
||||
// While we don't use the result of this call we are using it as a consistent way of
|
||||
// doing the security checks.
|
||||
if (pos > 0)
|
||||
{
|
||||
//Path.GetFullPath(tempPath.Substring(0, pos));
|
||||
String newPath = scope String(usePath.Length - pos);
|
||||
newPath.Append(usePath, pos);
|
||||
usePath = newPath;
|
||||
}
|
||||
/*}
|
||||
catch (SecurityException) {
|
||||
// If the user did not have permissions to the path, make sure that we don't leak expanded short paths
|
||||
// Only re-normalize if the original path had a ~ in it.
|
||||
if (path.IndexOf("~", StringComparison.Ordinal) != -1)
|
||||
{
|
||||
normalizedPath = NormalizePath(path, /*fullCheck*/ false, /*expandShortPaths*/ false);
|
||||
}
|
||||
}
|
||||
catch (PathTooLongException) { }
|
||||
catch (NotSupportedException) { } // Security can throw this on "c:\foo:"
|
||||
catch (IOException) { }
|
||||
catch (ArgumentException) { } // The normalizePath with fullCheck will throw this for file: and http:*/
|
||||
}
|
||||
|
||||
usePath = normalizedPath;
|
||||
|
||||
int root = GetRootLength(usePath);
|
||||
int i = usePath.Length;
|
||||
if (i > root)
|
||||
{
|
||||
i = usePath.Length;
|
||||
if (i == root) return .Err;
|
||||
while (i > root && usePath[--i] != DirectorySeparatorChar && usePath[i] != AltDirectorySeparatorChar) {}
|
||||
outDir.Append(usePath, 0, i);
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
return .Err;
|
||||
}
|
||||
|
||||
// Gets the length of the root DirectoryInfo or whatever DirectoryInfo markers
|
||||
// are specified for the first part of the DirectoryInfo name.
|
||||
//
|
||||
internal static int GetRootLength(String path)
|
||||
{
|
||||
CheckInvalidPathChars(path);
|
||||
|
||||
int i = 0;
|
||||
int length = path.Length;
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
if (length >= 1 && (IsDirectorySeparator(path[0])))
|
||||
{
|
||||
// handles UNC names and directories off current drive's root.
|
||||
i = 1;
|
||||
if (length >= 2 && (IsDirectorySeparator(path[1])))
|
||||
{
|
||||
i = 2;
|
||||
int32 n = 2;
|
||||
while (i < length && ((path[i] != DirectorySeparatorChar && path[i] != AltDirectorySeparatorChar) || --n > 0)) i++;
|
||||
}
|
||||
}
|
||||
else if (length >= 2 && path[1] == VolumeSeparatorChar)
|
||||
{
|
||||
// handles A:\foo.
|
||||
i = 2;
|
||||
if (length >= 3 && (IsDirectorySeparator(path[2]))) i++;
|
||||
}
|
||||
return i;
|
||||
#else
|
||||
if (length >= 1 && (IsDirectorySeparator(path[0]))) {
|
||||
i = 1;
|
||||
}
|
||||
return i;
|
||||
#endif //BF_PLATFORM_WINDOWS
|
||||
}
|
||||
|
||||
internal static bool IsDirectorySeparator(char8 c)
|
||||
{
|
||||
return (c==DirectorySeparatorChar || c == AltDirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/*
|
||||
public static char8[] GetInvalidPathChars()
|
||||
{
|
||||
return RealInvalidPathChars;
|
||||
}
|
||||
|
||||
public static char8[] GetInvalidFileNameChars()
|
||||
{
|
||||
return (char8[]) InvalidFileNameChars.Clone();
|
||||
} */
|
||||
|
||||
public static void GetFileNameWithoutExtension(String inPath, String outFileName)
|
||||
{
|
||||
int lastSlash = Math.Max(inPath.LastIndexOf('\\'), inPath.LastIndexOf('/'));
|
||||
|
||||
int i;
|
||||
if ((i = inPath.LastIndexOf('.')) != -1)
|
||||
{
|
||||
int len = i - lastSlash - 1;
|
||||
if (len > 0)
|
||||
outFileName.Append(inPath, lastSlash + 1, i - lastSlash - 1);
|
||||
}
|
||||
else
|
||||
outFileName.Append(inPath, lastSlash + 1);
|
||||
}
|
||||
|
||||
public static Result<void> GetExtension(String inPath, String outExt)
|
||||
{
|
||||
int i;
|
||||
if ((i = inPath.LastIndexOf('.')) != -1)
|
||||
outExt.Append(inPath, i);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void> GetTempPath(String outPath)
|
||||
{
|
||||
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpFile_GetTempPath(outPtr, outSize, (Platform.BfpFileResult*)outResult);
|
||||
});
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static Result<void> GetTempFileName(String outPath)
|
||||
{
|
||||
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpFile_GetTempFileName(outPtr, outSize, (Platform.BfpFileResult*)outResult);
|
||||
});
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static void InternalCombine(String target, params String[] components)
|
||||
{
|
||||
for (var component in components)
|
||||
{
|
||||
if ((target.Length > 0) && (!target.EndsWith("\\")) && (!target.EndsWith("/")))
|
||||
target.Append(Path.DirectorySeparatorChar);
|
||||
target.Append(component);
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetActualPathName(StringView inPath, String outPath)
|
||||
{
|
||||
Platform.GetStrHelper(outPath, scope (outPtr, outSize, outResult) =>
|
||||
{
|
||||
Platform.BfpFile_GetActualPath(scope String()..Append(inPath), outPtr, outSize, (Platform.BfpFileResult*)outResult);
|
||||
});
|
||||
}
|
||||
|
||||
public static bool Equals(StringView filePathA, StringView filePathB)
|
||||
{
|
||||
Debug.Assert(!filePathA.Contains(Path.AltDirectorySeparatorChar));
|
||||
Debug.Assert(!filePathB.Contains(Path.AltDirectorySeparatorChar));
|
||||
return filePathA.Equals(filePathB, !Environment.IsFileSystemCaseSensitive);
|
||||
}
|
||||
|
||||
static void GetDriveStringTo(String outDrive, String path)
|
||||
{
|
||||
if ((path.Length >= 2) && (path[1] == ':'))
|
||||
outDrive.Append(path, 0, 2);
|
||||
}
|
||||
|
||||
public static void GetRelativePath(StringView fullPath, StringView curDir, String outRelPath)
|
||||
{
|
||||
String curPath1 = scope String(curDir);
|
||||
String curPath2 = scope String(fullPath);
|
||||
|
||||
if (curPath1.Contains(Path.AltDirectorySeparatorChar))
|
||||
curPath1.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
if (curPath2.Contains(Path.AltDirectorySeparatorChar))
|
||||
curPath1.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
|
||||
String driveString1 = scope String();
|
||||
GetDriveStringTo(driveString1, curPath1);
|
||||
String driveString2 = scope String();
|
||||
GetDriveStringTo(driveString2, curPath2);
|
||||
|
||||
StringComparison compareType = Environment.IsFileSystemCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
|
||||
|
||||
// On seperate drives?
|
||||
if (!String.Equals(driveString1, driveString2, compareType))
|
||||
{
|
||||
outRelPath.Set(fullPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (driveString1.Length > 0)
|
||||
curPath1.Remove(0, Math.Min(driveString1.Length + 1, curPath1.Length));
|
||||
if (driveString2.Length > 0)
|
||||
curPath2.Remove(0, Math.Min(driveString2.Length + 1, curPath2.Length));
|
||||
|
||||
while ((curPath1.Length > 0) && (curPath2.Length > 0))
|
||||
{
|
||||
int slashPos1 = curPath1.IndexOf(Path.DirectorySeparatorChar);
|
||||
if (slashPos1 == -1)
|
||||
slashPos1 = curPath1.Length;
|
||||
int slashPos2 = curPath2.IndexOf(Path.DirectorySeparatorChar);
|
||||
if (slashPos2 == -1)
|
||||
slashPos2 = curPath2.Length;
|
||||
|
||||
String section1 = scope String();
|
||||
section1.Append(curPath1, 0, slashPos1);
|
||||
String section2 = scope String();
|
||||
section2.Append(curPath2, 0, slashPos2);
|
||||
|
||||
if (!String.Equals(section1, section2, compareType))
|
||||
{
|
||||
// a/b/c
|
||||
// d/e/f
|
||||
|
||||
while (curPath1.Length > 0)
|
||||
{
|
||||
slashPos1 = curPath1.IndexOf(Path.DirectorySeparatorChar);
|
||||
if (slashPos1 == -1)
|
||||
slashPos1 = curPath1.Length;
|
||||
|
||||
if (slashPos1 + 1 >= curPath1.Length)
|
||||
curPath1.Clear();
|
||||
else
|
||||
curPath1.Remove(0, slashPos1 + 1);
|
||||
if (Path.DirectorySeparatorChar == '\\')
|
||||
curPath2.Insert(0, "..\\");
|
||||
else
|
||||
curPath2.Insert(0, "../");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (slashPos1 + 1 >= curPath1.Length)
|
||||
curPath1.Clear();
|
||||
else
|
||||
curPath1.Remove(0, slashPos1 + 1);
|
||||
|
||||
if (slashPos2 + 2 >= curPath2.Length)
|
||||
curPath1 = "";
|
||||
else
|
||||
curPath2.Remove(0, slashPos2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
outRelPath.Set(curPath2);
|
||||
}
|
||||
|
||||
public static void GetAbsolutePath(StringView relPath, StringView relToAbsPath, String outAbsPath)
|
||||
{
|
||||
String driveString = null;
|
||||
|
||||
var relPath;
|
||||
if (relPath == outAbsPath)
|
||||
relPath = scope:: String(relPath);
|
||||
|
||||
if ((relPath.Length >= 2) && (relPath[1] == ':'))
|
||||
{
|
||||
outAbsPath.Append(relPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((relPath.Length > 1) &&
|
||||
(relPath[0] == '/') || (relPath[0] == '\\'))
|
||||
{
|
||||
outAbsPath.Append(relPath);
|
||||
return;
|
||||
}
|
||||
|
||||
int startLen = outAbsPath.Length;
|
||||
outAbsPath.Append(relToAbsPath);
|
||||
//outAbsPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
|
||||
char8 slashChar = Path.DirectorySeparatorChar;
|
||||
|
||||
if ((outAbsPath.Length >= 2) && (outAbsPath[1] == ':'))
|
||||
{
|
||||
driveString = scope:: String();
|
||||
driveString.Append(outAbsPath, 0, 2);
|
||||
outAbsPath.Remove(0, 2);
|
||||
}
|
||||
|
||||
// Append a trailing slash if necessary
|
||||
if ((outAbsPath.Length > 0) && (outAbsPath[outAbsPath.Length - 1] != '\\') && (outAbsPath[outAbsPath.Length - 1] != '/'))
|
||||
outAbsPath.Append(slashChar);
|
||||
|
||||
int32 relIdx = 0;
|
||||
for (; ;)
|
||||
{
|
||||
if (outAbsPath.Length == 0)
|
||||
break;
|
||||
|
||||
int32 firstSlash = -1;
|
||||
for (int32 i = relIdx; i < relPath.Length; i++)
|
||||
if ((relPath[i] == '\\') || (relPath[i] == '/'))
|
||||
{
|
||||
firstSlash = i;
|
||||
break;
|
||||
}
|
||||
if (firstSlash == -1)
|
||||
break;
|
||||
|
||||
String chDir = scope String();
|
||||
chDir.Append(relPath, relIdx, firstSlash - relIdx);
|
||||
|
||||
//relPath.Substring(relIdx, firstSlash - relIdx, chDir);
|
||||
//chDir.Append(relPath.Ptr + relIdx, firstSlash - relIdx);
|
||||
|
||||
relIdx = firstSlash + 1;
|
||||
|
||||
if (chDir == "..")
|
||||
{
|
||||
int32 lastDirStart = (int32)outAbsPath.Length - 1;
|
||||
while ((lastDirStart > 0) && (outAbsPath[lastDirStart - 1] != '\\') && (outAbsPath[lastDirStart - 1] != '/'))
|
||||
lastDirStart--;
|
||||
|
||||
String lastDir = scope String();
|
||||
lastDir.Append(outAbsPath, lastDirStart, outAbsPath.Length - lastDirStart - 1);
|
||||
if (lastDir == "..")
|
||||
{
|
||||
outAbsPath.Append("..");
|
||||
outAbsPath.Append(Path.DirectorySeparatorChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
//newPath.erase(newPath.begin() + lastDirStart, newPath.end());
|
||||
outAbsPath.Remove(lastDirStart, outAbsPath.Length - lastDirStart);
|
||||
}
|
||||
}
|
||||
else if (chDir == "")
|
||||
{
|
||||
outAbsPath.Append(Path.DirectorySeparatorChar);
|
||||
break;
|
||||
}
|
||||
else if (chDir != ".")
|
||||
{
|
||||
//newPath += chDir + slashChar;
|
||||
outAbsPath.Append(chDir);
|
||||
outAbsPath.Append(Path.DirectorySeparatorChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (driveString != null)
|
||||
outAbsPath.Insert(0, driveString);
|
||||
//relPath.Substring(relIdx, outAbsPath);
|
||||
outAbsPath.Append(relPath, relIdx);
|
||||
//outAbsPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||
for (int i = startLen; i < outAbsPath.Length; i++)
|
||||
{
|
||||
if (outAbsPath[i] == Path.AltDirectorySeparatorChar)
|
||||
outAbsPath[i] = Path.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool WildcareCompare(StringView path, StringView wildcard)
|
||||
{
|
||||
bool matches = true;
|
||||
char8* afterLastWild = null; // The location after the last '*', if we’ve encountered one
|
||||
char8* afterLastPath = null; // The location in the path string, from which we started after last wildcard
|
||||
char8 t, w;
|
||||
|
||||
char8* pathPtr = path.Ptr;
|
||||
char8* pathEnd = path.EndPtr;
|
||||
char8* wildPtr = wildcard.Ptr;
|
||||
char8* wildEnd = wildcard.EndPtr;
|
||||
|
||||
// Walk the text strings one character at a time.
|
||||
while (true)
|
||||
{
|
||||
// How do you match a unique text string?
|
||||
if (pathPtr == pathEnd)
|
||||
{
|
||||
// Easy: unique up on it!
|
||||
if (wildPtr == wildEnd)
|
||||
{
|
||||
break; // "x" matches "x"
|
||||
}
|
||||
w = *wildPtr;
|
||||
if (w == '*')
|
||||
{
|
||||
wildPtr++;
|
||||
continue;// "x*" matches "x" or "xy"
|
||||
}
|
||||
else if (afterLastPath != null)
|
||||
{
|
||||
if (afterLastPath == pathEnd)
|
||||
{
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
pathPtr = afterLastPath++;
|
||||
wildPtr = afterLastWild;
|
||||
continue;
|
||||
}
|
||||
|
||||
matches = false;
|
||||
break; // "x" doesn't match "xy"
|
||||
}
|
||||
else
|
||||
{
|
||||
t = *pathPtr;
|
||||
w = *wildPtr;
|
||||
|
||||
if (!Environment.IsFileSystemCaseSensitive)
|
||||
{
|
||||
t = t.ToUpper;
|
||||
w = w.ToUpper;
|
||||
}
|
||||
|
||||
// How do you match a tame text string?
|
||||
if (t != w)
|
||||
{
|
||||
// The tame way: unique up on it!
|
||||
if (w == '*')
|
||||
{
|
||||
afterLastWild = ++wildPtr;
|
||||
afterLastPath = pathPtr;
|
||||
if (wildPtr == wildEnd)
|
||||
{
|
||||
break; // "*" matches "x"
|
||||
}
|
||||
w = *wildPtr;
|
||||
continue; // "*y" matches "xy"
|
||||
}
|
||||
else if (afterLastWild != null)
|
||||
{
|
||||
if (afterLastWild != wildPtr)
|
||||
{
|
||||
wildPtr = afterLastWild;
|
||||
w = *wildPtr;
|
||||
|
||||
if (!Environment.IsFileSystemCaseSensitive)
|
||||
w = w.ToUpper;
|
||||
|
||||
if (t == w)
|
||||
{
|
||||
wildPtr++;
|
||||
}
|
||||
}
|
||||
pathPtr++;
|
||||
continue; // "*sip*" matches "mississippi"
|
||||
}
|
||||
else
|
||||
{
|
||||
matches = false;
|
||||
break; // "x" doesn't match "y"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pathPtr++;
|
||||
wildPtr++;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
}
|
100
BeefLibs/corlib/src/System/IO/Pipe.bf
Normal file
100
BeefLibs/corlib/src/System/IO/Pipe.bf
Normal file
|
@ -0,0 +1,100 @@
|
|||
namespace System.IO
|
||||
{
|
||||
enum PipeOptions
|
||||
{
|
||||
None = 0,
|
||||
AllowTimeouts = 1
|
||||
}
|
||||
|
||||
class NamedPipe : FileStreamBase
|
||||
{
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Create(StringView machineName, StringView pipeName, PipeOptions options)
|
||||
{
|
||||
Runtime.Assert(mBfpFile == null);
|
||||
|
||||
String path = scope String();
|
||||
path.Append(pipeName);
|
||||
|
||||
Platform.BfpFileCreateKind createKind = .CreateAlways;
|
||||
Platform.BfpFileCreateFlags createFlags = .Pipe;
|
||||
|
||||
if (options.HasFlag(.AllowTimeouts))
|
||||
createFlags |= .AllowTimeouts;
|
||||
|
||||
createKind = .CreateIfNotExists;
|
||||
createFlags |= .Read;
|
||||
createFlags |= .Write;
|
||||
|
||||
Platform.BfpFileAttributes fileFlags = .Normal;
|
||||
|
||||
Platform.BfpFileResult fileResult = .Ok;
|
||||
mBfpFile = Platform.BfpFile_Create(path, createKind, createFlags, fileFlags, &fileResult);
|
||||
|
||||
if ((mBfpFile == null) || (fileResult != .Ok))
|
||||
{
|
||||
switch (fileResult)
|
||||
{
|
||||
case .ShareError:
|
||||
return .Err(.SharingViolation);
|
||||
case .NotFound:
|
||||
return .Err(.NotFound);
|
||||
default:
|
||||
return .Err(.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Open(StringView machineName, StringView pipeName, PipeOptions options)
|
||||
{
|
||||
Runtime.Assert(mBfpFile == null);
|
||||
|
||||
String path = scope String();
|
||||
path.Append(pipeName);
|
||||
|
||||
Platform.BfpFileCreateKind createKind = .CreateAlways;
|
||||
Platform.BfpFileCreateFlags createFlags = .Pipe;
|
||||
|
||||
createKind = .OpenExisting;
|
||||
createFlags |= .Read;
|
||||
createFlags |= .Write;
|
||||
|
||||
Platform.BfpFileAttributes fileFlags = .Normal;
|
||||
|
||||
Platform.BfpFileResult fileResult = .Ok;
|
||||
mBfpFile = Platform.BfpFile_Create(path, createKind, createFlags, fileFlags, &fileResult);
|
||||
|
||||
if ((mBfpFile == null) || (fileResult != .Ok))
|
||||
{
|
||||
switch (fileResult)
|
||||
{
|
||||
case .ShareError:
|
||||
return .Err(.SharingViolation);
|
||||
case .NotFound:
|
||||
return .Err(.NotFound);
|
||||
default:
|
||||
return .Err(.Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
}
|
100
BeefLibs/corlib/src/System/IO/SaveFileDialog.bf
Normal file
100
BeefLibs/corlib/src/System/IO/SaveFileDialog.bf
Normal file
|
@ -0,0 +1,100 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Text;
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
class SaveFileDialog : FileDialog
|
||||
{
|
||||
public this()
|
||||
{
|
||||
//mOptions &= ~Windows.OFN_PATHMUSTEXIST;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
mOptions = 0;
|
||||
}
|
||||
|
||||
public virtual bool OverwritePrompt
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetOption(Windows.OFN_OVERWRITEPROMPT);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetOption(Windows.OFN_OVERWRITEPROMPT, value);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Result<DialogResult> RunFileDialog(ref Windows.OpenFileName ofn)
|
||||
{
|
||||
bool result = Windows.GetSaveFileNameW(ref ofn);
|
||||
if (!result)
|
||||
return .Err;
|
||||
|
||||
if (!Multiselect)
|
||||
{
|
||||
let pathName = new String();
|
||||
UTF16.Decode(ofn.mFile, pathName);
|
||||
mFileNames = new String[](pathName);
|
||||
return DialogResult.OK;
|
||||
}
|
||||
|
||||
int32 entryCount = 0;
|
||||
int32 prevNull = -1;
|
||||
for (int32 i = 0; true; i++)
|
||||
{
|
||||
if (ofn.mFile[i] == (char8)0)
|
||||
{
|
||||
if (prevNull == i - 1)
|
||||
break;
|
||||
prevNull = i;
|
||||
entryCount++;
|
||||
}
|
||||
}
|
||||
|
||||
String pathName = null;
|
||||
prevNull = -1;
|
||||
mFileNames = new String[Math.Max(1, entryCount - 1)];
|
||||
entryCount = 0;
|
||||
for (int32 i = 0; true; i++)
|
||||
{
|
||||
if (ofn.mFile[i] == (char8)0)
|
||||
{
|
||||
if (prevNull == i - 1)
|
||||
break;
|
||||
if (prevNull == -1)
|
||||
{
|
||||
pathName = scope:: String();
|
||||
UTF16.Decode(ofn.mFile, pathName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var str = new String(pathName.Length + 1 + i - prevNull - 1);
|
||||
str.Append(pathName);
|
||||
str.Append(Path.DirectorySeparatorChar);
|
||||
UTF16.Decode(ofn.mFile + prevNull + 1, str);
|
||||
|
||||
mFileNames[entryCount++] = str;
|
||||
}
|
||||
prevNull = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ((entryCount == 0) && (pathName != null))
|
||||
mFileNames[0] = new String(pathName);
|
||||
|
||||
return DialogResult.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
4
BeefLibs/corlib/src/System/IO/SearchOption.bf
Normal file
4
BeefLibs/corlib/src/System/IO/SearchOption.bf
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace System.IO
|
||||
{
|
||||
|
||||
}
|
10
BeefLibs/corlib/src/System/IO/SecurityAttributes.bf
Normal file
10
BeefLibs/corlib/src/System/IO/SecurityAttributes.bf
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace System.IO
|
||||
{
|
||||
[CRepr]
|
||||
struct SecurityAttributes
|
||||
{
|
||||
internal int32 mLength = (int32)sizeof(SecurityAttributes);
|
||||
internal uint8* mSecurityDescriptor = null;
|
||||
internal int32 mInheritHandle = 0;
|
||||
}
|
||||
}
|
309
BeefLibs/corlib/src/System/IO/Stream.bf
Normal file
309
BeefLibs/corlib/src/System/IO/Stream.bf
Normal file
|
@ -0,0 +1,309 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
abstract class Stream
|
||||
{
|
||||
private ReadWriteTask _activeReadWriteTask;
|
||||
private SemaphoreSlim _asyncActiveSemaphore;
|
||||
|
||||
public abstract int64 Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public abstract int64 Length
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract bool CanRead
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract bool CanWrite
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return Length == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Result<int> TryRead(Span<uint8> data);
|
||||
public abstract Result<int> TryWrite(Span<uint8> data);
|
||||
public abstract void Close();
|
||||
|
||||
public Result<T> Read<T>() where T : struct
|
||||
{
|
||||
T val = ?;
|
||||
int size = Try!(TryRead(.((uint8*)&val, sizeof(T))));
|
||||
if (size != sizeof(T))
|
||||
return .Err;
|
||||
return .Ok(val);
|
||||
}
|
||||
|
||||
public Result<void> Write<T>(T val) where T : struct
|
||||
{
|
||||
var val;
|
||||
int size = Try!(TryWrite(.((uint8*)&val, sizeof(T))));
|
||||
if (size != sizeof(T))
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> Write<T, T2>(T val) where T : Span<T2>
|
||||
{
|
||||
int trySize = val.Length * sizeof(T2);
|
||||
int size = Try!(TryWrite(.((uint8*)val.Ptr, trySize)));
|
||||
if (size != trySize)
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> WriteStrSized32(StringView val)
|
||||
{
|
||||
int trySize = val.Length;
|
||||
Try!(Write((int32)trySize));
|
||||
int size = Try!(TryWrite(.((uint8*)val.Ptr, trySize)));
|
||||
if (size != trySize)
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> WriteStrUnsized(StringView val)
|
||||
{
|
||||
int trySize = val.Length;
|
||||
int size = Try!(TryWrite(.((uint8*)val.Ptr, trySize)));
|
||||
if (size != trySize)
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> Write(String val)
|
||||
{
|
||||
int trySize = val.Length;
|
||||
int size = Try!(TryWrite(Span<uint8>((uint8*)val.Ptr, trySize)));
|
||||
if (size != trySize)
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public virtual void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public void Align(int alignSize)
|
||||
{
|
||||
int64 pos = Length;
|
||||
int64 alignAdd = alignSize - (pos % alignSize);
|
||||
if (alignAdd == alignSize)
|
||||
return;
|
||||
|
||||
int64 emptyData = 0;
|
||||
while (alignAdd > 0)
|
||||
{
|
||||
int64 writeSize = Math.Min(alignAdd, sizeof(decltype(emptyData)));
|
||||
TryWrite(.((uint8*)&emptyData, (int)writeSize));
|
||||
alignAdd -= writeSize;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Result<int> CopyTo(Stream destStream)
|
||||
{
|
||||
uint8[4096] buffer;
|
||||
int totalBytes = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int readBytes = Try!(TryRead(.(&buffer, sizeof(decltype(buffer)))));
|
||||
Try!(destStream.TryWrite(.(&buffer, readBytes)));
|
||||
if (readBytes <= 0)
|
||||
break;
|
||||
totalBytes += readBytes;
|
||||
}
|
||||
|
||||
return .Ok(totalBytes);
|
||||
}
|
||||
|
||||
public virtual IAsyncResult BeginRead(uint8[] buffer, int offset, int count, AsyncCallback callback, Object state)
|
||||
{
|
||||
//Contract.Ensures(Contract.Result<IAsyncResult>() != null);
|
||||
return BeginReadInternal(buffer, offset, count, callback, state, false);
|
||||
}
|
||||
|
||||
// Task used by BeginRead / BeginWrite to do Read / Write asynchronously.
|
||||
// A single instance of this task serves four purposes:
|
||||
// 1. The work item scheduled to run the Read / Write operation
|
||||
// 2. The state holding the arguments to be passed to Read / Write
|
||||
// 3. The IAsyncResult returned from BeginRead / BeginWrite
|
||||
// 4. The completion action that runs to invoke the user-provided callback.
|
||||
// This last item is a bit tricky. Before the AsyncCallback is invoked, the
|
||||
// IAsyncResult must have completed, so we can't just invoke the handler
|
||||
// from within the task, since it is the IAsyncResult, and thus it's not
|
||||
// yet completed. Instead, we use AddCompletionAction to install this
|
||||
// task as its own completion handler. That saves the need to allocate
|
||||
// a separate completion handler, it guarantees that the task will
|
||||
// have completed by the time the handler is invoked, and it allows
|
||||
// the handler to be invoked synchronously upon the completion of the
|
||||
// task. This all enables BeginRead / BeginWrite to be implemented
|
||||
// with a single allocation.
|
||||
private sealed class ReadWriteTask : Task<int>, ITaskCompletionAction
|
||||
{
|
||||
internal readonly bool _isRead;
|
||||
internal Stream _stream;
|
||||
internal uint8 [] _buffer;
|
||||
internal int _offset;
|
||||
internal int _count;
|
||||
private AsyncCallback _callback;
|
||||
//private ExecutionContext _context;
|
||||
|
||||
internal void ClearBeginState() // Used to allow the args to Read/Write to be made available for GC
|
||||
{
|
||||
_stream = null;
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
public this(
|
||||
bool isRead,
|
||||
Func<Object, int> func, Object state,
|
||||
Stream stream, uint8[] buffer, int offset, int count, AsyncCallback callback) :
|
||||
base(func, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach)
|
||||
{
|
||||
Contract.Requires(func != null);
|
||||
Contract.Requires(stream != null);
|
||||
Contract.Requires(buffer != null);
|
||||
Contract.EndContractBlock();
|
||||
|
||||
//StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
|
||||
|
||||
// Store the arguments
|
||||
_isRead = isRead;
|
||||
_stream = stream;
|
||||
_buffer = buffer;
|
||||
_offset = offset;
|
||||
_count = count;
|
||||
|
||||
// If a callback was provided, we need to:
|
||||
// - Store the user-provided handler
|
||||
// - Capture an ExecutionContext under which to invoke the handler
|
||||
// - Add this task as its own completion handler so that the Invoke method
|
||||
// will run the callback when this task completes.
|
||||
if (callback != null)
|
||||
{
|
||||
_callback = callback;
|
||||
/*_context = ExecutionContext.Capture(ref stackMark,
|
||||
ExecutionContext.CaptureOptions.OptimizeDefaultCase | ExecutionContext.CaptureOptions.IgnoreSyncCtx);*/
|
||||
base.AddCompletionAction(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvokeAsyncCallback(Object completedTask)
|
||||
{
|
||||
var rwc = (ReadWriteTask)completedTask;
|
||||
var callback = rwc._callback;
|
||||
rwc._callback = null;
|
||||
callback(rwc);
|
||||
}
|
||||
|
||||
void ITaskCompletionAction.Invoke(Task completingTask)
|
||||
{
|
||||
var callback = _callback;
|
||||
_callback = null;
|
||||
callback(completingTask);
|
||||
}
|
||||
}
|
||||
|
||||
internal IAsyncResult BeginReadInternal(uint8[] buffer, int offset, int count, AsyncCallback callback, Object state, bool serializeAsynchronously)
|
||||
{
|
||||
// To avoid a race with a stream's position pointer & generating ----
|
||||
// conditions with internal buffer indexes in our own streams that
|
||||
// don't natively support async IO operations when there are multiple
|
||||
// async requests outstanding, we will block the application's main
|
||||
// thread if it does a second IO request until the first one completes.
|
||||
|
||||
//TODO: Implement
|
||||
/*var semaphore = EnsureAsyncActiveSemaphoreInitialized();
|
||||
Task semaphoreTask = null;
|
||||
if (serializeAsynchronously)
|
||||
{
|
||||
semaphoreTask = semaphore.WaitAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
semaphore.Wait();
|
||||
}*/
|
||||
|
||||
// Create the task to asynchronously do a Read. This task serves both
|
||||
// as the asynchronous work item and as the IAsyncResult returned to the user.
|
||||
var asyncResult = new ReadWriteTask(true /*isRead*/, new (obj) =>
|
||||
{
|
||||
// The ReadWriteTask stores all of the parameters to pass to Read.
|
||||
// As we're currently inside of it, we can get the current task
|
||||
// and grab the parameters from it.
|
||||
var thisTask = Task.InternalCurrent as ReadWriteTask;
|
||||
Contract.Assert(thisTask != null, "Inside ReadWriteTask, InternalCurrent should be the ReadWriteTask");
|
||||
|
||||
// Do the Read and return the number of bytes read
|
||||
int bytesRead = thisTask._stream.TryRead(.(thisTask._buffer, thisTask._offset, thisTask._count));
|
||||
thisTask.ClearBeginState(); // just to help alleviate some memory pressure
|
||||
return bytesRead;
|
||||
}, state, this, buffer, offset, count, callback);
|
||||
|
||||
// Schedule it
|
||||
|
||||
//TODO:
|
||||
/*if (semaphoreTask != null)
|
||||
RunReadWriteTaskWhenReady(semaphoreTask, asyncResult);
|
||||
else
|
||||
RunReadWriteTask(asyncResult);*/
|
||||
|
||||
|
||||
return asyncResult; // return it
|
||||
}
|
||||
|
||||
public virtual Result<int> EndRead(IAsyncResult asyncResult)
|
||||
{
|
||||
if (asyncResult == null)
|
||||
Runtime.FatalError();
|
||||
//Contract.Ensures(Contract.Result<int>() >= 0);
|
||||
Contract.EndContractBlock();
|
||||
|
||||
var readTask = _activeReadWriteTask;
|
||||
|
||||
if (readTask == null)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
else if (readTask != asyncResult)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
else if (!readTask._isRead)
|
||||
{
|
||||
Runtime.FatalError();
|
||||
}
|
||||
|
||||
|
||||
int result = readTask.GetAwaiter().GetResult(); // block until completion, then get result / propagate any exception
|
||||
|
||||
_activeReadWriteTask = null;
|
||||
Contract.Assert(_asyncActiveSemaphore != null, "Must have been initialized in order to get here.");
|
||||
_asyncActiveSemaphore.Release();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
612
BeefLibs/corlib/src/System/IO/StreamReader.bf
Normal file
612
BeefLibs/corlib/src/System/IO/StreamReader.bf
Normal file
|
@ -0,0 +1,612 @@
|
|||
// This file contains portions of code released by Microsoft under the MIT license as part
|
||||
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
||||
// The original source was submitted to https://github.com/Microsoft/referencesource
|
||||
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
class StreamReader
|
||||
{
|
||||
private const int32 DefaultFileStreamBufferSize = 4096;
|
||||
private const int32 MinBufferSize = 128;
|
||||
|
||||
Stream mStream ~ if (mOwnsStream) delete _;
|
||||
public bool mOwnsStream;
|
||||
int mCharLen;
|
||||
int mCharPos;
|
||||
|
||||
private int mBytePos;
|
||||
|
||||
// Record the number of valid bytes in the byteBuffer, for a few checks.
|
||||
private int mByteLen;
|
||||
// We will support looking for byte order marks in the stream and trying
|
||||
// to decide what the encoding might be from the byte order marks, IF they
|
||||
// exist. But that's all we'll do.
|
||||
private bool mDetectEncoding;
|
||||
|
||||
// Whether we must still check for the encoding's given preamble at the
|
||||
// beginning of this file.
|
||||
private bool mCheckPreamble;
|
||||
|
||||
// Whether the stream is most likely not going to give us back as much
|
||||
// data as we want the next time we call it. We must do the computation
|
||||
// before we do any byte order mark handling and save the result. Note
|
||||
// that we need this to allow users to handle streams used for an
|
||||
// interactive protocol, where they block waiting for the remote end
|
||||
// to send a response, like logging in on a Unix machine.
|
||||
private bool mIsBlocked;
|
||||
|
||||
private uint8[] mPreamble ~ delete _; // Encoding's preamble, which identifies this encoding.
|
||||
|
||||
private uint8[] mByteBuffer;
|
||||
private char8[] mCharBuffer;
|
||||
private bool mOwnsBuffers;
|
||||
|
||||
private int32 mMaxCharsPerBuffer;
|
||||
private Encoding mEncoding;
|
||||
|
||||
public Stream BaseStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return mStream;
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
if (mOwnsBuffers)
|
||||
{
|
||||
delete mByteBuffer;
|
||||
delete mCharBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
public Encoding CurrentEncoding
|
||||
{
|
||||
get
|
||||
{
|
||||
return mEncoding;
|
||||
}
|
||||
}
|
||||
|
||||
public LineReader Lines
|
||||
{
|
||||
get
|
||||
{
|
||||
return LineReader(this);
|
||||
}
|
||||
}
|
||||
|
||||
[AllowAppend]
|
||||
public this(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int32 bufferSize, bool ownsSteam = false)
|
||||
{
|
||||
int32 useBufferSize = bufferSize;
|
||||
if (useBufferSize < MinBufferSize) useBufferSize = MinBufferSize;
|
||||
let maxCharsPerBuffer = (encoding != null) ? encoding.GetMaxCharCount(useBufferSize) : bufferSize;
|
||||
|
||||
let byteBuffer = append uint8[useBufferSize];
|
||||
let charBuffer = append char8[maxCharsPerBuffer];
|
||||
/*let byteBuffer = new uint8[useBufferSize];
|
||||
let charBuffer = new char8[maxCharsPerBuffer];
|
||||
mOwnsBuffers = true;*/
|
||||
|
||||
mMaxCharsPerBuffer = (.)maxCharsPerBuffer;
|
||||
mByteBuffer = byteBuffer;
|
||||
mCharBuffer = charBuffer;
|
||||
mOwnsStream = ownsSteam;
|
||||
|
||||
Init(stream, encoding, detectEncodingFromByteOrderMarks, bufferSize);
|
||||
}
|
||||
|
||||
[AllowAppend]
|
||||
public this(Stream stream) : this(stream, .UTF8, false, 4096)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int GetMaxCharCount(int32 byteCount)
|
||||
{
|
||||
if (mEncoding == null)
|
||||
return byteCount;
|
||||
// UTF-8 to UTF-8
|
||||
return mEncoding.GetMaxCharCount(byteCount);
|
||||
}
|
||||
|
||||
void Init(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int32 bufferSize)
|
||||
{
|
||||
mStream = stream;
|
||||
this.mEncoding = encoding;
|
||||
//decoder = encoding.GetDecoder();
|
||||
|
||||
if (mByteBuffer == null)
|
||||
{
|
||||
int32 useBufferSize = bufferSize;
|
||||
if (useBufferSize < MinBufferSize) useBufferSize = MinBufferSize;
|
||||
mMaxCharsPerBuffer = (.)GetMaxCharCount(useBufferSize);
|
||||
mByteBuffer = new uint8[useBufferSize];
|
||||
mCharBuffer = new char8[mMaxCharsPerBuffer];
|
||||
mOwnsBuffers = true;
|
||||
}
|
||||
|
||||
mByteLen = 0;
|
||||
mBytePos = 0;
|
||||
mDetectEncoding = detectEncodingFromByteOrderMarks;
|
||||
//mPreamble = encoding.GetPreamble();
|
||||
mCheckPreamble = false;//(mPreamble.Length > 0);
|
||||
mIsBlocked = false;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
delete mStream;
|
||||
mStream = null;
|
||||
}
|
||||
|
||||
public bool EndOfStream
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mCharPos < mCharLen)
|
||||
return false;
|
||||
|
||||
// This may block on pipes!
|
||||
int numRead = ReadBuffer();
|
||||
return numRead == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Open(StringView fileName)
|
||||
{
|
||||
Contract.Assert(mStream == null);
|
||||
|
||||
var fileStream = new FileStream();
|
||||
Encoding encoding = null;
|
||||
Init(fileStream, encoding, true, DefaultFileStreamBufferSize);
|
||||
mOwnsStream = true;
|
||||
|
||||
if (fileStream.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) case .Err(let err))
|
||||
return .Err(err);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Task<String> ReadLineAsync()
|
||||
{
|
||||
// If we have been inherited into a subclass, the following implementation could be incorrect
|
||||
// since it does not call through to Read() which a subclass might have overriden.
|
||||
// To be safe we will only use this implementation in cases where we know it is safe to do so,
|
||||
// and delegate to our base class (which will call into Read) when we are not sure.
|
||||
//TODO:
|
||||
//if (this.GetType() != typeof(StreamReader))
|
||||
//return base.ReadLineAsync();
|
||||
|
||||
//if (mStream == null)
|
||||
//__Error.ReaderClosed();
|
||||
|
||||
//CheckAsyncTaskInProgress();
|
||||
|
||||
Task<String> task = ReadLineAsyncInternal();
|
||||
//_asyncReadTask = task;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
class ReadLineTask : Task<String>
|
||||
{
|
||||
StreamReader mStreamReader;
|
||||
WaitEvent mDoneEvent = new WaitEvent() ~ delete _;
|
||||
|
||||
internal this(StreamReader streamReader)
|
||||
{
|
||||
Debug.WriteLine("ReadLineTask this {0}", this);
|
||||
|
||||
mStreamReader = streamReader;
|
||||
ThreadPool.QueueUserWorkItem(new => Proc);
|
||||
}
|
||||
|
||||
internal ~this()
|
||||
{
|
||||
//Debug.WriteLine("ReadLineTask ~this waiting {0}", this);
|
||||
mDoneEvent.WaitFor();
|
||||
//Debug.WriteLine("ReadLineTask ~this done {0}", this);
|
||||
|
||||
delete m_result;
|
||||
}
|
||||
|
||||
void Proc()
|
||||
{
|
||||
//Debug.WriteLine("ReadLineTask Proc start {0}", this);
|
||||
m_result = new String();
|
||||
var result = mStreamReader.ReadLine(m_result);
|
||||
//Debug.WriteLine("ReadLineTask Proc finishing {0}", this);
|
||||
Finish(false);
|
||||
Ref();
|
||||
if (result case .Ok)
|
||||
Notify(false);
|
||||
mDoneEvent.Set();
|
||||
Deref();
|
||||
}
|
||||
}
|
||||
|
||||
private Task<String> ReadLineAsyncInternal()
|
||||
{
|
||||
/*if (CharPos_Prop == CharLen_Prop && (await ReadBufferAsync().ConfigureAwait(false)) == 0)
|
||||
return null;
|
||||
|
||||
String sb = null;
|
||||
|
||||
do
|
||||
{
|
||||
char8[] tmpCharBuffer = CharBuffer_Prop;
|
||||
int tmpCharLen = CharLen_Prop;
|
||||
int tmpCharPos = CharPos_Prop;
|
||||
int i = tmpCharPos;
|
||||
|
||||
do
|
||||
{
|
||||
char8 ch = tmpCharBuffer[i];
|
||||
|
||||
// Note the following common line feed char8s:
|
||||
// \n - UNIX \r\n - DOS \r - Mac
|
||||
if (ch == '\r' || ch == '\n')
|
||||
{
|
||||
String s;
|
||||
|
||||
if (sb != null)
|
||||
{
|
||||
sb.Append(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
|
||||
s = sb;//.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
s = new String(tmpCharBuffer, tmpCharPos, i - tmpCharPos);
|
||||
}
|
||||
|
||||
CharPos_Prop = tmpCharPos = i + 1;
|
||||
|
||||
if (ch == '\r' && (tmpCharPos < tmpCharLen || (await ReadBufferAsync().ConfigureAwait(false)) > 0))
|
||||
{
|
||||
tmpCharPos = CharPos_Prop;
|
||||
if (CharBuffer_Prop[tmpCharPos] == '\n')
|
||||
CharPos_Prop = ++tmpCharPos;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
} while (i < tmpCharLen);
|
||||
|
||||
i = tmpCharLen - tmpCharPos;
|
||||
if (sb == null) sb = new String(i + 80);
|
||||
sb.Append(tmpCharBuffer, tmpCharPos, i);
|
||||
|
||||
} while (await ReadBufferAsync().ConfigureAwait(false) > 0);
|
||||
|
||||
return sb.ToString();*/
|
||||
|
||||
|
||||
ReadLineTask task = new ReadLineTask(this);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public Result<void> ReadToEnd(String outText)
|
||||
{
|
||||
outText.Reserve((.)mStream.Length + 1);
|
||||
|
||||
while (true)
|
||||
{
|
||||
Try!(ReadBuffer());
|
||||
if (mCharLen == 0)
|
||||
break;
|
||||
|
||||
outText.Append(StringView(mCharBuffer, 0, mCharLen));
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
// Trims the preamble bytes from the byteBuffer. This routine can be called multiple times
|
||||
// and we will buffer the bytes read until the preamble is matched or we determine that
|
||||
// there is no match. If there is no match, every byte read previously will be available
|
||||
// for further consumption. If there is a match, we will compress the buffer for the
|
||||
// leading preamble bytes
|
||||
private bool IsPreamble()
|
||||
{
|
||||
if (!mCheckPreamble)
|
||||
return mCheckPreamble;
|
||||
|
||||
//Contract.Assert(bytePos <= _preamble.Length, "_compressPreamble was called with the current bytePos greater than the preamble buffer length. Are two threads using this StreamReader at the same time?");
|
||||
int len = (mByteLen >= (mPreamble.Count)) ? (mPreamble.Count - mBytePos) : (mByteLen - mBytePos);
|
||||
|
||||
for (int32 i = 0; i < len; i++,mBytePos++)
|
||||
{
|
||||
if (mByteBuffer[mBytePos] != mPreamble[mBytePos])
|
||||
{
|
||||
mBytePos = 0;
|
||||
mCheckPreamble = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Contract.Assert(mBytePos <= mPreamble.Count, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
|
||||
|
||||
if (mCheckPreamble)
|
||||
{
|
||||
if (mBytePos == mPreamble.Count)
|
||||
{
|
||||
// We have a match
|
||||
CompressBuffer(mPreamble.Count);
|
||||
mBytePos = 0;
|
||||
mCheckPreamble = false;
|
||||
mDetectEncoding = false;
|
||||
}
|
||||
}
|
||||
|
||||
return mCheckPreamble;
|
||||
}
|
||||
|
||||
// Trims n bytes from the front of the buffer.
|
||||
private void CompressBuffer(int n)
|
||||
{
|
||||
//Contract.Assert(byteLen >= n, "CompressBuffer was called with a number of bytes greater than the current buffer length. Are two threads using this StreamReader at the same time?");
|
||||
//Buffer.InternalBlockCopy(byteBuffer, n, byteBuffer, 0, byteLen - n);
|
||||
mByteBuffer.CopyTo(mByteBuffer, n, 0, mByteLen - n);
|
||||
mByteLen -= (int32)n;
|
||||
}
|
||||
|
||||
void DetectEncoding()
|
||||
{
|
||||
mEncoding = Encoding.DetectEncoding(mByteBuffer, var bomSize);
|
||||
if (mEncoding != null)
|
||||
{
|
||||
if (bomSize > 0)
|
||||
CompressBuffer(bomSize);
|
||||
mDetectEncoding = false;
|
||||
}
|
||||
else if (mByteLen > 4)
|
||||
{
|
||||
mDetectEncoding = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual Result<int> ReadBuffer()
|
||||
{
|
||||
mCharLen = 0;
|
||||
mCharPos = 0;
|
||||
|
||||
if (!mCheckPreamble)
|
||||
mByteLen = 0;
|
||||
repeat
|
||||
{
|
||||
if (mCheckPreamble)
|
||||
{
|
||||
//Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble. Are two threads using this StreamReader at the same time?");
|
||||
int len = Try!(mStream.TryRead(.(mByteBuffer, mBytePos, mByteBuffer.Count - mBytePos)));
|
||||
/*switch (mStream.Read(mByteBuffer, mBytePos, mByteBuffer.Length - mBytePos))
|
||||
{
|
||||
case .Ok(var gotLen):
|
||||
len = gotLen;
|
||||
break;
|
||||
case .Err: return .Err;
|
||||
}*/
|
||||
//Contract.Assert(len >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
// EOF but we might have buffered bytes from previous
|
||||
// attempt to detect preamble that needs to be decoded now
|
||||
if (mByteLen > 0)
|
||||
{
|
||||
//TODO:
|
||||
/*char8Len += decoder.GetChars(byteBuffer, 0, byteLen, char8Buffer, char8Len);
|
||||
|
||||
// Need to zero out the byteLen after we consume these bytes so that we don't keep infinitely hitting this code path
|
||||
bytePos = byteLen = 0;*/
|
||||
}
|
||||
|
||||
return mCharLen;
|
||||
}
|
||||
|
||||
mByteLen += len;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Contract.Assert(bytePos == 0, "bytePos can be non zero only when we are trying to _checkPreamble. Are two threads using this StreamReader at the same time?");
|
||||
|
||||
mByteLen = Try!(mStream.TryRead(.(mByteBuffer, 0, mByteBuffer.Count)));
|
||||
/*switch (mStream.Read(mByteBuffer, 0, mByteBuffer.Length))
|
||||
{
|
||||
case .Ok(var byteLen):
|
||||
mByteLen = byteLen;
|
||||
break;
|
||||
case .Err: return .Err;
|
||||
}*/
|
||||
//Contract.Assert(byteLen >= 0, "Stream.Read returned a negative number! This is a bug in your stream class.");
|
||||
|
||||
if (mByteLen == 0) // We're at EOF
|
||||
return mCharLen;
|
||||
}
|
||||
|
||||
// _isBlocked == whether we read fewer bytes than we asked for.
|
||||
// Note we must check it here because CompressBuffer or
|
||||
// DetectEncoding will change byteLen.
|
||||
mIsBlocked = (mByteLen < mByteBuffer.Count);
|
||||
|
||||
// Check for preamble before detect encoding. This is not to override the
|
||||
// user suppplied Encoding for the one we implicitly detect. The user could
|
||||
// customize the encoding which we will loose, such as ThrowOnError on UTF8
|
||||
if (IsPreamble())
|
||||
continue;
|
||||
|
||||
// If we're supposed to detect the encoding and haven't done so yet,
|
||||
// do it. Note this may need to be called more than once.
|
||||
|
||||
if (mDetectEncoding && mByteLen >= 2)
|
||||
DetectEncoding();
|
||||
else if (mEncoding == null)
|
||||
mEncoding = Encoding.ASCII;
|
||||
|
||||
switch (mEncoding.DecodeToUTF8(.(mByteBuffer, 0, mByteLen), .(&mCharBuffer[mCharLen], mCharBuffer.Count - mCharLen)))
|
||||
{
|
||||
case .Ok(let outChars):
|
||||
mCharLen += outChars;
|
||||
Debug.Assert(outChars <= mCharBuffer.Count);
|
||||
break;
|
||||
case .Err(let err):
|
||||
switch (err)
|
||||
{
|
||||
case .PartialDecode(let decodedBytes, let outChars):
|
||||
//TODO: Handle this partial read...
|
||||
Debug.Assert(outChars <= mCharBuffer.Count);
|
||||
mCharLen += outChars;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
while (mCharLen == 0);
|
||||
|
||||
//Console.WriteLine("ReadBuffer called. char8s: "+char8Len);
|
||||
return mCharLen;
|
||||
}
|
||||
|
||||
int GetChars(uint8[] byteBuffer, int byteOffset, int byteLength, char8[] char8Buffer, int char8Offset)
|
||||
{
|
||||
//TODO: This only handles UTF-8 to UTF-8
|
||||
for (int32 i = 0; i < byteLength; i++)
|
||||
char8Buffer[char8Offset + i] = (char8)byteBuffer[i + byteOffset];
|
||||
return byteLength;
|
||||
}
|
||||
|
||||
// Reads a line. A line is defined as a sequence of char8acters followed by
|
||||
// a carriage return ('\r'), a line feed ('\n'), or a carriage return
|
||||
// immediately followed by a line feed. The resulting string does not
|
||||
// contain the terminating carriage return and/or line feed. The returned
|
||||
// value is null if the end of the input stream has been reached.
|
||||
//
|
||||
public Result<void> ReadLine(String strBuffer)
|
||||
{
|
||||
if (mStream == null)
|
||||
{
|
||||
return .Err;
|
||||
//__Error.ReaderClosed();
|
||||
}
|
||||
|
||||
#if FEATURE_ASYNC_IO
|
||||
CheckAsyncTaskInProgress();
|
||||
#endif
|
||||
|
||||
if (mCharPos == mCharLen)
|
||||
{
|
||||
if (Try!(ReadBuffer()) == 0) return .Err;
|
||||
}
|
||||
|
||||
repeat
|
||||
{
|
||||
int i = mCharPos;
|
||||
repeat
|
||||
{
|
||||
char8 ch = mCharBuffer[i];
|
||||
// Note the following common line feed char8s:
|
||||
// \n - UNIX \r\n - DOS \r - Mac
|
||||
if (ch == '\r' || ch == '\n')
|
||||
{
|
||||
/*String s;
|
||||
if (sb != null)
|
||||
{
|
||||
sb.Append(char8Buffer, char8Pos, i - char8Pos);
|
||||
s = sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
s = new String(char8Buffer, char8Pos, i - char8Pos);
|
||||
}*/
|
||||
|
||||
strBuffer.Append(mCharBuffer.CArray() + mCharPos, i - mCharPos);
|
||||
|
||||
mCharPos = i + 1;
|
||||
if (ch == '\r' && (mCharPos < mCharLen || Try!(ReadBuffer()) > 0))
|
||||
{
|
||||
if (mCharBuffer[mCharPos] == '\n') mCharPos++;
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while (i < mCharLen);
|
||||
i = mCharLen - mCharPos;
|
||||
//if (sb == null) sb = new StringBuilder(i + 80);
|
||||
//sb.Append(char8Buffer, char8Pos, i);
|
||||
strBuffer.Append(mCharBuffer.CArray() + mCharPos, i);
|
||||
}
|
||||
while (Try!(ReadBuffer()) > 0);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public struct LineReader : IEnumerator<Result<StringView>>
|
||||
{
|
||||
StreamReader mStreamReader;
|
||||
String mCurrentLine;
|
||||
Result<void> mLastReadResult;
|
||||
|
||||
internal this(StreamReader streamReader)
|
||||
{
|
||||
mStreamReader = streamReader;
|
||||
mCurrentLine = new String();
|
||||
mLastReadResult = default(Result<void>);
|
||||
}
|
||||
|
||||
public Result<StringView> Current
|
||||
{
|
||||
get
|
||||
{
|
||||
Try!(mLastReadResult);
|
||||
return (StringView)mCurrentLine;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool MoveNext() mut
|
||||
{
|
||||
mCurrentLine.Clear();
|
||||
mLastReadResult = mStreamReader.ReadLine(mCurrentLine);
|
||||
return !(mLastReadResult case .Err);
|
||||
}
|
||||
|
||||
public void Dispose() mut
|
||||
{
|
||||
delete mCurrentLine;
|
||||
mCurrentLine = null;
|
||||
}
|
||||
|
||||
public Result<Result<StringView>> GetNext() mut
|
||||
{
|
||||
if (!MoveNext())
|
||||
return .Err;
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
BeefLibs/corlib/src/System/IO/StreamWriter.bf
Normal file
83
BeefLibs/corlib/src/System/IO/StreamWriter.bf
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public class StreamWriter
|
||||
{
|
||||
Stream mStream ~ if (mOwnsStream) delete _;
|
||||
public bool mOwnsStream;
|
||||
public Encoding mEncoding;
|
||||
|
||||
public bool AutoFlush;
|
||||
|
||||
public this(Stream stream, Encoding encoding, int32 bufferSize, bool ownsStream = false)
|
||||
{
|
||||
Debug.Assert(encoding != null);
|
||||
mStream = stream;
|
||||
mEncoding = encoding;
|
||||
mOwnsStream = ownsStream;
|
||||
}
|
||||
|
||||
public Result<void> Write(Span<uint8> data)
|
||||
{
|
||||
var spanLeft = data;
|
||||
while (!spanLeft.IsEmpty)
|
||||
{
|
||||
int bytesWritten = Try!(mStream.TryWrite(spanLeft));
|
||||
spanLeft.Adjust(bytesWritten);
|
||||
}
|
||||
if (AutoFlush)
|
||||
Flush();
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> Write(StringView str)
|
||||
{
|
||||
var curSpan = str;
|
||||
|
||||
Span<uint8> outSpan = scope uint8[4096];
|
||||
|
||||
while (!curSpan.IsEmpty)
|
||||
{
|
||||
switch (mEncoding.Encode(curSpan, outSpan))
|
||||
{
|
||||
case .Ok(let encodedBytes):
|
||||
Try!(Write(Span<uint8>(outSpan.Ptr, encodedBytes)));
|
||||
return .Ok;
|
||||
case .Err(let err):
|
||||
switch (err)
|
||||
{
|
||||
case .PartialEncode(int inChars, int encodedBytes):
|
||||
Try!(Write(Span<uint8>(outSpan.Ptr, encodedBytes)));
|
||||
curSpan.Adjust(inChars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void> WriteLine(StringView str)
|
||||
{
|
||||
Try!(Write(str));
|
||||
return Write("\n");
|
||||
}
|
||||
|
||||
public Result<void> Write(StringView fmt, params Object[] args)
|
||||
{
|
||||
return Write(scope String()..AppendF(fmt, params args));
|
||||
}
|
||||
|
||||
public Result<void> WriteLine(StringView fmt, params Object[] args)
|
||||
{
|
||||
Try!(Write(scope String()..AppendF(fmt, params args)));
|
||||
return Write("\n");
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
mStream.Flush();
|
||||
}
|
||||
}
|
||||
}
|
165
BeefLibs/corlib/src/System/IO/StringStream.bf
Normal file
165
BeefLibs/corlib/src/System/IO/StringStream.bf
Normal file
|
@ -0,0 +1,165 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
class StringStream : Stream
|
||||
{
|
||||
enum StringKind
|
||||
{
|
||||
Append,
|
||||
Reference,
|
||||
Owned
|
||||
}
|
||||
|
||||
StringKind mStringKind;
|
||||
String mString;
|
||||
int mPosition;
|
||||
|
||||
[AllowAppend]
|
||||
public this()
|
||||
{
|
||||
let appendStr = append String();
|
||||
mString = appendStr;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
if (mStringKind == .Append)
|
||||
delete:append mString;
|
||||
else if (mStringKind == .Owned)
|
||||
DeleteOwned();
|
||||
}
|
||||
|
||||
protected virtual void DeleteOwned()
|
||||
{
|
||||
delete mString;
|
||||
}
|
||||
|
||||
public enum StringViewInitKind
|
||||
{
|
||||
Copy,
|
||||
Reference,
|
||||
}
|
||||
|
||||
[AllowAppend]
|
||||
public this(StringView str, StringViewInitKind initKind)
|
||||
{
|
||||
let appendStr = append String();
|
||||
mString = appendStr;
|
||||
mStringKind = .Append;
|
||||
|
||||
switch (initKind)
|
||||
{
|
||||
case .Copy:
|
||||
mString.Set(str);
|
||||
case .Reference:
|
||||
mString = new String();
|
||||
mString.Reference(str.Ptr, str.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public enum StringInitKind
|
||||
{
|
||||
Copy,
|
||||
Reference,
|
||||
TakeOwnership
|
||||
}
|
||||
|
||||
[AllowAppend]
|
||||
public this(String str, StringInitKind initKind)
|
||||
{
|
||||
let appendStr = append String();
|
||||
mString = appendStr;
|
||||
|
||||
switch (initKind)
|
||||
{
|
||||
case .Copy:
|
||||
mString.Set(str);
|
||||
mStringKind = .Append;
|
||||
case .Reference:
|
||||
mString = str;
|
||||
mStringKind = .Reference;
|
||||
case .TakeOwnership:
|
||||
mString = str;
|
||||
mStringKind = .Owned;
|
||||
}
|
||||
}
|
||||
|
||||
public StringView Content
|
||||
{
|
||||
get
|
||||
{
|
||||
return mString;
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
mPosition = (.)value;
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return mString.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override Result<int> TryRead(Span<uint8> data)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
return .Ok(0);
|
||||
int readBytes = Math.Min(data.Length, mString.Length - mPosition);
|
||||
if (readBytes <= 0)
|
||||
return .Ok(readBytes);
|
||||
|
||||
Internal.MemCpy(data.Ptr, &mString[mPosition], readBytes);
|
||||
mPosition += readBytes;
|
||||
return .Ok(readBytes);
|
||||
}
|
||||
|
||||
public override Result<int> TryWrite(Span<uint8> data)
|
||||
{
|
||||
let count = data.Length;
|
||||
if (count == 0)
|
||||
return .Ok(0);
|
||||
int growSize = mPosition + count - mString.Length;
|
||||
if (growSize > 0)
|
||||
mString.PrepareBuffer(growSize);
|
||||
Internal.MemCpy(&mString[mPosition], data.Ptr, count);
|
||||
mPosition += count;
|
||||
return .Ok(count);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
13
BeefLibs/corlib/src/System/IO/WatcherChangeTypes.bf
Normal file
13
BeefLibs/corlib/src/System/IO/WatcherChangeTypes.bf
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace System.IO
|
||||
{
|
||||
enum WatcherChangeTypes
|
||||
{
|
||||
FileCreated = 1,
|
||||
DirectoryCreated = 2,
|
||||
Deleted = 4,
|
||||
Changed = 8,
|
||||
Renamed = 0x10,
|
||||
Failed = 0x20,
|
||||
All = FileCreated | DirectoryCreated | Deleted | Changed | Renamed | Failed
|
||||
}
|
||||
}
|
7
BeefLibs/corlib/src/System/IPrintable.bf
Normal file
7
BeefLibs/corlib/src/System/IPrintable.bf
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace System
|
||||
{
|
||||
interface IPrintable
|
||||
{
|
||||
void Print(String outString);
|
||||
}
|
||||
}
|
57
BeefLibs/corlib/src/System/IRefCounted.bf
Normal file
57
BeefLibs/corlib/src/System/IRefCounted.bf
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System
|
||||
{
|
||||
interface IRefCounted
|
||||
{
|
||||
void AddRef();
|
||||
void ReleaseRef();
|
||||
}
|
||||
|
||||
class RefCounted : IRefCounted
|
||||
{
|
||||
protected int32 mRefCount = 1;
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Debug.Assert(mRefCount == 0);
|
||||
}
|
||||
|
||||
public int RefCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return mRefCount;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteUnchecked()
|
||||
{
|
||||
mRefCount = 0;
|
||||
delete this;
|
||||
}
|
||||
|
||||
public void AddRef()
|
||||
{
|
||||
Interlocked.Increment(ref mRefCount);
|
||||
}
|
||||
|
||||
public void ReleaseRef()
|
||||
{
|
||||
int refCount = Interlocked.Decrement(ref mRefCount);
|
||||
Debug.Assert(refCount >= 0);
|
||||
if (refCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
public int ReleaseRefNoDelete()
|
||||
{
|
||||
int refCount = Interlocked.Decrement(ref mRefCount);
|
||||
Debug.Assert(refCount >= 0);
|
||||
return refCount;
|
||||
}
|
||||
}
|
||||
}
|
81
BeefLibs/corlib/src/System/Int.bf
Normal file
81
BeefLibs/corlib/src/System/Int.bf
Normal file
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
|
||||
namespace System
|
||||
{
|
||||
struct Int : int, IInteger, IHashable, IFormattable, IOpComparable, IIsNaN, IOpNegatable, IOpAddable
|
||||
{
|
||||
public enum ParseError
|
||||
{
|
||||
case Ok;
|
||||
case NoValue;
|
||||
case InvalidChar(int partialResult);
|
||||
}
|
||||
|
||||
public static int operator<=>(Self a, Self b)
|
||||
{
|
||||
return (SelfBase)a <=> (SelfBase)b;
|
||||
}
|
||||
|
||||
public static Self operator+(Self lhs, Self rhs)
|
||||
{
|
||||
return (SelfBase)lhs + (SelfBase)rhs;
|
||||
}
|
||||
|
||||
public static Self operator-(Self value)
|
||||
{
|
||||
return (SelfBase)value;
|
||||
}
|
||||
|
||||
int IHashable.GetHashCode()
|
||||
{
|
||||
return (int)this;
|
||||
}
|
||||
|
||||
bool IIsNaN.IsNaN
|
||||
{
|
||||
[SkipCall]
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ToString(String outString, String format, IFormatProvider formatProvider)
|
||||
{
|
||||
if (sizeof(int) == sizeof(int64))
|
||||
{
|
||||
((int64)this).ToString(outString, format, formatProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
((int32)this).ToString(outString, format, formatProvider);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ToString(String outString)
|
||||
{
|
||||
if (sizeof(int) == sizeof(int64))
|
||||
{
|
||||
((int64)this).ToString(outString);
|
||||
}
|
||||
else
|
||||
{
|
||||
((int32)this).ToString(outString);
|
||||
}
|
||||
}
|
||||
|
||||
public static Result<int, ParseError> Parse(StringView val)
|
||||
{
|
||||
if (sizeof(int) == sizeof(int64))
|
||||
{
|
||||
var result = Int64.Parse(val);
|
||||
return *(Result<int, ParseError>*)&result;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = Int32.Parse(val);
|
||||
return *(Result<int, ParseError>*)&result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue