Compare commits
38 Commits
ea03818fe4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d55c1c533 | |||
| e4ee03364c | |||
| c77b3e0742 | |||
| e391f0238a | |||
| 823169ca91 | |||
| ee20c9c5ec | |||
| af658b1656 | |||
| 26c118195c | |||
| c461418185 | |||
| 29cde170c6 | |||
| 448161239e | |||
| a4c2ae3e28 | |||
| 1eb208cba3 | |||
| 2b5d6b6a54 | |||
| 386d71260c | |||
| dc35725b64 | |||
| 7d814ee4cb | |||
| 9082d7a4d0 | |||
| f9e4e533bb | |||
| 4da75bfa8a | |||
| cfa26ce807 | |||
| f2cdfc86b7 | |||
| fa9c5ac689 | |||
| 613b11e88c | |||
| 052169df1c | |||
| 826c11aa55 | |||
| 7fccb3810f | |||
| 4c08ad0064 | |||
| 2c790f9b29 | |||
| 359c8e25ca | |||
| 249627dc78 | |||
| c31370f37a | |||
| 4b33339d3a | |||
| 245f631ed5 | |||
| 47cc208cc8 | |||
| eef912d4c5 | |||
| 5a55a69069 | |||
| 5297231205 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,6 +18,7 @@ nuget/
|
||||
.idea/
|
||||
.editorconfig
|
||||
*.user
|
||||
*.DotSettings
|
||||
|
||||
#backups
|
||||
.old*/
|
||||
|
||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
||||
[submodule "DTLib.Ben.Demystifier"]
|
||||
path = DTLib.Ben.Demystifier
|
||||
url = https://github.com/Timerix22/DTLib.Ben.Demystifier.git
|
||||
[submodule "DTLib.Demystifier"]
|
||||
path = DTLib.Demystifier
|
||||
url = https://timerix.ddns.net:3322/Timerix/DTLib.Demystifier.git
|
||||
[submodule "DTLib.XXHash"]
|
||||
path = DTLib.XXHash
|
||||
url = https://timerix.ddns.net:3322/Timerix/DTLib.XXHash.git
|
||||
|
||||
1
DTLib.Demystifier
Submodule
1
DTLib.Demystifier
Submodule
Submodule DTLib.Demystifier added at 4eaade6e92
@@ -1,39 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!--package info-->
|
||||
<PackageId>DTLib.Dtsod</PackageId>
|
||||
<Version>1.3.3</Version>
|
||||
<Authors>Timerix</Authors>
|
||||
<Description>Definitely not json</Description>
|
||||
<RepositoryType>GIT</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/Timerix22/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/Timerix22/DTLib</PackageProjectUrl>
|
||||
<Configuration>Release</Configuration>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<!--compilation properties-->
|
||||
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<DebugType>embedded</DebugType>
|
||||
<!--language features-->
|
||||
<LangVersion>11</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--external dependencies-->
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--DTLib dependencies-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ProjectReference Include="..\DTLib\DTLib.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib" Version="1.3.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--project files-->
|
||||
<ItemGroup>
|
||||
<Compile Remove="Experimental/**/*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,328 +0,0 @@
|
||||
namespace DTLib.Dtsod;
|
||||
|
||||
// v21
|
||||
// парсер теперь не может игнорировать комменты, потом починю
|
||||
// теперь числовые значения конвертируются в правильный тип, а не в int64/uint64 (новый вариант switch из c#9.0 делал какую-то дичь)
|
||||
// исправлены некоторые другие баги
|
||||
|
||||
public class DtsodV21 : Dictionary<string, dynamic>, IDtsod
|
||||
{
|
||||
public DtsodVersion Version { get; } = DtsodVersion.V21;
|
||||
|
||||
public IDictionary<string, dynamic> ToDictionary() => this;
|
||||
|
||||
readonly string Text;
|
||||
|
||||
//public Dictionary<string, dynamic> Values { get; set; }
|
||||
public DtsodV21(string text)
|
||||
{
|
||||
Text = text;
|
||||
foreach (KeyValuePair<string, dynamic> pair in Parse(text))
|
||||
Add(pair.Key, pair.Value);
|
||||
}
|
||||
public DtsodV21(IDictionary<string, dynamic> rawDict)
|
||||
{
|
||||
Text = "";
|
||||
foreach (KeyValuePair<string, dynamic> pair in rawDict)
|
||||
Add(pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
|
||||
// выдаёт Exception
|
||||
public new dynamic this[string key]
|
||||
{
|
||||
get => TryGetValue(key, out dynamic value) ? value : throw new Exception($"Dtsod[{key}] key not found");
|
||||
set
|
||||
{
|
||||
if (!TrySetValue(key, value)) throw new Exception($"Dtsod[{key}] key not found");
|
||||
}
|
||||
}
|
||||
|
||||
// не выдаёт KeyNotFoundException
|
||||
public new bool TryGetValue(string key, out dynamic value)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = base[key];
|
||||
return true;
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public bool TrySetValue(string key, dynamic value)
|
||||
{
|
||||
try
|
||||
{
|
||||
base[key] = value;
|
||||
return true;
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => Text;
|
||||
|
||||
enum ValueType
|
||||
{
|
||||
List,
|
||||
Complex,
|
||||
String,
|
||||
Default
|
||||
}
|
||||
|
||||
Dictionary<string, dynamic> Parse(string text)
|
||||
{
|
||||
Dictionary<string, dynamic> parsed = new();
|
||||
int i = 0;
|
||||
for (; i < text.Length; i++)
|
||||
ReadName();
|
||||
return parsed;
|
||||
|
||||
// СЛОМАНО
|
||||
/*void ReadCommentLine()
|
||||
{
|
||||
for (; i < text.Length && text[i] != '\n'; i++) DebugNoTime("h", text[i].ToString());
|
||||
}*/
|
||||
|
||||
void ReadName()
|
||||
{
|
||||
|
||||
bool isListElem = false;
|
||||
dynamic value;
|
||||
StringBuilder defaultNameBuilder = new();
|
||||
|
||||
for (; i < text.Length; i++)
|
||||
{
|
||||
switch (text[i])
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case ':':
|
||||
i++;
|
||||
string name = defaultNameBuilder.ToString();
|
||||
value = ReadValue();
|
||||
// если value это null, эта строка выдавала ошибку
|
||||
//DebugNoTime("c", $"parsed.Add({name}, {value} { value.GetType() })");
|
||||
if (isListElem)
|
||||
{
|
||||
if (!parsed.ContainsKey(name))
|
||||
parsed.Add(name, new List<dynamic>());
|
||||
parsed[name].Add(value);
|
||||
}
|
||||
else
|
||||
parsed.Add(name, value);
|
||||
return;
|
||||
// строка, начинающаяся с # будет считаться комментом
|
||||
case '#':
|
||||
//ReadCommentLine();
|
||||
break;
|
||||
case '}':
|
||||
throw new Exception("Parse.ReadName() error: unexpected '}' at " + i + " char");
|
||||
// если $ перед названием параметра поставить, значение value добавится в лист с названием name
|
||||
case '$':
|
||||
if (defaultNameBuilder.ToString().Length != 0)
|
||||
throw new Exception("Parse.ReadName() error: unexpected '$' at " + i + " char");
|
||||
isListElem = true;
|
||||
break;
|
||||
case ';':
|
||||
throw new Exception("Parse.ReadName() error: unexpected ';' at " + i + " char");
|
||||
default:
|
||||
defaultNameBuilder.Append(text[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic ReadValue()
|
||||
{
|
||||
ValueType type = ValueType.Default;
|
||||
dynamic value = null;
|
||||
|
||||
string ReadString()
|
||||
{
|
||||
i++;
|
||||
StringBuilder valueBuilder = new();
|
||||
valueBuilder.Append('"');
|
||||
for (; text[i] != '"' || text[i - 1] == '\\'; i++)
|
||||
{
|
||||
valueBuilder.Append(text[i]);
|
||||
}
|
||||
valueBuilder.Append('"');
|
||||
type = ValueType.String;
|
||||
return valueBuilder.ToString();
|
||||
}
|
||||
|
||||
List<dynamic> ReadList()
|
||||
{
|
||||
i++;
|
||||
List<dynamic> output = new();
|
||||
StringBuilder valueBuilder = new();
|
||||
for (; text[i] != ']'; i++)
|
||||
{
|
||||
switch (text[i])
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case ',':
|
||||
ParseValueToRightType(valueBuilder.ToString());
|
||||
output.Add(value);
|
||||
valueBuilder.Clear();
|
||||
break;
|
||||
default:
|
||||
valueBuilder.Append(text[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valueBuilder.Length > 0)
|
||||
{
|
||||
ParseValueToRightType(valueBuilder.ToString());
|
||||
output.Add(value);
|
||||
}
|
||||
type = ValueType.List;
|
||||
return output;
|
||||
}
|
||||
|
||||
Dictionary<string, dynamic> ReadComplex()
|
||||
{
|
||||
StringBuilder valueBuilder = new();
|
||||
int balance = 1;
|
||||
i++;
|
||||
for (; balance != 0; i++)
|
||||
{
|
||||
switch (text[i])
|
||||
{
|
||||
case '"':
|
||||
valueBuilder.Append(ReadString());
|
||||
break;
|
||||
case '}':
|
||||
balance--;
|
||||
if (balance != 0)
|
||||
valueBuilder.Append(text[i]);
|
||||
break;
|
||||
case '{':
|
||||
balance++;
|
||||
valueBuilder.Append(text[i]);
|
||||
break;
|
||||
default:
|
||||
valueBuilder.Append(text[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
i--; // i++ в for выполняется даже когда balance == 0, то есть text[i] получается == ;, что ломает всё
|
||||
type = ValueType.Complex;
|
||||
return Parse(valueBuilder.ToString());
|
||||
}
|
||||
|
||||
void ParseValueToRightType(string stringValue)
|
||||
{
|
||||
|
||||
switch (stringValue)
|
||||
{
|
||||
|
||||
// bool
|
||||
case "true":
|
||||
case "false":
|
||||
value = stringValue.ToBool();
|
||||
break;
|
||||
// null
|
||||
case "null":
|
||||
value = null;
|
||||
break;
|
||||
default:
|
||||
if (stringValue.Contains('"'))
|
||||
value = stringValue.Remove(stringValue.Length - 1).Remove(0, 1);
|
||||
// double
|
||||
else if (stringValue.Contains('.'))
|
||||
value = stringValue.ToDouble();
|
||||
// ushort; ulong; uint
|
||||
else if (stringValue.Length > 2 && stringValue[stringValue.Length - 2] == 'u')
|
||||
{
|
||||
switch (stringValue[stringValue.Length - 1])
|
||||
{
|
||||
case 's':
|
||||
value = stringValue.Remove(stringValue.Length - 2).ToUShort();
|
||||
break;
|
||||
case 'i':
|
||||
value = stringValue.Remove(stringValue.Length - 2).ToUInt();
|
||||
break;
|
||||
case 'l':
|
||||
value = stringValue.Remove(stringValue.Length - 2).ToULong();
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Dtsod.Parse.ReadValue() error: value= wrong type <u{stringValue[stringValue.Length - 1]}>");
|
||||
}
|
||||
}
|
||||
// short; long; int
|
||||
else
|
||||
switch (stringValue[stringValue.Length - 1])
|
||||
{
|
||||
case 's':
|
||||
value = stringValue.Remove(stringValue.Length - 1).ToShort();
|
||||
break;
|
||||
case 'l':
|
||||
value = stringValue.Remove(stringValue.Length - 1).ToLong();
|
||||
break;
|
||||
default:
|
||||
value = stringValue.ToInt();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder defaultValueBuilder = new();
|
||||
for (; i < text.Length; i++)
|
||||
{
|
||||
switch (text[i])
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case '"':
|
||||
value = ReadString();
|
||||
break;
|
||||
case ';':
|
||||
switch (type)
|
||||
{
|
||||
case ValueType.String:
|
||||
ParseValueToRightType(value);
|
||||
break;
|
||||
case ValueType.Default:
|
||||
ParseValueToRightType(defaultValueBuilder.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
case '[':
|
||||
value = ReadList();
|
||||
break;
|
||||
case '{':
|
||||
value = ReadComplex();
|
||||
break;
|
||||
// строка, начинающаяся с # будет считаться комментом
|
||||
case '#':
|
||||
//ReadCommentLine();
|
||||
break;
|
||||
default:
|
||||
defaultValueBuilder.Append(text[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw new Exception("Dtsod.Parse.ReadValue error: wtf it's the end of function");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace DTLib.Dtsod;
|
||||
|
||||
public enum DtsodVersion : byte
|
||||
{
|
||||
V21 = 21,
|
||||
V22 = 22,
|
||||
V23 = 23,
|
||||
V24 = 24,
|
||||
V25,
|
||||
#if DEBUG
|
||||
V30 = 30
|
||||
#endif
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
namespace DTLib.Dtsod.ClassSerializer;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
|
||||
public class DtsodSerializableAttribute : Attribute
|
||||
{
|
||||
public DtsodVersion Version;
|
||||
public DtsodSerializableAttribute(DtsodVersion ver) => Version = ver;
|
||||
}
|
||||
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class SerializeAsAttribute: Attribute
|
||||
{
|
||||
public string Key;
|
||||
/// <summary>
|
||||
/// use it only for base types
|
||||
/// </summary>
|
||||
/// <param name="key">name the field will be serialized as</param>
|
||||
public SerializeAsAttribute(string key) => Key = key;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class SerializationMethodAttribute<TSource,TSerialized> : Attribute
|
||||
{
|
||||
public Func<TSource, TSerialized> Method;
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="method">how to serialize field</param>
|
||||
public SerializationMethodAttribute(Func<TSource, TSerialized> method) => Method = method;
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class DeserializationMethodAttribute<TSource,TSerialized> : Attribute
|
||||
{
|
||||
public Func<TSerialized, TSource> Method;
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="method">how to deserialize field</param>
|
||||
public DeserializationMethodAttribute( Func<TSerialized, TSource> method) => Method = method;
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
using System.Globalization;
|
||||
using DTLib.Dtsod.ClassSerializer;
|
||||
|
||||
namespace DTLib.Dtsod;
|
||||
|
||||
public class DtsodV30 : DtsodDict<string, dynamic>, IDtsod
|
||||
{
|
||||
public DtsodVersion Version { get; } = DtsodVersion.V30;
|
||||
|
||||
public IDictionary<string, dynamic> ToDictionary() => this;
|
||||
|
||||
public DtsodV30() : base() => UpdateLazy();
|
||||
public DtsodV30(IDictionary<string, dynamic> dict) : base(dict) => UpdateLazy();
|
||||
public DtsodV30(string serialized) : this() => Append(Deserialize(serialized));
|
||||
|
||||
static IDictionary<string, dynamic> Deserialize(string text)
|
||||
{
|
||||
char c;
|
||||
int i = -1; // ++i в ReadType
|
||||
StringBuilder b = new();
|
||||
|
||||
|
||||
Type ReadType()
|
||||
{
|
||||
|
||||
while (i < text.Length - 1)
|
||||
{
|
||||
c = text[++i];
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case '#':
|
||||
SkipComment();
|
||||
break;
|
||||
case ':':
|
||||
string _type = b.ToString();
|
||||
b.Clear();
|
||||
return TypeHelper.Instance.TypeFromString(_type);
|
||||
case '=':
|
||||
case '"':
|
||||
case '\'':
|
||||
case ';':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}");
|
||||
default:
|
||||
b.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("DtsodV30.Deserialize.ReadType() error: end of text\ntext:\n" + text);
|
||||
}
|
||||
|
||||
string ReadName()
|
||||
{
|
||||
while (i < text.Length - 1)
|
||||
{
|
||||
c = text[++i];
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case '#':
|
||||
SkipComment();
|
||||
break;
|
||||
case '=':
|
||||
string _name = b.ToString();
|
||||
b.Clear();
|
||||
return _name;
|
||||
case ':':
|
||||
case '"':
|
||||
case '\'':
|
||||
case ';':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}");
|
||||
default:
|
||||
b.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("DtsodV30.Deserialize.ReadName() error: end of text\ntext:\n" + text);
|
||||
}
|
||||
|
||||
object ReadValue(ref bool endoflist)
|
||||
{
|
||||
void ReadString()
|
||||
{
|
||||
c = text[++i]; //пропускает начальный символ '"'
|
||||
while ((c != '"' && c != '\'') || (text[i - 1] == '\\' && text[i - 2] != '\\'))
|
||||
{
|
||||
b.Append(c);
|
||||
if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text);
|
||||
c = text[i];
|
||||
}
|
||||
}
|
||||
|
||||
List<object> ReadList()
|
||||
{
|
||||
List<object> list = new();
|
||||
bool _endoflist = false;
|
||||
while (!_endoflist)
|
||||
list.Add(CreateInstance(ReadType(), ReadValue(ref _endoflist)));
|
||||
b.Clear();
|
||||
return list;
|
||||
}
|
||||
|
||||
IDictionary<string, dynamic> ReadDictionary()
|
||||
{
|
||||
short bracketBalance = 1;
|
||||
c = text[++i]; //пропускает начальный символ '{'
|
||||
while (bracketBalance != 0)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case '#':
|
||||
SkipComment();
|
||||
break;
|
||||
case '{':
|
||||
bracketBalance++;
|
||||
b.Append(c);
|
||||
break;
|
||||
case '}':
|
||||
bracketBalance--;
|
||||
if (bracketBalance != 0)
|
||||
b.Append(c);
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
b.Append('"');
|
||||
ReadString();
|
||||
b.Append('"');
|
||||
break;
|
||||
default:
|
||||
b.Append(c);
|
||||
break;
|
||||
}
|
||||
|
||||
if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text);
|
||||
c = text[i];
|
||||
}
|
||||
|
||||
b.Clear();
|
||||
return Deserialize(b.ToString());
|
||||
}
|
||||
|
||||
while (i < text.Length-1)
|
||||
{
|
||||
c = text[++i];
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case '#':
|
||||
SkipComment();
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
ReadString();
|
||||
break;
|
||||
case ';': // один параметр
|
||||
case ',': // для листов
|
||||
string str = b.ToString();
|
||||
b.Clear();
|
||||
return str == "null" ? null : str;
|
||||
case '[':
|
||||
return ReadList();
|
||||
case ']':
|
||||
endoflist = true;
|
||||
goto case ',';
|
||||
case '{':
|
||||
return ReadDictionary();
|
||||
case '=':
|
||||
case ':':
|
||||
case '}':
|
||||
throw new Exception($"DtsodV30.Deserialize() error: unexpected {c}");
|
||||
default:
|
||||
b.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("DtsodV30.Deserialize.ReadValue() error: end of text\ntext:\n" + text);
|
||||
}
|
||||
|
||||
void SkipComment()
|
||||
{
|
||||
while (text[i] != '\n')
|
||||
if (++i >= text.Length) throw new Exception("DtsodV30.Deserialize() error: end of text\ntext:\n" + text);
|
||||
}
|
||||
|
||||
object CreateInstance(Type type, object ctor_arg)
|
||||
{
|
||||
if (TypeHelper.Instance.BaseTypeConstructors.TryGetValue(type, out Func<string, dynamic> ctor))
|
||||
return (object)ctor.Invoke((string)ctor_arg);
|
||||
else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute)))
|
||||
return Activator.CreateInstance(type, ((IDictionary<string, object>)ctor_arg).Values.ToArray());
|
||||
else if (typeof(ICollection).IsAssignableFrom(type))
|
||||
{
|
||||
var method_As = typeof(TypeHelper).GetMethod("As",
|
||||
System.Reflection.BindingFlags.Static |
|
||||
System.Reflection.BindingFlags.NonPublic)
|
||||
.MakeGenericMethod(type.GetGenericArguments()[0]);
|
||||
object collection = type.GetConstructor(Type.EmptyTypes).Invoke(null);
|
||||
var method_Add = type.GetMethod("Add");
|
||||
Log(method_Add.Name);
|
||||
foreach (object el in (IEnumerable)ctor_arg)
|
||||
{
|
||||
var pel = method_As.Invoke(null, new object[] { el });
|
||||
method_Add.Invoke(collection, new object[] { pel });
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
else throw new Exception($"can't create instance of {type.FullName}");
|
||||
}
|
||||
|
||||
Dictionary<string, dynamic> output = new();
|
||||
Type type;
|
||||
string name;
|
||||
object value;
|
||||
|
||||
for (; i < text.Length; i++)
|
||||
{
|
||||
type = ReadType();
|
||||
name = ReadName();
|
||||
bool _ = false;
|
||||
value = ReadValue(ref _);
|
||||
output.Add(name, CreateInstance(type, value));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// public override void Append(ICollection<KeyValuePair<string, dynamic>> anotherDtsod) => base.Append(anotherDtsod);//UpdateLazy();
|
||||
|
||||
public override void Add(string key, dynamic value) => base.Add(key, (object)value);//UpdateLazy();
|
||||
|
||||
protected static string Serialize(IDictionary<string, dynamic> dtsod, ushort tabsCount = 0)
|
||||
{
|
||||
StringBuilder b = new();
|
||||
foreach (KeyValuePair<string, dynamic> pair in dtsod)
|
||||
{
|
||||
SerializeObject(pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
void SerializeObject(string name, dynamic inst)
|
||||
{
|
||||
Type type = inst.GetType();
|
||||
b.Append(TypeHelper.Instance.TypeToString(type)).Append(':')
|
||||
.Append(name).Append('=');
|
||||
if (TypeHelper.Instance.BaseTypeNames.ContainsKey(type))
|
||||
{
|
||||
if (type == typeof(decimal) || type == typeof(double) || type == typeof(float))
|
||||
b.Append(inst.ToString(CultureInfo.InvariantCulture));
|
||||
else b.Append(inst.ToString());
|
||||
}
|
||||
else if (typeof(IDictionary<string, dynamic>).IsAssignableFrom(type))
|
||||
b.Append("\n{\n").Append(Serialize(inst, tabsCount++)).Append("};\n");
|
||||
else if (type.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute)))
|
||||
{
|
||||
var props = type.GetProperties().Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(DtsodSerializableAttribute)));
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var propval = prop.GetValue(inst);
|
||||
|
||||
}
|
||||
}
|
||||
else throw new Exception($"can't serialize type {type.FullName}");
|
||||
}
|
||||
|
||||
return b.ToString();
|
||||
}
|
||||
|
||||
protected Lazy<string> serialized;
|
||||
protected void UpdateLazy() => serialized = new(() => Serialize(this));
|
||||
public override string ToString() => serialized.Value;
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
namespace DTLib.Dtsod;
|
||||
|
||||
public class TypeHelper
|
||||
{
|
||||
static Lazy<TypeHelper> _inst = new();
|
||||
public static TypeHelper Instance => _inst.Value;
|
||||
|
||||
internal readonly Dictionary<Type, Func<string, dynamic>> BaseTypeConstructors = new()
|
||||
{
|
||||
{ typeof(bool), (inp) => inp.ToBool() },
|
||||
{ typeof(char), (inp) => inp.ToChar() },
|
||||
{ typeof(string), (inp) => inp.ToString() },
|
||||
{ typeof(byte), (inp) => inp.ToByte() },
|
||||
{ typeof(sbyte), (inp) => inp.ToSByte() },
|
||||
{ typeof(short), (inp) => inp.ToShort() },
|
||||
{ typeof(ushort), (inp) => inp.ToUShort() },
|
||||
{ typeof(int), (inp) => inp.ToInt() },
|
||||
{ typeof(uint), (inp) => inp.ToUInt() },
|
||||
{ typeof(long), (inp) => inp.ToLong() },
|
||||
{ typeof(ulong), (inp) => inp.ToULong() },
|
||||
{ typeof(float), (inp) => inp.ToFloat() },
|
||||
{ typeof(double), (inp) => inp.ToDouble() },
|
||||
{ typeof(decimal), (inp) => inp.ToDecimal() }
|
||||
};
|
||||
|
||||
internal Dictionary<Type, string> BaseTypeNames = new()
|
||||
{
|
||||
{ typeof(bool), "bool" },
|
||||
{ typeof(char), "char" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(byte), "byte" },
|
||||
{ typeof(sbyte), "sbyte" },
|
||||
{ typeof(short), "short" },
|
||||
{ typeof(ushort), "ushort" },
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(uint), "uint" },
|
||||
{ typeof(long), "long" },
|
||||
{ typeof(ulong), "ulong" },
|
||||
{ typeof(float), "float" },
|
||||
{ typeof(double), "double" },
|
||||
{ typeof(decimal), "decimal" }
|
||||
};
|
||||
|
||||
private Dictionary<string, Type> ST_extensions = new()
|
||||
{
|
||||
{ "List<bool>", typeof(List<bool>) },
|
||||
{ "List<char>", typeof(List<char>) },
|
||||
{ "List<string>", typeof(List<string>) },
|
||||
{ "List<byte>", typeof(List<byte>) },
|
||||
{ "List<sbyte>", typeof(List<sbyte>) },
|
||||
{ "List<short>", typeof(List<short>) },
|
||||
{ "List<ushort>", typeof(List<ushort>) },
|
||||
{ "List<int>", typeof(List<int>) },
|
||||
{ "List<uint>", typeof(List<uint>) },
|
||||
{ "List<long>", typeof(List<long>) },
|
||||
{ "List<ulong>", typeof(List<ulong>) },
|
||||
{ "List<float>", typeof(List<float>) },
|
||||
{ "List<double>", typeof(List<double>) },
|
||||
{ "List<decimal>", typeof(List<decimal>) },
|
||||
};
|
||||
private Dictionary<Type, string> TS_extensions = new()
|
||||
{
|
||||
{ typeof(List<bool>), "List<bool>" },
|
||||
{ typeof(List<char>), "List<char>" },
|
||||
{ typeof(List<string>), "List<string>" },
|
||||
{ typeof(List<byte>), "List<byte>" },
|
||||
{ typeof(List<sbyte>), "List<sbyte>" },
|
||||
{ typeof(List<short>), "List<short>" },
|
||||
{ typeof(List<ushort>), "List<ushort>" },
|
||||
{ typeof(List<int>), "List<int>" },
|
||||
{ typeof(List<uint>), "List<uint>" },
|
||||
{ typeof(List<long>), "List<long>" },
|
||||
{ typeof(List<ulong>), "List<ulong>" },
|
||||
{ typeof(List<float>), "List<float>" },
|
||||
{ typeof(List<double>), "List<double>" },
|
||||
{ typeof(List<decimal>), "List<decimal>" },
|
||||
};
|
||||
|
||||
public TypeHelper Extend(string name, Type type)
|
||||
{
|
||||
ST_extensions.Add(name, type);
|
||||
TS_extensions.Add(type, name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public string TypeToString(Type t) =>
|
||||
BaseTypeNames.TryGetValue(t, out string name)
|
||||
? name
|
||||
: TS_extensions.TryGetValue(t, out name)
|
||||
? name
|
||||
: t.FullName;
|
||||
public Type TypeFromString(string str) => str switch
|
||||
{
|
||||
"bool" => typeof(bool),
|
||||
"char" => typeof(char),
|
||||
"string" => typeof(string),
|
||||
"byte" => typeof(byte),
|
||||
"sbyte" => typeof(sbyte),
|
||||
"short" => typeof(short),
|
||||
"ushort" => typeof(ushort),
|
||||
"int" => typeof(int),
|
||||
"uint" => typeof(uint),
|
||||
"long" => typeof(long),
|
||||
"ulong" => typeof(ulong),
|
||||
"float" => typeof(float),
|
||||
"double" => typeof(double),
|
||||
"decimal" => typeof(decimal),
|
||||
_ => ST_extensions.TryGetValue(str, out var t)
|
||||
? t
|
||||
: Type.GetType(str, false)
|
||||
?? throw new Exception($"DtsodV30.Deserialize.ParseType() error: type {str} doesn't exists")
|
||||
};
|
||||
internal static T As<T>(object inst) where T : class => inst as T;
|
||||
}
|
||||
@@ -2,33 +2,32 @@
|
||||
<PropertyGroup>
|
||||
<!--package info-->
|
||||
<PackageId>DTLib.Logging.Microsoft</PackageId>
|
||||
<Version>1.0.0</Version>
|
||||
<Version>1.1.3</Version>
|
||||
<Authors>Timerix</Authors>
|
||||
<Description>DTLib logger wrapper with dependency injection</Description>
|
||||
<RepositoryType>GIT</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/Timerix22/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/Timerix22/DTLib</PackageProjectUrl>
|
||||
<RepositoryUrl>https://timerix.ddns.net:3322/Timerix/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://timerix.ddns.net:3322/Timerix/DTLib</PackageProjectUrl>
|
||||
<Configuration>Release</Configuration>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<!--compilation properties-->
|
||||
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<DebugType>embedded</DebugType>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<!--language features-->
|
||||
<LangVersion>12</LangVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--external dependencies-->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--DTLib dependencies-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ProjectReference Include="..\DTLib.Logging\DTLib.Logging.csproj" />
|
||||
<ProjectReference Include="..\DTLib\DTLib.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib.Logging" Version="1.3.3" />
|
||||
<PackageReference Include="DTLib" Version="1.7.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!--package info-->
|
||||
<PackageId>DTLib.Logging</PackageId>
|
||||
<Version>1.3.3</Version>
|
||||
<Authors>Timerix</Authors>
|
||||
<Description>Loggers with dependency injection</Description>
|
||||
<RepositoryType>GIT</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/Timerix22/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/Timerix22/DTLib</PackageProjectUrl>
|
||||
<Configuration>Release</Configuration>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<!--compilation properties-->
|
||||
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<DebugType>embedded</DebugType>
|
||||
<!--language features-->
|
||||
<LangVersion>12</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--DTLib dependencies-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ProjectReference Include="..\DTLib\DTLib.csproj" />
|
||||
<ProjectReference Include="..\DTLib.Ben.Demystifier\DTLib.Ben.Demystifier.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib" Version="1.3.3" />
|
||||
<PackageReference Include="DTLib.Ben.Demystifier" Version="1.0.6" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,29 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!--package info-->
|
||||
<PackageId>DTLib.Network</PackageId>
|
||||
<Version>1.4.0</Version>
|
||||
<Authors>Timerix</Authors>
|
||||
<Description>Some sick network protocols</Description>
|
||||
<RepositoryType>GIT</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/Timerix22/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/Timerix22/DTLib</PackageProjectUrl>
|
||||
<Configuration>Release</Configuration>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<!--compilation properties-->
|
||||
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<DebugType>embedded</DebugType>
|
||||
<!--language features-->
|
||||
<LangVersion>12</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--DTLib dependencies-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ProjectReference Include="..\DTLib.Dtsod\DTLib.Dtsod.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib.Dtsod" Version="1.3.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
// включает init и record из c# 9.0
|
||||
#if !NET6_0 && !NET7_0 && !NET8_0
|
||||
namespace System.Runtime.CompilerServices;
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class IsExternalInit { }
|
||||
#endif
|
||||
@@ -1,32 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!--compilation properties-->
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<DebugType>embedded</DebugType>
|
||||
<!--language features-->
|
||||
<LangVersion>12</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--DTLib dependencies-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ProjectReference Include="..\DTLib.Logging\DTLib.Logging.csproj" />
|
||||
<ProjectReference Include="..\DTLib.Network\DTLib.Network.csproj" />
|
||||
<ProjectReference Include="..\DTLib.Dtsod\DTLib.Dtsod.csproj" />
|
||||
<ProjectReference Include="..\DTLib\DTLib.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib" Version="1.3.3" />
|
||||
<PackageReference Include="DTLib.Dtsod" Version="1.3.3" />
|
||||
<PackageReference Include="DTLib.Network" Version="1.4.0" />
|
||||
<PackageReference Include="DTLib.Logging" Version="1.3.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--project files-->
|
||||
<ItemGroup>
|
||||
<None Update="Dtsod\TestResources\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
using KerepWrapper.Autoarr;
|
||||
using KerepWrapper.KerepTypes;
|
||||
|
||||
namespace DTLib.Tests;
|
||||
|
||||
public static class TestAutoarr
|
||||
{
|
||||
public static void TestAll()
|
||||
{
|
||||
var ar = new Autoarr<KVPair>(4, 4, false);
|
||||
Fill(ar);
|
||||
Print(ar);
|
||||
Free(ar);
|
||||
}
|
||||
|
||||
public static void Fill(Autoarr<KVPair> ar)
|
||||
{
|
||||
Logger.Log("c", "----------[TestAutoarr/Fill]----------");
|
||||
for(uint i=0;i<ar.MaxLength;i++)
|
||||
ar.Add(new KVPair($"key_{i}",new Unitype(i)));
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
|
||||
public static void Print(Autoarr<KVPair> ar)
|
||||
{
|
||||
Logger.Log("c", "----------[TestAutoarr/Print]---------");
|
||||
foreach (KVPair pair in ar)
|
||||
Logger.Log("h", pair.ToString());
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
|
||||
public static void Free(Autoarr<KVPair> ar)
|
||||
{
|
||||
Logger.Log("c", "----------[TestAutoarr/Free]----------");
|
||||
ar.Dispose();
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,88 +0,0 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace DTLib.Tests;
|
||||
|
||||
public static class TestDtsodV23
|
||||
{
|
||||
public static void TestAll()
|
||||
{
|
||||
TestBaseTypes();
|
||||
TestLists();
|
||||
TestComplexes();
|
||||
TestReSerialization();
|
||||
TestSpeed();
|
||||
TestMemoryConsumption();
|
||||
}
|
||||
|
||||
public static void TestBaseTypes()
|
||||
{
|
||||
ColoredConsole.WriteLine("c", "-----[TestDtsodV23/TestBaseTypes]-----");
|
||||
DtsodV23 dtsod = new(File.ReadAllText(Path.Concat("Dtsod","TestResources","DtsodV23", "base_types.dtsod")));
|
||||
foreach (var pair in dtsod)
|
||||
ColoredConsole.WriteLine("b", pair.Value.GetType().Name + ' ', "w", pair.Key + ' ', "c", pair.Value.ToString());
|
||||
ColoredConsole.WriteLine("g", "test completed");
|
||||
}
|
||||
public static void TestLists()
|
||||
{
|
||||
ColoredConsole.WriteLine("c", "-------[TestDtsodV23/TestLists]-------");
|
||||
DtsodV23 dtsod = new(File.ReadAllText(Path.Concat("Dtsod","TestResources","DtsodV23", "lists.dtsod")));
|
||||
foreach (var pair in dtsod)
|
||||
{
|
||||
ColoredConsole.WriteLine("b", pair.Value.GetType().Name + ' ', "w", pair.Key, "c",
|
||||
$" count: {pair.Value.Count}");
|
||||
foreach (var el in pair.Value)
|
||||
ColoredConsole.WriteLine("b", '\t'+el.GetType().Name + ' ', "c", el.ToString());
|
||||
}
|
||||
ColoredConsole.WriteLine("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestComplexes()
|
||||
{
|
||||
ColoredConsole.WriteLine("c", "-----[TestDtsodV23/TestComplexes]-----");
|
||||
DtsodV23 dtsod = new(File.ReadAllText(Path.Concat("Dtsod","TestResources","DtsodV23", "complexes.dtsod")));
|
||||
foreach (var complex in dtsod)
|
||||
{
|
||||
ColoredConsole.WriteLine("b", complex.Value.GetType().Name + ' ', "w", complex.Key,
|
||||
"b", " size: ", "c", complex.Value.Keys.Count.ToString());
|
||||
foreach (var pair in (DtsodV23) complex.Value)
|
||||
ColoredConsole.WriteLine("b", '\t' + pair.Value.GetType().Name + ' ', "w", pair.Key + ' ',
|
||||
"c", pair.Value.ToString());
|
||||
}
|
||||
ColoredConsole.WriteLine("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestReSerialization()
|
||||
{
|
||||
ColoredConsole.WriteLine("c", "--[TestDtsodV23/TestReSerialization]--");
|
||||
var dtsod = new DtsodV23(new DtsodV23(new DtsodV23(
|
||||
new DtsodV23(File.ReadAllText(Path.Concat("Dtsod","TestResources","DtsodV23", "complexes.dtsod")))).ToString()).ToString());
|
||||
ColoredConsole.WriteLine("y", dtsod.ToString());
|
||||
ColoredConsole.WriteLine("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestSpeed()
|
||||
{
|
||||
ColoredConsole.WriteLine("c", "-------[TestDtsodV23/TestSpeed]-------");
|
||||
IDtsod dtsod=null;
|
||||
string text = File.ReadAllText(Path.Concat("Dtsod","TestResources","DtsodV23", "messages.dtsod"));
|
||||
LogOperationTime("V21 deserialization",64,()=>dtsod=new DtsodV21(text));
|
||||
LogOperationTime("V21 serialization", 64, () => _=dtsod.ToString());
|
||||
LogOperationTime("V23 deserialization", 64, () => dtsod = new DtsodV23(text));
|
||||
LogOperationTime("V23 serialization", 64, () => _ = dtsod.ToString());
|
||||
ColoredConsole.WriteLine("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestMemoryConsumption()
|
||||
{
|
||||
ColoredConsole.WriteLine("c", "----[TestDtsodV23/TestMemConsumpt]----");
|
||||
string text = File.ReadAllText(Path.Concat("Dtsod","TestResources","DtsodV23", "messages.dtsod"));
|
||||
var a = GC.GetTotalMemory(true);
|
||||
var dtsods = new DtsodV23[64];
|
||||
for (int i = 0; i < dtsods.Length; i++)
|
||||
dtsods[i] = new(text);
|
||||
var b = GC.GetTotalMemory(true);
|
||||
ColoredConsole.WriteLine("b", "at the start: ","c",$"{a/1024} kb\n",
|
||||
"b", "at the end: ", "c", $"{b / 1024} kb\n{dtsods.Count()}","b"," dtsods initialized");
|
||||
ColoredConsole.WriteLine("g", "test completed");
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
using KerepWrapper.Dtsod;
|
||||
using KerepWrapper.Autoarr;
|
||||
using KerepWrapper.KerepTypes;
|
||||
|
||||
namespace DTLib.Tests;
|
||||
|
||||
public static class TestDtsodV24
|
||||
{
|
||||
public static void TestAll()
|
||||
{
|
||||
TestBaseTypes();
|
||||
TestComplexes();
|
||||
TestLists();
|
||||
TestReSerialization();
|
||||
TestSpeed();
|
||||
}
|
||||
|
||||
public static void TestBaseTypes()
|
||||
{
|
||||
Logger.Log("c", "-----[TestDtsodV24/TestBaseTypes]-----");
|
||||
DtsodV24 dtsod = new(File.ReadAllText($"Path.Concat("Dtsod","TestResources","DtsodV24", "base_types.dtsod")));
|
||||
foreach (var pair in dtsod)
|
||||
Logger.Log("b", pair.ToString());
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestComplexes()
|
||||
{
|
||||
Logger.Log("c", "-----[TestDtsodV24/TestComplexes]-----");
|
||||
DtsodV24 dtsod = new(File.ReadAllText($"Path.Concat("Dtsod","TestResources","DtsodV24", "complexes.dtsod")));
|
||||
Logger.Log("h", dtsod.ToString());
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestLists()
|
||||
{
|
||||
Logger.Log("c", "-------[TestDtsodV24/TestLists]-------");
|
||||
DtsodV24 dtsod = new(File.ReadAllText($"Path.Concat("Dtsod","TestResources","DtsodV24", "lists.dtsod")));
|
||||
foreach (KVPair pair in dtsod)
|
||||
{
|
||||
var list = new Autoarr<Unitype>(pair.value.VoidPtr, false);
|
||||
Logger.Log("b", pair.key.HGlobalUTF8ToString(), "w", $" length: {list.Length}");
|
||||
foreach (var el in list)
|
||||
{
|
||||
Logger.Log("h", '\t' + el.ToString());
|
||||
if (el.TypeCode == KerepTypeCode.AutoarrUnitypePtr)
|
||||
{
|
||||
var ar = new Autoarr<Unitype>(el.VoidPtr, false);
|
||||
foreach (var k in ar)
|
||||
{
|
||||
Logger.Log($"\t\t{k.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestReSerialization()
|
||||
{
|
||||
Logger.Log("c", "--[TestDtsodV24/TestReSerialization]--");
|
||||
var dtsod = new DtsodV24(new DtsodV24(new DtsodV24(
|
||||
new DtsodV24(File.ReadAllText($"Path.Concat("Dtsod","TestResources","DtsodV24", "complexes.dtsod"))).ToString()).ToString()).ToString()));
|
||||
Logger.Log("h", dtsod.ToString());
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
|
||||
public static void TestSpeed()
|
||||
{
|
||||
Logger.Log("c", "-------[TestDtsodV24/TestSpeed]-------");
|
||||
IDtsod dtsod=null;
|
||||
string _text = File.ReadAllText(Path.Concat("Dtsod","TestResources","DtsodV23", "messages.dtsod"));
|
||||
string text = "";
|
||||
LogOperationTime( "V23 to V24 conversion", 32, ()=>
|
||||
text = DtsodConverter.ConvertVersion(new DtsodV23(_text), DtsodVersion.V24).ToString()
|
||||
);
|
||||
File.WriteAllText($"Path.Concat("Dtsod","TestResources","DtsodV24", "messages.dtsod",text));
|
||||
LogOperationTime("V24 deserialization", 64, () => dtsod = new DtsodV24(text));
|
||||
LogOperationTime("V24 serialization", 64, () => text = dtsod.ToString());
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
using System.Runtime.InteropServices;
|
||||
using KerepWrapper.Dtsod;
|
||||
using KerepWrapper.KerepTypes;
|
||||
|
||||
namespace DTLib.Tests;
|
||||
|
||||
public static class TestPInvoke
|
||||
{
|
||||
public static void TestAll()
|
||||
{
|
||||
DependencyResolver.CopyLibs();
|
||||
TestUTF8();
|
||||
TestPrintf();
|
||||
TestMarshalling();
|
||||
}
|
||||
|
||||
public static void TestUTF8()
|
||||
{
|
||||
Logger.Log("c", "--------[TestPInvoke/TestUTF8]--------", "b", "");
|
||||
IntPtr ptr;
|
||||
string str="_$\"\\\\'''\ta ыыы000;2;=:%d;```";
|
||||
for(int i=0; i<1000; i++)
|
||||
{
|
||||
ptr = Unmanaged.StringToHGlobalUTF8(str);
|
||||
str = Unmanaged.HGlobalUTF8ToString(ptr);
|
||||
}
|
||||
Logger.Log("y", str);
|
||||
}
|
||||
|
||||
[DllImport("kerep", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern void pinvoke_print([MarshalAs(UnmanagedType.LPStr)] string msg);
|
||||
|
||||
public static void TestPrintf()
|
||||
{
|
||||
Logger.Log("c", "---------[TestPInvoke/Printf]---------", "b", "");
|
||||
pinvoke_print("ъъ~ 中文");
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
|
||||
[DllImport("kerep", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern unsafe void test_marshalling([MarshalAs(UnmanagedType.LPStr)] string text, out IntPtr kptr);
|
||||
|
||||
public static unsafe void TestMarshalling()
|
||||
{
|
||||
Logger.Log("c", "---------[TestAutoarr/TestMarshalling]----------");
|
||||
string msg = "ъъ~ 中文";
|
||||
test_marshalling(msg, out var kptr);
|
||||
KVPair k = *(KVPair*)kptr;
|
||||
Logger.Log("b", k.ToString());
|
||||
Logger.Log("g", "test completed");
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,14 +0,0 @@
|
||||
bool: false;
|
||||
char: 'v';
|
||||
byte: 255b;
|
||||
sbyte: -125sb;
|
||||
short: 14003s;
|
||||
ushort: 32025us;
|
||||
int: -2515;
|
||||
uint: 0u;
|
||||
long: -29863854396l;
|
||||
ulong: 87659057946ul;
|
||||
float: 39.944f;
|
||||
double: 965.557;
|
||||
decimal: -84.20de;
|
||||
string: "_$\"\\\\'''\n\ta ыыы000;2;=:%d;```";
|
||||
@@ -1,9 +0,0 @@
|
||||
message:
|
||||
{
|
||||
type: "sent";
|
||||
time: "15.12.2021 20:51:24 +03:00";
|
||||
author_id: 293798876950036480ul;
|
||||
channel_id: 913088838761603212ul;
|
||||
message_id: 920734809096077353ul;
|
||||
text: "_$\"\\\\'''\n\ta ыыы000;2;=:%d;```";
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
chars: ['a','b','c'];
|
||||
uints: [10,20,30,0,0];
|
||||
floats: [8.2,5.225,-0.9993];
|
||||
strings:["aaa","bbb","ccc"];
|
||||
things:["aaa",'b',-122];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
bool: false ;
|
||||
int: -2515;
|
||||
uint: 0u;
|
||||
float: 39.944f;
|
||||
double: 965.557f;
|
||||
string: "_$\"\\\\'''\n\ta ыыы000;2;=:%d;```";
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
message:
|
||||
{
|
||||
type: "sent";
|
||||
time: "15.12.2021 20:51:24 +03:00";
|
||||
author_id: -293798876950036480;
|
||||
channel_id: 913088838761603212;
|
||||
message_id: 920734809096077353u;
|
||||
text: "_$\"\\\\'''\n\ta ыыы000;2;=:%d;```";
|
||||
den: -0.99f;
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
uints: [10,20,30,0,0 ];
|
||||
floats: [8.2f,5.225f,-0.9993f];
|
||||
strings: ["aaa","bbb","ccc"];
|
||||
things: ["aaa",true,-122];
|
||||
$complex: {
|
||||
a:141;
|
||||
b:00f;
|
||||
c:false;
|
||||
};
|
||||
$complex: {
|
||||
d:-1u;
|
||||
e:005;
|
||||
f:-1f;
|
||||
};
|
||||
list_of_lists: [ [ "sss" ] ];
|
||||
blank_list: [];
|
||||
qqq: [
|
||||
{
|
||||
|
||||
},
|
||||
{
|
||||
q: 0.0f;
|
||||
}
|
||||
];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +0,0 @@
|
||||
bool:b=false;
|
||||
char:c='v';
|
||||
string:s="hello";
|
||||
byte:by=255;
|
||||
sbyte:sb=-125;
|
||||
short:sh=14003;
|
||||
ushort:us=32025;
|
||||
int:i=-2515;
|
||||
uint:ui=0;
|
||||
long:l=-29863854396;
|
||||
ulong:ul=87659057946;
|
||||
float:f=39.944;
|
||||
double:do=965.557;
|
||||
decimal:de=-84.20;
|
||||
@@ -1 +0,0 @@
|
||||
List<string>:list=[string:"a",string:"b"];
|
||||
@@ -1,48 +0,0 @@
|
||||
global using System;
|
||||
global using System.Collections;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Text;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using DTLib;
|
||||
global using DTLib.Extensions;
|
||||
global using DTLib.Filesystem;
|
||||
global using DTLib.Dtsod;
|
||||
global using static DTLib.Tests.TesterLog;
|
||||
global using static DTLib.Tests.Program;
|
||||
global using DTLib.Console;
|
||||
global using DTLib.Logging;
|
||||
|
||||
namespace DTLib.Tests;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static ILogger Logger = new CompositeLogger(new ConsoleLogger(),
|
||||
new FileLogger("logs", "DTLib.Tests"))
|
||||
|
||||
{
|
||||
DebugLogEnabled = true
|
||||
};
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
System.Console.OutputEncoding = Encoding.UTF8;
|
||||
System.Console.InputEncoding = Encoding.UTF8;
|
||||
var mainContext = new ContextLogger("Main", Logger);
|
||||
|
||||
try
|
||||
{
|
||||
new LaunchArgumentParser().WithNoExit().ParseAndHandle(args);
|
||||
TestDtsodV23.TestAll();
|
||||
// TestPInvoke.TestAll();
|
||||
// TestAutoarr.TestAll();
|
||||
// TestDtsodV24.TestAll();
|
||||
}
|
||||
catch(LaunchArgumentParser.ExitAfterHelpException)
|
||||
{ }
|
||||
catch (Exception ex)
|
||||
{ mainContext.LogError(ex); }
|
||||
|
||||
System.Console.ResetColor();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace DTLib.Tests;
|
||||
|
||||
public static class TesterLog
|
||||
{
|
||||
public static void LogOperationTime(string op_name, int repeats, Action operation)
|
||||
{
|
||||
Stopwatch clock = new();
|
||||
clock.Start();
|
||||
for (int i = 0; i < repeats; i++)
|
||||
operation();
|
||||
clock.Stop();
|
||||
double time=(double)(clock.ElapsedTicks)/Stopwatch.Frequency/repeats;
|
||||
Logger.LogInfo(nameof(TesterLog), $"operation {op_name} lasted {time.ToString(MyTimeFormat.ForText)} seconds");
|
||||
}
|
||||
}
|
||||
30
DTLib.Web/DTLib.Web.csproj
Normal file
30
DTLib.Web/DTLib.Web.csproj
Normal file
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!--package info-->
|
||||
<PackageId>DTLib.Web</PackageId>
|
||||
<Version>1.4.0</Version>
|
||||
<Authors>Timerix</Authors>
|
||||
<Description>HTTP Server with simple routing</Description>
|
||||
<RepositoryType>GIT</RepositoryType>
|
||||
<RepositoryUrl>https://timerix.ddns.net:3322/Timerix/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://timerix.ddns.net:3322/Timerix/DTLib</PackageProjectUrl>
|
||||
<Configuration>Release</Configuration>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<!--compilation properties-->
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<!--language features-->
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--DTLib dependencies-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ProjectReference Include="..\DTLib\DTLib.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib" Version="1.7.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
18
DTLib.Web/HttpMethod.cs
Normal file
18
DTLib.Web/HttpMethod.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace DTLib.Web;
|
||||
|
||||
/// <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods"/>
|
||||
[Flags]
|
||||
public enum HttpMethod : ushort
|
||||
{
|
||||
NONE = 0,
|
||||
GET = 1,
|
||||
POST = 2,
|
||||
PUT = 4,
|
||||
DELETE = 8,
|
||||
PATCH = 16,
|
||||
HEAD = 32,
|
||||
OPTIONS = 64,
|
||||
TRACE = 128,
|
||||
CONNECT = 256,
|
||||
ANY = 65535
|
||||
}
|
||||
9
DTLib.Web/Routes/DelegateRouteHandler.cs
Normal file
9
DTLib.Web/Routes/DelegateRouteHandler.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public class DelegateRouteHandler(
|
||||
Func<HttpListenerContext, ContextLogger, Task<HttpStatusCode>> routeHandler
|
||||
) : IRouteHandler
|
||||
{
|
||||
public Task<HttpStatusCode> HandleRequest(HttpListenerContext ctx, ContextLogger requestLogger)
|
||||
=> routeHandler(ctx, requestLogger);
|
||||
}
|
||||
6
DTLib.Web/Routes/IRouteHandler.cs
Normal file
6
DTLib.Web/Routes/IRouteHandler.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public interface IRouteHandler
|
||||
{
|
||||
Task<HttpStatusCode> HandleRequest(HttpListenerContext ctx, ContextLogger requestLogger);
|
||||
}
|
||||
6
DTLib.Web/Routes/IRouter.cs
Normal file
6
DTLib.Web/Routes/IRouter.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public interface IRouter
|
||||
{
|
||||
Task Resolve(HttpListenerContext ctx, ContextLogger requestLogger);
|
||||
}
|
||||
40
DTLib.Web/Routes/ServeFilesRouteHandler.cs
Normal file
40
DTLib.Web/Routes/ServeFilesRouteHandler.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public class ServeFilesRouteHandler(IOPath _publicDir, string _homePageUrl = "index.html") : IRouteHandler
|
||||
{
|
||||
public async Task<HttpStatusCode> HandleRequest(HttpListenerContext ctx, ContextLogger requestLogger)
|
||||
{
|
||||
if (ctx.Request.HttpMethod != "GET")
|
||||
return HttpStatusCode.BadRequest;
|
||||
|
||||
string requestPath = ctx.Request.Url?.AbsolutePath!;
|
||||
if (string.IsNullOrEmpty(requestPath) || requestPath == "/")
|
||||
{
|
||||
requestPath = _homePageUrl;
|
||||
}
|
||||
|
||||
string ext = Path.Extension(requestPath).Str;
|
||||
IOPath filePath = Path.Concat(_publicDir, requestPath);
|
||||
if (!File.Exists(filePath))
|
||||
return HttpStatusCode.NotFound;
|
||||
|
||||
List<(string key, string val)> headers = ext switch
|
||||
{
|
||||
"html" => [("Content-Type", "text/html")],
|
||||
"css" => [("Content-Type", "text/css")],
|
||||
"js" or "jsx" or "ts" or "tsx" or "map" => [("Content-Type", "text/javascript")],
|
||||
_ =>
|
||||
[
|
||||
("Content-Type", "binary/octet-stream"),
|
||||
("Content-Disposition", $"attachment filename={filePath.LastName()}")
|
||||
]
|
||||
};
|
||||
foreach (var header in headers)
|
||||
ctx.Response.Headers.Set(header.key, header.val);
|
||||
|
||||
var fileStream = File.OpenRead(filePath);
|
||||
ctx.Response.ContentLength64 = fileStream.Length;
|
||||
await fileStream.CopyToAsync(ctx.Response.OutputStream);
|
||||
return HttpStatusCode.OK;
|
||||
}
|
||||
}
|
||||
65
DTLib.Web/Routes/SimpleRouter.cs
Normal file
65
DTLib.Web/Routes/SimpleRouter.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
namespace DTLib.Web.Routes;
|
||||
|
||||
public class SimpleRouter : IRouter
|
||||
{
|
||||
/// route for any url that doesn't have its own handler
|
||||
public record RouteWithMethod(HttpMethod method, IRouteHandler routeHandler)
|
||||
{
|
||||
public bool CheckMethod(HttpMethod requestMethod) => (requestMethod & method) != 0;
|
||||
|
||||
public bool CheckMethod(string requestMethodStr)
|
||||
=> Enum.TryParse<HttpMethod>(requestMethodStr, out var requestMethod)
|
||||
&& CheckMethod(requestMethod);
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, RouteWithMethod> _routes = new();
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public RouteWithMethod? DefaultRoute { get; set; }
|
||||
|
||||
public SimpleRouter(ILogger logger)
|
||||
{
|
||||
_logger = new ContextLogger(nameof(SimpleRouter), logger);
|
||||
}
|
||||
|
||||
public void MapRoute(string url, HttpMethod method, IRouteHandler route)
|
||||
=> _routes.Add(url, new RouteWithMethod(method, route));
|
||||
|
||||
public void MapRoute(string url, HttpMethod method,
|
||||
Func<HttpListenerContext, ContextLogger, Task<HttpStatusCode>> route)
|
||||
=> MapRoute(url, method, new DelegateRouteHandler(route));
|
||||
|
||||
public async Task Resolve(HttpListenerContext ctx, ContextLogger requestLogger)
|
||||
{
|
||||
HttpStatusCode status = HttpStatusCode.InternalServerError;
|
||||
try
|
||||
{
|
||||
string? requestPath = ctx.Request.Url?.AbsolutePath;
|
||||
if (string.IsNullOrEmpty(requestPath))
|
||||
requestPath = "/";
|
||||
|
||||
if(!_routes.TryGetValue(requestPath!, out var routeWithMethod))
|
||||
routeWithMethod = DefaultRoute;
|
||||
|
||||
if (routeWithMethod is null)
|
||||
{
|
||||
_logger.LogWarn(nameof(SimpleRouter),
|
||||
$"couldn't resolve request path {ctx.Request.HttpMethod} {requestPath}");
|
||||
status = HttpStatusCode.NotFound;
|
||||
}
|
||||
else if (!routeWithMethod.CheckMethod(ctx.Request.HttpMethod))
|
||||
{
|
||||
_logger.LogWarn(nameof(SimpleRouter),
|
||||
$"received request with invalid method {ctx.Request.HttpMethod} {requestPath}");
|
||||
status = HttpStatusCode.MethodNotAllowed;
|
||||
}
|
||||
else status = await routeWithMethod.routeHandler.HandleRequest(ctx, requestLogger);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ctx.Response.StatusCode = (int)status;
|
||||
await ctx.Response.OutputStream.FlushAsync();
|
||||
ctx.Response.OutputStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
73
DTLib.Web/WebApp.cs
Normal file
73
DTLib.Web/WebApp.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Text;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using DTLib.Filesystem;
|
||||
global using DTLib.Logging;
|
||||
global using System.Net;
|
||||
using System.Diagnostics;
|
||||
using DTLib.Extensions;
|
||||
using DTLib.Web.Routes;
|
||||
|
||||
namespace DTLib.Web;
|
||||
|
||||
public class WebApp
|
||||
{
|
||||
private readonly string _baseUrl;
|
||||
private readonly ContextLogger _logger;
|
||||
private readonly IRouter _router;
|
||||
private readonly CancellationToken _stopToken;
|
||||
|
||||
public WebApp(string baseUrl, ILogger logger, IRouter router, CancellationToken stopToken = default)
|
||||
{
|
||||
_logger = new ContextLogger(nameof(WebApp), logger);
|
||||
_baseUrl = baseUrl;
|
||||
_stopToken = stopToken;
|
||||
_router = router;
|
||||
}
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
_logger.LogInfo($"starting server at '{_baseUrl}'...");
|
||||
HttpListener server = new HttpListener();
|
||||
server.Prefixes.Add(_baseUrl);
|
||||
server.Start();
|
||||
_logger.LogInfo("server started");
|
||||
long requestId = 1;
|
||||
|
||||
try
|
||||
{
|
||||
while (!_stopToken.IsCancellationRequested)
|
||||
{
|
||||
var ctx = await server.GetContextAsync().AsCancellable(_stopToken);
|
||||
HandleRequestAsync(ctx, new ContextLogger($"Request-{requestId++}", _logger));
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{}
|
||||
|
||||
server.Stop();
|
||||
_logger.LogInfo("server stopped");
|
||||
}
|
||||
|
||||
private async void HandleRequestAsync(HttpListenerContext ctx, ContextLogger requestLogger)
|
||||
{
|
||||
try
|
||||
{
|
||||
requestLogger.LogInfo($"{ctx.Request.HttpMethod} {ctx.Request.RawUrl} from {ctx.Request.RemoteEndPoint}...");
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
await _router.Resolve(ctx, requestLogger);
|
||||
stopwatch.Stop();
|
||||
requestLogger.LogInfo($"responded {ctx.Response.StatusCode}" +
|
||||
$" ({(HttpStatusCode)ctx.Response.StatusCode})" +
|
||||
$" in {stopwatch.ElapsedMilliseconds}ms");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
requestLogger.LogWarn(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1
DTLib.XXHash
Submodule
1
DTLib.XXHash
Submodule
Submodule DTLib.XXHash added at 3e1a2c00e6
59
DTLib.sln
59
DTLib.sln
@@ -5,15 +5,7 @@ VisualStudioVersion = 17.0.32014.148
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DTLib", "DTLib\DTLib.csproj", "{B620E5E9-800F-4B2D-B4A5-062E05DB704F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DTLib.Tests", "DTLib.Tests\DTLib.Tests.csproj", "{72BA37EF-07EC-4D34-966A-20D5E83ADB32}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Dtsod", "DTLib.Dtsod\DTLib.Dtsod.csproj", "{ADE425F5-8645-47F0-9AA8-33FA748D36BE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Network", "DTLib.Network\DTLib.Network.csproj", "{24B7D0A2-0462-424D-B3F5-29A6655FE472}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Logging", "DTLib.Logging\DTLib.Logging.csproj", "{00B76172-32BB-4B72-9891-47FAEF63386C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Ben.Demystifier", "DTLib.Ben.Demystifier\DTLib.Ben.Demystifier.csproj", "{AC7CB524-4D59-42E0-9F96-1C201A92494B}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Demystifier", "DTLib.Demystifier\DTLib.Demystifier.csproj", "{AC7CB524-4D59-42E0-9F96-1C201A92494B}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6308F24E-A4FF-46B3-B72F-30E05DDCB1D5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
@@ -26,63 +18,38 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
push_packages.sh = push_packages.sh
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KerepWrapper", "KerepWrapper\KerepWrapper.csproj", "{9ADC4AA1-1DE4-4BB6-AF38-E84C1A142032}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Logging.Microsoft", "DTLib.Logging.Microsoft\DTLib.Logging.Microsoft.csproj", "{9CCBAFA1-F191-4CEC-A4FA-CFF9F6CA081F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.XXHash", "DTLib.XXHash\DTLib.XXHash\DTLib.XXHash.csproj", "{C7029741-816D-41B2-A2C4-E20565B1739D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib.Web", "DTLib.Web\DTLib.Web.csproj", "{9A3220EB-CCED-4172-9BD4-C3700FF36539}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Publish|Any CPU = Publish|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B620E5E9-800F-4B2D-B4A5-062E05DB704F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B620E5E9-800F-4B2D-B4A5-062E05DB704F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B620E5E9-800F-4B2D-B4A5-062E05DB704F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B620E5E9-800F-4B2D-B4A5-062E05DB704F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B620E5E9-800F-4B2D-B4A5-062E05DB704F}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B620E5E9-800F-4B2D-B4A5-062E05DB704F}.Publish|Any CPU.Build.0 = Release|Any CPU
|
||||
{72BA37EF-07EC-4D34-966A-20D5E83ADB32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{72BA37EF-07EC-4D34-966A-20D5E83ADB32}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{72BA37EF-07EC-4D34-966A-20D5E83ADB32}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{72BA37EF-07EC-4D34-966A-20D5E83ADB32}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{72BA37EF-07EC-4D34-966A-20D5E83ADB32}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ADE425F5-8645-47F0-9AA8-33FA748D36BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ADE425F5-8645-47F0-9AA8-33FA748D36BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ADE425F5-8645-47F0-9AA8-33FA748D36BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ADE425F5-8645-47F0-9AA8-33FA748D36BE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ADE425F5-8645-47F0-9AA8-33FA748D36BE}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ADE425F5-8645-47F0-9AA8-33FA748D36BE}.Publish|Any CPU.Build.0 = Release|Any CPU
|
||||
{24B7D0A2-0462-424D-B3F5-29A6655FE472}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{24B7D0A2-0462-424D-B3F5-29A6655FE472}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{24B7D0A2-0462-424D-B3F5-29A6655FE472}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{24B7D0A2-0462-424D-B3F5-29A6655FE472}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{24B7D0A2-0462-424D-B3F5-29A6655FE472}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{24B7D0A2-0462-424D-B3F5-29A6655FE472}.Publish|Any CPU.Build.0 = Release|Any CPU
|
||||
{00B76172-32BB-4B72-9891-47FAEF63386C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00B76172-32BB-4B72-9891-47FAEF63386C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00B76172-32BB-4B72-9891-47FAEF63386C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00B76172-32BB-4B72-9891-47FAEF63386C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00B76172-32BB-4B72-9891-47FAEF63386C}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00B76172-32BB-4B72-9891-47FAEF63386C}.Publish|Any CPU.Build.0 = Release|Any CPU
|
||||
{AC7CB524-4D59-42E0-9F96-1C201A92494B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AC7CB524-4D59-42E0-9F96-1C201A92494B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AC7CB524-4D59-42E0-9F96-1C201A92494B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AC7CB524-4D59-42E0-9F96-1C201A92494B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AC7CB524-4D59-42E0-9F96-1C201A92494B}.Publish|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AC7CB524-4D59-42E0-9F96-1C201A92494B}.Publish|Any CPU.Build.0 = Release|Any CPU
|
||||
{9ADC4AA1-1DE4-4BB6-AF38-E84C1A142032}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9ADC4AA1-1DE4-4BB6-AF38-E84C1A142032}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9ADC4AA1-1DE4-4BB6-AF38-E84C1A142032}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9ADC4AA1-1DE4-4BB6-AF38-E84C1A142032}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9ADC4AA1-1DE4-4BB6-AF38-E84C1A142032}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9CCBAFA1-F191-4CEC-A4FA-CFF9F6CA081F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9CCBAFA1-F191-4CEC-A4FA-CFF9F6CA081F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9CCBAFA1-F191-4CEC-A4FA-CFF9F6CA081F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9CCBAFA1-F191-4CEC-A4FA-CFF9F6CA081F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9CCBAFA1-F191-4CEC-A4FA-CFF9F6CA081F}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9CCBAFA1-F191-4CEC-A4FA-CFF9F6CA081F}.Publish|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7029741-816D-41B2-A2C4-E20565B1739D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C7029741-816D-41B2-A2C4-E20565B1739D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7029741-816D-41B2-A2C4-E20565B1739D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C7029741-816D-41B2-A2C4-E20565B1739D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9A3220EB-CCED-4172-9BD4-C3700FF36539}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9A3220EB-CCED-4172-9BD4-C3700FF36539}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9A3220EB-CCED-4172-9BD4-C3700FF36539}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9A3220EB-CCED-4172-9BD4-C3700FF36539}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -1,86 +1,76 @@
|
||||
namespace DTLib.Console;
|
||||
|
||||
//
|
||||
// вывод и ввод цветного текста в консоли
|
||||
// работает медленнее чем хотелось бы
|
||||
//
|
||||
public static class ColoredConsole
|
||||
{
|
||||
// парсит название цвета в ConsoleColor
|
||||
public static ConsoleColor ParseColor(string color) => color switch
|
||||
{
|
||||
//case "magneta":
|
||||
"m" => ConsoleColor.Magenta,
|
||||
//case "green":
|
||||
"g" => ConsoleColor.Green,
|
||||
//case "red":
|
||||
"r" => ConsoleColor.Red,
|
||||
//case "yellow":
|
||||
"y" => ConsoleColor.Yellow,
|
||||
//case "white":
|
||||
"w" => ConsoleColor.White,
|
||||
//case "blue":
|
||||
"b" => ConsoleColor.Blue,
|
||||
//case "cyan":
|
||||
"c" => ConsoleColor.Cyan,
|
||||
//case "h":
|
||||
"h" or "gray" => ConsoleColor.Gray,
|
||||
//case "black":
|
||||
"black" => ConsoleColor.Black,
|
||||
_ => throw new Exception($"ColoredConsole.ParseColor({color}) error: incorrect color"),
|
||||
};
|
||||
public static int Width => System.Console.WindowWidth;
|
||||
public static int Height => System.Console.WindowHeight;
|
||||
|
||||
public static void Write(ConsoleColor color,string msg)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Fg(ConsoleColor color) => System.Console.ForegroundColor = color;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Bg(ConsoleColor color) => System.Console.BackgroundColor = color;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ResetColor() => System.Console.ResetColor();
|
||||
|
||||
/// <summary>
|
||||
/// Clears console buffer and resets color
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear()
|
||||
{
|
||||
System.Console.ForegroundColor = color;
|
||||
ResetColor();
|
||||
System.Console.Clear();
|
||||
}
|
||||
|
||||
public static void Write(string msg, ConsoleColor? fg = null, ConsoleColor? bg = null)
|
||||
{
|
||||
if(fg != null) Fg(fg.Value);
|
||||
if(bg != null) Bg(bg.Value);
|
||||
System.Console.Write(msg);
|
||||
System.Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
public static void Write(string msg) => Write(ConsoleColor.Gray, msg);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLine() => System.Console.Write('\n');
|
||||
|
||||
// вывод цветного текста
|
||||
public static void Write(params string[] input)
|
||||
public static void WriteLine(string msg, ConsoleColor? fg = null, ConsoleColor? bg = null)
|
||||
{
|
||||
if (input.Length == 1)
|
||||
{
|
||||
Write(input[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.Length % 2 != 0)
|
||||
throw new Exception("ColoredConsole.Write() error: every text string must have color string before");
|
||||
|
||||
for (ushort i = 0; i < input.Length; i++)
|
||||
{
|
||||
System.Console.ForegroundColor = ParseColor(input[i++]);
|
||||
System.Console.Write(input[i]);
|
||||
}
|
||||
System.Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
public static void WriteLine() => System.Console.WriteLine();
|
||||
public static void WriteLine(ConsoleColor color,string msg)
|
||||
{
|
||||
System.Console.ForegroundColor = color;
|
||||
System.Console.WriteLine(msg);
|
||||
System.Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
public static void WriteLine(params string[] input)
|
||||
{
|
||||
Write(input);
|
||||
Write(msg, fg, bg);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
// ввод цветного текста
|
||||
public static string Read(ConsoleColor color)
|
||||
public static string ReadLine(ConsoleColor? fg = null, ConsoleColor? bg = null)
|
||||
{
|
||||
System.Console.ForegroundColor = color;
|
||||
var r = System.Console.ReadLine();
|
||||
System.Console.ForegroundColor = ConsoleColor.Gray;
|
||||
return r;
|
||||
if(fg != null) Fg(fg.Value);
|
||||
if(bg != null) Bg(bg.Value);
|
||||
return System.Console.ReadLine() ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string Read(string color) => Read(ParseColor(color));
|
||||
public static void WriteHLine(char c, ConsoleColor? fg = null, ConsoleColor? bg = null) =>
|
||||
WriteLine(c.Multiply(Width - 1), fg, bg);
|
||||
|
||||
public static void WriteTitle(string title, char spacing = '-',
|
||||
string left_framing = "[", string right_framing = "]",
|
||||
ConsoleColor? fg = null, ConsoleColor? bg = null)
|
||||
{
|
||||
int both_spacing_length = Width - title.Length - left_framing.Length - right_framing.Length - 1;
|
||||
int left_spacing_length = both_spacing_length / 2;
|
||||
int right_spacing_length = left_spacing_length + both_spacing_length % 2;
|
||||
|
||||
var b = new StringBuilder();
|
||||
if(left_spacing_length > 0)
|
||||
b.Append(spacing.Multiply(left_spacing_length));
|
||||
b.Append(left_framing);
|
||||
b.Append(title);
|
||||
b.Append(right_framing);
|
||||
if(right_spacing_length > 0)
|
||||
b.Append(spacing.Multiply(right_spacing_length));
|
||||
|
||||
WriteLine(b.ToString(), fg, bg);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteCentred(string title, ConsoleColor? fg = null, ConsoleColor? bg = null)
|
||||
=> WriteTitle(title, ' ', "", "", fg, bg);
|
||||
}
|
||||
|
||||
@@ -1,64 +1,96 @@
|
||||
namespace DTLib.Console;
|
||||
|
||||
#nullable enable
|
||||
public class LaunchArgument
|
||||
public record LaunchArgument
|
||||
{
|
||||
public string[] Aliases;
|
||||
public string Description;
|
||||
protected string? ParamName1;
|
||||
protected string? ParamName2;
|
||||
public Action? Handler;
|
||||
public Action<string>? HandlerWithArg1;
|
||||
public Action<string, string>? HandlerWithArg2;
|
||||
public int RequiredArgsCount;
|
||||
public int Priority;
|
||||
public struct Param
|
||||
{
|
||||
public readonly string Name;
|
||||
public string? Value { get; internal set; } = null;
|
||||
|
||||
private LaunchArgument(string[] aliases, string description, int priority)
|
||||
public Param(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly string[] Aliases;
|
||||
public readonly string Description;
|
||||
public readonly int Priority;
|
||||
public readonly Param[] Params;
|
||||
|
||||
private readonly Action? Handler;
|
||||
private readonly Action<string>? Handler1Param;
|
||||
private readonly Action<string, string>? Handler2Params;
|
||||
|
||||
public LaunchArgument(string[] aliases, string description,
|
||||
Action handler, int priority = 0)
|
||||
{
|
||||
Aliases = aliases;
|
||||
Description = description;
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
public LaunchArgument(string[] aliases, string description,
|
||||
Action handler, int priority = 0)
|
||||
: this(aliases, description, priority)
|
||||
{
|
||||
Handler = handler;
|
||||
RequiredArgsCount = 0;
|
||||
Params = [];
|
||||
}
|
||||
|
||||
public LaunchArgument(string[] aliases, string description,
|
||||
Action<string> handler, string paramName1, int priority=0)
|
||||
: this(aliases, description, priority)
|
||||
{
|
||||
HandlerWithArg1 = handler;
|
||||
ParamName1 = paramName1;
|
||||
RequiredArgsCount = 1;
|
||||
Aliases = aliases;
|
||||
Description = description;
|
||||
Priority = priority;
|
||||
|
||||
Handler1Param = handler;
|
||||
Params = [
|
||||
new Param(paramName1)
|
||||
];
|
||||
}
|
||||
public LaunchArgument(string[] aliases, string description,
|
||||
Action<string, string> handler, string paramName1, string paramName2, int priority=0)
|
||||
: this(aliases, description, priority)
|
||||
{
|
||||
HandlerWithArg2 = handler;
|
||||
ParamName1 = paramName1;
|
||||
ParamName2 = paramName2;
|
||||
RequiredArgsCount = 2;
|
||||
Aliases = aliases;
|
||||
Description = description;
|
||||
Priority = priority;
|
||||
|
||||
Handler2Params = handler;
|
||||
Params = [
|
||||
new Param(paramName1),
|
||||
new Param(paramName2),
|
||||
];
|
||||
}
|
||||
|
||||
public StringBuilder AppendHelpInfo(StringBuilder b)
|
||||
internal StringBuilder AppendHelpInfo(StringBuilder b)
|
||||
{
|
||||
b.Append(Aliases[0]);
|
||||
for (int i = 1; i < Aliases.Length; i++)
|
||||
b.Append(", ").Append(Aliases[i]);
|
||||
if (!string.IsNullOrEmpty(ParamName1))
|
||||
b.Append(" [").Append(ParamName1).Append("] ");
|
||||
if (!string.IsNullOrEmpty(ParamName2))
|
||||
b.Append(" [").Append(ParamName2).Append("] ");
|
||||
foreach (var param in Params)
|
||||
b.Append(" [").Append(param.Name).Append("]");
|
||||
b.Append(" - ").Append(Description);
|
||||
return b;
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
$"{{{{{Aliases.MergeToString(", ")}}}, Handler: {Handler is null}, HandlerWithArg: {HandlerWithArg1 is null}}}";
|
||||
internal void Handle()
|
||||
{
|
||||
switch (Params.Length)
|
||||
{
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(Params.Length.ToString());
|
||||
case 0:
|
||||
Handler!.Invoke();
|
||||
break;
|
||||
case 1:
|
||||
if (Params[0].Value is null)
|
||||
throw new NullReferenceException($"Argument '{Aliases[0]}' hasnt got Param[0] value");
|
||||
Handler1Param!.Invoke(Params[0].Value!);
|
||||
break;
|
||||
case 2:
|
||||
if (Params[0].Value is null)
|
||||
throw new NullReferenceException($"Argument '{Aliases[0]}' hasnt got Param[0] value");
|
||||
if (Params[1].Value is null)
|
||||
throw new NullReferenceException($"Argument '{Aliases[0]}' hasnt got Param[1] value");
|
||||
Handler2Params!.Invoke(Params[0].Value!, Params[1].Value!);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,31 +2,135 @@ namespace DTLib.Console;
|
||||
|
||||
public class LaunchArgumentParser
|
||||
{
|
||||
private Dictionary<string, LaunchArgument> argDict = new();
|
||||
private List<LaunchArgument> argList = new();
|
||||
public bool ExitIfNoArgs = true;
|
||||
public string HelpMessageHeader = "USAGE:";
|
||||
public bool AllowedNoArguments;
|
||||
public bool AllowedUnknownArguments;
|
||||
// ReSharper disable once CollectionNeverQueried.Global
|
||||
public readonly List<string> UnknownArguments = new();
|
||||
|
||||
public class ExitAfterHelpException : Exception
|
||||
private readonly Dictionary<string, LaunchArgument> argDict = new();
|
||||
private readonly List<LaunchArgument> argList = new();
|
||||
|
||||
public LaunchArgumentParser()
|
||||
{
|
||||
internal ExitAfterHelpException() : base("your program can use this exception to exit after displaying help message")
|
||||
{ }
|
||||
var help = new LaunchArgument(new[] { "h", "help" },
|
||||
"shows help message", HelpHandler);
|
||||
Add(help);
|
||||
var helpArg = new LaunchArgument(new[] { "ha", "helparg" },
|
||||
"shows help message for specific argument",
|
||||
HelpArgHandler, "argument");
|
||||
Add(helpArg);
|
||||
}
|
||||
public LaunchArgumentParser(ICollection<LaunchArgument> arguments) : this() => WithArgs(arguments);
|
||||
public LaunchArgumentParser(params LaunchArgument[] arguments) : this() => WithArgs(arguments);
|
||||
|
||||
public LaunchArgumentParser WithArgs(IEnumerable<LaunchArgument> args)
|
||||
{
|
||||
foreach (var arg in args)
|
||||
Add(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LaunchArgumentParser WithArgs(params LaunchArgument[] args)
|
||||
{
|
||||
foreach (var arg in args)
|
||||
Add(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LaunchArgumentParser WithHelpMessageHeader(string header)
|
||||
{
|
||||
HelpMessageHeader = header;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LaunchArgumentParser AllowNoArguments()
|
||||
{
|
||||
AllowedNoArguments = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LaunchArgumentParser AllowUnknownArguments()
|
||||
{
|
||||
AllowedUnknownArguments = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public string CreateHelpMessage()
|
||||
{
|
||||
StringBuilder b = new();
|
||||
StringBuilder b = new(HelpMessageHeader);
|
||||
foreach (var arg in argList)
|
||||
arg.AppendHelpInfo(b).Append('\n');
|
||||
b.Remove(b.Length-1, 1);
|
||||
{
|
||||
b.Append('\n');
|
||||
arg.AppendHelpInfo(b);
|
||||
}
|
||||
return b.ToString();
|
||||
}
|
||||
|
||||
public string CreateHelpArgMessage(string argAlias)
|
||||
{
|
||||
StringBuilder b = new();
|
||||
var arg = Parse(argAlias);
|
||||
if(!TryParseArg(argAlias, out var arg))
|
||||
throw new Exception($"unknown argument '{argAlias}'");
|
||||
arg.AppendHelpInfo(b);
|
||||
return b.ToString();
|
||||
}
|
||||
|
||||
public void Add(LaunchArgument arg)
|
||||
{
|
||||
argList.Add(arg);
|
||||
foreach (string alias in arg.Aliases)
|
||||
argDict.Add(alias, arg);
|
||||
}
|
||||
|
||||
public bool TryParseArg(string argAlias, out LaunchArgument arg)
|
||||
{
|
||||
// different argument providing patterns
|
||||
arg = null!;
|
||||
return argAlias.StartsWith("--") && argDict.TryGetValue(argAlias.Substring(2), out arg) || // --arg
|
||||
argAlias.StartsWith('-') && argDict.TryGetValue(argAlias.Substring(1), out arg) || // -arg
|
||||
argAlias.StartsWith('/') && argDict.TryGetValue(argAlias.Substring(1), out arg); // /arg
|
||||
}
|
||||
|
||||
/// <param name="args">program launch args</param>
|
||||
/// <exception cref="Exception">argument {args[i]} should have a parameter after it</exception>
|
||||
/// <exception cref="NullReferenceException">argument hasn't got any handlers</exception>
|
||||
/// <exception cref="ExitAfterHelpException">happens after help message is displayed</exception>
|
||||
public void ParseAndHandle(string[] args)
|
||||
{
|
||||
// show help message and throw ExitAfterHelpException
|
||||
if (args.Length == 0 && !AllowedNoArguments)
|
||||
HelpHandler();
|
||||
|
||||
List<LaunchArgument> execQueue = new();
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (!TryParseArg(args[i], out var arg))
|
||||
{
|
||||
if (!AllowedUnknownArguments)
|
||||
throw new Exception($"unknown argument '{args[i]}'");
|
||||
UnknownArguments.Add(args[i]);
|
||||
}
|
||||
for (int j = 0; j < arg.Params.Length; j++)
|
||||
{
|
||||
if (++i >= args.Length)
|
||||
throw new Exception(
|
||||
$"argument '{arg.Aliases[0]}' should have parameter '{arg.Params[j]}' after it");
|
||||
arg.Params[j].Value = args[i];
|
||||
}
|
||||
|
||||
execQueue.Add(arg);
|
||||
}
|
||||
|
||||
// ascending sort by priority
|
||||
execQueue.Sort((a0, a1) => a0.Priority - a1.Priority);
|
||||
// finally executing handlers
|
||||
foreach (var a in execQueue)
|
||||
a.Handle();
|
||||
}
|
||||
|
||||
private void HelpHandler()
|
||||
{
|
||||
System.Console.WriteLine(CreateHelpMessage());
|
||||
@@ -39,104 +143,11 @@ public class LaunchArgumentParser
|
||||
throw new ExitAfterHelpException();
|
||||
}
|
||||
|
||||
|
||||
public LaunchArgumentParser()
|
||||
public class ExitAfterHelpException : Exception
|
||||
{
|
||||
var help = new LaunchArgument(new[] { "h", "help" },
|
||||
"shows help message", HelpHandler);
|
||||
Add(help);
|
||||
var helpArg = new LaunchArgument( new[]{ "ha", "helparg" },
|
||||
"shows help message for particular argument",
|
||||
HelpArgHandler, "argAlias");
|
||||
Add(helpArg);
|
||||
}
|
||||
|
||||
public LaunchArgumentParser WithNoExit()
|
||||
public ExitAfterHelpException()
|
||||
: base("your program can use this exception to exit after displaying help message")
|
||||
{
|
||||
ExitIfNoArgs = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LaunchArgumentParser(ICollection<LaunchArgument> arguments) : this()
|
||||
{
|
||||
foreach (var arg in arguments)
|
||||
Add(arg);
|
||||
}
|
||||
public LaunchArgumentParser(params LaunchArgument[] arguments) : this()
|
||||
{
|
||||
for (var i = 0; i < arguments.Length; i++)
|
||||
Add(arguments[i]);
|
||||
}
|
||||
|
||||
public void Add(LaunchArgument arg)
|
||||
{
|
||||
argList.Add(arg);
|
||||
for(int a=0; a<arg.Aliases.Length; a++)
|
||||
argDict.Add(arg.Aliases[a], arg);
|
||||
}
|
||||
|
||||
public LaunchArgument Parse(string argAlias)
|
||||
{
|
||||
// different argument providing patterns
|
||||
if (!argDict.TryGetValue(argAlias, out var arg) && // arg
|
||||
!(argAlias.StartsWith("--") && argDict.TryGetValue(argAlias.Substring(2), out arg)) && // --arg
|
||||
!(argAlias.StartsWith('-') && argDict.TryGetValue(argAlias.Substring(1), out arg)) && // -arg
|
||||
!(argAlias.StartsWith('/') && argDict.TryGetValue(argAlias.Substring(1), out arg))) // /arg
|
||||
throw new Exception($"invalid argument: {argAlias}\n{CreateHelpMessage()}");
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/// <param name="args">program launch args</param>
|
||||
/// <exception cref="Exception">argument {args[i]} should have a parameter after it</exception>
|
||||
/// <exception cref="NullReferenceException">argument hasn't got any handlers</exception>
|
||||
/// <exception cref="ExitAfterHelpException">happens after help message is displayed</exception>
|
||||
public void ParseAndHandle(string[] args)
|
||||
{
|
||||
// show help and throw
|
||||
if (args.Length == 0 && ExitIfNoArgs)
|
||||
HelpHandler();
|
||||
|
||||
List<LaunchArgument> execQueue = new();
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
LaunchArgument arg = Parse(args[i]);
|
||||
|
||||
switch (arg.RequiredArgsCount)
|
||||
{
|
||||
case 0:
|
||||
if (arg.Handler is null)
|
||||
throw new NullReferenceException($"argument <{args[i]}> hasn't got any handlers");
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
if (arg.HandlerWithArg1 is null)
|
||||
throw new NullReferenceException($"argument <{args[i]}> hasn't got any handlers");
|
||||
if (i + 1 >= args.Length)
|
||||
throw new Exception($"argument <{args[i]}> should have a parameter after it");
|
||||
string arg1 = args[++i];
|
||||
arg.Handler = () => arg.HandlerWithArg1(arg1);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (arg.HandlerWithArg2 is null)
|
||||
throw new NullReferenceException($"argument <{args[i]}> hasn't got any handlers");
|
||||
if (i + 2 >= args.Length)
|
||||
throw new Exception($"argument <{args[i]}> should have two params after it");
|
||||
string arg1 = args[++i], arg2 = args[++i];
|
||||
arg.Handler = () => arg.HandlerWithArg2(arg1, arg2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
execQueue.Add(arg);
|
||||
}
|
||||
|
||||
// ascending sort by priority
|
||||
execQueue.Sort((a0, a1) => a0.Priority-a1.Priority);
|
||||
// finally executing handlers
|
||||
foreach (var a in execQueue)
|
||||
a.Handler!();
|
||||
}
|
||||
}
|
||||
@@ -2,30 +2,35 @@
|
||||
<PropertyGroup>
|
||||
<!--package info-->
|
||||
<PackageId>DTLib</PackageId>
|
||||
<Version>1.3.3</Version>
|
||||
<Version>1.7.4</Version>
|
||||
<Authors>Timerix</Authors>
|
||||
<Description>Library for all my C# projects</Description>
|
||||
<RepositoryType>GIT</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/Timerix22/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/Timerix22/DTLib</PackageProjectUrl>
|
||||
<RepositoryUrl>https://timerix.ddns.net:3322/Timerix/DTLib</RepositoryUrl>
|
||||
<PackageProjectUrl>https://timerix.ddns.net:3322/Timerix/DTLib</PackageProjectUrl>
|
||||
<Configuration>Release</Configuration>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<!--compilation properties-->
|
||||
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<DebugType>embedded</DebugType>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<!--language features-->
|
||||
<LangVersion>12</LangVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<Nullable>enable</Nullable>
|
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--project files-->
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<Compile Remove="Experimental\**" />
|
||||
</ItemGroup>
|
||||
<!--external dependencies-->
|
||||
<ItemGroup >
|
||||
<Compile Remove="Experimental\ConsoleGUI\**" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--DTLib dependencies-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<ProjectReference Include="..\DTLib.Demystifier\DTLib.Demystifier.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' != 'Debug' ">
|
||||
<PackageReference Include="DTLib.Demystifier" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=console/@EntryIndexedValue">False</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -5,13 +5,7 @@ public static class DtsodConverter
|
||||
public static IDtsod ConvertVersion(IDtsod src, DtsodVersion targetVersion)
|
||||
=> targetVersion switch
|
||||
{
|
||||
DtsodVersion.V21 => new DtsodV21(src.ToDictionary()),
|
||||
DtsodVersion.V22 => throw new Exception("DtsodV22 is deprecated"),
|
||||
DtsodVersion.V23 => new DtsodV23(src.ToDictionary()),
|
||||
// DtsodVersion.V24 => new DtsodV24(src.ToDictionary()),
|
||||
#if DEBUG
|
||||
//DtsodVersion.V30 => new DtsodV30(src.ToDictionary()),
|
||||
#endif
|
||||
_ => throw new Exception($"DtsodConverter.Convert() error: unknown target version <{targetVersion}>"),
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace DTLib.Dtsod;
|
||||
|
||||
public abstract class DtsodDict<TKey, TVal> : IDictionary<TKey, TVal>, IDictionary
|
||||
public abstract class DtsodDict<TKey, TVal> : IDictionary<TKey, TVal>, IDictionary where TKey : notnull
|
||||
{
|
||||
// да, вместо собственной реализации интерфейса это ссылки на Dictionary
|
||||
readonly Dictionary<TKey, TVal> baseDict;
|
||||
@@ -18,7 +18,7 @@ public abstract class DtsodDict<TKey, TVal> : IDictionary<TKey, TVal>, IDictiona
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool TryGetValue(TKey key, out TVal value) => baseDict.TryGetValue(key, out value);
|
||||
public virtual bool TryGetValue(TKey key, out TVal value) => baseDict.TryGetValue(key, out value!);
|
||||
|
||||
public bool TrySetValue(TKey key, TVal value)
|
||||
{
|
||||
@@ -34,7 +34,7 @@ public abstract class DtsodDict<TKey, TVal> : IDictionary<TKey, TVal>, IDictiona
|
||||
public virtual void Add(KeyValuePair<TKey, TVal> pair)
|
||||
=> ((ICollection<KeyValuePair<TKey, TVal>>)baseDict).Add(pair);
|
||||
|
||||
public void Add(object key, object value)
|
||||
public void Add(object key, object? value)
|
||||
{
|
||||
((IDictionary) baseDict).Add(key, value);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public abstract class DtsodDict<TKey, TVal> : IDictionary<TKey, TVal>, IDictiona
|
||||
|
||||
public ICollection<TVal> Values => baseDict.Values;
|
||||
public bool IsReadOnly { get; } = false;
|
||||
public object this[object key]
|
||||
public object? this[object key]
|
||||
{
|
||||
get => ((IDictionary) baseDict)[key];
|
||||
set => ((IDictionary) baseDict)[key] = value;
|
||||
@@ -29,7 +29,7 @@ public class DtsodV23 : DtsodDict<string, dynamic>, IDtsod
|
||||
{
|
||||
string name = ReadName();
|
||||
if (name.IsNullOrEmpty()) goto end;
|
||||
dynamic value = ReadValue(out bool _);
|
||||
var value = ReadValue(out bool _);
|
||||
if (partOfDollarList)
|
||||
{
|
||||
if (!output.TryGetValue(name, out var dollarList))
|
||||
@@ -85,7 +85,7 @@ public class DtsodV23 : DtsodDict<string, dynamic>, IDtsod
|
||||
: throw new Exception("DtsodV23.Deserialize.ReadName() error: end of text\ntext:\n" + _text);
|
||||
}
|
||||
|
||||
dynamic ReadValue(out bool endOfList)
|
||||
dynamic? ReadValue(out bool endOfList)
|
||||
{
|
||||
endOfList = false;
|
||||
|
||||
@@ -160,7 +160,7 @@ public class DtsodV23 : DtsodDict<string, dynamic>, IDtsod
|
||||
return list;
|
||||
}
|
||||
|
||||
dynamic ParseValue(string value_str)
|
||||
dynamic? ParseValue(string value_str)
|
||||
{
|
||||
switch (value_str)
|
||||
{
|
||||
@@ -168,7 +168,7 @@ public class DtsodV23 : DtsodDict<string, dynamic>, IDtsod
|
||||
case "false":
|
||||
return value_str.ToBool();
|
||||
case "null":
|
||||
return null;
|
||||
return null!;
|
||||
default:
|
||||
if (value_str.Contains('"'))
|
||||
return value_str.Substring(1, value_str.Length - 2).Replace("\\\\","\\").Replace("\\\"","\"");
|
||||
@@ -220,7 +220,7 @@ public class DtsodV23 : DtsodDict<string, dynamic>, IDtsod
|
||||
}
|
||||
}
|
||||
|
||||
object value = null;
|
||||
object? value = null;
|
||||
while (++i < text.Length)
|
||||
{
|
||||
c = text[i];
|
||||
@@ -299,7 +299,7 @@ public class DtsodV23 : DtsodDict<string, dynamic>, IDtsod
|
||||
{ typeof(decimal), (val, b) => b.Append(val.ToString(CultureInfo.InvariantCulture)).Append("de") }
|
||||
};
|
||||
|
||||
protected StringBuilder Serialize(DtsodV23 dtsod, ref int tabscount, StringBuilder b = null)
|
||||
protected StringBuilder Serialize(DtsodV23 dtsod, ref int tabscount, StringBuilder? b = null)
|
||||
{
|
||||
tabscount++;
|
||||
b ??= new StringBuilder();
|
||||
6
DTLib/Dtsod/DtsodVersion.cs
Normal file
6
DTLib/Dtsod/DtsodVersion.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace DTLib.Dtsod;
|
||||
|
||||
public enum DtsodVersion : byte
|
||||
{
|
||||
V23 = 23
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System.Threading;
|
||||
using DTLib.Console;
|
||||
|
||||
namespace DTLib.Experimental
|
||||
{
|
||||
public class CompressedArray
|
||||
{
|
||||
public class Array1D<T> where T : IComparable<T>
|
||||
{
|
||||
byte[] Description;
|
||||
T[] Memory;
|
||||
|
||||
public Array1D() { }
|
||||
public Array1D(T[] sourceArray) => CompressArray(sourceArray);
|
||||
|
||||
public void CompressArray(T[] sourceArray)
|
||||
{
|
||||
var listMem = new List<T>();
|
||||
var listDesc = new List<byte>();
|
||||
T prevElement = sourceArray[0];
|
||||
listMem.Add(sourceArray[0]);
|
||||
listDesc.Add(1);
|
||||
byte repeats = 1;
|
||||
for (int i = 1; i < sourceArray.Length; i++)
|
||||
{
|
||||
if (prevElement.CompareTo(sourceArray[i]) == 0)
|
||||
repeats++;
|
||||
else
|
||||
{
|
||||
listMem.Add(sourceArray[i]);
|
||||
listDesc.Add(1);
|
||||
if (repeats > 1)
|
||||
{
|
||||
listDesc[listDesc.Count - 2] = repeats;
|
||||
repeats = 1;
|
||||
}
|
||||
}
|
||||
prevElement = sourceArray[i];
|
||||
}
|
||||
Memory = listMem.ToArray();
|
||||
Description = listDesc.ToArray();
|
||||
ColoredConsole.Write("b", "listMem.Count: ", "c", listMem.Count.ToString(), "b", " listDesc.Count: ", "c",
|
||||
$"{listDesc.Count}\n");
|
||||
for (short i = 0; i < listDesc.Count; i++)
|
||||
{
|
||||
ColoredConsole.Write("y", $"{Description[i]}:{Memory[i]}\n");
|
||||
}
|
||||
}
|
||||
|
||||
// блокирует обращение к памяти из нескольких потоков
|
||||
Mutex storageUsing = new();
|
||||
|
||||
// возвращает элемент по индексу так, как если бы шло обращение к обычном массиву
|
||||
public T GetElement(int index)
|
||||
{
|
||||
storageUsing.WaitOne();
|
||||
T output = default;
|
||||
int sum = 0;
|
||||
for (int i = 0; i < Description.Length; i++)
|
||||
{
|
||||
if (sum < index)
|
||||
sum += Description[i];
|
||||
else output = sum == index ? Memory[i] : Memory[i - 1];
|
||||
}
|
||||
storageUsing.ReleaseMutex();
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
using DTLib.Dtsod;
|
||||
using DTLib.Filesystem;
|
||||
using System.Collections.Generic;
|
||||
using static DTLib.PublicLog;
|
||||
|
||||
namespace DTLib.ConsoleGUI
|
||||
{
|
||||
public class Container : List<IDrawable>, IDrawable
|
||||
{
|
||||
public (ushort x, ushort y) AnchorPoint { get; set; }
|
||||
public ushort Width { get; set; }
|
||||
public ushort Height { get; set; }
|
||||
public char[] Textmap { get; private set; }
|
||||
public char[] Colormap { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
|
||||
public Container(string name, string layout_file)
|
||||
{
|
||||
Name=name;
|
||||
ParseLayoutFile(layout_file);
|
||||
}
|
||||
|
||||
void ParseLayoutFile(string layout_file)
|
||||
{
|
||||
DtsodV22 layout = new(File.ReadAllText(layout_file));
|
||||
AnchorPoint=(layout[Name]["anchor"][0], layout[Name]["anchor"][1]);
|
||||
Width=layout[Name]["width"];
|
||||
Height=layout[Name]["height"];
|
||||
foreach(string element_name in layout[Name]["children"].Keys)
|
||||
{
|
||||
switch(layout[Name]["children"][element_name]["type"])
|
||||
{
|
||||
case "label":
|
||||
Add(new Label(element_name,
|
||||
layout[Name]["children"][element_name]["resdir"]+$"\\{element_name}.textmap",
|
||||
layout[Name]["children"][element_name]["resdir"]+$"\\{element_name}.colormap")
|
||||
{
|
||||
AnchorPoint=(layout[Name]["children"][element_name]["anchor"][0],
|
||||
layout[Name]["children"][element_name]["anchor"][1])
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GenTextmap()
|
||||
{
|
||||
Textmap=new char[Width*Height];
|
||||
for(int i = 0; i<Textmap.Length; i++)
|
||||
Textmap[i]=' ';
|
||||
foreach(IDrawable element in this)
|
||||
{
|
||||
element.GenTextmap();
|
||||
Log("m", $"Length: {element.Textmap.Length} calculated: {element.Width*element.Height}\n");
|
||||
for(ushort y = 0; y<element.Height; y++)
|
||||
for(ushort x = 0; x<element.Width; x++)
|
||||
{
|
||||
//Textmap[(element.AnchorPoint.y + y) * Width + element.AnchorPoint.x + x] = element.Textmap[y * element.Width + x];
|
||||
Textmap[(y)*Width+x]=element.Textmap[y*element.Width+x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GenColormap()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace DTLib.ConsoleGUI
|
||||
{
|
||||
public class Control : Label
|
||||
{
|
||||
|
||||
public new void GenColormap()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public new void GenTextmap()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace DTLib.ConsoleGUI
|
||||
{
|
||||
public interface IDrawable
|
||||
{
|
||||
public (ushort x, ushort y) AnchorPoint { get; set; }
|
||||
public ushort Width { get; }
|
||||
public ushort Height { get; }
|
||||
public char[] Textmap { get; }
|
||||
public char[] Colormap { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public void GenTextmap();
|
||||
|
||||
public void GenColormap();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using DTLib.Filesystem;
|
||||
|
||||
namespace DTLib.ConsoleGUI
|
||||
{
|
||||
public class Label : IDrawable
|
||||
{
|
||||
public (ushort x, ushort y) AnchorPoint { get; set; } = (0, 0);
|
||||
public ushort Width { get; private set; }
|
||||
public ushort Height { get; private set; }
|
||||
public char[] Textmap { get; private set; }
|
||||
public char[] Colormap { get; private set; }
|
||||
|
||||
public string TextmapFile { get; set; }
|
||||
public string ColormapFile { get; set; }
|
||||
public string Name { get; init; }
|
||||
|
||||
public Label() { }
|
||||
|
||||
public Label(string name, string textmapFile, string colormapFile)
|
||||
{
|
||||
TextmapFile=textmapFile;
|
||||
ColormapFile=colormapFile;
|
||||
Name=name;
|
||||
}
|
||||
|
||||
public void GenColormap() => Colormap=File.ReadAllText(ColormapFile).ToCharArray();
|
||||
|
||||
public void GenTextmap()
|
||||
{
|
||||
Textmap=File.ReadAllText(TextmapFile).ToCharArray();
|
||||
Width=12;
|
||||
Height=3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using DTLib.Filesystem;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace DTLib.ConsoleGUI
|
||||
{
|
||||
//
|
||||
// создание gui из текста в консоли
|
||||
//
|
||||
public class Window : Container
|
||||
{
|
||||
|
||||
public Window(string layout_file) : base("window", layout_file)
|
||||
{
|
||||
Console.Clear();
|
||||
Console.SetWindowSize(Width+1, Height+1);
|
||||
Console.SetBufferSize(Width+1, Height+1);
|
||||
Console.OutputEncoding=Encoding.Unicode;
|
||||
Console.InputEncoding=Encoding.Unicode;
|
||||
Console.CursorVisible=false;
|
||||
}
|
||||
|
||||
// выводит все символы
|
||||
public void RenderFile(string file)
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine(File.ReadAllText(file));
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
GenTextmap();
|
||||
Console.WriteLine(SimpleConverter.MergeToString(Textmap));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
using DTLib.Filesystem;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DTLib.ConsoleGUI
|
||||
{
|
||||
//
|
||||
// создание gui из текста в консоли
|
||||
//
|
||||
public class WindowOld
|
||||
{
|
||||
public int WindowWidth { get; private set; }
|
||||
public int WindowHeight { get; private set; }
|
||||
public char[,] Text;
|
||||
public char[,] nowText;
|
||||
public char[,] TextColors;
|
||||
public char[,] nowTextColors;
|
||||
|
||||
public Container WindowContainer;
|
||||
|
||||
public WindowOld(int windowWidth, int windowHeight)
|
||||
{
|
||||
WindowWidth=windowWidth;
|
||||
WindowHeight=windowHeight;
|
||||
Text=new char[windowWidth, windowHeight];
|
||||
TextColors=new char[windowWidth, windowHeight];
|
||||
nowText=TextColors;
|
||||
nowTextColors=new char[windowWidth, windowHeight];
|
||||
Console.WindowWidth=WindowWidth+1;
|
||||
Console.WindowHeight=WindowHeight+1;
|
||||
Console.BufferWidth=WindowWidth+1;
|
||||
Console.BufferHeight=WindowHeight+1;
|
||||
Console.OutputEncoding=Encoding.Unicode;
|
||||
Console.InputEncoding=Encoding.Unicode;
|
||||
Console.CursorVisible=false;
|
||||
// заполнение массивов
|
||||
for(sbyte y = 0; y<WindowHeight; y++)
|
||||
{
|
||||
for(sbyte x = 0; x<WindowWidth; x++)
|
||||
{
|
||||
Text[x, y]=' ';
|
||||
TextColors[x, y]='w';
|
||||
}
|
||||
}
|
||||
nowText=TextColors;
|
||||
}
|
||||
|
||||
/*// считывает массив символов из файла
|
||||
// ширина и высота текста должны быть как указанные при инициализации объекта этого класса
|
||||
public void ReadFromFile(string path)
|
||||
{
|
||||
var r = new StreamReader(path, SimpleConverter.UTF8);
|
||||
char[] s = new char[1];
|
||||
// считывание текста
|
||||
sbyte y = 0, x = 0;
|
||||
r.Read(s, 0, 1);
|
||||
while (!r.EndOfStream && y < WindowHeight)
|
||||
{
|
||||
if (x == WindowWidth)
|
||||
{
|
||||
r.Read(s, 0, 1);
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Text[x, y] = s[0];
|
||||
x++;
|
||||
}
|
||||
r.Read(s, 0, 1);
|
||||
}
|
||||
r.Read(s, 0, 1);
|
||||
// считывание цвета
|
||||
// если не находит цвет в файле, оставляет старый
|
||||
if (s[0] == '\n')
|
||||
{
|
||||
r.Read(s, 0, 1);
|
||||
y = 0;
|
||||
x = 0;
|
||||
while (!r.EndOfStream && y < WindowHeight)
|
||||
{
|
||||
if (x == WindowWidth)
|
||||
{
|
||||
r.Read(s, 0, 1);
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
else
|
||||
{
|
||||
TextColors[x, y] = s[0];
|
||||
x++;
|
||||
}
|
||||
r.Read(s, 0, 1);
|
||||
}
|
||||
}
|
||||
r.Close();
|
||||
}*/
|
||||
|
||||
public void ResetCursor() => Console.SetCursorPosition(0, WindowHeight);
|
||||
|
||||
// заменяет символ выведенный, использовать после ShowAll()
|
||||
public void ChangeChar(sbyte x, sbyte y, char ch)
|
||||
{
|
||||
Text[x, y]=ch;
|
||||
nowText[x, y]=ch;
|
||||
Console.SetCursorPosition(x, y);
|
||||
ColoredConsole.Write(TextColors[x, y].ToString(), ch.ToString());
|
||||
}
|
||||
|
||||
public void ChangeColor(sbyte x, sbyte y, char color)
|
||||
{
|
||||
TextColors[x, y]=color;
|
||||
nowTextColors[x, y]=color;
|
||||
Console.SetCursorPosition(x, y);
|
||||
ColoredConsole.Write(color.ToString(), Text[x, y].ToString());
|
||||
}
|
||||
|
||||
public void ChangeCharAndColor(sbyte x, sbyte y, char color, char ch)
|
||||
{
|
||||
Text[x, y]=ch;
|
||||
nowText[x, y]=ch;
|
||||
TextColors[x, y]=color;
|
||||
nowTextColors[x, y]=color;
|
||||
Console.SetCursorPosition(x, y);
|
||||
ColoredConsole.Write(color.ToString(), ch.ToString());
|
||||
}
|
||||
|
||||
public void ChangeLine(sbyte x, sbyte y, char color, string line)
|
||||
{
|
||||
Console.SetCursorPosition(x, y);
|
||||
for(sbyte i = 0; i<line.Length; i++)
|
||||
{
|
||||
Text[x+i, y]=line[i];
|
||||
nowText[x+i, y]=line[i];
|
||||
TextColors[x+i, y]=color;
|
||||
nowTextColors[x+i, y]=color;
|
||||
}
|
||||
ColoredConsole.Write(color.ToString(), line);
|
||||
}
|
||||
|
||||
// выводит все символы
|
||||
public void ShowAll()
|
||||
{
|
||||
var l = new List<string>();
|
||||
for(sbyte y = 0; y<WindowHeight; y++)
|
||||
{
|
||||
for(sbyte x = 0; x<WindowWidth; x++)
|
||||
{
|
||||
l.Add(TextColors[x, y].ToString());
|
||||
l.Add(Text[x, y].ToString());
|
||||
nowText[x, y]=Text[x, y];
|
||||
nowTextColors[x, y]=TextColors[x, y];
|
||||
}
|
||||
l.Add("w");
|
||||
l.Add("\n");
|
||||
}
|
||||
ColoredConsole.Write(l.ToArray());
|
||||
//Console.WriteLine();
|
||||
}
|
||||
|
||||
public void UpdateAll()
|
||||
{
|
||||
for(sbyte y = 0; y<WindowHeight; y++)
|
||||
{
|
||||
for(sbyte x = 0; x<WindowWidth; x++)
|
||||
{
|
||||
Console.SetCursorPosition(x, y);
|
||||
if(TextColors[x, y]!=nowTextColors[x, y]||Text[x, y]!=nowText[x, y])
|
||||
{
|
||||
ColoredConsole.Write(TextColors[x, y].ToString(), Text[x, y].ToString());
|
||||
nowText[x, y]=Text[x, y];
|
||||
nowTextColors[x, y]=TextColors[x, y];
|
||||
}
|
||||
}
|
||||
Console.Write('\n');
|
||||
}
|
||||
}
|
||||
|
||||
public void RenderFile(string file)
|
||||
{
|
||||
Console.Clear();
|
||||
Console.WriteLine(File.ReadAllText(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace DTLib.Experimental;
|
||||
|
||||
// по идее это нужно, чтоб делать так: SomeEvent?.Invoke().Wait()
|
||||
public delegate Task EventHandlerAsyncDelegate();
|
||||
public delegate Task EventHandlerAsyncDelegate<T>(T e);
|
||||
public delegate Task EventHandlerAsyncDelegate<T0, T1>(T0 e0, T1 e1);
|
||||
public delegate Task EventHandlerAsyncDelegate<T0, T1, T2>(T0 e0, T1 e1, T2 e2);
|
||||
public delegate Task EventHandlerAsyncDelegate<T0, T1, T2, T3>(T0 e0, T1 e1, T2 e2, T3 e3);
|
||||
@@ -1,45 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace DTLib.Experimental;
|
||||
|
||||
public class FileInstance
|
||||
{
|
||||
public enum FileOpenMode
|
||||
{
|
||||
// open a file for reading
|
||||
Read=1,
|
||||
// (re)create a file for writing
|
||||
Write=2,
|
||||
// opens file for writing additional data to the end / creates new file
|
||||
Append=4,
|
||||
// (re)creates file for reading/writing
|
||||
ReadWrite=Read|Write,
|
||||
// opens file for readng/writing additional data to the end / creates new file
|
||||
ReadAppend=Read|Append
|
||||
}
|
||||
|
||||
public readonly FileOpenMode Mode;
|
||||
public readonly FileStream Stream;
|
||||
public string Name;
|
||||
|
||||
public FileInstance(string path, FileOpenMode mode)
|
||||
{
|
||||
if (path.IsNullOrEmpty())
|
||||
throw new NullReferenceException("path is null");
|
||||
if(!System.IO.File.Exists(path))
|
||||
{
|
||||
if (mode == FileOpenMode.Read)
|
||||
throw new Exception($"file <{path}> is not found");
|
||||
}
|
||||
Mode = mode;
|
||||
Stream = mode switch
|
||||
{
|
||||
FileOpenMode.Read => System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
|
||||
FileOpenMode.Write => System.IO.File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite),
|
||||
FileOpenMode.Append => System.IO.File.Open(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite),
|
||||
FileOpenMode.ReadWrite => System.IO.File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite),
|
||||
FileOpenMode.ReadAppend => System.IO.File.Open(path, FileMode.Append, FileAccess.ReadWrite, FileShare.ReadWrite),
|
||||
_ => throw new Exception($"unknown file mode: {mode}")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
namespace DTLib.Experimental.Reactive
|
||||
{
|
||||
public class ReactiveListener<T> : ReactiveProvider<T>
|
||||
{
|
||||
public ReactiveListener() { }
|
||||
public ReactiveListener(ReactiveStream<T> stream) : base(stream) { }
|
||||
public ReactiveListener(ICollection<ReactiveStream<T>> streams) : base(streams) { }
|
||||
|
||||
|
||||
public event Action<ReactiveStream<T>, T> ElementAddedEvent;
|
||||
void ElementAdded(ReactiveStream<T> stream, TimeSignedObject<T> e) => ElementAdded(stream, e.Value);
|
||||
void ElementAdded(ReactiveStream<T> stream, T e) =>
|
||||
Task.Run(() => ElementAddedEvent?.Invoke(stream, e));
|
||||
|
||||
public override void Join(ReactiveStream<T> stream)
|
||||
{
|
||||
base.Join(stream);
|
||||
lock (Streams) stream.ElementAddedEvent += ElementAdded;
|
||||
}
|
||||
|
||||
public override void Leave(ReactiveStream<T> stream)
|
||||
{
|
||||
base.Leave(stream);
|
||||
lock (Streams) stream.ElementAddedEvent -= ElementAdded;
|
||||
}
|
||||
|
||||
public T GetFirst()
|
||||
{
|
||||
if (Streams.Count == 0) throw new Exception("ReactiveListener is not connected to any streams");
|
||||
TimeSignedObject<T> rezult = null;
|
||||
foreach (ReactiveStream<T> stream in Streams)
|
||||
if (stream.Count != 0)
|
||||
{
|
||||
TimeSignedObject<T> e = stream[0];
|
||||
if (rezult is null) rezult = e;
|
||||
else if (rezult.Time > e.Time) rezult = e;
|
||||
}
|
||||
return rezult.Value;
|
||||
}
|
||||
public T GetLast()
|
||||
{
|
||||
if (Streams.Count == 0) throw new Exception("ReactiveListener is not connected to any streams");
|
||||
TimeSignedObject<T> rezult = null;
|
||||
foreach (ReactiveStream<T> stream in Streams)
|
||||
if (stream.Count != 0)
|
||||
{
|
||||
TimeSignedObject<T> e = stream[stream.Count - 1];
|
||||
if (rezult is null) rezult = e;
|
||||
else if (rezult.Time < e.Time) rezult = e;
|
||||
}
|
||||
return rezult.Value;
|
||||
}
|
||||
|
||||
public T FindOne(Func<T, bool> condition)
|
||||
{
|
||||
if (Streams.Count == 0) throw new Exception("ReactiveListener is not connected to any streams");
|
||||
foreach (ReactiveStream<T> stream in Streams)
|
||||
foreach (TimeSignedObject<T> el in stream)
|
||||
if (condition(el.Value))
|
||||
return el.Value;
|
||||
return default;
|
||||
}
|
||||
|
||||
public TimeSignedObject<T> FindOne(Func<TimeSignedObject<T>, bool> condition)
|
||||
{
|
||||
if (Streams.Count == 0) throw new Exception("ReactiveListener is not connected to any streams");
|
||||
foreach (ReactiveStream<T> stream in Streams)
|
||||
foreach (TimeSignedObject<T> el in stream)
|
||||
if (condition(el))
|
||||
return el;
|
||||
return default;
|
||||
}
|
||||
|
||||
public List<T> FindAll(Func<T, bool> condition)
|
||||
{
|
||||
if (Streams.Count == 0) throw new Exception("ReactiveListener is not connected to any streams");
|
||||
List<T> rezults = new();
|
||||
foreach (ReactiveStream<T> stream in Streams)
|
||||
foreach (TimeSignedObject<T> el in stream)
|
||||
if (condition(el.Value))
|
||||
rezults.Add(el.Value);
|
||||
return rezults;
|
||||
}
|
||||
|
||||
public List<TimeSignedObject<T>> FindAll(Func<TimeSignedObject<T>, bool> condition)
|
||||
{
|
||||
if (Streams.Count == 0) throw new Exception("ReactiveListener is not connected to any streams");
|
||||
List<TimeSignedObject<T>> rezults = new();
|
||||
foreach (ReactiveStream<T> stream in Streams)
|
||||
foreach (TimeSignedObject<T> el in stream)
|
||||
if (condition(el))
|
||||
rezults.Add(el);
|
||||
return rezults;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
namespace DTLib.Experimental.Reactive
|
||||
{
|
||||
public abstract class ReactiveProvider<T>
|
||||
{
|
||||
protected List<ReactiveStream<T>> Streams
|
||||
{
|
||||
get
|
||||
{ lock (_streams) return _streams; }
|
||||
set
|
||||
{ lock (_streams) _streams = value; }
|
||||
}
|
||||
private List<ReactiveStream<T>> _streams = new();
|
||||
|
||||
public ReactiveProvider() { }
|
||||
public ReactiveProvider(ReactiveStream<T> stream) => Streams.Add(stream);
|
||||
public ReactiveProvider(ICollection<ReactiveStream<T>> streams) => Streams = streams.ToList();
|
||||
|
||||
public virtual void Join(ReactiveStream<T> stream)
|
||||
{
|
||||
if (IsConnetcedTo(stream)) throw new Exception("ReactiveListener is already connected to the stream");
|
||||
Streams.Add(stream);
|
||||
}
|
||||
|
||||
public virtual void Leave(ReactiveStream<T> stream)
|
||||
{
|
||||
if (!Streams.Remove(stream)) throw new Exception("ReactiveListener is not connected to the stream");
|
||||
}
|
||||
|
||||
public bool IsConnetcedTo(ReactiveStream<T> stream) => Streams.Contains(stream);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace DTLib.Experimental.Reactive
|
||||
{
|
||||
public class ReactiveSender<T> : ReactiveProvider<T>
|
||||
{
|
||||
|
||||
public ReactiveSender() { }
|
||||
public ReactiveSender(ReactiveStream<T> stream) : base(stream) { }
|
||||
public ReactiveSender(ICollection<ReactiveStream<T>> streams) : base(streams) { }
|
||||
|
||||
public void Send(T e)
|
||||
{
|
||||
foreach (ReactiveStream<T> s in Streams)
|
||||
s.Add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
namespace DTLib.Experimental.Reactive
|
||||
{
|
||||
public class ReactiveStream<T> : IList<TimeSignedObject<T>>
|
||||
{
|
||||
List<TimeSignedObject<T>> _storage = new();
|
||||
List<TimeSignedObject<T>> Storage
|
||||
{
|
||||
get
|
||||
{ lock (_storage) return _storage; }
|
||||
}
|
||||
|
||||
public int Count => Storage.Count;
|
||||
|
||||
public TimeSignedObject<T> this[int index]
|
||||
{
|
||||
get => Storage[index];
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public event Action<ReactiveStream<T>, TimeSignedObject<T>> ElementAddedEvent;
|
||||
public void Add(TimeSignedObject<T> elem)
|
||||
{
|
||||
Storage.Add(elem);
|
||||
ElementAddedEvent?.Invoke(this, elem);
|
||||
}
|
||||
public void Add(T elem) => Add(new TimeSignedObject<T>(elem));
|
||||
|
||||
public void Clear() => Storage.Clear();
|
||||
public int IndexOf(TimeSignedObject<T> item) => Storage.IndexOf(item);
|
||||
public bool Contains(TimeSignedObject<T> item) => Storage.Contains(item);
|
||||
|
||||
public IEnumerator<TimeSignedObject<T>> GetEnumerator() => new Enumerator(Storage);
|
||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(Storage);
|
||||
|
||||
struct Enumerator : IEnumerator<TimeSignedObject<T>>
|
||||
{
|
||||
public Enumerator(List<TimeSignedObject<T>> storage)
|
||||
{
|
||||
_storage = storage;
|
||||
_index = storage.Count - 1;
|
||||
}
|
||||
|
||||
List<TimeSignedObject<T>> _storage;
|
||||
int _index;
|
||||
public TimeSignedObject<T> Current => _storage[_index];
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
public void Dispose() => _storage = null;
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_index < 0)
|
||||
return false;
|
||||
_index--;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset() => _index = _storage.Count - 1;
|
||||
}
|
||||
|
||||
bool ICollection<TimeSignedObject<T>>.IsReadOnly { get; } = false;
|
||||
|
||||
public void Insert(int index, TimeSignedObject<T> item) => throw new NotImplementedException();
|
||||
public void RemoveAt(int index) => throw new NotImplementedException();
|
||||
public void CopyTo(TimeSignedObject<T>[] array, int arrayIndex) => throw new NotImplementedException();
|
||||
public bool Remove(TimeSignedObject<T> item) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace DTLib.Experimental.Reactive
|
||||
{
|
||||
public class TimeSignedObject<T>
|
||||
{
|
||||
public T Value { get; init; }
|
||||
public long Time { get; init; }
|
||||
|
||||
public TimeSignedObject(T value)
|
||||
{
|
||||
Value = value;
|
||||
Time = DateTime.Now.Ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
namespace DTLib.Experimental
|
||||
{
|
||||
//
|
||||
// Вычисление псевдослучайного числа из множества параметров.
|
||||
// Работает медленнее чем класс System.Random, но выдаёт более случайные значения
|
||||
//
|
||||
public class SecureRandom
|
||||
{
|
||||
/*private RNGCryptoServiceProvider crypt = new();
|
||||
|
||||
// получение массива случайных байтов
|
||||
public byte[] GenBytes(uint length)
|
||||
{
|
||||
byte[] output = new byte[length];
|
||||
crypt.GetNonZeroBytes(output);
|
||||
return output;
|
||||
}
|
||||
|
||||
// получение случайного числа от 0 до 2147483647
|
||||
public int NextInt(uint from, int to)
|
||||
{
|
||||
int output = 0;
|
||||
int rez = 0;
|
||||
while (true)
|
||||
{
|
||||
rez = output * 10 + NextBytes(1)[0];
|
||||
if (rez < to && rez > from)
|
||||
{
|
||||
output = rez;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
// включает init и record из c# 9.0
|
||||
#if !NET6_0 && !NET7_0 && !NET8_0
|
||||
namespace System.Runtime.CompilerServices;
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class IsExternalInit { }
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#if NETSTANDARD2_1 || NET6_0 || NET7_0 || NET8_0
|
||||
#if NETSTANDARD2_1
|
||||
namespace DTLib.Extensions;
|
||||
|
||||
public static class SpanHelper
|
||||
|
||||
37
DTLib/Extensions/TaskExtensions.cs
Normal file
37
DTLib/Extensions/TaskExtensions.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace DTLib.Extensions;
|
||||
|
||||
public static class TaskExtensions
|
||||
{
|
||||
// https://stackoverflow.com/a/69861689
|
||||
public static Task<T> AsCancellable<T>(this Task<T> task, CancellationToken token)
|
||||
{
|
||||
if (!token.CanBeCanceled)
|
||||
{
|
||||
return task;
|
||||
}
|
||||
|
||||
var tcs = new TaskCompletionSource<T>();
|
||||
// This cancels the returned task:
|
||||
// 1. If the token has been canceled, it cancels the TCS straightaway
|
||||
// 2. Otherwise, it attempts to cancel the TCS whenever
|
||||
// the token indicates cancelled
|
||||
token.Register(() => tcs.TrySetCanceled(token),
|
||||
useSynchronizationContext: false);
|
||||
|
||||
task.ContinueWith(t =>
|
||||
{
|
||||
// Complete the TCS per task status
|
||||
// If the TCS has been cancelled, this continuation does nothing
|
||||
if (task.IsCanceled)
|
||||
tcs.TrySetCanceled();
|
||||
else if (task.IsFaulted)
|
||||
tcs.TrySetException(t.Exception!);
|
||||
else tcs.TrySetResult(t.Result);
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskContinuationOptions.ExecuteSynchronously,
|
||||
TaskScheduler.Default);
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public static class Directory
|
||||
|
||||
System.IO.Directory.CreateDirectory(dir.Str);
|
||||
}
|
||||
|
||||
/// копирует все файлы и папки
|
||||
public static void Copy(IOPath sourceDir, IOPath newDir, bool owerwrite)
|
||||
{
|
||||
@@ -32,7 +33,7 @@ public static class Directory
|
||||
Copy_internal(sourceDir, newDir, owerwrite, conflicts);
|
||||
}
|
||||
|
||||
private static void Copy_internal(IOPath sourceDir, IOPath newDir, bool owerwrite, List<IOPath> conflicts)
|
||||
private static void Copy_internal(IOPath sourceDir, IOPath newDir, bool owerwrite, List<IOPath>? conflicts)
|
||||
{
|
||||
bool countConflicts = conflicts is not null;
|
||||
List<IOPath> files = GetAllFiles(sourceDir);
|
||||
@@ -41,7 +42,7 @@ public static class Directory
|
||||
{
|
||||
var newfile = files[i].ReplaceBase(sourceDir, newDir);
|
||||
if (countConflicts && File.Exists(newfile))
|
||||
conflicts.Add(newfile);
|
||||
conflicts!.Add(newfile);
|
||||
File.Copy(files[i], newfile, owerwrite);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +55,7 @@ public static class Directory
|
||||
Delete(target_path);
|
||||
else throw new Exception($"directory {target_path} already exists");
|
||||
}
|
||||
else Directory.Create(target_path.ParentDir());
|
||||
else Create(target_path.ParentDir());
|
||||
System.IO.Directory.Move(current_path.Str, target_path.Str);
|
||||
}
|
||||
|
||||
@@ -86,9 +87,9 @@ public static class Directory
|
||||
all_subdirs = new List<IOPath>();
|
||||
return GetAllFiles_internal(dir, all_subdirs);
|
||||
}
|
||||
private static List<IOPath> GetAllFiles_internal(IOPath dir, List<IOPath> all_subdirs)
|
||||
|
||||
private static List<IOPath> GetAllFiles_internal(IOPath dir, List<IOPath>? all_subdirs)
|
||||
{
|
||||
bool rememberSubdirs = all_subdirs is not null;
|
||||
var all_files = new List<IOPath>();
|
||||
IOPath[] cur_files = GetFiles(dir);
|
||||
for (int i = 0; i < cur_files.Length; i++)
|
||||
@@ -96,20 +97,12 @@ public static class Directory
|
||||
IOPath[] cur_subdirs = GetDirectories(dir);
|
||||
for (int i = 0; i < cur_subdirs.Length; i++)
|
||||
{
|
||||
if(rememberSubdirs)
|
||||
all_subdirs.Add(cur_subdirs[i]);
|
||||
all_subdirs?.Add(cur_subdirs[i]);
|
||||
all_files.AddRange(GetAllFiles_internal(cur_subdirs[i], all_subdirs));
|
||||
}
|
||||
|
||||
return all_files;
|
||||
}
|
||||
|
||||
public static string GetCurrent() => System.IO.Directory.GetCurrentDirectory();
|
||||
|
||||
public static void CreateSymlink(string sourcePath, string symlinkPath)
|
||||
{
|
||||
if (symlinkPath.Contains(Path.Sep))
|
||||
Create(Path.ParentDir(symlinkPath));
|
||||
if (!Symlink.CreateSymbolicLink(symlinkPath, sourcePath, Symlink.SymlinkTarget.Directory))
|
||||
throw new InvalidOperationException($"some error occured while creating symlink\nDirectory.CreateSymlink({symlinkPath}, {sourcePath})");
|
||||
}
|
||||
public static IOPath GetCurrent() => new IOPath(System.IO.Directory.GetCurrentDirectory(), true);
|
||||
}
|
||||
@@ -5,14 +5,14 @@ namespace DTLib.Filesystem;
|
||||
|
||||
public static class EmbeddedResources
|
||||
{
|
||||
public static Stream GetResourceStream(string resourcePath, Assembly assembly = null)
|
||||
public static Stream GetResourceStream(string resourcePath, Assembly? assembly = null)
|
||||
{
|
||||
assembly ??= Assembly.GetCallingAssembly();
|
||||
return assembly.GetManifestResourceStream(resourcePath)
|
||||
?? throw new Exception($"embedded resource <{resourcePath}> not found in assembly {assembly.FullName}");
|
||||
}
|
||||
|
||||
public static byte[] ReadBynary(string resourcePath, Assembly assembly = null)
|
||||
public static byte[] ReadBynary(string resourcePath, Assembly? assembly = null)
|
||||
{
|
||||
// is required to get the Assembly called ReadBynary
|
||||
// otherwise Assembly.GetCallingAssembly() in GetResourceStream will get DTLib assembly always
|
||||
@@ -22,14 +22,14 @@ public static class EmbeddedResources
|
||||
}
|
||||
|
||||
|
||||
public static string ReadText(string resourcePath, Assembly assembly = null)
|
||||
public static string ReadText(string resourcePath, Assembly? assembly = null)
|
||||
{
|
||||
assembly ??= Assembly.GetCallingAssembly();
|
||||
using var reader = new StreamReader(GetResourceStream(resourcePath, assembly));
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
public static void CopyToFile(string resourcePath, string filePath, Assembly assembly = null)
|
||||
public static void CopyToFile(string resourcePath, string filePath, Assembly? assembly = null)
|
||||
{
|
||||
assembly ??= Assembly.GetCallingAssembly();
|
||||
using var file = File.OpenWrite(filePath);
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
namespace DTLib.Filesystem;
|
||||
using FileMode = System.IO.FileMode;
|
||||
using FileAccess = System.IO.FileAccess;
|
||||
|
||||
namespace DTLib.Filesystem;
|
||||
|
||||
public static class File
|
||||
{
|
||||
/// возвращает размер файла в байтах
|
||||
public static long GetSize(IOPath file) => new System.IO.FileInfo(file.Str).Length;
|
||||
|
||||
public static bool Exists(IOPath file)
|
||||
{
|
||||
if (System.IO.File.Exists(file.Str)) return true;
|
||||
return false;
|
||||
}
|
||||
public static bool Exists(IOPath file) => System.IO.File.Exists(file.Str);
|
||||
|
||||
/// если файл не существует, создаёт файл с папками из его пути и закрывает этот фвйл
|
||||
public static void Create(IOPath file)
|
||||
@@ -18,23 +17,22 @@ public static class File
|
||||
|
||||
Directory.Create(file.ParentDir());
|
||||
using System.IO.FileStream stream = System.IO.File.Create(file.Str);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public static void Copy(IOPath srcPath, IOPath newPath, bool overwrite)
|
||||
{
|
||||
if (Exists(newPath))
|
||||
{
|
||||
if(overwrite) System.IO.File.Delete(newPath.Str);
|
||||
if (overwrite)
|
||||
Delete(newPath);
|
||||
else throw new Exception($"file <{newPath}> alredy exists");
|
||||
}
|
||||
else Directory.Create(newPath.ParentDir());
|
||||
using var srcFile=System.IO.File.Open(srcPath.Str, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
|
||||
using var newFile=System.IO.File.Open(newPath.Str, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.ReadWrite);
|
||||
|
||||
using var srcFile = OpenRead(srcPath);
|
||||
using var newFile = OpenWrite(newPath);
|
||||
srcFile.CopyTo(newFile);
|
||||
srcFile.Close();
|
||||
newFile.Flush();
|
||||
newFile.Close();
|
||||
}
|
||||
|
||||
public static void Move(IOPath current_path, IOPath target_path, bool overwrite)
|
||||
@@ -46,6 +44,7 @@ public static class File
|
||||
else throw new Exception($"file {target_path} already exists");
|
||||
}
|
||||
else Directory.Create(target_path.ParentDir());
|
||||
|
||||
System.IO.File.Move(current_path.Str, target_path.Str);
|
||||
}
|
||||
|
||||
@@ -53,62 +52,81 @@ public static class File
|
||||
|
||||
public static byte[] ReadAllBytes(IOPath file)
|
||||
{
|
||||
|
||||
using System.IO.FileStream stream = OpenRead(file);
|
||||
int size = GetSize(file).ToInt();
|
||||
byte[] output = new byte[size];
|
||||
if (stream.Read(output, 0, size) < size)
|
||||
throw new Exception("can't read all bytes");
|
||||
stream.Close();
|
||||
return output;
|
||||
}
|
||||
|
||||
public static string ReadAllText(IOPath file) => ReadAllBytes(file).BytesToString(StringConverter.UTF8);
|
||||
|
||||
public static async Task<byte[]> ReadAllBytesAsync(IOPath file)
|
||||
{
|
||||
using System.IO.FileStream stream = OpenRead(file);
|
||||
int size = GetSize(file).ToInt();
|
||||
byte[] output = new byte[size];
|
||||
if (await stream.ReadAsync(output, 0, size).ConfigureAwait(false) < size)
|
||||
throw new Exception("can't read all bytes");
|
||||
return output;
|
||||
}
|
||||
|
||||
public static string ReadAllText(IOPath file) =>
|
||||
ReadAllBytes(file).BytesToString(StringConverter.UTF8);
|
||||
public static async Task<string> ReadAllTextAsync(IOPath file) =>
|
||||
(await ReadAllBytesAsync(file)).BytesToString(StringConverter.UTF8);
|
||||
|
||||
public static void WriteAllBytes(IOPath file, byte[] content)
|
||||
{
|
||||
using System.IO.FileStream stream = OpenWrite(file);
|
||||
stream.Write(content, 0, content.Length);
|
||||
stream.Close();
|
||||
}
|
||||
public static async Task WriteAllBytesAsync(IOPath file, byte[] content)
|
||||
{
|
||||
using System.IO.FileStream stream = OpenWrite(file);
|
||||
await stream.WriteAsync(content, 0, content.Length);
|
||||
}
|
||||
|
||||
public static void WriteAllText(IOPath file, string content) => WriteAllBytes(file, content.ToBytes(StringConverter.UTF8));
|
||||
public static void WriteAllText(IOPath file, string content) =>
|
||||
WriteAllBytes(file, content.ToBytes(StringConverter.UTF8));
|
||||
public static async Task WriteAllTextAsync(IOPath file, string content) =>
|
||||
await WriteAllBytesAsync(file, content.ToBytes(StringConverter.UTF8));
|
||||
|
||||
public static void AppendAllBytes(IOPath file, byte[] content)
|
||||
{
|
||||
using System.IO.FileStream stream = OpenAppend(file);
|
||||
stream.Write(content, 0, content.Length);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public static void AppendAllText(IOPath file, string content) => AppendAllBytes(file, content.ToBytes(StringConverter.UTF8));
|
||||
public static async Task AppendAllBytesAsync(IOPath file, byte[] content)
|
||||
{
|
||||
using System.IO.FileStream stream = OpenAppend(file);
|
||||
await stream.WriteAsync(content, 0, content.Length);
|
||||
}
|
||||
|
||||
public static void AppendAllText(IOPath file, string content) =>
|
||||
AppendAllBytes(file, content.ToBytes(StringConverter.UTF8));
|
||||
|
||||
public static async Task AppendAllTextAsync(IOPath file, string content) =>
|
||||
await AppendAllBytesAsync(file, content.ToBytes(StringConverter.UTF8));
|
||||
|
||||
public static System.IO.FileStream OpenRead(IOPath file)
|
||||
{
|
||||
if (!Exists(file))
|
||||
throw new Exception($"file not found: <{file}>");
|
||||
return System.IO.File.Open(file.Str, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
|
||||
return System.IO.File.Open(file.Str, FileMode.Open, FileAccess.Read,
|
||||
System.IO.FileShare.ReadWrite | System.IO.FileShare.Delete);
|
||||
}
|
||||
|
||||
public static System.IO.FileStream OpenWrite(IOPath file)
|
||||
{
|
||||
if (Exists(file))
|
||||
Delete(file);
|
||||
Create(file);
|
||||
return System.IO.File.Open(file.Str, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write, System.IO.FileShare.ReadWrite);
|
||||
Directory.Create(file.ParentDir());
|
||||
return System.IO.File.Open(file.Str, FileMode.Create, FileAccess.Write, System.IO.FileShare.Read);
|
||||
}
|
||||
|
||||
public static System.IO.FileStream OpenAppend(IOPath file)
|
||||
{
|
||||
|
||||
Create(file);
|
||||
return System.IO.File.Open(file.Str, System.IO.FileMode.Append, System.IO.FileAccess.Write, System.IO.FileShare.ReadWrite);
|
||||
}
|
||||
|
||||
public static void CreateSymlink(IOPath sourcePath, IOPath symlinkPath)
|
||||
{
|
||||
if (symlinkPath.Contains(Path.Sep))
|
||||
Directory.Create(symlinkPath.ParentDir());
|
||||
if (!Symlink.CreateSymbolicLink(symlinkPath.Str, sourcePath.Str, Symlink.SymlinkTarget.File))
|
||||
throw new InvalidOperationException($"some error occured while creating symlink\nFile.CreateSymlink({symlinkPath}, {sourcePath})");
|
||||
Directory.Create(file.ParentDir());
|
||||
return System.IO.File.Open(file.Str, FileMode.Append, FileAccess.Write, System.IO.FileShare.Read);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#if NETSTANDARD2_1 || NET6_0 || NET7_0 || NET8_0
|
||||
#if NETSTANDARD2_1 || NET6_0_OR_GREATER
|
||||
#define USE_SPAN
|
||||
#endif
|
||||
|
||||
@@ -15,43 +15,54 @@ public readonly struct IOPath
|
||||
|
||||
public IOPath(char[] path, bool separatorsFixed=false)
|
||||
{
|
||||
if (path.Length == 0)
|
||||
throw new Exception("path is null or empty");
|
||||
Str = separatorsFixed ? new string(path) : FixSeparators(path);
|
||||
}
|
||||
|
||||
public IOPath(string path, bool separatorsFixed=false)
|
||||
public IOPath(string path, bool separatorsFixed=false) : this(path.ToCharArray(), separatorsFixed)
|
||||
{
|
||||
if (path.IsNullOrEmpty())
|
||||
throw new Exception("path is null or empty");
|
||||
Str = separatorsFixed ? path : FixSeparators(path.ToCharArray());
|
||||
}
|
||||
|
||||
static string FixSeparators(char[] path)
|
||||
{
|
||||
int length = path.Length;
|
||||
if (path[length-1] == Path.Sep || path[length-1] == Path.NotSep)
|
||||
char lastChar = path[path.Length - 1];
|
||||
if (lastChar == Path.Sep || lastChar == Path.NotSep)
|
||||
length--; // removing trailing sep
|
||||
char[] fixed_path = new char[length];
|
||||
StringBuilder sb = new StringBuilder();
|
||||
bool prevWasSeparator = false;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (path[i] == Path.NotSep)
|
||||
fixed_path[i] = Path.Sep;
|
||||
else fixed_path[i] = path[i];
|
||||
{
|
||||
// prevent double separators like this "a//b"
|
||||
if(!prevWasSeparator)
|
||||
sb.Append(Path.Sep);
|
||||
prevWasSeparator = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(path[i]);
|
||||
prevWasSeparator = false;
|
||||
}
|
||||
return new string(fixed_path);
|
||||
}
|
||||
|
||||
public static IOPath[] ArrayCast(string[] a, bool correct_separators=false)
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static IOPath[] ArrayCast(string[] a, bool separatorsFixed=false)
|
||||
{
|
||||
IOPath[] b = new IOPath[a.Length];
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
b[i] = new IOPath(a[i], correct_separators);
|
||||
b[i] = new IOPath(a[i], separatorsFixed);
|
||||
return b;
|
||||
}
|
||||
public static IOPath[] ListCast(IList<string> a, bool correct_separators=false)
|
||||
public static IOPath[] ListCast(IList<string> a, bool separatorsFixed=false)
|
||||
{
|
||||
IOPath[] b = new IOPath[a.Count];
|
||||
for (int i = 0; i < a.Count; i++)
|
||||
b[i] = new IOPath(a[i], correct_separators);
|
||||
b[i] = new IOPath(a[i], separatorsFixed);
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -63,7 +74,7 @@ public readonly struct IOPath
|
||||
public static explicit operator string(IOPath p) => p.Str;
|
||||
public override string ToString() => Str;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is null) return false;
|
||||
return Str == obj.ToString();
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
namespace DTLib.Filesystem;
|
||||
|
||||
//
|
||||
// некоторые старые методы, которые хорошо бы вырезать
|
||||
//
|
||||
public static class OldFilework
|
||||
{
|
||||
// чтение параметров из конфига
|
||||
public static string ReadFromConfig(string configfile, string key)
|
||||
{
|
||||
lock (new object())
|
||||
{
|
||||
key += ": ";
|
||||
using var reader = new System.IO.StreamReader(configfile);
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string st = reader.ReadLine();
|
||||
if (st.StartsWith(key))
|
||||
{
|
||||
string value = "";
|
||||
for (int i = key.Length; i < st.Length; i++)
|
||||
{
|
||||
if (st[i] == '#')
|
||||
return value;
|
||||
if (st[i] == '%')
|
||||
{
|
||||
bool stop = false;
|
||||
string placeholder = "";
|
||||
i++;
|
||||
while (!stop)
|
||||
{
|
||||
if (st[i] == '%')
|
||||
{
|
||||
stop = true;
|
||||
value += ReadFromConfig(configfile, placeholder);
|
||||
}
|
||||
else
|
||||
{
|
||||
placeholder += st[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
value += st[i];
|
||||
}
|
||||
reader.Close();
|
||||
//if (value.NullOrEmpty()) throw new System.Exception($"ReadFromConfig({configfile}, {key}) error: key not found");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
reader.Close();
|
||||
throw new Exception($"ReadFromConfig({configfile}, {key}) error: key not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,19 @@ public static class Path
|
||||
char c = r[i];
|
||||
switch (c)
|
||||
{
|
||||
case '\n': case '\r':
|
||||
case ':': case ';':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case ':':
|
||||
case ';':
|
||||
break;
|
||||
case '/': case '\\':
|
||||
case '/':
|
||||
case '\\':
|
||||
b.Append('-');
|
||||
break;
|
||||
case '<': case '>':
|
||||
case '?': case '|':
|
||||
case '<':
|
||||
case '>':
|
||||
case '?':
|
||||
case '|':
|
||||
b.Append('_');
|
||||
break;
|
||||
case '"':
|
||||
@@ -46,6 +51,7 @@ public static class Path
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new IOPath(b.ToString(), true);
|
||||
}
|
||||
|
||||
@@ -71,6 +77,7 @@ public static class Path
|
||||
}
|
||||
else needSeparator[i] = false;
|
||||
}
|
||||
|
||||
lengthSum += parts[parts.Length - 1].Length;
|
||||
var buffer = new char[lengthSum];
|
||||
parts[0].Str.CopyTo(buffer, 0);
|
||||
@@ -89,20 +96,32 @@ public static class Path
|
||||
/// returns just dir name or file name with extension
|
||||
public static IOPath LastName(this IOPath path)
|
||||
{
|
||||
if(path.Length < 2)
|
||||
return path;
|
||||
int i = path.LastIndexOf(Sep);
|
||||
if (i == path.Length - 1) // ends with separator
|
||||
i = path.LastIndexOf(Sep, i - 1);
|
||||
if (i == -1) return path;
|
||||
if (i == -1)
|
||||
return path;
|
||||
return path.Substring(i + 1);
|
||||
}
|
||||
|
||||
public static IOPath Extension(this IOPath path)
|
||||
{
|
||||
int i = path.LastIndexOf('.');
|
||||
if (i == -1) return LastName(path);
|
||||
if (i == -1)
|
||||
return LastName(path);
|
||||
return path.Substring(i + 1);
|
||||
}
|
||||
|
||||
public static IOPath RemoveExtension(this IOPath path)
|
||||
{
|
||||
int i = path.LastIndexOf('.');
|
||||
if (i > 0)
|
||||
return path.Substring(0, i);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static IOPath ParentDir(this IOPath path)
|
||||
{
|
||||
int i = path.LastIndexOf(Sep);
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DTLib.Filesystem;
|
||||
|
||||
internal class Symlink
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern bool CreateSymbolicLink(string symlinkName, string sourceName, SymlinkTarget type);
|
||||
|
||||
internal enum SymlinkTarget
|
||||
{
|
||||
File,
|
||||
Directory
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace DTLib;
|
||||
|
||||
//
|
||||
// хеширует массивы байтов алшоритмом SHA256 и файлы алгоримом XXHash32
|
||||
//
|
||||
public class Hasher
|
||||
{
|
||||
readonly HashAlgorithm sha256 = SHA256.Create();
|
||||
readonly HashAlgorithm xxh32 = XXHash32.Create();
|
||||
|
||||
// хеш массива
|
||||
public byte[] Hash(byte[] input) => sha256.ComputeHash(input);
|
||||
|
||||
// хеш из двух массивов
|
||||
public byte[] Hash(byte[] input, byte[] salt)
|
||||
{
|
||||
var rez = new List<byte>();
|
||||
rez.AddRange(input);
|
||||
rez.AddRange(salt);
|
||||
return sha256.ComputeHash(rez.ToArray());
|
||||
}
|
||||
|
||||
// хеш двух массивов зацикленный
|
||||
public byte[] HashCycled(byte[] input, byte[] salt, ushort cycles)
|
||||
{
|
||||
for (uint i = 0; i < cycles; i++)
|
||||
{
|
||||
input = Hash(input, salt);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
// хеш зацикленный
|
||||
public byte[] HashCycled(byte[] input, ushort cycles)
|
||||
{
|
||||
for (uint i = 0; i < cycles; i++)
|
||||
{
|
||||
input = Hash(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
// хеш файла
|
||||
public byte[] HashFile(IOPath filename)
|
||||
{
|
||||
using System.IO.FileStream fileStream = File.OpenRead(filename);
|
||||
//var then = DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second;
|
||||
byte[] hash = xxh32.ComputeHash(fileStream);
|
||||
//var now = DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second;
|
||||
//InternalLog.Log($"xxh32 hash: {hash.HashToString()} time: {now - then}");
|
||||
fileStream.Close();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
9
DTLib/InitSetterFix.cs
Normal file
9
DTLib/InitSetterFix.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
// включает init и record из c# 9.0
|
||||
#if NETSTANDARD
|
||||
using System.ComponentModel;
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class IsExternalInit { }
|
||||
}
|
||||
#endif
|
||||
@@ -2,23 +2,16 @@ namespace DTLib.Logging;
|
||||
|
||||
public class DefaultLogFormat : ILogFormat
|
||||
{
|
||||
|
||||
public bool PrintTimeStamp { get; set; }
|
||||
public bool PrintContext { get; set; }
|
||||
public bool PrintSeverity { get; set; }
|
||||
|
||||
public DefaultLogFormat(bool printTimeStamp = true, bool printContext = true, bool printSeverity = true)
|
||||
{
|
||||
PrintTimeStamp = printTimeStamp;
|
||||
PrintContext = printContext;
|
||||
PrintSeverity = printSeverity;
|
||||
}
|
||||
public bool PrintTimeStamp { get; set; } = true;
|
||||
public bool PrintContext { get; set; } = true;
|
||||
public bool PrintSeverity { get; set; } = true;
|
||||
public string TimeStampFormat { get; set; } = MyTimeFormat.ForText;
|
||||
|
||||
public string CreateMessage(string context, LogSeverity severity, object message)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (PrintTimeStamp)
|
||||
sb.Append('[').Append(DateTime.Now.ToString(MyTimeFormat.ForText)).Append(']');
|
||||
sb.Append('[').Append(DateTime.Now.ToString(TimeStampFormat)).Append(']');
|
||||
if (PrintContext && PrintSeverity)
|
||||
sb.Append('[').Append(context).Append('/').Append(severity.ToString()).Append(']');
|
||||
else if(PrintContext)
|
||||
@@ -28,7 +21,6 @@ public class DefaultLogFormat : ILogFormat
|
||||
if (sb.Length != 0)
|
||||
sb.Append(": ");
|
||||
sb.Append(message);
|
||||
sb.Append('\n');
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,5 @@ namespace DTLib.Logging;
|
||||
|
||||
public interface ILogFormat
|
||||
{
|
||||
bool PrintTimeStamp { get; set; }
|
||||
bool PrintContext { get; set; }
|
||||
bool PrintSeverity { get; set; }
|
||||
|
||||
string CreateMessage(string context, LogSeverity severity, object message);
|
||||
}
|
||||
9
DTLib/Logging/LogSeverity.cs
Normal file
9
DTLib/Logging/LogSeverity.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace DTLib.Logging;
|
||||
|
||||
public enum LogSeverity
|
||||
{
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error
|
||||
}
|
||||
@@ -1,14 +1,6 @@
|
||||
namespace DTLib.Logging;
|
||||
|
||||
public enum LogSeverity
|
||||
{
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error
|
||||
}
|
||||
|
||||
internal static class LogSeverityHelper
|
||||
public static class LogSeverityHelper
|
||||
{
|
||||
public static bool CheckSeverity(this ILogger logger, LogSeverity severity)
|
||||
=> severity switch
|
||||
@@ -1,4 +1,4 @@
|
||||
using DTLib.Ben.Demystifier;
|
||||
using DTLib.Demystifier;
|
||||
|
||||
namespace DTLib.Logging;
|
||||
|
||||
@@ -22,7 +22,7 @@ public static class LoggerExtensions
|
||||
public static void LogWarn(this ILogger logger, string context, object message)
|
||||
=> logger.Log(context, LogSeverity.Warn, message);
|
||||
|
||||
/// uses Ben.Demystifier to serialize exception
|
||||
/// uses Demystifier to serialize exception
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogWarn(this ILogger logger, string context, Exception ex)
|
||||
=> logger.Log(context, LogSeverity.Warn, ex.ToStringDemystified());
|
||||
@@ -32,7 +32,7 @@ public static class LoggerExtensions
|
||||
=> logger.Log(context, LogSeverity.Error, message);
|
||||
|
||||
|
||||
/// uses Ben.Demystifier to serialize exception
|
||||
/// uses Demystifier to serialize exception
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void LogError(this ILogger logger, string context, Exception ex)
|
||||
=> logger.Log(context, LogSeverity.Error, ex.ToStringDemystified());
|
||||
@@ -11,8 +11,8 @@ public class CompositeLogger : ILogger
|
||||
set
|
||||
{
|
||||
_debugLogEnabled = value;
|
||||
for (int i = 0; i < _loggers.Length; i++)
|
||||
_loggers[i].DebugLogEnabled = value;
|
||||
foreach (var childLogger in ChildLoggers)
|
||||
childLogger.DebugLogEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ public class CompositeLogger : ILogger
|
||||
set
|
||||
{
|
||||
_infoLogEnabled = true;
|
||||
for (int i = 0; i < _loggers.Length; i++)
|
||||
_loggers[i].InfoLogEnabled = value;
|
||||
foreach (var childLogger in ChildLoggers)
|
||||
childLogger.InfoLogEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ public class CompositeLogger : ILogger
|
||||
set
|
||||
{
|
||||
_warnLogEnabled = value;
|
||||
for (int i = 0; i < _loggers.Length; i++)
|
||||
_loggers[i].WarnLogEnabled = value;
|
||||
foreach (var childLogger in ChildLoggers)
|
||||
childLogger.WarnLogEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,46 +44,38 @@ public class CompositeLogger : ILogger
|
||||
set
|
||||
{
|
||||
_errorLogenabled = value;
|
||||
for (int i = 0; i < _loggers.Length; i++)
|
||||
_loggers[i].ErrorLogEnabled = value;
|
||||
foreach (var childLogger in ChildLoggers)
|
||||
childLogger.ErrorLogEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ILogFormat Format { get; set; }
|
||||
public readonly List<ILogger> ChildLoggers;
|
||||
|
||||
protected ILogger[] _loggers;
|
||||
private bool _debugLogEnabled =
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
private bool _debugLogEnabled;
|
||||
private bool _infoLogEnabled = true;
|
||||
private bool _warnLogEnabled = true;
|
||||
private bool _errorLogenabled = true;
|
||||
|
||||
public CompositeLogger(ILogFormat format, params ILogger[] loggers)
|
||||
public CompositeLogger(ILogFormat format, params ILogger[] childLoggers)
|
||||
{
|
||||
Format = format;
|
||||
_loggers = loggers;
|
||||
ChildLoggers = new List<ILogger>(childLoggers);
|
||||
}
|
||||
|
||||
public CompositeLogger(params ILogger[] loggers) : this(new DefaultLogFormat(), loggers)
|
||||
public CompositeLogger(params ILogger[] childLoggers) : this(new DefaultLogFormat(), childLoggers)
|
||||
{}
|
||||
|
||||
|
||||
public void Log(string context, LogSeverity severity, object message, ILogFormat format)
|
||||
{
|
||||
if(!this.CheckSeverity(severity))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _loggers.Length; i++)
|
||||
_loggers[i].Log(context, severity, message, format);
|
||||
foreach (var childLogger in ChildLoggers)
|
||||
childLogger.Log(context, severity, message, format);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (int i = 0; i < _loggers.Length; i++)
|
||||
_loggers[i].Dispose();
|
||||
foreach (var childLogger in ChildLoggers)
|
||||
childLogger.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,7 @@ namespace DTLib.Logging;
|
||||
// вывод лога в консоль и файл
|
||||
public class ConsoleLogger : ILogger
|
||||
{
|
||||
public bool DebugLogEnabled { get; set; } =
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
public bool DebugLogEnabled { get; set; } = false;
|
||||
public bool InfoLogEnabled { get; set; } = true;
|
||||
public bool WarnLogEnabled { get; set; } = true;
|
||||
public bool ErrorLogEnabled { get; set; } = true;
|
||||
@@ -32,7 +27,7 @@ public class ConsoleLogger : ILogger
|
||||
|
||||
var msg = format.CreateMessage(context, severity, message);
|
||||
lock (consolelocker)
|
||||
ColoredConsole.Write(ColorFromSeverity(severity),msg);
|
||||
ColoredConsole.WriteLine(msg, fg: ColorFromSeverity(severity));
|
||||
}
|
||||
|
||||
private static ConsoleColor ColorFromSeverity(LogSeverity severity)
|
||||
@@ -34,7 +34,7 @@ public class ContextLogger : ILogger
|
||||
public void LogWarn(object message)
|
||||
=> ParentLogger.LogWarn(Context, message);
|
||||
|
||||
/// uses Ben.Demystifier to serialize exception
|
||||
/// uses Demystifier to serialize exception
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void LogWarn(Exception ex)
|
||||
=> ParentLogger.LogWarn(Context, ex);
|
||||
@@ -43,7 +43,7 @@ public class ContextLogger : ILogger
|
||||
public void LogError(object message)
|
||||
=> ParentLogger.LogError(Context, message);
|
||||
|
||||
/// uses Ben.Demystifier to serialize exception
|
||||
/// uses Demystifier to serialize exception
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void LogError(Exception ex)
|
||||
=> ParentLogger.LogError(Context, ex);
|
||||
@@ -2,12 +2,7 @@
|
||||
|
||||
public class FileLogger : ILogger
|
||||
{
|
||||
public bool DebugLogEnabled { get; set; } =
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
public bool DebugLogEnabled { get; set; } = false;
|
||||
public bool InfoLogEnabled { get; set; } = true;
|
||||
public bool WarnLogEnabled { get; set; } = true;
|
||||
public bool ErrorLogEnabled { get; set; } = true;
|
||||
@@ -20,7 +15,7 @@ public class FileLogger : ILogger
|
||||
{
|
||||
Format = format;
|
||||
LogfileName = logfile;
|
||||
LogfileStream = File.OpenAppend(logfile);
|
||||
LogfileStream = File.OpenWrite(logfile);
|
||||
}
|
||||
|
||||
public FileLogger(IOPath logfile) : this(logfile, new DefaultLogFormat())
|
||||
@@ -41,7 +36,9 @@ public class FileLogger : ILogger
|
||||
var msg = format.CreateMessage(context, severity, message);
|
||||
lock (LogfileStream)
|
||||
{
|
||||
LogfileStream.FluentWrite(msg.ToBytes(StringConverter.UTF8)).Flush();
|
||||
LogfileStream.FluentWriteString(msg)
|
||||
.FluentWriteByte('\n'.ToByte())
|
||||
.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +46,8 @@ public class FileLogger : ILogger
|
||||
{
|
||||
try
|
||||
{
|
||||
LogfileStream?.Flush();
|
||||
LogfileStream?.Dispose();
|
||||
LogfileStream.Flush();
|
||||
LogfileStream.Dispose();
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
44
DTLib/Logging/Loggers/LineWriterLogger.cs
Normal file
44
DTLib/Logging/Loggers/LineWriterLogger.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
namespace DTLib.Logging;
|
||||
|
||||
public interface ILineWriter : IDisposable
|
||||
{
|
||||
public void WriteLine(string msg);
|
||||
}
|
||||
|
||||
public class LineWriterLogger : ILogger
|
||||
{
|
||||
public bool DebugLogEnabled { get; set; } = false;
|
||||
public bool InfoLogEnabled { get; set; } = true;
|
||||
public bool WarnLogEnabled { get; set; } = true;
|
||||
public bool ErrorLogEnabled { get; set; } = true;
|
||||
public ILogFormat Format { get; set; }
|
||||
|
||||
private readonly ILineWriter _lineWriter;
|
||||
|
||||
public LineWriterLogger(ILineWriter lineWriter, ILogFormat format)
|
||||
{
|
||||
_lineWriter = lineWriter;
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public LineWriterLogger(ILineWriter lineWriter)
|
||||
: this(lineWriter, new DefaultLogFormat())
|
||||
{}
|
||||
|
||||
public void Log(string context, LogSeverity severity, object message, ILogFormat format)
|
||||
{
|
||||
if(!this.CheckSeverity(severity))
|
||||
return;
|
||||
|
||||
var msg = format.CreateMessage(context, severity, message);
|
||||
lock (_lineWriter)
|
||||
_lineWriter.WriteLine(msg);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_lineWriter.Dispose();
|
||||
}
|
||||
|
||||
~LineWriterLogger() => Dispose();
|
||||
}
|
||||
@@ -4,4 +4,6 @@ public static class MyTimeFormat
|
||||
{
|
||||
public const string ForFileNames="yyyy.MM.dd_HH-mm-ss_zz";
|
||||
public const string ForText="yyyy.MM.dd HH:mm:ss zz";
|
||||
public const string TimeOnly="HH:mm:ss";
|
||||
public const string DateOnly="yyyy.MM.dd";
|
||||
}
|
||||
@@ -58,15 +58,20 @@ public class FSP
|
||||
return fileStream.ToArray();
|
||||
}
|
||||
|
||||
private static readonly byte[] fspHeaderStrBytes = "FSP_file_size:".ToBytes();
|
||||
public void DownloadToStream(System.IO.Stream fileStream)
|
||||
{
|
||||
lock (MainSocket)
|
||||
{
|
||||
BytesDownloaded = 0;
|
||||
Filesize = BitConverter.ToInt64(MainSocket.GetPackage(), 0);
|
||||
var fspHeader = MainSocket.GetPackage();
|
||||
if (!fspHeader.StartsWith(fspHeaderStrBytes))
|
||||
throw new Exception(
|
||||
$"invalid fsp header: '{fspHeader.BytesToString()}' " +
|
||||
$"({fspHeader.MergeToString(',')})");
|
||||
Filesize = BitConverter.ToInt64(fspHeader, fspHeaderStrBytes.Length);
|
||||
if (Filesize < 0)
|
||||
throw new Exception("FileSize < 0");
|
||||
MainSocket.SendPackage("ready");
|
||||
|
||||
while (BytesDownloaded < Filesize)
|
||||
{
|
||||
@@ -89,8 +94,11 @@ public class FSP
|
||||
Filesize = fileStream.Length;
|
||||
lock (MainSocket)
|
||||
{
|
||||
MainSocket.SendPackage(BitConverter.GetBytes(Filesize));
|
||||
MainSocket.GetAnswer("ready");
|
||||
byte[] filesizeBytes = BitConverter.GetBytes(Filesize);
|
||||
byte[] fspHeader = new byte[fspHeaderStrBytes.Length + filesizeBytes.Length];
|
||||
fspHeaderStrBytes.CopyTo(fspHeader, 0);
|
||||
filesizeBytes.CopyTo(fspHeader, fspHeaderStrBytes.Length);
|
||||
MainSocket.SendPackage(fspHeader);
|
||||
|
||||
while (BytesUploaded < Filesize)
|
||||
{
|
||||
@@ -1,39 +1,58 @@
|
||||
using System.Threading;
|
||||
|
||||
namespace DTLib;
|
||||
namespace DTLib;
|
||||
|
||||
//
|
||||
// простой и понятный класс для выполнения каких-либо действий в отдельном потоке раз в некоторое время
|
||||
//
|
||||
public class Timer
|
||||
public class Timer : IDisposable
|
||||
{
|
||||
Task TimerTask;
|
||||
bool Repeat;
|
||||
CancellationTokenSource кансель = new();
|
||||
private Task? _timerTask;
|
||||
private CancellationTokenSource _cts = new();
|
||||
private bool _repeat;
|
||||
private int _delayMs;
|
||||
private Action _action;
|
||||
|
||||
// таймер сразу запускается
|
||||
public Timer(bool repeat, int delay, Action method)
|
||||
public Timer(bool repeat, int delayMs, Action action)
|
||||
{
|
||||
Repeat = repeat;
|
||||
TimerTask = new Task(() =>
|
||||
{
|
||||
do
|
||||
{
|
||||
if (кансель.Token.IsCancellationRequested)
|
||||
return;
|
||||
Task.Delay(delay).Wait();
|
||||
method();
|
||||
} while (Repeat);
|
||||
});
|
||||
_repeat = repeat;
|
||||
_delayMs = delayMs;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void InvokeAction()
|
||||
{
|
||||
_action();
|
||||
}
|
||||
|
||||
public void Start() => TimerTask.Start();
|
||||
public void Start()
|
||||
{
|
||||
_timerTask = new Task(Loop);
|
||||
_timerTask.Start();
|
||||
}
|
||||
|
||||
// завершение потока
|
||||
public void Stop()
|
||||
{
|
||||
Repeat = false;
|
||||
кансель.Cancel();
|
||||
_cts.Cancel();
|
||||
_cts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
private void Loop()
|
||||
{
|
||||
var ct = _cts.Token;
|
||||
do
|
||||
{
|
||||
if (ct.IsCancellationRequested)
|
||||
return;
|
||||
Task.Delay(_delayMs).Wait();
|
||||
if (ct.IsCancellationRequested)
|
||||
return;
|
||||
InvokeAction();
|
||||
} while (_repeat);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user