diff --git a/Tests/Cosmos.Compiler.Tests.Bcl/Helper/EqualityHelper.cs b/Tests/Cosmos.Compiler.Tests.Bcl/Helper/EqualityHelper.cs
index 248208968..5c49776e8 100644
--- a/Tests/Cosmos.Compiler.Tests.Bcl/Helper/EqualityHelper.cs
+++ b/Tests/Cosmos.Compiler.Tests.Bcl/Helper/EqualityHelper.cs
@@ -16,5 +16,43 @@ namespace Cosmos.Compiler.Tests.Bcl.Helper
else
return false;
}
+
+ ///
+ /// Utility method to test Byte[] equality.
+ ///
+ /// Byte array.
+ /// Byte array.
+ /// True if the elements in the arrays are equal otherwise false.
+ public static bool ByteArrayAreEquals(byte[] a1, byte[] a2)
+ {
+ if (ReferenceEquals(a1, a2))
+ {
+ //mDebugger.Send("a1 and a2 are the same Object");
+ return true;
+ }
+
+ if (a1 == null || a2 == null)
+ {
+ //mDebugger.Send("a1 or a2 is null so are different");
+ return false;
+ }
+
+ if (a1.Length != a2.Length)
+ {
+ //mDebugger.Send("a1.Length != a2.Length so are different");
+ return false;
+ }
+
+ for (int i = 0; i < a1.Length; i++)
+ {
+ if (a1[i] != a2[i])
+ {
+ //mDebugger.Send("In position " + i + " a byte is different");
+ return false;
+ }
+ }
+
+ return true;
+ }
}
}
diff --git a/Tests/Cosmos.Kernel.Tests.Fat/Kernel.cs b/Tests/Cosmos.Kernel.Tests.Fat/Kernel.cs
index 8f9b9d72c..a7fc81dd2 100644
--- a/Tests/Cosmos.Kernel.Tests.Fat/Kernel.cs
+++ b/Tests/Cosmos.Kernel.Tests.Fat/Kernel.cs
@@ -39,7 +39,7 @@ namespace Cosmos.Kernel.Tests.Fat
PathTest.Execute(mDebugger);
DirectoryTest.Execute(mDebugger);
#endif
- //FileTest.Execute(mDebugger);
+// FileTest.Execute(mDebugger);
#if false
FileStreamTest.Execute(mDebugger);
DirectoryInfoTest.Execute(mDebugger);
diff --git a/Tests/Cosmos.Kernel.Tests.Fat/System.IO/StreamWriterTest.cs b/Tests/Cosmos.Kernel.Tests.Fat/System.IO/StreamWriterTest.cs
index 4c3fec2cc..768b0c376 100644
--- a/Tests/Cosmos.Kernel.Tests.Fat/System.IO/StreamWriterTest.cs
+++ b/Tests/Cosmos.Kernel.Tests.Fat/System.IO/StreamWriterTest.cs
@@ -2,6 +2,7 @@
using Cosmos.TestRunner;
using Cosmos.Debug.Kernel;
using System;
+using System.Text;
namespace Cosmos.Kernel.Tests.Fat.System.IO
{
@@ -11,32 +12,63 @@ namespace Cosmos.Kernel.Tests.Fat.System.IO
/// Tests System.IO.StreamWriter plugs.
///
public static void Execute(Debugger mDebugger)
- {
+ {
+ string file = @"0:\test.txt";
+
mDebugger.Send("START TEST: StreamWriter:");
- mDebugger.Send("Create StreamWriter");
- using (var xSW = new StreamWriter(@"0:\test.txt"))
+ /*
+ * To Show that UTF-8 is effectively working you write in the file "Cosmos is wonderful!" in Japanase
+ * and read it again
+ */
+ var text = "Cosmos 素晴らしいです!";
+
+ using (var xSW = new StreamWriter(file))
{
-
- if (xSW != null)
- {
- try
- {
- mDebugger.Send("Start writing");
+ if (xSW == null)
+ Assert.IsTrue(false, $"Failed to create StreamWriter for file {file}");
- xSW.Write("0123");
- //xSW.Write("A line of text for testing\nSecond line");
- }
- catch (Exception e)
- {
- Assert.IsTrue(false, $"Couldn't write to file 0:\test.txt using StreamWriter {e.Message}");
- }
- }
- else
+ try
{
- Assert.IsTrue(false, @"Failed to create StreamWriter for file 0:\test.txt");
+ mDebugger.Send("Start writing");
+
+ xSW.Write(text);
}
-
+ catch
+ {
+ Assert.IsTrue(false, $"Couldn't write to file {file} using StreamWriter");
+ }
+ }
+ mDebugger.Send("END TEST");
+
+#if true
+ /* We use StreamReader() instead of File now it is more "correct" and we test 2 classes in one too! */
+ mDebugger.Send("START TEST: StreamReader:");
+ using (var xSR = new StreamReader(file))
+ {
+ if (xSR == null)
+ Assert.IsTrue(false, $"Failed to create StreamReader for file {file}");
+
+ try
+ {
+ mDebugger.Send("Start reading");
+ var readText = xSR.ReadToEnd();
+ Assert.IsTrue(text == readText, "Failed to write and read file");
+ }
+ catch
+ {
+ Assert.IsTrue(false, $"Couldn't read from file {file} using StreamReader");
+ }
+ }
+#endif
+
+ using (StreamReader xSR2 = new StreamReader(file, Encoding.UTF8, true))
+ {
+ mDebugger.Send("Reading using different Ctor!!!");
+ var readText2 = xSR2.ReadToEnd();
+ mDebugger.Send($"Read {readText2}");
+
+ Assert.IsTrue(text == readText2, "Failed to write and read file");
}
mDebugger.Send("END TEST");
diff --git a/Tests/Cosmos.TestRunner.Core/TestKernelSets.cs b/Tests/Cosmos.TestRunner.Core/TestKernelSets.cs
index d5eae9a34..e9fbfd384 100644
--- a/Tests/Cosmos.TestRunner.Core/TestKernelSets.cs
+++ b/Tests/Cosmos.TestRunner.Core/TestKernelSets.cs
@@ -17,17 +17,19 @@ namespace Cosmos.TestRunner.Core
{
//yield return typeof(BoxingTests.Kernel);
- yield return typeof(Cosmos.Compiler.Tests.TypeSystem.Kernel);
- yield return typeof(Cosmos.Compiler.Tests.Bcl.Kernel);
+// yield return typeof(Cosmos.Compiler.Tests.TypeSystem.Kernel);
+// yield return typeof(Cosmos.Compiler.Tests.Bcl.Kernel);
//yield return typeof(Cosmos.Compiler.Tests.Encryption.Kernel);
+#if false
yield return typeof(Cosmos.Compiler.Tests.Exceptions.Kernel);
yield return typeof(Cosmos.Compiler.Tests.LinqTests.Kernel);
yield return typeof(Cosmos.Compiler.Tests.MethodTests.Kernel);
yield return typeof(Cosmos.Compiler.Tests.SimpleWriteLine.Kernel);
yield return typeof(Cosmos.Compiler.Tests.SingleEchoTest.Kernel);
+#endif
/* Let's test only this for now */
yield return typeof(Cosmos.Kernel.Tests.Fat.Kernel);
- yield return typeof(Cosmos.Kernel.Tests.IO.Kernel);
+ //yield return typeof(Cosmos.Kernel.Tests.IO.Kernel);
#if false
yield return typeof(SimpleStructsAndArraysTest.Kernel);
yield return typeof(VGACompilerCrash.Kernel);
diff --git a/source/Cosmos.Core_Plugs/System/StringImpl.cs b/source/Cosmos.Core_Plugs/System/StringImpl.cs
index 035b80149..deb729227 100644
--- a/source/Cosmos.Core_Plugs/System/StringImpl.cs
+++ b/source/Cosmos.Core_Plugs/System/StringImpl.cs
@@ -1,4 +1,4 @@
-#define COSMOSDEBUG
+//#define COSMOSDEBUG
using System;
using System.Globalization;
using Cosmos.Common;
diff --git a/source/Cosmos.System2/Encoding/CosmosEncoding.cs b/source/Cosmos.System2/Encoding/CosmosEncoding.cs
index 36e09c2f9..fa61ab62e 100644
--- a/source/Cosmos.System2/Encoding/CosmosEncoding.cs
+++ b/source/Cosmos.System2/Encoding/CosmosEncoding.cs
@@ -6,8 +6,45 @@ namespace Cosmos.System2.Encoding
{
public abstract class CosmosEncoding
{
- public abstract Byte[] GetBytes(String s);
- public abstract String GetString(Byte[] bytes);
+ public abstract int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex);
+
+ //public abstract String GetString(Byte[] bytes);
+
public abstract int GetMaxByteCount(int ByteCount);
+
+ public virtual byte[] GetBytes(string s)
+ {
+ byte[] bytes = new byte[GetMaxByteCount(s.Length)];
+ char[] textToEncode = s.ToCharArray();
+ int nBytes;
+
+ nBytes = GetBytes(textToEncode, 0, textToEncode.Length, bytes, 0);
+
+ /*
+ * This could be not the fastest method (it creates a new array and then does a copy of the old
+ * until 'nBytes') but the alternative way was to call a version of GetBytes() that only counts
+ * the encoded bytes and allocate the array using the correct size and then call GetBytes().
+ * This is the approach used by the real Encoding class I'm unsure it is faster sincerely...
+ * in the end is doing the encoding two times!
+ *
+ * Remeber - in any case - that this is a temporary solution we should plug the real Encoding class...
+ */
+ Array.Resize(ref bytes, nBytes);
+ return bytes;
+ }
+
+ public abstract int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex);
+
+ public string GetString(byte[] bytes)
+ {
+ int numChar;
+ char[] chars = new char[bytes.Length];
+
+ numChar = GetChars(bytes, 0, bytes.Length, chars, 0);
+
+ Array.Resize(ref chars, numChar);
+
+ return new string(chars);
+ }
}
}
diff --git a/source/Cosmos.System2/Encoding/CosmosUTF8Encoding.cs b/source/Cosmos.System2/Encoding/CosmosUTF8Encoding.cs
index 8d2fa17eb..75a558970 100644
--- a/source/Cosmos.System2/Encoding/CosmosUTF8Encoding.cs
+++ b/source/Cosmos.System2/Encoding/CosmosUTF8Encoding.cs
@@ -8,32 +8,242 @@ namespace Cosmos.System2.Encoding
{
public class CosmosUTF8Encoding : CosmosEncoding
{
- public override byte[] GetBytes(string s)
- {
- Global.mFileSystemDebugger.SendInternal($"Encoding string {s}");
+ private const uint UNI_REPLACEMENT_CHAR = 0x0000FFFD;
+ private const uint UNI_SUR_HIGH_START = 0xD800;
+ private const uint UNI_SUR_HIGH_END = 0xDBFF;
+ private const uint UNI_SUR_LOW_START = 0xDC00;
+ private const uint UNI_SUR_LOW_END = 0xDFFF;
+ private const uint UNI_MAX_BMP = 0x0000FFFF;
+ private const uint UNI_MAX_UTF16 = 0x0010FFFF;
+ private const int halfShift = 10;
+ private const int halfBase = 0x0010000;
+ private const uint halfMask = 0x3FF;
- //byte[] xResult = new byte[GetMaxByteCount(s.Length)];
- List xResult = new List();
- /* Only Ascii for now */
- foreach (var aChar in s)
+ /*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+ private static int[] trailingBytesForUTF8 = new int[] {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+ };
+
+ /*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+ static uint[] offsetsFromUTF8 = new uint[] { 0x00000000, 0x00003080, 0x000E2080, 0x03C82080, 0xFA082080,
+ 0x82082080 };
+
+ private static int GetCharBytes(uint ch, byte[] bytes, int byteIndex, int bytePos)
+ {
+ int bytesToWrite;
+
+ // Filter out byte order marks and invalid character 0xFFFF
+ if ((ch == 0xFEFF) || (ch == 0xFFFE) || (ch == 0xFFFF))
{
- Global.mFileSystemDebugger.SendInternal($"Encoding char {aChar}");
- if (aChar > 0 || aChar < 127)
- xResult.Add((byte)aChar);
- else
- throw new ArgumentOutOfRangeException("Input string contains invalid characters for UTF-8");
+ return bytePos;
}
- return xResult.ToArray();
- //throw new NotImplementedException("GetBytes()");
+ /* Figure out how many bytes the result will require */
+ if (ch < 0x80) /* 0XXX XXXX one byte */
+ bytesToWrite = 1;
+ else if (ch < 0x800) /* 110X XXXX two bytes */
+ bytesToWrite = 2;
+ else if (ch < 0x10000) /* 1110 XXXX three bytes */
+ bytesToWrite = 3;
+ else if (ch < 0x110000) /* 1111 0XXX four bytes */
+ bytesToWrite = 4;
+ else /* Invalid Unicode sequence Encode it as UNI_REPLACEMENT_CHAR */
+ {
+ ch = UNI_REPLACEMENT_CHAR;
+ return GetCharBytes(ch, bytes, byteIndex, bytePos);
+ }
+
+ /* Check if there is sufficient space on bytes before writing on it */
+ if (bytes.Length - (byteIndex + bytePos) < bytesToWrite)
+ throw new ArgumentException("bytes has no sufficient space");
+
+ switch (bytesToWrite)
+ {
+ case 1:
+ bytes[byteIndex + bytePos + 0] = (byte)ch;
+ break;
+
+ case 2:
+ bytes[byteIndex + bytePos + 0] = (byte)(0xC0 | (ch >> 6));
+ bytes[byteIndex + bytePos + 1] = (byte)(0x80 | (ch & 0x3F));
+ break;
+
+ case 3:
+ bytes[byteIndex + bytePos + 0] = (byte)(0xE0 | (ch >> 12));
+ bytes[byteIndex + bytePos + 1] = (byte)(0x80 | ((ch >> 6) & 0x3F));
+ bytes[byteIndex + bytePos + 2] = (byte)(0x80 | (ch & 0x3F));
+ break;
+
+ case 4:
+ bytes[byteIndex + bytePos + 0] = (byte)(0xF0 | (ch >> 18));
+ bytes[byteIndex + bytePos + 1] = (byte)(0x80 | ((ch >> 12) & 0x3F));
+ bytes[byteIndex + bytePos + 2] = (byte)(0x80 | ((ch >> 6) & 0x3F));
+ bytes[byteIndex + bytePos + 3] = (byte)(0x80 | (ch & 0x3F));
+ break;
+ }
+
+ //bytePos += bytesToWrite;
+ return bytesToWrite;
}
- /* Some UFT-8 char can occupy 3 bytes */
- public override int GetMaxByteCount(int ByteCount) => 3 * ByteCount;
-
- public override string GetString(byte[] bytes)
+ private static uint HandleSurrogatePairs(uint SurrFirst, uint SurrSecond)
{
- throw new NotImplementedException("GetString()");
+ if (SurrSecond >= UNI_SUR_LOW_START && SurrSecond <= UNI_SUR_LOW_END)
+ {
+ return ((SurrFirst - UNI_SUR_HIGH_START) << halfShift)
+ + (SurrSecond - UNI_SUR_LOW_START) + halfBase;
+ }
+ else /* it's an unpaired high surrogate */
+ {
+ throw new ArgumentException("Source contains unpaired surrogate");
+ }
+ }
+
+ public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
+ {
+ if (chars == null)
+ {
+ Global.mFileSystemDebugger.SendInternal($"chars is null returning 0");
+ return 0;
+ }
+
+ if (charIndex == 0 && charCount == 0)
+ {
+ Global.mFileSystemDebugger.SendInternal($"charIndex and charCount both 0 returning 0");
+ return 0;
+ }
+
+ int bytePos = 0;
+
+ for (int i = charIndex; i < charCount; i++)
+ {
+ uint ch = chars[i];
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END)
+ {
+ /* There is the next part of the surrogate? */
+ if (chars.Length >= i + 1)
+ {
+ i++;
+ ch = HandleSurrogatePairs(ch, chars[i]);
+ }
+ else
+ throw new ArgumentException("Source contains unpaired surrogate");
+ }
+
+ bytePos += GetCharBytes(ch, bytes, byteIndex, bytePos);
+ }
+
+ return bytePos;
+ }
+
+ /* Some UFT-8 "character" can occupy 4 bytes */
+ public override int GetMaxByteCount(int ByteCount) => 4 * ByteCount;
+
+ private static uint GetCharFromUFT8(byte[] bytes, out int bytesConsumed, int bytePos)
+ {
+ //uint ch = bytes[bytePos];
+ uint ch = 0;
+
+ int UtfTrailingBytes = trailingBytesForUTF8[bytes[bytePos]];
+ int Uft8CharLen = UtfTrailingBytes + 1;
+ bytesConsumed = Uft8CharLen;
+
+ int i = bytePos;
+ /* We "consume" the bytes and do the needed bitmasking to obtain the corrisponding codepoint */
+ do
+ {
+ ch += bytes[i];
+ i++;
+ --Uft8CharLen;
+ if (Uft8CharLen != 0)
+ ch <<= 6;
+ } while (Uft8CharLen > 0);
+ ch -= offsetsFromUTF8[UtfTrailingBytes];
+
+ /* Target is a character <= 0xFFFF */
+ if (ch <= UNI_MAX_BMP)
+ {
+ /* Invalid surrugates */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
+ return UNI_REPLACEMENT_CHAR;
+ /* normal case */
+ else
+ return (char)ch;
+ }
+ else if (ch > UNI_MAX_UTF16)
+ {
+ return UNI_REPLACEMENT_CHAR;
+ }
+ /* surrogate pairs */
+ else
+ {
+ ushort lo = 0;
+ ushort hi = 0;
+ ch -= halfBase;
+ hi = (ushort)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ lo = (ushort)((ch & halfMask) + UNI_SUR_LOW_START);
+ /*
+ * We pack the two halves of the pair in an uint sadly we need to unpack them later
+ * the alternative was to make this function return an array of character that will be really
+ * used only in this case :-(
+ */
+ ch = (uint)((uint)hi << 16 | (uint)lo);
+
+ return ch;
+ }
+ }
+
+ public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
+ {
+ uint ch = 0;
+ int bytesConsumed = 0;
+ //for (i = byteIndex; i < byteCount; i++)
+ int numChar = 0;
+ int bytePos = byteIndex;
+ while (byteCount != 0)
+ {
+ ch = GetCharFromUFT8(bytes, out bytesConsumed, bytePos);
+ /* check that chars has sufficient space */
+ if (chars.Length < (charIndex + numChar))
+ throw new ArgumentException("chars has no sufficient space");
+
+ if (ch < UNI_SUR_HIGH_START)
+ chars[charIndex + numChar] = (char)ch;
+ else
+ {
+ /* Unpach the uint in the two paired surrugates */
+ char chHigh = (char)(ch >> 16);
+ char chLow = (char)(ch & 0xFFFF);
+ chars[charIndex + numChar] = chHigh;
+ chars[charIndex + numChar + 1] = chLow;
+ numChar++;
+ }
+
+ /* skip the part of 'bytes' we have already consumed */
+ byteCount -= bytesConsumed;
+ bytePos += bytesConsumed;
+ numChar++;
+ }
+
+ return numChar;
}
}
}
diff --git a/source/Cosmos.System2_Plugs/System/IO/CosmosFileSystem.cs b/source/Cosmos.System2_Plugs/System/IO/CosmosFileSystem.cs
index f6c8872e2..e1507f98e 100644
--- a/source/Cosmos.System2_Plugs/System/IO/CosmosFileSystem.cs
+++ b/source/Cosmos.System2_Plugs/System/IO/CosmosFileSystem.cs
@@ -89,15 +89,15 @@ namespace Cosmos.System_Plugs.System.IO
public static object /* FileStreamBase */ Open(object aThis, string fullPath, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, FileStream parent)
{
- Global.mFileSystemDebugger.SendInternal("In FileStream.InitializeStream");
+ Global.mFileSystemDebugger.SendInternal("In CosmosFileSystem.Open");
if (fullPath == null)
{
- Global.mFileSystemDebugger.SendInternal("In FileStream.Ctor: Path == null is true");
+ Global.mFileSystemDebugger.SendInternal("In CosmosFileSystem.Open: Path == null is true");
throw new ArgumentNullException("The file path cannot be null.");
}
if (fullPath.Length == 0)
{
- Global.mFileSystemDebugger.SendInternal("In FileStream.Ctor: Path.Length == 0 is true");
+ Global.mFileSystemDebugger.SendInternal("In CosmosFileSystem.Open: Path.Length == 0 is true");
throw new ArgumentException("The file path cannot be empty.");
}
@@ -108,6 +108,17 @@ namespace Cosmos.System_Plugs.System.IO
Stream aStream = null;
+ Global.mFileSystemDebugger.SendInternal($"Create Mode aPath {fullPath}");
+
+ var xEntry = VFSManager.CreateFile(fullPath);
+ if (xEntry == null)
+ {
+ return null;
+ }
+
+ aStream = VFSManager.GetFileStream(fullPath);
+
+#if false
switch (mode)
{
case FileMode.Append:
@@ -129,7 +140,8 @@ namespace Cosmos.System_Plugs.System.IO
case FileMode.Create:
Global.mFileSystemDebugger.SendInternal("Create Mode aPath will be overwritten if existing");
// TODO it seems that GetFileStream effectively Creates the file if not exist
- aStream = File.Create(fullPath);
+ //aStream = File.Create(fullPath);
+ aStream = VFSManager.GetFileStream(fullPath);
break;
case FileMode.CreateNew:
@@ -141,7 +153,8 @@ namespace Cosmos.System_Plugs.System.IO
Global.mFileSystemDebugger.SendInternal("CreateNew Mode with aPath not existing new file created");
// TODO it seems that GetFileStream effectively Creates the file if it does not exist
- aStream = File.Create(fullPath);
+ //aStream = File.Create(fullPath);
+ aStream = VFSManager.GetFileStream(fullPath);
break;
case FileMode.Open:
@@ -181,6 +194,7 @@ namespace Cosmos.System_Plugs.System.IO
Global.mFileSystemDebugger.SendInternal("The mode " + mode + "is out of range");
throw new ArgumentOutOfRangeException("The file mode is invalid");
}
+#endif
return aStream;
}
@@ -211,5 +225,37 @@ namespace Cosmos.System_Plugs.System.IO
}
}
}
+
+ public static void DeleteFile(object aThis, string fullPath)
+ {
+ Global.mFileSystemDebugger.SendInternal($"DeleteFile : fullPath = {fullPath}");
+ VFSManager.DeleteFile(fullPath);
+ }
+
+ public static void CopyFile(object aThis, string sourceFullPath, string destFullPath, bool overwrite)
+ {
+ Global.mFileSystemDebugger.SendInternal($"CopyFile {sourceFullPath} into {destFullPath}");
+
+ // The destination path may just be a directory into which the file should be copied.
+ // If it is, append the filename from the source onto the destination directory
+ if (Directory.Exists(destFullPath))
+ {
+ destFullPath = Path.Combine(destFullPath, Path.GetFileName(sourceFullPath));
+ }
+
+ // Copy the contents of the file from the source to the destination, creating the destination in the process
+ using (var src = new FileStream(sourceFullPath, FileMode.Open))
+ using (var dst = new FileStream(destFullPath, overwrite ? FileMode.Create : FileMode.CreateNew))
+ {
+ int xSize = (int)src.Length;
+ Global.mFileSystemDebugger.SendInternal($"size of {sourceFullPath} is {xSize} bytes");
+ byte[] content = new byte[xSize];
+ Global.mFileSystemDebugger.SendInternal($"content byte buffer allocated");
+ src.Read(content, 0, xSize);
+ Global.mFileSystemDebugger.SendInternal($"content byte buffer read");
+ dst.Write(content, 0, xSize);
+ Global.mFileSystemDebugger.SendInternal($"content byte buffer written");
+ }
+ }
}
}
diff --git a/source/Cosmos.System2_Plugs/System/IO/FileImpl.cs b/source/Cosmos.System2_Plugs/System/IO/FileImpl.cs
index 52b0a04f4..d5d456e37 100644
--- a/source/Cosmos.System2_Plugs/System/IO/FileImpl.cs
+++ b/source/Cosmos.System2_Plugs/System/IO/FileImpl.cs
@@ -9,6 +9,7 @@ using Cosmos.IL2CPU.API;
using Cosmos.IL2CPU.API.Attribs;
using Cosmos.System.FileSystem;
using Cosmos.System.FileSystem.VFS;
+using System.Text;
namespace Cosmos.System_Plugs.System.IO
{
@@ -16,6 +17,7 @@ namespace Cosmos.System_Plugs.System.IO
[Plug(Target = typeof(File))]
public static class FileImpl
{
+#if false
public static bool Exists(string aFile)
{
Global.mFileSystemDebugger.SendInternal("File.Exists:");
@@ -29,7 +31,17 @@ namespace Cosmos.System_Plugs.System.IO
return VFSManager.FileExists(aFile);
}
-
+#endif
+ public static string ReadAllText(string aFile)
+ {
+ string result;
+ using (StreamReader streamReader = new StreamReader(aFile, Encoding.UTF8, true))
+ {
+ result = streamReader.ReadToEnd();
+ }
+ return result;
+ }
+#if false
public static string ReadAllText(string aFile)
{
Global.mFileSystemDebugger.SendInternal("File.ReadAllText:");
@@ -55,7 +67,9 @@ namespace Cosmos.System_Plugs.System.IO
return xResultStr;
}
}
+#endif
+#if false
public static void WriteAllText(string aFile, string aText)
{
Global.mFileSystemDebugger.SendInternal("Creating stream with file " + aFile);
@@ -93,6 +107,7 @@ namespace Cosmos.System_Plugs.System.IO
}
}
+
public static void AppendAllText(string aFile, string aText)
{
Global.mFileSystemDebugger.SendInternal("Creating stream in Append Mode with file " + aFile);
@@ -106,6 +121,7 @@ namespace Cosmos.System_Plugs.System.IO
}
}
+
public static string[] ReadAllLines(string aFile)
{
String text = ReadAllText(aFile);
@@ -119,15 +135,34 @@ namespace Cosmos.System_Plugs.System.IO
return result;
}
-
+#endif
+
+ /*
+ * Plug needed for the usual issue that Array can not be converted in IEnumerable... it is starting
+ * to become annoying :-(
+ */
public static void WriteAllLines(string aFile, string[] contents)
{
- string text = String.Join(Environment.NewLine, contents);
-
+ if (aFile == null)
+ {
+ throw new ArgumentNullException("path");
+ }
+ if (contents == null)
+ {
+ throw new ArgumentNullException("contents");
+ }
+ if (aFile.Length == 0)
+ {
+ throw new ArgumentException("Empty", "aFile");
+ }
+
Global.mFileSystemDebugger.SendInternal("Writing contents");
- Global.mFileSystemDebugger.SendInternal(text);
-
- WriteAllText(aFile, text);
+
+ using (var xSW = new StreamWriter(aFile))
+ {
+ foreach (var current in contents)
+ xSW.WriteLine(current);
+ }
}
public static byte[] ReadAllBytes(string aFile)
@@ -155,40 +190,14 @@ namespace Cosmos.System_Plugs.System.IO
}
}
+#if false
public static void Copy(string srcFile, string destFile)
{
- try
+ using (var xFS = new FileStream(srcFile, FileMode.Open))
{
- byte[] srcFileBytes = File.ReadAllBytes(srcFile);
- File.WriteAllBytes(destFile, srcFileBytes);
- }
- catch (IOException ioEx)
- {
- throw new IOException("File Copy", ioEx);
- }
- }
-
- public static void Copy(string srcFile, string destFile, bool overwriting)
- {
- if (overwriting)
- {
- if (File.Exists(destFile))
- {
- File.Delete(destFile);
- }
-
- Copy(srcFile, destFile);
- }
- else
- {
- if (!File.Exists(destFile))
- {
- Copy(srcFile, destFile);
- }
- else
- {
- throw new IOException("destFileName exists and overwrite is false.");
- }
+ var xBuff = new byte[(int)xFS.Length];
+ var yFS = new FileStream(destFile, FileMode.Create);
+ yFS.Write(xBuff, 0, xBuff.Length);
}
}
@@ -199,6 +208,7 @@ namespace Cosmos.System_Plugs.System.IO
VFSManager.DeleteFile(xFullPath);
}
+
public static FileStream Create(string aFile)
{
Global.mFileSystemDebugger.SendInternal("File.Create:");
@@ -216,5 +226,6 @@ namespace Cosmos.System_Plugs.System.IO
return new FileStream(aFile, FileMode.Open);
}
+#endif
}
}
diff --git a/source/Cosmos.System2_Plugs/System/IO/FileStreamImpl.cs b/source/Cosmos.System2_Plugs/System/IO/FileStreamImpl.cs
index 9c6c646a1..1f85d8078 100644
--- a/source/Cosmos.System2_Plugs/System/IO/FileStreamImpl.cs
+++ b/source/Cosmos.System2_Plugs/System/IO/FileStreamImpl.cs
@@ -19,19 +19,37 @@ namespace Cosmos.System_Plugs.System.IO
// public static unsafe void Ctor(String aThis, [FieldAccess(Name = "$$Storage$$")]ref Char[] aStorage, Char[] aChars, int aStartIndex, int aLength,
- public static void Ctor(FileStream aThis, string aPathname, FileMode aMode,
- [FieldAccess(Name = InnerStreamFieldId)] ref Stream innerStream)
+ private static void Init(string aPathname, FileMode aMode, ref Stream innerStream)
{
Global.mFileSystemDebugger.SendInternal("FileStream.Ctor:");
innerStream = InitializeStream(aPathname, aMode);
}
+
+ public static void Ctor(FileStream aThis, string aPathname, FileMode aMode,
+ [FieldAccess(Name = InnerStreamFieldId)] ref Stream innerStream)
+ {
+ Init(aPathname, aMode, ref innerStream);
+#if false
+ Global.mFileSystemDebugger.SendInternal("FileStream.Ctor:");
+
+ innerStream = InitializeStream(aPathname, aMode);
+#endif
+ }
+
public static void CCtor()
{
// plug cctor as it (indirectly) uses Thread.MemoryBarrier()
}
+ public static void Ctor(FileStream aThis, string aPathname, FileMode aMode, FileAccess access,
+ FileShare share, int bufferSize, FileOptions options,
+ [FieldAccess(Name = InnerStreamFieldId)] ref Stream innerStream)
+ {
+ Init(aPathname, aMode, ref innerStream);
+ }
+
public static int Read(FileStream aThis, byte[] aBuffer, int aOffset, int aCount,
[FieldAccess(Name = InnerStreamFieldId)] ref Stream innerStream)
{
@@ -48,7 +66,7 @@ namespace Cosmos.System_Plugs.System.IO
public static void Write(FileStream aThis, byte[] aBuffer, int aOffset, int aCount,
[FieldAccess(Name = InnerStreamFieldId)] ref Stream innerStream)
{
- Global.mFileSystemDebugger.SendInternal("FileStream.Write:");
+ Global.mFileSystemDebugger.SendInternal($"FileStream.Write: aOffset {aOffset} aCount {aCount}");
innerStream.Write(aBuffer, aOffset, aCount);
}
@@ -83,6 +101,7 @@ namespace Cosmos.System_Plugs.System.IO
public static void Flush(FileStream aThis,
[FieldAccess(Name = InnerStreamFieldId)] ref Stream innerStream)
{
+ Global.mFileSystemDebugger.SendInternal($"In FileStream.InitializeStream Flush()");
innerStream.Flush();
}
@@ -143,7 +162,13 @@ namespace Cosmos.System_Plugs.System.IO
case FileMode.Create:
Global.mFileSystemDebugger.SendInternal("Create Mode aPath will be overwritten if existing");
// TODO it seems that GetFileStream effectively Creates the file if not exist
- aStream = File.Create(aPath);
+ var xEntry = VFSManager.CreateFile(aPath);
+ if (xEntry == null)
+ {
+ return null;
+ }
+ //aStream = File.Create(aPath);
+ aStream = VFSManager.GetFileStream(aPath);
break;
case FileMode.CreateNew:
diff --git a/source/Cosmos.System2_Plugs/System/IO/PathInternalImpl.cs b/source/Cosmos.System2_Plugs/System/IO/PathInternalImpl.cs
index df417b683..2bae1507e 100644
--- a/source/Cosmos.System2_Plugs/System/IO/PathInternalImpl.cs
+++ b/source/Cosmos.System2_Plugs/System/IO/PathInternalImpl.cs
@@ -2,7 +2,7 @@
using Cosmos.IL2CPU.API.Attribs;
using Cosmos.System;
-namespace Cosmos.Kernel.Tests.Fat.System.IO
+namespace Cosmos.System_Plugs.System.IO
{
[Plug(TargetName = "System.IO.PathInternal, System.IO.FileSystem")]
public static class PathInternalImpl
diff --git a/source/Cosmos.System2_Plugs/System/IO/StreamWriterImpl.cs b/source/Cosmos.System2_Plugs/System/IO/StreamWriterImpl.cs
index 484cc3789..7f58f6f93 100644
--- a/source/Cosmos.System2_Plugs/System/IO/StreamWriterImpl.cs
+++ b/source/Cosmos.System2_Plugs/System/IO/StreamWriterImpl.cs
@@ -4,6 +4,7 @@ using System.IO;
using Cosmos.System;
using Cosmos.IL2CPU.API.Attribs;
using Cosmos.System2.Encoding;
+using System.Text;
namespace Cosmos.System_Plugs.System.IO
{
@@ -11,38 +12,19 @@ namespace Cosmos.System_Plugs.System.IO
[Plug(Target = typeof(StreamWriter))]
public static class StreamWriterImpl
{
+ private const string StreamFieldId = "System.IO.Stream System.IO.StreamWriter._stream";
+ private const string CharPosFieldId = "System.Int32 System.IO.StreamWriter._charPos";
+ private const string CharLenFieldId = "System.Int32 System.IO.StreamWriter._charLen";
+ private const string CharBufferFieldId = "System.Char[] System.IO.StreamWriter._charBuffer";
+ private const string ByteBufferFieldId = "System.Byte[] System.IO.StreamWriter._byteBuffer";
-#if false
- public static void Ctor(StreamWriter aThis, string path)
- {
- throw new NotImplementedException("StreamWriter Ctor(String path)");
- }
-#endif
private static CosmosEncoding FileEncoding;
//private static Stream InnerStream;
-#if false
- private static void Init(Stream stream, CosmosEncoding encoding)
+ private static void Init(String path, bool append, ref Stream stream, CosmosEncoding encoding,
+ ref char[] charBuffer, int bufferSize, ref byte[] byteBuffer, bool shouldLeaveOpen,
+ ref char[] CoreNewLine)
{
- //InnerStream = stream;
- FileEncoding = encoding;
- }
-#endif
- private static void Init(CosmosEncoding encoding, ref char[] charBuffer, int bufferSize, ref byte[] byteBuffer, bool shouldLeaveOpen)
- {
- FileEncoding = encoding;
- charBuffer = new char[bufferSize];
- byteBuffer = new byte[bufferSize * FileEncoding.GetMaxByteCount(bufferSize)];
- }
-
- public static void Ctor(StreamWriter aThis, string path,
- [FieldAccess(Name = "System.IO.Stream System.IO.StreamWriter._stream")] ref Stream _stream,
- [FieldAccess(Name = "System.Int32 System.IO.StreamWriter._charPos")] ref int _charPos,
- [FieldAccess(Name = "System.Char[] System.IO.StreamWriter._charBuffer")] ref char[] _charBuffer,
- [FieldAccess(Name = "System.Byte[] System.IO.StreamWriter._byteBuffer")] ref byte[] _byteBuffer
- )
- {
- Global.mFileSystemDebugger.SendInternal($"StreamWriter.Ctor() with path {path}");
if (path == null)
{
throw new ArgumentNullException("path");
@@ -52,47 +34,76 @@ namespace Cosmos.System_Plugs.System.IO
throw new ArgumentException("Empty path");
}
- _stream = new FileStream(path, FileMode.Create);
- Init(new CosmosUTF8Encoding(), ref _charBuffer, 128, ref _byteBuffer, false);
+ Global.mFileSystemDebugger.SendInternal($"StreamWriter.Init() with path {path} append {append} bufferSize {bufferSize}");
+ stream = new FileStream(path, append ? FileMode.Append : FileMode.Create);
+ FileEncoding = encoding;
+ charBuffer = new char[bufferSize];
+ byteBuffer = new byte[FileEncoding.GetMaxByteCount(bufferSize)];
+ CoreNewLine = new char[] { '\n', '\r' };
+ }
+
+ /*
+ * This constructor is really plugged only to enforce our simplified version of UTF8 encoding using other
+ * enconding will be silently ignored (I liked to check for this and throw Exception but == operator
+ * does not work with Encoding neither Equals)
+ */
+ public static void Ctor(StreamWriter aThis, string path, bool append, Encoding encoding, int bufferSize,
+ [FieldAccess(Name = StreamFieldId)] ref Stream _stream,
+ [FieldAccess(Name = CharPosFieldId)] ref int _charPos,
+ [FieldAccess(Name = CharLenFieldId)] ref int _charLen,
+ [FieldAccess(Name = CharBufferFieldId)] ref char[] _charBuffer,
+ [FieldAccess(Name = ByteBufferFieldId)] ref byte[] _byteBuffer,
+ [FieldAccess(Name = "System.Char[] System.IO.TextWriter.CoreNewLine")] ref char[] CoreNewLine
+ )
+ {
+#if false
+ //if (!Equals(encoding, Encoding.UTF8))
+ if (!encoding.Equals(Encoding.UTF8))
+ throw new NotImplementedException("Only UFT8 Encoding implemented");
+#endif
+
+ Global.mFileSystemDebugger.SendInternal($"StreamWriter.Ctor() with path {path} append {append} Encoding and bufferSize {bufferSize}");
+ Init(path, append, ref _stream, new CosmosUTF8Encoding(), ref _charBuffer, _charLen = bufferSize, ref _byteBuffer, false, ref CoreNewLine);
}
public static void Flush(StreamWriter aThis, bool flushStream, bool flushEncoder,
- [FieldAccess(Name = "System.IO.Stream System.IO.StreamWriter._stream")] ref Stream _stream,
- [FieldAccess(Name = "System.Int32 System.IO.StreamWriter._charPos")] ref int _charPos,
- [FieldAccess(Name = "System.Char[] System.IO.StreamWriter._charBuffer")] ref char[] _charBuffer,
- [FieldAccess(Name = "System.Byte[] System.IO.StreamWriter._byteBuffer")] ref byte[] _byteBuffer
+ [FieldAccess(Name = StreamFieldId)] ref Stream _stream,
+ [FieldAccess(Name = CharPosFieldId)] ref int _charPos,
+ [FieldAccess(Name = CharBufferFieldId)] ref char[] _charBuffer,
+ [FieldAccess(Name = ByteBufferFieldId)] ref byte[] _byteBuffer
)
{
- Global.mFileSystemDebugger.SendInternal($"_charPos is {_charPos}");
- // Debug code why is not working?
- if (_charBuffer == null)
- Global.mFileSystemDebugger.SendInternal("_charBuffer is NULL!");
- else if (_charBuffer.Length == 0)
- Global.mFileSystemDebugger.SendInternal("_charBuffer is Empty!");
- /* First 4 chars should be '0', '1', '2' and '3' */
- else
+ if (_stream == null)
{
- Global.mFileSystemDebugger.SendInternal("Printing first 4 chars of _charBuffer: ");
- Global.mFileSystemDebugger.SendInternal(_charBuffer[0]);
- Global.mFileSystemDebugger.SendInternal(_charBuffer[1]);
- Global.mFileSystemDebugger.SendInternal(_charBuffer[2]);
- Global.mFileSystemDebugger.SendInternal(_charBuffer[3]);
+ throw new ObjectDisposedException(null, "Object already disposed");
+ }
+ if (_charPos == 0 && !flushStream && !flushEncoder)
+ {
+ return;
}
+ if (_charBuffer == null)
+ return;
+ if (_charBuffer.Length == 0)
+ return;
-#if false
- Global.mFileSystemDebugger.SendInternal($"StreamWriter.Flush() with _charPos {_charPos} and _charBuffer{new String(_charBuffer)}");
+ int numBytes = FileEncoding.GetBytes(_charBuffer, 0, _charPos, _byteBuffer, 0);
+ _charPos = 0;
- FileEncoding.GetBytes(new string(_charBuffer));
- _stream.Write(_byteBuffer, 0, _byteBuffer.Length);
- _stream.Flush();
-#endif
- throw new NotImplementedException("Flush()");
+ if (numBytes > 0)
+ {
+ Global.mFileSystemDebugger.SendInternal($"numBytes is {numBytes} doing Write...");
+ _stream.Write(_byteBuffer, 0, numBytes);
+ Global.mFileSystemDebugger.SendInternal("Write done!");
+ }
+
+ Global.mFileSystemDebugger.SendInternal("Flush() ended");
+
+ //_stream.Flush();
}
public static void Cctor()
{
}
}
-
}