1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-20 17:08:00 +02:00

Moving corlib files out of "System" directory into root

This commit is contained in:
Brian Fiete 2019-09-19 05:46:35 -07:00
parent 4cd58262e4
commit 7dbfd15292
179 changed files with 3 additions and 0 deletions

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, Platform.BfpFileResult> 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(result);
}
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(StringView 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, .Files);
}
}
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,31 @@
// 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
{
// 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,212 @@
using System.Threading;
using System.Diagnostics.Contracts;
using System.Diagnostics;
namespace System.IO
{
abstract class FileStreamBase : Stream
{
protected Platform.BfpFile* mBfpFile;
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 override 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,285 @@
#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()
{
mSelectedPath.Clear();
mDescriptionText.Clear();
mSelectedPathNeedsCheck = false;
ShowNewFolderButton = true;
}
protected Result<DialogResult> RunDialog_New(Windows.HWnd hWndOwner, FolderBrowserDialog.COM_IFileDialog* fileDialog)
{
if (!mSelectedPath.IsEmpty)
{
COM_IShellItem* folderShellItem = null;
Windows.SHCreateItemFromParsingName(mSelectedPath.ToScopedNativeWChar!(), null, COM_IShellItem.sIID, (void**)&folderShellItem);
if (folderShellItem != null)
{
fileDialog.VT.SetDefaultFolder(fileDialog, folderShellItem);
folderShellItem.VT.Release(folderShellItem);
}
}
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 static Guid sIID = .(0x43826d1e, 0xe718, 0x42ee, 0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe);
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,600 @@
// 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);
}
/// Tests if the given path contains a root. A path is considered rooted
/// if it starts with a backslash ("\") or a drive letter and a colon (":").
public static bool IsPathRooted(StringView path)
{
CheckInvalidPathChars(path);
int length = path.Length;
if ((length >= 1 && (path[0] == DirectorySeparatorChar || path[0] == AltDirectorySeparatorChar)) || (length >= 2 && path[1] == VolumeSeparatorChar))
return true;
return false;
}
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,127 @@
#if BF_PLATFORM_WINDOWS
namespace System.IO
{
static class Shell
{
public struct COM_IPersist : Windows.COM_IUnknown
{
public struct VTable : COM_IUnknown.VTable
{
public function HResult(COM_IPersistFile* self, Guid* pClassID) GetClassID;
}
}
public struct COM_IPersistFile : COM_IPersist
{
public static Guid sIID = .(0x0000010b, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
public struct VTable : COM_IPersist.VTable
{
public function HResult(COM_IPersistFile* self) IsDirty;
public function HResult(COM_IPersistFile* self, char16* pszFileName) Load;
public function HResult(COM_IPersistFile* self, char16* pszFileName, Windows.IntBool remember) Save;
public function HResult(COM_IPersistFile* self, char16* pszFileName) SaveCompleted;
public function HResult(COM_IPersistFile* self, char16* pszName) GetCurFile;
}
public new VTable* VT
{
get
{
return (.)mVT;
}
}
}
public struct COM_IShellLink : Windows.COM_IUnknown
{
public static Guid sCLSID = .(0x00021401, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
public static Guid sIID = .(0x000214F9, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
struct IDLIST;
public struct VTable : Windows.COM_IUnknown.VTable
{
public function HResult(COM_IShellLink* self, char16* pszFile, int32 cch, Windows.NativeFindData* pfd, uint32 fFlags) GetPath;
public function HResult(COM_IShellLink* self, IDLIST** ppidl) GetIDList;
public function HResult(COM_IShellLink* self, IDLIST* pidl) SetIDList;
public function HResult(COM_IShellLink* self, char16* pszName, int32 cch) GetDescription;
public function HResult(COM_IShellLink* self, char16* pszName) SetDescription;
public function HResult(COM_IShellLink* self, char16* pszDir, int32 cch) GetWorkingDirectory;
public function HResult(COM_IShellLink* self, char16* pszDir) SetWorkingDirectory;
public function HResult(COM_IShellLink* self, char16* pszArgs, int32 cch) GetArguments;
public function HResult(COM_IShellLink* self, char16* pszArgs) SetArguments;
public function HResult(COM_IShellLink* self, uint16 *pwHotkey) GetHotkey;
public function HResult(COM_IShellLink* self, uint16 wHotkey) SetHotkey;
public function HResult(COM_IShellLink* self, int32 *piShowCmd) GetShowCmd;
public function HResult(COM_IShellLink* self, int32 iShowCmd) SetShowCmd;
public function HResult(COM_IShellLink* self, char16* pszIconPath, int32 cch, int32 *piIcon) GetIconLocation;
public function HResult(COM_IShellLink* self, char16* pszIconPath, int32 iIcon) SetIconLocation;
public function HResult(COM_IShellLink* self, char16* pszPathRel, uint32 dwReserved) SetRelativePath;
public function HResult(COM_IShellLink* self, Windows.HWnd hwnd, uint32 fFlags) Resolve;
public function HResult(COM_IShellLink* self, char16* pszFile) SetPath;
}
public new VTable* VT
{
get
{
return (.)mVT;
}
}
}
public enum ShellError
{
case AccessDenied;
case UnknownError;
public this(Windows.COM_IUnknown.HResult result)
{
switch (result)
{
case .E_ACCESSDENIED: this = .AccessDenied;
default: this = .UnknownError;
}
}
}
public static Result<void, ShellError> CreateShortcut(StringView linkPath, StringView targetPath, StringView arguments, StringView workingDirectory, StringView description)
{
COM_IShellLink* shellLink = null;
COM_IPersistFile* persistFile = null;
defer
{
if (persistFile != null)
persistFile.VT.Release(persistFile);
if (shellLink != null)
shellLink.VT.Release(shellLink);
}
mixin TryHR(Windows.COM_IUnknown.HResult result)
{
if (result != .OK)
{
return .Err(ShellError(result));
}
}
TryHR!(Windows.COM_IUnknown.CoCreateInstance(ref COM_IShellLink.sCLSID, null, .INPROC_SERVER, ref COM_IShellLink.sIID, (void**)&shellLink));
TryHR!(shellLink.VT.SetPath(shellLink, targetPath.ToScopedNativeWChar!()));
if (!arguments.IsEmpty)
TryHR!(shellLink.VT.SetArguments(shellLink, arguments.ToScopedNativeWChar!()));
if (!workingDirectory.IsEmpty)
TryHR!(shellLink.VT.SetWorkingDirectory(shellLink, workingDirectory.ToScopedNativeWChar!()));
if (!description.IsEmpty)
TryHR!(shellLink.VT.SetDescription(shellLink, description.ToScopedNativeWChar!()));
TryHR!(shellLink.VT.QueryInterface(shellLink, ref COM_IPersistFile.sIID, (void**)&persistFile));
TryHR!(persistFile.VT.Save(persistFile, linkPath.ToScopedNativeWChar!(), true));
return .Ok;
}
}
}
#endif

View file

@ -0,0 +1,325 @@
// 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 enum SeekKind
{
Absolute,
Relative,
FromEnd
}
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 virtual Result<void> Seek(int64 pos, SeekKind seekKind = .Absolute)
{
if (seekKind == .Absolute)
Position = pos;
else
Runtime.FatalError();
return .Ok;
}
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 characters 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,102 @@
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()
{
}
public this(Stream stream, Encoding encoding, int32 bufferSize, bool ownsStream = false)
{
Debug.Assert(encoding != null);
mStream = stream;
mEncoding = encoding;
mOwnsStream = ownsStream;
}
public Result<void, FileOpenError> Create(StringView fileName)
{
Debug.Assert(mStream == null);
var fileStream = new FileStream();
mStream = fileStream;
mEncoding = .UTF8;
mOwnsStream = true;
if (fileStream.Open(fileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite) case .Err(let err))
return .Err(err);
return .Ok;
}
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,78 @@
using System.IO;
namespace System.IO
{
class Substream : Stream
{
public bool mOwnsStream;
Stream mChildStream ~ { if (mOwnsStream) delete _; };
int64 mOffset;
int64 mLength;
public override int64 Position
{
get
{
return mChildStream.Position + mOffset;
}
set
{
mChildStream.Position = value + mOffset;
}
}
public override int64 Length
{
get
{
return mLength;
}
}
public override bool CanRead
{
get
{
return mChildStream.CanRead;
}
}
public override bool CanWrite
{
get
{
return mChildStream.CanWrite;
}
}
public this(Stream childStream, int64 offset, int64 length, bool ownsStream = false)
{
mChildStream = childStream;
mOffset = offset;
mLength = length;
mOwnsStream = ownsStream;
}
public override Result<int> TryRead(Span<uint8> data)
{
return mChildStream.TryRead(data);
}
public override Result<int> TryWrite(Span<uint8> data)
{
return mChildStream.TryWrite(data);
}
public override void Close()
{
mChildStream.Close();
}
public override void Flush()
{
mChildStream.Flush();
}
}
}

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