From eb24c9f94bf145db2943393cb193eb214aa306bb Mon Sep 17 00:00:00 2001 From: Timerix Date: Mon, 26 Jul 2021 00:01:51 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D1=8C=D1=82?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Color.cs | 24 +++ ColoredConsole.cs | 74 +++++++ CompressedArray.cs | 72 +++++++ DTLib.csproj | 55 +++++ DTLib.sln | 22 ++ Dtsod.cs | 419 +++++++++++++++++++++++++++++++++++++ DtsodParser2.cs | 207 ++++++++++++++++++ Filework.cs | 253 ++++++++++++++++++++++ Hasher.cs | 60 ++++++ Network.cs | 400 +++++++++++++++++++++++++++++++++++ Properties/AssemblyInfo.cs | 35 ++++ PublicLog.cs | 14 ++ SecureRandom.cs | 37 ++++ SimpleConverter.cs | 151 +++++++++++++ TImer.cs | 36 ++++ XXHash.cs | 180 ++++++++++++++++ cs9somefix.cs | 8 + 17 files changed, 2047 insertions(+) create mode 100644 Color.cs create mode 100644 ColoredConsole.cs create mode 100644 CompressedArray.cs create mode 100644 DTLib.csproj create mode 100644 DTLib.sln create mode 100644 Dtsod.cs create mode 100644 DtsodParser2.cs create mode 100644 Filework.cs create mode 100644 Hasher.cs create mode 100644 Network.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 PublicLog.cs create mode 100644 SecureRandom.cs create mode 100644 SimpleConverter.cs create mode 100644 TImer.cs create mode 100644 XXHash.cs create mode 100644 cs9somefix.cs diff --git a/Color.cs b/Color.cs new file mode 100644 index 0000000..db09e38 --- /dev/null +++ b/Color.cs @@ -0,0 +1,24 @@ +using System; + +namespace DTLib +{ + public class Color + { + public record RGBA(byte R, byte G, byte B, byte A) + { + public RGBA(byte[] arrayRGBA) : this(arrayRGBA[0], arrayRGBA[1], arrayRGBA[2], arrayRGBA[3]) + { + if (arrayRGBA.Length != 4) throw new Exception("Color.RGBA(byte[] arrayRGBA) error: arrayRGBA.Length != 4\n"); + } + } + + public record RGB(byte R, byte G, byte B) + { + public RGB(byte[] arrayRGB) : this(arrayRGB[0], arrayRGB[1], arrayRGB[2]) + { + if (arrayRGB.Length != 3) throw new Exception("Color.RGB(byte[] arrayRGB) error: arrayRGB.Length != 3\n"); + } + } + } + +} diff --git a/ColoredConsole.cs b/ColoredConsole.cs new file mode 100644 index 0000000..1d03111 --- /dev/null +++ b/ColoredConsole.cs @@ -0,0 +1,74 @@ +using System; +using System.Text; + +namespace DTLib +{ + // + // вывод и ввод цветного текста в консоли + // работает медленнее чем хотелось бы + // + public static class ColoredConsole + { + // парсит название цвета в ConsoleColor + public static ConsoleColor ParseColor(string color) + { + return 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 "gray": + "gray" => ConsoleColor.Gray, + //case "black": + "black" => ConsoleColor.Black, + _ => throw new Exception($"ColoredConsole.ParseColor({color}) error: incorrect color"), + }; + } + + // вывод цветного текста + public static void Write(params string[] input) + { + if (input.Length == 1) + { + if (Console.ForegroundColor != ConsoleColor.Gray) Console.ForegroundColor = ConsoleColor.Gray; + Console.Write(input[0]); + } + else if (input.Length % 2 == 0) + { + StringBuilder strB = new(); + for (ushort i = 0; i < input.Length; i++) + { + var c = ParseColor(input[i]); + if (Console.ForegroundColor != c) + { + Console.Write(strB.ToString()); + Console.ForegroundColor = c; + strB.Clear(); + } + strB.Append(input[++i]); + } + if (strB.Length > 0) Console.Write(strB.ToString()); + } + else throw new Exception("ColoredConsole.Write() error: every text string must have color string before"); + } + + // ввод цветного текста + public static string Read(string color) + { + var c = ParseColor(color); + if (Console.ForegroundColor != c) Console.ForegroundColor = c; + return Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/CompressedArray.cs b/CompressedArray.cs new file mode 100644 index 0000000..8b6b69e --- /dev/null +++ b/CompressedArray.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace DTLib +{ + public class CompressedArray + { + public class Array1D where T : IComparable + { + byte[] Description; + T[] Memory; + + public Array1D() { } + public Array1D(T[] sourceArray) + { + CompressArray(sourceArray); + } + + public void CompressArray(T[] sourceArray) + { + var listMem = new List(); + var listDesc = new List(); + 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 if (sum == index) output = Memory[i]; + else output = Memory[i - 1]; + } + storageUsing.ReleaseMutex(); + return output; + } + } + } +} diff --git a/DTLib.csproj b/DTLib.csproj new file mode 100644 index 0000000..572c5c2 --- /dev/null +++ b/DTLib.csproj @@ -0,0 +1,55 @@ + + + + + Debug + AnyCPU + {CE793497-2D5C-42D8-B311-E9B32AF9CDFB} + Library + Properties + DTLib + DTLib + v4.8 + 9.0 + 512 + true + + + none + true + bin\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DTLib.sln b/DTLib.sln new file mode 100644 index 0000000..60172e0 --- /dev/null +++ b/DTLib.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31410.357 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DTLib", "DTLib.csproj", "{CE793497-2D5C-42D8-B311-E9B32AF9CDFB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Build|Any CPU = Build|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CE793497-2D5C-42D8-B311-E9B32AF9CDFB}.Build|Any CPU.ActiveCfg = Build|Any CPU + {CE793497-2D5C-42D8-B311-E9B32AF9CDFB}.Build|Any CPU.Build.0 = Build|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {64E0B2C4-C103-43B8-AF8C-03C1C2302008} + EndGlobalSection +EndGlobal diff --git a/Dtsod.cs b/Dtsod.cs new file mode 100644 index 0000000..f2c8865 --- /dev/null +++ b/Dtsod.cs @@ -0,0 +1,419 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using static DTLib.PublicLog; + +namespace DTLib +{ + // + // это как json но не совсем + // + public class Dtsod : Dictionary + { + static readonly bool debug = false; + + public string Text { get; } + //public Dictionary Values { get; set; } + public Dtsod(string text) + { + Text = text; + foreach (KeyValuePair pair in ParseNew(text)) + Add(pair.Key, pair.Value); + } + + // выдаёт Exception + new public dynamic this[string key] + { + get + { + if (TryGetValue(key, out dynamic value)) return value; + else throw new Exception($"Dtsod[{key}] key not found"); + } + set + { + if (TrySetValue(key, value)) return; + else throw new Exception($"Dtsod[{key}] key not found"); + } + } + + // не выдаёт KeyNotFoundException + new public 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, + /*Double, + Long, + Ulong, + Short, + Ushort, + Int, + Uint, + Null, + Boolean,*/ + Default + } + + Dictionary ParseNew(string text) + { + Dictionary parsed = new(); + int i = 0; + for (; i < text.Length; i++) ReadName(); + return parsed; + + void ReadName() + { + void ReadCommentLine() + { + for (; i < text.Length && text[i] != '\n'; i++) ; + } + + bool isListElem = false; + dynamic value = null; + StringBuilder defaultNameBuilder = new(); + + if (debug) LogNoTime("m", "ReadName"); + for (; i < text.Length; i++) + { + if (debug) LogNoTime("w", text[i].ToString()); + switch (text[i]) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case ':': + i++; + value = ReadValue(); + string name = defaultNameBuilder.ToString(); + if (debug) LogNoTime("c", $"parsed.Add({name}, {value})\n"); + if (isListElem) + { + if (!parsed.ContainsKey(name)) parsed.Add(name, new List()); + parsed[name].Add(value); + } + else parsed.Add(name, value); + if (debug) LogNoTime("g", "ReadName return\n"); + 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; + + string ReadString() + { + i++; + StringBuilder valueBuilder = new(); + valueBuilder.Append('"'); + for (; text[i] != '"' || text[i - 1] == '\\'; i++) + { + if (debug) LogNoTime("gray", text[i].ToString()); + valueBuilder.Append(text[i]); + } + valueBuilder.Append('"'); + if (debug) LogNoTime("gray", text[i].ToString()); + type = ValueType.String; + return valueBuilder.ToString(); + } + + List ReadList() + { + i++; + List output = new(); + StringBuilder valueBuilder = new(); + for (; text[i] != ']'; i++) + { + if (debug) LogNoTime("c", text[i].ToString()); + switch (text[i]) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case ',': + output.Add(ParseValueToRightType(valueBuilder.ToString())); + valueBuilder.Clear(); + break; + default: + valueBuilder.Append(text[i]); + break; + } + } + if (valueBuilder.Length > 0) + output.Add(ParseValueToRightType(valueBuilder.ToString())); + if (debug) LogNoTime("c", text[i].ToString()); + type = ValueType.List; + return output; + } + + Dictionary ReadComplex() + { + i++; + StringBuilder valueBuilder = new(); + for (; text[i] != '}'; i++) + { + if (debug) LogNoTime("y", text[i].ToString()); + if (text[i] == '"') + { + valueBuilder.Append(ReadString()); + } + else valueBuilder.Append(text[i]); + } + if (debug) LogNoTime("y", text[i].ToString()); + type = ValueType.Complex; + if (debug) LogNoTime("g", valueBuilder.ToString()); + return ParseNew(valueBuilder.ToString()); + } + + dynamic ParseValueToRightType(string stringValue) + { + + if (debug) LogNoTime("g", $"\nParseValueToRightType({stringValue})"); + return stringValue switch + { + _ when stringValue.Contains('"') => stringValue.Remove(stringValue.Length - 1).Remove(0, 1), + // bool + "true" or "false" => stringValue.ToBool(), + // null + "null" => null, + // double + _ when stringValue.Contains('.') => stringValue.ToDouble(), + // ushort, ulong, uint + _ when (stringValue.Length > 2 && stringValue[stringValue.Length - 2] == 'u') => stringValue[stringValue.Length - 1] switch + { + 's' => stringValue.Remove(stringValue.Length - 2).ToUShort(), + 'i' => stringValue.Remove(stringValue.Length - 2).ToUInt(), + 'l' => stringValue.Remove(stringValue.Length - 2).ToULong(), + _ => throw new Exception($"Dtsod.Parse.ReadValue() error: wrong type ") + }, + // short, long, int + _ => stringValue[stringValue.Length - 1] switch + { + 's' => stringValue.Remove(stringValue.Length - 1).ToShort(), + 'l' => stringValue.Remove(stringValue.Length - 1).ToLong(), + _ => stringValue.ToInt() + } + }; + } + + dynamic value = null; + StringBuilder defaultValueBuilder = new(); + if (debug) LogNoTime("m", "\nReadValue\n"); + for (; i < text.Length; i++) + { + if (debug) LogNoTime("b", text[i].ToString()); + switch (text[i]) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '"': + value = ReadString(); + break; + case ';': + if (debug) LogNoTime("g", $"\nReadValue returns type {type} value <{value}>\n"); + return type switch + { + ValueType.List or ValueType.Complex => value, + ValueType.String => ParseValueToRightType(value), + ValueType.Default => ParseValueToRightType(defaultValueBuilder.ToString()), + _ => throw new Exception($"Dtlib.Parse.ReadValue() error: can't convert value to type <{type}>") + }; + case '[': + value = ReadList(); + break; + case '{': + value = ReadComplex(); + break; + default: + defaultValueBuilder.Append(text[i]); + break; + } + } + throw new Exception("Dtsod.Parse.ReadValue error: end of text"); + } + } + + + Dictionary ParseOld(string text) + { + Dictionary output = new(); + StringBuilder nameStrB = new(); + StringBuilder valStrB = new(); + dynamic value = null; + bool readValue = false; + bool readString = false; + bool readListElem = false; + bool isList = false; + + dynamic StringToElse(string str) + { + if (readString) return str; + // bool + switch (str) + { + // предустановленные значения + case "true": return true; + case "false": return false; + case "null": return null; + default: + // double + if (str.Contains(".")) return SimpleConverter.ToDouble(str); + // ushort, uint, ulong + else if (str.Length > 2 && str[str.Length - 2] == 'u') + return str[str.Length - 1] switch + { + 's' => SimpleConverter.ToUShort(str.Remove(str.Length - 2)), + 'i' => SimpleConverter.ToUInt(str.Remove(str.Length - 2)), + 'l' => SimpleConverter.ToULong(str.Remove(str.Length - 2)), + _ => throw new Exception($"ParseConfig() error: unknown data type "), + }; + // short, int, long + else return str[str.Length - 1] switch + { + 's' => SimpleConverter.ToShort(str.Remove(str.Length - 1)), + 'l' => SimpleConverter.ToLong(str.Remove(str.Length - 1)), + _ => SimpleConverter.ToInt(str), + }; + } + } + + for (int i = 0; i < text.Length; i++) + { + if (debug) LogNoTime(text[i].ToString()); + + void ReadString() + { + i++; + while (text[i] != '"' || text[i - 1] == '\\') + { + if (debug) LogNoTime(text[i].ToString()); + valStrB.Append(text[i]); + i++; + } + } + + switch (text[i]) + { + case '{': + i++; + for (; text[i] != '}'; i++) + { + if (text[i] == '"') ReadString(); + else valStrB.Append(text[i]); + } + value = ParseOld(valStrB.ToString()); + valStrB.Clear(); + break; + case '}': + throw new Exception("ParseConfig() error: unexpected '}' at " + i + "char"); + case '"': + readString = true; + ReadString(); + break; + case ':': + readValue = true; + break; + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '[': + isList = true; + value = new List(); + break; + case ',': + case ']': + if (isList) value.Add(StringToElse(valStrB.ToString())); + else throw new Exception($"unexpected <{text[i]}> at text[{i}]"); + valStrB.Clear(); + break; + case ';': + // конвертация value в нужный тип данных + if (!isList && valStrB.Length > 0) value = StringToElse(valStrB.ToString()); + if (readListElem) + { + if (!output.ContainsKey(nameStrB.ToString())) output.Add(nameStrB.ToString(), new List()); + output[nameStrB.ToString()].Add(value); + } + else output.Add(nameStrB.ToString(), value); + nameStrB.Clear(); + valStrB.Clear(); + value = null; + readValue = false; + readString = false; + readListElem = false; + isList = false; + break; + // коммент + case '#': + for (; i < text.Length && text[i] != '\n'; i++) ; + break; + // если $ перед названием параметра поставить, значение (value) добавится в лист с таким названием (nameStrB.ToString()) + case '$': + if (nameStrB.ToString().Length != 0) throw new Exception("unexpected usage of '$' at char " + i.ToString()); + readListElem = true; + break; + default: + if (readValue) valStrB.Append(text[i]); + else nameStrB.Append(text[i]); + break; + }; + } + return output; + } + } +} diff --git a/DtsodParser2.cs b/DtsodParser2.cs new file mode 100644 index 0000000..b2ade7e --- /dev/null +++ b/DtsodParser2.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DTLib +{ + class DtsodParser2 + { + + enum ValueType + { + List, + Complex, + String, + Double, + Long, + Ulong, + Short, + Ushort, + Int, + Uint, + Null, + Boolean, + Default + } + + Dictionary ParseNew(string text) + { + Dictionary parsed = new(); + int i = 0; + for (; i < text.Length; i++) ReadName(); + return parsed; + + void ReadName() + { + bool isListElem = false; + + void ReadCommentLine() + { + for (; i < text.Length && text[i] != '\n'; i++) ; + } + + dynamic value = null; + StringBuilder defaultNameBuilder = new(); + + for (; i < text.Length; i++) + { + switch (text[i]) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case ':': + value = ReadValue(); + break; + // строка, начинающаяся с # будет считаться комментом + case '#': + ReadCommentLine(); + break; + case '}': + throw new Exception("ParseConfig() error: unexpected '}' at " + i + "char"); + // если $ перед названием параметра поставить, значение value добавится в лист с названием name + case '$': + if (defaultNameBuilder.ToString().Length != 0) throw new Exception("unexpected usage of '$' at char " + i.ToString()); + isListElem = true; + break; + case ';': + string name = defaultNameBuilder.ToString(); + if (isListElem) + { + if (!parsed.ContainsKey(name)) parsed.Add(name, new List()); + parsed[name].Add(value); + } + else parsed.Add(name, value); + return; + default: + defaultNameBuilder.Append(text[i]); + break; + } + } + } + + dynamic ReadValue() + { + ValueType type = ValueType.Default; + + string ReadString() + { + i++; + StringBuilder valueBuilder = new(); + for (; text[i] != '"' || text[i - 1] == '\\'; i++) + valueBuilder.Append(text[i]); + type = ValueType.String; + return valueBuilder.ToString(); + } + + List ReadList() + { + List output = new(); + StringBuilder valueBuilder = new(); + for (; text[i] != ']'; i++) + { + switch (text[i]) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case ',': + output.Add(valueBuilder.ToString()); + break; + default: + valueBuilder.Append(text[i]); + break; + } + } + type = ValueType.List; + return output; + } + + Dictionary ReadComplex() + { + i++; + StringBuilder valueBuilder = new(); + for (; text[i] != '}'; i++) + { + if (text[i] == '"') valueBuilder.Append(ReadString()); + else valueBuilder.Append(text[i]); + } + type = ValueType.Complex; + return ParseNew(valueBuilder.ToString()); + } + + dynamic value = null; + StringBuilder defaultValueBuilder = new(); + for (; i < text.Length; i++) + { + switch (text[i]) + { + case ' ': + case '\t': + case '\r': + case '\n': + break; + case '"': + value = ReadString(); + break; + case ';': + if (type == ValueType.Default) + { + string valueString = defaultValueBuilder.ToString(); + type = valueString switch + { + "true" or "false" => ValueType.Boolean, + "null" => ValueType.Null, + _ when valueString.Contains('.') => ValueType.Null, + _ when (valueString.Length > 2 && valueString[valueString.Length - 2] == 'u') => valueString[valueString.Length - 1] switch + { + 's' => ValueType.Ushort, + 'i' => ValueType.Uint, + 'l' => ValueType.Ulong, + _ => throw new Exception($"Dtsod.Parse.ReadValue() error: wrong type ") + }, + _ => valueString[valueString.Length - 1] switch + { + 's' => ValueType.Short, + 'l' => ValueType.Long, + _ => ValueType.Int + } + }; + } + + return type switch + { + ValueType.String or ValueType.List or ValueType.Complex => value, + ValueType.Double => SimpleConverter.ToDouble(value), + ValueType.Long => SimpleConverter.ToLong(value.Remove(value.Length - 1)), + ValueType.Ulong => SimpleConverter.ToULong(value.Remove(value.Length - 2)), + ValueType.Short => SimpleConverter.ToShort(value.Remove(value.Length - 1)), + ValueType.Ushort => SimpleConverter.ToUShort(value.Remove(value.Length - 2)), + ValueType.Int => SimpleConverter.ToInt(value), + ValueType.Uint => SimpleConverter.ToUInt(value), + ValueType.Boolean => SimpleConverter.ToBool(value), + ValueType.Null => null, + _ => throw new Exception($"Dtlib.Parse.ReadValue() error: can't convert value to type <{type}>") + }; + case '[': + value = ReadList(); + break; + case '{': + value = ReadComplex(); + break; + default: + defaultValueBuilder.Append(text[i]); + break; + } + } + throw new Exception("Dtsod.Parse.ReadValue error: end of text"); + } + } + } +} diff --git a/Filework.cs b/Filework.cs new file mode 100644 index 0000000..a3469b3 --- /dev/null +++ b/Filework.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; + +namespace DTLib +{ + // + // методы для работы с файловой системой + // + public static class Filework + { + // записывает текст в файл и закрывает файл + public static void LogToFile(string logfile, string msg) + { + lock (new object()) + { + File.AppendAllText(logfile, msg); + } + } + + // чтение параметров из конфига + 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 == "") 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"); + } + } + + + public static class Directory + { + public static bool Exists(string dir) => System.IO.Directory.Exists(dir); + + // создает папку, если её не существует + public static void Create(string dir) + { + if (!Directory.Exists(dir)) + { + // проверяет существование папки, в которой нужно создать dir + if (dir.Contains("\\") && !Directory.Exists(dir.Remove(dir.LastIndexOf('\\')))) + Create(dir.Remove(dir.LastIndexOf('\\'))); + System.IO.Directory.CreateDirectory(dir); + } + } + // копирует все файлы и папки + public static void Copy(string source_dir, string new_dir, bool owerwrite = false) + { + Create(new_dir); + List subdirs = new List(); + List files = GetAllFiles(source_dir, ref subdirs); + for (int i = 0; i < subdirs.Count; i++) + { + Create(subdirs[i].Replace(source_dir, new_dir)); + } + for (int i = 0; i < files.Count; i++) + { + string f = files[i].Replace(source_dir, new_dir); + File.Copy(files[i], f, owerwrite); + //PublicLog.Log(new string[] {"g", $"file <", "c", files[i], "b", "> have copied to <", "c", newfile, "b", ">\n'" }); + } + } + + // копирует все файлы и папки и выдаёт список конфликтующих файлов + public static void Copy(string source_dir, string new_dir, out List conflicts, bool owerwrite = false) + { + conflicts = new List(); + var subdirs = new List(); + var files = GetAllFiles(source_dir, ref subdirs); + Create(new_dir); + for (int i = 0; i < subdirs.Count; i++) + { + Create(subdirs[i].Replace(source_dir, new_dir)); + } + for (int i = 0; i < files.Count; i++) + { + string newfile = files[i].Replace(source_dir, new_dir); + if (File.Exists(newfile)) conflicts.Add(newfile); + File.Copy(files[i], newfile, owerwrite); + //PublicLog.Log(new string[] {"g", $"file <", "c", files[i], "b", "> have copied to <", "c", newfile, "b", ">\n'" }); + } + } + + // удаляет папку со всеми подпапками и файлами + public static void Delete(string dir) + { + var subdirs = new List(); + var files = GetAllFiles(dir, ref subdirs); + for (int i = 0; i < files.Count; i++) + File.Delete(files[i]); + for (int i = subdirs.Count - 1; i >= 0; i--) + System.IO.Directory.Delete(subdirs[i]); + System.IO.Directory.Delete(dir); + } + + public static string[] GetFiles(string dir) => System.IO.Directory.GetFiles(dir); + public static string[] GetFiles(string dir, string searchPattern) => System.IO.Directory.GetFiles(dir, searchPattern); + public static string[] GetDirectories(string dir) => System.IO.Directory.GetDirectories(dir); + + // выдает список всех файлов + public static List GetAllFiles(string dir) + { + List all_files = new List(); + string[] cur_files = Directory.GetFiles(dir); + for (int i = 0; i < cur_files.Length; i++) + { + all_files.Add(cur_files[i]); + //PublicLog.Log(new string[] { "b", "file found: <", "c", cur_files[i], "b", ">\n" }); + } + string[] cur_subdirs = Directory.GetDirectories(dir); + for (int i = 0; i < cur_subdirs.Length; i++) + { + //PublicLog.Log(new string[] { "b", "subdir found: <", "c", cur_subdirs[i], "b", ">\n" }); + all_files.AddRange(GetAllFiles(cur_subdirs[i])); + } + return all_files; + } + + // выдает список всех файлов и подпапок в папке + public static List GetAllFiles(string dir, ref List all_subdirs) + { + List all_files = new List(); + string[] cur_files = Directory.GetFiles(dir); + for (int i = 0; i < cur_files.Length; i++) + { + all_files.Add(cur_files[i]); + //PublicLog.Log(new string[] { "b", "file found: <", "c", cur_files[i], "b", ">\n" }); + } + string[] cur_subdirs = Directory.GetDirectories(dir); + for (int i = 0; i < cur_subdirs.Length; i++) + { + all_subdirs.Add(cur_subdirs[i]); + //PublicLog.Log(new string[] { "b", "subdir found: <", "c", cur_subdirs[i], "b", ">\n" }); + all_files.AddRange(GetAllFiles(cur_subdirs[i], ref all_subdirs)); + } + return all_files; + } + + public static string GetCurrent() => System.IO.Directory.GetCurrentDirectory(); + } + + public static class File + { + public static int GetSize(string file) => new System.IO.FileInfo(file).Length.ToInt(); + + public static bool Exists(string file) => System.IO.File.Exists(file); + + // если файл не существует, создаёт файл, создаёт папки из его пути + public static void Create(string file) + { + if (!File.Exists(file)) + { + if (file.Contains("\\")) Directory.Create(file.Remove(file.LastIndexOf('\\'))); + using var stream = System.IO.File.Create(file); + stream.Close(); + } + } + + public static void Copy(string srcPath, string newPath, bool replace = false) + { + if (!replace && Exists(newPath)) throw new Exception($"file <{newPath}> alredy exists"); + Create(newPath); + WriteAllBytes(newPath, ReadAllBytes(srcPath)); + } + + public static void Delete(string file) => System.IO.File.Delete(file); + + public static byte[] ReadAllBytes(string file) + { + using var stream = File.OpenRead(file); + int size = GetSize(file); + byte[] output = new byte[size]; + stream.Read(output, 0, size); + stream.Close(); + return output; + } + + public static string ReadAllText(string file) => ReadAllBytes(file).ToStr(); + + public static void WriteAllBytes(string file, byte[] content) + { + using var stream = File.OpenWrite(file); + stream.Write(content, 0, content.Length); + stream.Close(); + } + + public static void WriteAllText(string file, string content) => WriteAllBytes(file, content.ToBytes()); + + public static void AppendAllBytes(string file, byte[] content) + { + using var stream = File.OpenAppend(file); + stream.Write(content, 0, content.Length); + stream.Close(); + } + + public static void AppendAllText(string file, string content) => AppendAllBytes(file, content.ToBytes()); + + public static System.IO.FileStream OpenRead(string file) + { + if (!Exists(file)) throw new Exception($"file not found: <{file}>"); + return System.IO.File.OpenRead(file); + } + public static System.IO.FileStream OpenWrite(string file) + { + if (Exists(file)) Delete(file); + File.Create(file); + return System.IO.File.OpenWrite(file); + } + public static System.IO.FileStream OpenAppend(string file) + { + File.Create(file); + return System.IO.File.Open(file, System.IO.FileMode.Append); + } + } + } +} diff --git a/Hasher.cs b/Hasher.cs new file mode 100644 index 0000000..bec42c0 --- /dev/null +++ b/Hasher.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Security.Cryptography; +using static DTLib.Filework; + +namespace DTLib +{ + // + // хеширует массивы байтов алшоритмом SHA256 и файлы алгоримом XXHash32 + // + public class Hasher + { + readonly HashAlgorithm sha256 = SHA256.Create(); + readonly HashAlgorithm xxh32 = XXHash32.Create(); + + public Hasher() { } + + // хеш массива + public byte[] Hash(byte[] input) => sha256.ComputeHash(input); + + // хеш из двух массивов + public byte[] Hash(byte[] input, byte[] salt) + { + List rez = new List(); + 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(string filename) + { + using var fileStream = File.OpenRead(filename); + //var then = DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second; + var hash = xxh32.ComputeHash(fileStream); + //var now = DateTime.Now.Hour * 3600 + DateTime.Now.Minute * 60 + DateTime.Now.Second; + //PublicLog.Log($"xxh32 hash: {hash.HashToString()} time: {now - then}\n"); + fileStream.Close(); + return hash; + } + } +} diff --git a/Network.cs b/Network.cs new file mode 100644 index 0000000..7f53342 --- /dev/null +++ b/Network.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using static DTLib.Filework; +using static DTLib.PublicLog; + +namespace DTLib +{ + // + // весь униврсальный неткод тут + // большинство методов предназначены для работы с TCP сокетами (Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + // + public static class Network + { + + // ждёт пакет заданного размера с заданным началом и концом + public static byte[] GetPackage(this Socket socket) + { + int packageSize = 0; + byte[] data = new byte[2]; + // цикл выполняется пока не пройдёт 2000 мс + for (ushort s = 0; s < 400; s += 1) + { + if (packageSize == 0 && socket.Available >= 2) + { + socket.Receive(data, data.Length, 0); + packageSize = data.BytesToInt(); + + } + if (packageSize != 0 && socket.Available >= packageSize) + { + data = new byte[packageSize]; + socket.Receive(data, data.Length, 0); + return data; + } + else Thread.Sleep(5); + } + throw new Exception($"GetPackage() error: timeout. socket.Available={socket.Available}\n"); + } + + // отправляет пакет заданного размера, добавля в конец нули если длина data меньше чем packageSize + public static void SendPackage(this Socket socket, byte[] data) + { + if (data.Length > 65536) throw new Exception($"SendPackage() error: package is too big ({data.Length} bytes)"); + if (data.Length == 0) throw new Exception($"SendPackage() error: package has zero size"); + var list = new List(); + byte[] packageSize = data.Length.IntToBytes(); + if (packageSize.Length == 1) list.Add(0); + list.AddRange(packageSize); + list.AddRange(data); + socket.Send(list.ToArray()); + } + + /* + // скачивает файл с помощью FSP протокола + public static void FSP_Download(this Socket mainSocket, string filePath_server, string filePath_client) + { + Log("b", $"requesting file download: {filePath_server}\n"); + mainSocket.SendPackage("requesting file download".ToBytes()); + mainSocket.SendPackage(filePath_server.ToBytes()); + FSP_Download(mainSocket, filePath_client); + } + public static void FSP_Download(this Socket mainSocket, string filePath_client) + { + File.Create(filePath_client); + using var fileStream = File.OpenWrite(filePath_client); + var fileSize = mainSocket.GetPackage().ToStr().ToUInt(); + var hashstr = mainSocket.GetPackage().HashToString(); + mainSocket.SendPackage("ready".ToBytes()); + int packagesCount = 0; + byte[] buffer = new byte[5120]; + int fullPackagesCount = SimpleConverter.Truncate(fileSize / buffer.Length); + // рассчёт скорости + int seconds = 0; + var speedCounter = new Timer(true, 1000, () => + { + seconds++; + Log("c", $"speed= {packagesCount * buffer.Length / (seconds * 1000)} kb/s\n"); + }); + // получение файла + for (; packagesCount < fullPackagesCount; packagesCount++) + { + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + fileStream.Flush(); + } + speedCounter.Stop(); + // получение остатка + if ((fileSize - fileStream.Position) > 0) + { + mainSocket.SendPackage("remain request".ToBytes()); + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + } + fileStream.Flush(); + fileStream.Close(); + Log(new string[] { "g", $" downloaded {packagesCount * 5120 + buffer.Length} of {fileSize} bytes\n" }); + + } + + public static byte[] FSP_DownloadToMemory(this Socket mainSocket, string filePath_server) + { + Log("b", $"requesting file download: {filePath_server}\n"); + mainSocket.SendPackage("requesting file download".ToBytes()); + mainSocket.SendPackage(filePath_server.ToBytes()); + using var fileStream = new System.IO.MemoryStream(); + var fileSize = mainSocket.GetPackage().ToStr().ToUInt(); + var hashstr = mainSocket.GetPackage().HashToString(); + mainSocket.SendPackage("ready".ToBytes()); + int packagesCount = 0; + byte[] buffer = new byte[5120]; + int fullPackagesCount = SimpleConverter.Truncate(fileSize / buffer.Length); + // рассчёт скорости + int seconds = 0; + var speedCounter = new Timer(true, 1000, () => + { + seconds++; + Log("c", $"speed= {packagesCount * buffer.Length / (seconds * 1000)} kb/s\n"); + }); + // получение файла + for (; packagesCount < fullPackagesCount; packagesCount++) + { + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + fileStream.Flush(); + } + speedCounter.Stop(); + // получение остатка + if ((fileSize - fileStream.Position) > 0) + { + mainSocket.SendPackage("remain request".ToBytes()); + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + } + byte[] output = fileStream.GetBuffer(); + fileStream.Close(); + Log(new string[] { "g", $" downloaded {packagesCount * 5120 + buffer.Length} of {fileSize} bytes\n" }); + return output; + } + + // отдаёт файл с помощью FSP протокола + public static void FSP_Upload(this Socket mainSocket, string filePath) + { + Log("b", $"uploading file {filePath}\n"); + using var fileStream = File.OpenRead(filePath); + var fileSize = File.GetSize(filePath); + var fileHash = new Hasher().HashFile(filePath); + mainSocket.SendPackage(fileSize.ToString().ToBytes()); + mainSocket.SendPackage(fileHash); + if (mainSocket.GetPackage().ToStr() != "ready") throw new Exception("user socket isn't ready"); + byte[] buffer = new byte[5120]; + var hashstr = fileHash.HashToString(); + int packagesCount = 0; + int seconds = 0; + // рассчёт скорости + var speedCounter = new Timer(true, 1000, () => + { + seconds++; + Log("c", $"speed= {packagesCount * buffer.Length / (seconds * 1000)} kb/s\n"); + }); + // отправка файла + int fullPackagesCount = SimpleConverter.Truncate(fileSize / buffer.Length); + for (; packagesCount < fullPackagesCount; packagesCount++) + { + fileStream.Read(buffer, 0, buffer.Length); + mainSocket.SendPackage(buffer); + } + speedCounter.Stop(); + // досылка остатка + if ((fileSize - fileStream.Position) > 0) + { + if (mainSocket.GetPackage().ToStr() != "remain request") throw new Exception("FSP_Upload() error: didn't get remain request"); + buffer = new byte[(fileSize - fileStream.Position).ToInt()]; + fileStream.Read(buffer, 0, buffer.Length); + mainSocket.SendPackage(buffer); + } + fileStream.Close(); + Log(new string[] { "g", $" uploaded {packagesCount * 5120 + buffer.Length} of {fileSize} bytes\n" }); + } + */ + // получает с сайта публичный ip + public static string GetPublicIP() => new WebClient().DownloadString("https://ipv4bot.whatismyipaddress.com/"); + + // пингует айпи с помощью встроенной в винду проги, возвращает задержку + public static string PingIP(string address) + { + Process proc = new Process(); + proc.StartInfo.FileName = "cmd.exe"; + proc.StartInfo.Arguments = "/c @echo off & chcp 65001 >nul & ping -n 5 " + address; + proc.StartInfo.CreateNoWindow = true; + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.RedirectStandardOutput = true; + proc.Start(); + var outStream = proc.StandardOutput; + var rezult = outStream.ReadToEnd(); + rezult = rezult.Remove(0, rezult.LastIndexOf('=') + 2); + return rezult.Remove(rezult.Length - 4); + } + + public class FSP + { + Socket mainSocket; + public bool debug = true; + public FSP(Socket _mainSocket) => mainSocket = _mainSocket; + + public uint BytesDownloaded = 0; + public uint BytesUploaded = 0; + public uint Filesize = 0; + + // скачивает файл с помощью FSP протокола + public void DownloadFile(string filePath_server, string filePath_client) + { + if (debug) Log("b", $"requesting file download: {filePath_server}\n"); + mainSocket.SendPackage("requesting file download".ToBytes()); + mainSocket.SendPackage(filePath_server.ToBytes()); + DownloadFile(filePath_client); + } + + public void DownloadFile(string filePath_client) + { + File.Create(filePath_client); + using var fileStream = File.OpenWrite(filePath_client); + Filesize = mainSocket.GetPackage().ToStr().ToUInt(); + var hashstr = mainSocket.GetPackage().HashToString(); + mainSocket.SendPackage("ready".ToBytes()); + int packagesCount = 0; + byte[] buffer = new byte[5120]; + int fullPackagesCount = SimpleConverter.Truncate(Filesize / buffer.Length); + // рассчёт скорости + int seconds = 0; + var speedCounter = new Timer(true, 1000, () => + { + seconds++; + Log("c", $"speed= {packagesCount * buffer.Length / (seconds * 1000)} kb/s\n"); + }); + // получение файла + for (; packagesCount < fullPackagesCount; packagesCount++) + { + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + fileStream.Flush(); + } + speedCounter.Stop(); + // получение остатка + if ((Filesize - fileStream.Position) > 0) + { + mainSocket.SendPackage("remain request".ToBytes()); + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + } + fileStream.Flush(); + fileStream.Close(); + if (debug) Log(new string[] { "g", $" downloaded {packagesCount * 5120 + buffer.Length} of {Filesize} bytes\n" }); + } + public byte[] DownloadFileToMemory(string filePath_server) + { + if (debug) Log("b", $"requesting file download: {filePath_server}\n"); + mainSocket.SendPackage("requesting file download".ToBytes()); + mainSocket.SendPackage(filePath_server.ToBytes()); + using var fileStream = new System.IO.MemoryStream(); + var fileSize = mainSocket.GetPackage().ToStr().ToUInt(); + var hashstr = mainSocket.GetPackage().HashToString(); + mainSocket.SendPackage("ready".ToBytes()); + int packagesCount = 0; + byte[] buffer = new byte[5120]; + int fullPackagesCount = SimpleConverter.Truncate(fileSize / buffer.Length); + // рассчёт скорости + int seconds = 0; + var speedCounter = new Timer(true, 1000, () => + { + seconds++; + Log("c", $"speed= {packagesCount * buffer.Length / (seconds * 1000)} kb/s\n"); + }); + // получение файла + for (; packagesCount < fullPackagesCount; packagesCount++) + { + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + fileStream.Flush(); + } + speedCounter.Stop(); + // получение остатка + if ((fileSize - fileStream.Position) > 0) + { + mainSocket.SendPackage("remain request".ToBytes()); + buffer = mainSocket.GetPackage(); + fileStream.Write(buffer, 0, buffer.Length); + } + byte[] output = fileStream.GetBuffer(); + fileStream.Close(); + if (debug) Log(new string[] { "g", $" downloaded {packagesCount * 5120 + buffer.Length} of {fileSize} bytes\n" }); + return output; + } + + // отдаёт файл с помощью FSP протокола + public void UploadFile(string filePath) + { + if (debug) Log("b", $"uploading file {filePath}\n"); + using var fileStream = File.OpenRead(filePath); + Filesize = File.GetSize(filePath).ToUInt(); + var fileHash = new Hasher().HashFile(filePath); + mainSocket.SendPackage(Filesize.ToString().ToBytes()); + mainSocket.SendPackage(fileHash); + if (mainSocket.GetPackage().ToStr() != "ready") throw new Exception("user socket isn't ready"); + byte[] buffer = new byte[5120]; + var hashstr = fileHash.HashToString(); + int packagesCount = 0; + // отправка файла + int fullPackagesCount = SimpleConverter.Truncate(Filesize / buffer.Length); + for (; packagesCount < fullPackagesCount; packagesCount++) + { + fileStream.Read(buffer, 0, buffer.Length); + mainSocket.SendPackage(buffer); + } + // досылка остатка + if ((Filesize - fileStream.Position) > 0) + { + if (mainSocket.GetPackage().ToStr() != "remain request") throw new Exception("FSP_Upload() error: didn't get remain request"); + buffer = new byte[(Filesize - fileStream.Position).ToInt()]; + fileStream.Read(buffer, 0, buffer.Length); + mainSocket.SendPackage(buffer); + } + fileStream.Close(); + if (debug) Log(new string[] { "g", $" uploaded {packagesCount * 5120 + buffer.Length} of {Filesize} bytes\n" }); + } + + public void DownloadByManifest(string dirOnServer, string dirOnClient, bool overwrite = false, bool delete_excess = false) + { + if (!dirOnClient.EndsWith("\\")) dirOnClient += "\\"; + if (!dirOnServer.EndsWith("\\")) dirOnServer += "\\"; + Log("b", "downloading manifest <", "c", dirOnServer + "manifest.dtsod", "b", ">\n"); + var manifest = new Dtsod(DownloadFileToMemory(dirOnServer + "manifest.dtsod").ToStr()); + Log("g", $"found {manifest.Values.Count} files in manifest\n"); + var hasher = new Hasher(); + foreach (string fileOnServer in manifest.Keys) + { + string fileOnClient = dirOnClient+fileOnServer; + if (debug) Log("b", "file <", "c", fileOnClient, "b", ">... "); + if (!File.Exists(fileOnClient)) + { + if (debug) LogNoTime("y", "doesn't exist\n"); + DownloadFile(dirOnServer + fileOnServer, fileOnClient); + } + else if (overwrite && hasher.HashFile(fileOnClient).HashToString() != manifest[fileOnServer]) + { + if (debug) LogNoTime("y", "outdated\n"); + DownloadFile(dirOnServer + fileOnServer, fileOnClient); + } + else if (debug) LogNoTime("g", "without changes\n"); + } + // удаление лишних файлов + if (delete_excess) + { + List dirs = new(); + foreach (string file in Directory.GetAllFiles(dirOnClient, ref dirs)) + { + if (!manifest.ContainsKey(file)) + { + Log("y", $"deleting excess file: {file}"); + File.Delete(file); + } + } + // удаление пустых папок + foreach (string dir in dirs) + { + if (Directory.GetAllFiles(dir).Count == 0) + { + Log("y", $"deleting empty dir: {dir}"); + Directory.Delete(dir); + } + } + } + } + + static public void CreateManifest(string dir) + { + if (!dir.EndsWith("\\")) dir += "\\"; + Log($"b", $"creating manifest of {dir}\n"); + StringBuilder manifestBuilder = new(); + Hasher hasher = new(); + if (Directory.GetFiles(dir).Contains(dir + "manifest.dtsod")) File.Delete(dir + "manifest.dtsod"); + foreach (string _file in Directory.GetAllFiles(dir)) + { + string file = _file.Remove(0, dir.Length); + manifestBuilder.Append(file); + manifestBuilder.Append(": \""); + byte[] hash = hasher.HashFile(dir + file); + manifestBuilder.Append(hash.HashToString()); + manifestBuilder.Append("\";\n"); + } + File.WriteAllText(dir + "manifest.dtsod", manifestBuilder.ToString()); + Log($"g", $" manifest of {dir} created\n"); + } + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c0201c5 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// Общие сведения об этой сборке предоставляются следующим набором +// набора атрибутов. Измените значения этих атрибутов для изменения сведений, +// связанные со сборкой. +[assembly: AssemblyTitle("DTLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DTLib")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми +// для компонентов COM. Если необходимо обратиться к типу в этой сборке через +// COM, задайте атрибуту ComVisible значение TRUE для этого типа. +[assembly: ComVisible(false)] + +// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM +[assembly: Guid("ce793497-2d5c-42d8-b311-e9b32af9cdfb")] + +// Сведения о версии сборки состоят из указанных ниже четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер сборки +// Редакция +// +// Можно задать все значения или принять номера сборки и редакции по умолчанию +// используя "*", как показано ниже: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/PublicLog.cs b/PublicLog.cs new file mode 100644 index 0000000..aa08e32 --- /dev/null +++ b/PublicLog.cs @@ -0,0 +1,14 @@ +namespace DTLib +{ + // + // вывод логов со всех классов в библиотеке + // + public static class PublicLog + { + public delegate void LogDelegate(params string[] msg); + // вот к этому объекту подключайте методы для вывода логов + public static LogDelegate Log; + public static LogDelegate LogNoTime; + public static LogDelegate FSP_DownloadSpeed; + } +} diff --git a/SecureRandom.cs b/SecureRandom.cs new file mode 100644 index 0000000..f00bac9 --- /dev/null +++ b/SecureRandom.cs @@ -0,0 +1,37 @@ +using System.Security.Cryptography; + +namespace DTLib +{ + // + // Вычисление псевдослучайного числа из множества параметров. + // Работает медленнее чем класс 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; + } + } + }*/ + } +} diff --git a/SimpleConverter.cs b/SimpleConverter.cs new file mode 100644 index 0000000..22bb7a5 --- /dev/null +++ b/SimpleConverter.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DTLib +{ + // + // содержит методы расширения для различных операций и преобразований + // + public static class SimpleConverter + { + public static Encoding UTF8 = new UTF8Encoding(false); + // байты в кодировке UTF8 в строку + public static string ToStr(this byte[] bytes) => UTF8.GetString(bytes); + public static string ToStr(this List bytes) => UTF8.GetString(bytes.ToArray()); + + // хеш в виде массива байт в строку (хеш изначально не в кодировке UTF8, так что метод выше не работает с ним) + public static string HashToString(this byte[] hash) + { + var builder = new StringBuilder(); + for (int i = 0; i < hash.Length; i++) + { + builder.Append(hash[i].ToString("x2")); + } + return builder.ToString(); + } + + // строку в байты + public static byte[] ToBytes(this string str) => UTF8.GetBytes(str); + + // эти методы работают как надо, в отличии от стандартных, которые иногда дуркуют + public static bool StartsWith(this byte[] source, byte[] startsWith) + { + for (int i = 0; i < startsWith.Length; i++) + { + if (source[i] != startsWith[i]) return false; + } + return true; + } + + public static bool EndsWith(this byte[] source, byte[] endsWith) + { + for (int i = 0; i < endsWith.Length; i++) + { + if (source[source.Length - endsWith.Length + i] != endsWith[i]) return false; + } + return true; + } + + // Math.Truncate принимает как decimal, так и doublе, + // из-за чего вызов метода так: Math.Truncate(10/3) выдаст ошибку "неоднозначный вызов" + public static int Truncate(this T number) => Math.Truncate(number.ToDouble()).ToInt(); + + // сортирует в порядке возрастания элементы если это возможно, используя стандартный метод list.Sort(); + public static T[] Sort(this T[] array) + { + var list = array.ToList(); + list.Sort(); + return list.ToArray(); + } + + // массив в лист + public static List ToList(this T[] input) + { + var list = new List(); + list.AddRange(input); + return list; + } + + // удаление нескольких элементов массива + public static T[] RemoveRange(this T[] input, int startIndex, int count) + { + List list = input.ToList(); + list.RemoveRange(startIndex, count); + return list.ToArray(); + } + public static T[] RemoveRange(this T[] input, int startIndex) => input.RemoveRange(startIndex, input.Length - startIndex); + + // метод как у листов + public static bool Contains(this T[] array, T value) + { + for (int i = 0; i < array.Length; i++) + if (array[i].Equals(value)) return true; + return false; + } + + // конвертирует массив в строку + public static string MergeToString(this T[] array, string separator) + { + var b = new StringBuilder(); + for (int i = 0; i < array.Length; i++) + { + b.Append(array[i].ToString()); + b.Append(separator); + } + return b.ToString(); + } + + // сокращение конвертации + public static int ToInt(this T input) => Convert.ToInt32(input); + public static uint ToUInt(this T input) => Convert.ToUInt32(input); + public static long ToLong(this T input) => Convert.ToInt64(input); + public static ulong ToULong(this T input) => Convert.ToUInt64(input); + public static short ToShort(this T input) => Convert.ToInt16(input); + public static ushort ToUShort(this T input) => Convert.ToUInt16(input); + public static double ToDouble(this T input) => Convert.ToDouble(input, System.Globalization.CultureInfo.InvariantCulture); + public static byte ToByte(this T input) => Convert.ToByte(input); + public static sbyte ToSByte(this T input) => Convert.ToSByte(input); + public static bool ToBool(this T input) => Convert.ToBoolean(input); + + public static string AutoBuild(params object[] parts) + { + var builder = new StringBuilder(); + for (int i = 0; i < parts.Length; i++) + builder.Append(parts[i]); + return builder.ToString(); + } + + public static int BytesToInt(this byte[] bytes) + { + int output = 0; + for (ushort i = 0; i < bytes.Length; i++) output = output * 256 + bytes[i]; + return output; + } + + public static byte[] IntToBytes(this int num) + { + List output = new(); + while (num != 0) + { + output.Add(ToByte(num % 256)); + num = Truncate(num / 256); + } + output.Reverse(); + return output.ToArray(); + } + + public static string ToString(this IEnumerable collection, string separator) + { + StringBuilder builder = new(); + foreach (T elem in collection) + { + builder.Append(elem.ToString()); + builder.Append(separator); + } + builder.Remove(builder.Length - separator.Length, separator.Length); + return builder.ToString(); + } + + } +} \ No newline at end of file diff --git a/TImer.cs b/TImer.cs new file mode 100644 index 0000000..cb154ec --- /dev/null +++ b/TImer.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; + +namespace DTLib +{ + // + // простой и понятный класс для выполнения каких-либо действий в отдельном потоке раз в некоторое время + // + public class Timer + { + Thread TimerThread; + bool Repeat; + + // таймер сразу запускается + public Timer(bool repeat, int delay, Action method) + { + Repeat = repeat; + TimerThread = new Thread(() => + { + do + { + Thread.Sleep(delay); + method(); + } while (Repeat); + }); + TimerThread.Start(); + } + + // завершение потока + public void Stop() + { + Repeat = false; + TimerThread.Abort(); + } + } +} diff --git a/XXHash.cs b/XXHash.cs new file mode 100644 index 0000000..08c7c24 --- /dev/null +++ b/XXHash.cs @@ -0,0 +1,180 @@ +using System; +using System.Security.Cryptography; +namespace DTLib +{ + // + // честно взятый с гитхаба алгоритм хеширования + // выдаёт хеш в виде массива четырёх байтов + // + sealed class XXHash32 : HashAlgorithm + { + private const uint PRIME32_1 = 2654435761U; + private const uint PRIME32_2 = 2246822519U; + private const uint PRIME32_3 = 3266489917U; + private const uint PRIME32_4 = 668265263U; + private const uint PRIME32_5 = 374761393U; + private static readonly Func FuncGetLittleEndianUInt32; + private static readonly Func FuncGetFinalHashUInt32; + private uint _Seed32; + private uint _ACC32_1; + private uint _ACC32_2; + private uint _ACC32_3; + private uint _ACC32_4; + private uint _Hash32; + private int _RemainingLength; + private long _TotalLength = 0; + private int _CurrentIndex; + private byte[] _CurrentArray; + + static XXHash32() + { + if (BitConverter.IsLittleEndian) + { + FuncGetLittleEndianUInt32 = new Func((x, i) => + { + unsafe + { + fixed (byte* array = x) + { + return *(uint*)(array + i); + } + } + }); + FuncGetFinalHashUInt32 = new Func(i => (i & 0x000000FFU) << 24 | (i & 0x0000FF00U) << 8 | (i & 0x00FF0000U) >> 8 | (i & 0xFF000000U) >> 24); + } + else + { + FuncGetLittleEndianUInt32 = new Func((x, i) => + { + unsafe + { + fixed (byte* array = x) + { + return (uint)(array[i++] | (array[i++] << 8) | (array[i++] << 16) | (array[i] << 24)); + } + } + }); + FuncGetFinalHashUInt32 = new Func(i => i); + } + } + + // Creates an instance of class by default seed(0). + // + public static new XXHash32 Create() => new XXHash32(); + + // Initializes a new instance of the class by default seed(0). + public XXHash32() => Initialize(0); + + // Initializes a new instance of the class, and sets the to the specified value. + // Represent the seed to be used for xxHash32 computing. + public XXHash32(uint seed) => Initialize(seed); + + // Gets the value of the computed hash code. + // Hash computation has not yet completed. + public uint HashUInt32 => State == 0 ? _Hash32 : throw new InvalidOperationException("Hash computation has not yet completed."); + + // Gets or sets the value of seed used by xxHash32 algorithm. + // Hash computation has not yet completed. + public uint Seed + { + get => _Seed32; + set + { + if (value != _Seed32) + { + if (State != 0) throw new InvalidOperationException("Hash computation has not yet completed."); + _Seed32 = value; + Initialize(); + } + } + } + + // Initializes this instance for new hash computing. + public override void Initialize() + { + _ACC32_1 = _Seed32 + PRIME32_1 + PRIME32_2; + _ACC32_2 = _Seed32 + PRIME32_2; + _ACC32_3 = _Seed32 + 0; + _ACC32_4 = _Seed32 - PRIME32_1; + } + + // Routes data written to the object into the hash algorithm for computing the hash. + // The input to compute the hash code for. + // The offset into the byte array from which to begin using data. + // The number of bytes in the byte array to use as data. + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + if (State != 1) State = 1; + var size = cbSize - ibStart; + _RemainingLength = size & 15; + if (cbSize >= 16) + { + var limit = size - _RemainingLength; + do + { + _ACC32_1 = Round32(_ACC32_1, FuncGetLittleEndianUInt32(array, ibStart)); + ibStart += 4; + _ACC32_2 = Round32(_ACC32_2, FuncGetLittleEndianUInt32(array, ibStart)); + ibStart += 4; + _ACC32_3 = Round32(_ACC32_3, FuncGetLittleEndianUInt32(array, ibStart)); + ibStart += 4; + _ACC32_4 = Round32(_ACC32_4, FuncGetLittleEndianUInt32(array, ibStart)); + ibStart += 4; + } while (ibStart < limit); + } + _TotalLength += cbSize; + if (_RemainingLength != 0) + { + _CurrentArray = array; + _CurrentIndex = ibStart; + } + } + + // Finalizes the hash computation after the last data is processed by the cryptographic stream object. + // The computed hash code. + protected override byte[] HashFinal() + { + if (_TotalLength >= 16) + { + _Hash32 = RotateLeft32(_ACC32_1, 1) + RotateLeft32(_ACC32_2, 7) + RotateLeft32(_ACC32_3, 12) + RotateLeft32(_ACC32_4, 18); + } + else + { + _Hash32 = _Seed32 + PRIME32_5; + } + _Hash32 += (uint)_TotalLength; + while (_RemainingLength >= 4) + { + _Hash32 = RotateLeft32(_Hash32 + FuncGetLittleEndianUInt32(_CurrentArray, _CurrentIndex) * PRIME32_3, 17) * PRIME32_4; + _CurrentIndex += 4; + _RemainingLength -= 4; + } + unsafe + { + fixed (byte* arrayPtr = _CurrentArray) + { + while (_RemainingLength-- >= 1) + { + _Hash32 = RotateLeft32(_Hash32 + arrayPtr[_CurrentIndex++] * PRIME32_5, 11) * PRIME32_1; + } + } + } + _Hash32 = (_Hash32 ^ (_Hash32 >> 15)) * PRIME32_2; + _Hash32 = (_Hash32 ^ (_Hash32 >> 13)) * PRIME32_3; + _Hash32 ^= _Hash32 >> 16; + _TotalLength = State = 0; + return BitConverter.GetBytes(FuncGetFinalHashUInt32(_Hash32)); + } + + private static uint Round32(uint input, uint value) => RotateLeft32(input + (value * PRIME32_2), 13) * PRIME32_1; + + private static uint RotateLeft32(uint value, int count) => (value << count) | (value >> (32 - count)); + + private void Initialize(uint seed) + { + HashSizeValue = 32; + _Seed32 = seed; + Initialize(); + } + } +} diff --git a/cs9somefix.cs b/cs9somefix.cs new file mode 100644 index 0000000..b5c71f9 --- /dev/null +++ b/cs9somefix.cs @@ -0,0 +1,8 @@ +// включает init и record из c# 9.0 +using System.ComponentModel; + +namespace System.Runtime.CompilerServices +{ + [EditorBrowsable(EditorBrowsableState.Never)] + public class IsExternalInit { } +}