1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-21 09:27:59 +02:00

Initial checkin

This commit is contained in:
Brian Fiete 2019-08-23 11:56:54 -07:00
parent c74712dad9
commit 078564ac9e
3242 changed files with 1616395 additions and 0 deletions

View 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);
}

View 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]);
}
}
}
}

View file

@ -0,0 +1,4 @@
namespace System
{
public delegate void AsyncCallback(IAsyncResult ar);
}

View 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)
{
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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));
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
namespace System.Collections.Generic
{
}

View file

@ -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);
}
}

View file

@ -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(']');
}
}
}

View 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);
}*/
}
}

View 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;
}
}
}
}

View 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;
}
}
}
}

View 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();
}
}

View 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);
}
}
}

View 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);
}
}
}

View file

@ -0,0 +1,9 @@
namespace System
{
enum DateTimeKind
{
Unspecified = 0,
Utc = 1,
Local = 2,
}
}

View 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;
}
}
}

View file

@ -0,0 +1,13 @@
namespace System
{
enum DayOfWeek
{
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
}
}

View 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
{
}
}

View 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();
}
}
}
}

View 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);
}
}
}

View file

@ -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() { }
}
}

View file

@ -0,0 +1,6 @@
namespace System.Diagnostics.Contracts
{
class ContractsBcl
{
}
}

View file

@ -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;
}
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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
}
}

View file

@ -0,0 +1,13 @@
namespace System.Diagnostics
{
public enum ProcessPriorityClass
{
Normal = 0x20,
Idle = 0x40,
High = 0x80,
RealTime = 0x100,
BelowNormal = 0x4000,
AboveNormal = 0x8000
}
}

View 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)
{
}
}
}

View file

@ -0,0 +1,10 @@
namespace System.Diagnostics
{
public enum ProcessWindowStyle
{
Normal,
Hidden,
Minimized,
Maximized
}
}

View 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()
{
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}

View 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()
{
}
}
}

View 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(), )
}*/
}
}

View 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);
}
}
}

View 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;
}
}
}
}

View file

@ -0,0 +1,11 @@
namespace System
{
public class EventArgs
{
public static readonly EventArgs Empty = new EventArgs() ~ delete _;
public this()
{
}
}
}

View 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
}

View 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);
}
}
}

View 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;
}
}
}

View 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);
}

View 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);
}*/
}
}
}

View 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);
}
}
}

View file

@ -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.
}
}

View 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();
}
}
}

View 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
}
}

View 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;
}
}
}
}

View 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);
}
}
}

File diff suppressed because it is too large Load diff

View 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;
}
}
}
}

View file

@ -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
{
}
}

View 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,
}
}

View 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;
}
}
}
}

View 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));
}
}
}

View 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
}

View 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,
}
}

View 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
}
}

View 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;
}
}
}

View 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; }
}
}

View 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
{
}
}

View file

@ -0,0 +1,12 @@
namespace System
{
interface IEquatable
{
bool Equals(Object val);
}
interface IEquatable<T>
{
bool Equals(T val2);
}
}

View file

@ -0,0 +1,7 @@
namespace System
{
interface IFormatProvider
{
Object GetFormat(Type formatType);
}
}

View file

@ -0,0 +1,7 @@
namespace System
{
interface IFormattable
{
void ToString(String outString, String format, IFormatProvider formatProvider);
}
}

View file

@ -0,0 +1,7 @@
namespace System
{
interface IHashable
{
int GetHashCode();
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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!()));
}
}
}

View file

@ -0,0 +1,11 @@
using System;
namespace System.IO
{
public enum FileAccess
{
Read = 1,
Write = 2,
ReadWrite = 3,
}
}

View 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,
}
}

View 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,
}
}

View 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
}
}

View 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,
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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

View 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()
{
}
}
}

View 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
}
}

View 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()
{
}
}
}

View 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

View 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 weve 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;
}
}
}

View 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;
}
}
}

View 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

View file

@ -0,0 +1,4 @@
namespace System.IO
{
}

View 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;
}
}

View 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;
}
}
}

View 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;
}
}
}
}

View 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();
}
}
}

View 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()
{
}
}
}

View 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
}
}

View file

@ -0,0 +1,7 @@
namespace System
{
interface IPrintable
{
void Print(String outString);
}
}

View 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;
}
}
}

View 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