diff --git a/GeminiLab.Core2/Base64Extensions.cs b/GeminiLab.Core2/Base64Extensions.cs new file mode 100644 index 0000000..a969ff0 --- /dev/null +++ b/GeminiLab.Core2/Base64Extensions.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GeminiLab.Core2 { + public static class Base64Extensions { + public static string Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + private static byte[] decodeTable = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; + + public static string ToBase64(this byte[] source) { + var tail = source.Length % 3; + var groups = source.Length / 3; + var outputLen = groups * 4 + (tail > 0 ? 4 : 0); + + var sb = new StringBuilder(outputLen); + + for (int i = 0; i < groups; ++i) { + int val = source[i * 3] << 16 | source[i * 3 + 1] << 8 | source[i * 3 + 2]; + + sb.Append(Base64Chars[(val >> 18)]); + sb.Append(Base64Chars[(val >> 12) & 0x3F]); + sb.Append(Base64Chars[(val >> 6) & 0x3F]); + sb.Append(Base64Chars[val & 0x3F]); + } + + if (tail == 1) { + int val = source[source.Length - 1]; + sb.Append(Base64Chars[val >> 2]); + sb.Append(Base64Chars[(val & 0x03) << 4]); + sb.Append("=="); + } else if (tail == 2) { + int val = source[source.Length - 2] << 8 | source[source.Length - 1]; + sb.Append(Base64Chars[val >> 10]); + sb.Append(Base64Chars[(val >> 4) & 0x3F]); + sb.Append(Base64Chars[(val & 0x0F) << 2]); + sb.Append("="); + } + + return sb.ToString(); + } + + public static string ToBase64(this string source) => source.ToBase64(Encoding.UTF8); + + public static string ToBase64(this string source, Encoding encoding) => encoding.GetBytes(source).ToBase64(); + + public static byte[] AsBase64(this string source) { + int len = source.Length; + + if (len % 4 != 0) throw new ArgumentException(nameof(source)); + if (len == 0) return new byte[0]; + + int sp = source[len - 1] == '=' ? (source[len - 2] == '=' ? 2 : 1) : 0; + int groups = len / 4; + int outputLen = groups * 3 - sp; + + byte[] rv = new byte[outputLen]; + + for (int i = 0; i < groups; ++i) { + int val = 0; + for (int j = 0; j < 4; ++j) { + int chr; + if ((chr = source[i * 4 + j]) >= 0x80 || chr < 0 || decodeTable[chr] >= 0x40) throw new ArgumentException(nameof(source)); + + val = (val << 6) | decodeTable[chr]; + } + + if (i == groups - 1) { + byte[] v = { (byte)(val >> 16), (byte)((val >> 8) & 0xFF), (byte)(val & 0xFF) }; + + for (int j = 0; j < 3; ++j) { + if (j + sp >= 3) { + if (v[j] != 0) throw new ArgumentException(nameof(source)); + } else { + rv[i * 3 + j] = v[j]; + } + } + } else { + rv[i * 3] = (byte)(val >> 16); + rv[i * 3 + 1] = (byte)((val >> 8) & 0xFF); + rv[i * 3 + 2] = (byte)(val & 0xFF); + } + } + + return rv; + } + + public static string DecodeBase64EncodedString(this string source) => source.DecodeBase64EncodedString(Encoding.UTF8); + + public static string DecodeBase64EncodedString(this string source, Encoding encoding) => encoding.GetString(source.AsBase64()); + } +} diff --git a/GeminiLab.Core2/GeminiLab.Core2.csproj b/GeminiLab.Core2/GeminiLab.Core2.csproj index 15ecf4b..ae22d54 100644 --- a/GeminiLab.Core2/GeminiLab.Core2.csproj +++ b/GeminiLab.Core2/GeminiLab.Core2.csproj @@ -8,9 +8,14 @@ Gemini Laboratory Gemini Laboratory GeminiLab.Core2 - 0.9.0.0 + 0.9.2.0 Copyright © Gemini Laboratory 2017 - 2018 - 0.9.0.0 + 0.9.2.0 + true + true + 0.9.2 + https://github.com/GeminiLab/GeminiLab.Core2/blob/master/LICENSE + https://github.com/GeminiLab/GeminiLab.Core2/ diff --git a/GeminiLab.Core2/InfiniteEnumerable.cs b/GeminiLab.Core2/InfiniteEnumerable.cs index 6270585..5d36741 100644 --- a/GeminiLab.Core2/InfiniteEnumerable.cs +++ b/GeminiLab.Core2/InfiniteEnumerable.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; namespace GeminiLab.Core2 { @@ -135,7 +134,7 @@ public T GetNext() { public void Reset() { _first = true; - _accumulator = default(T); + _accumulator = default; _en = _source.GetEnumerator(); } diff --git a/GeminiLab.Core2/ML/Json/JsonObjects.cs b/GeminiLab.Core2/ML/Json/JsonObjects.cs index 00b83b1..84093e5 100644 --- a/GeminiLab.Core2/ML/Json/JsonObjects.cs +++ b/GeminiLab.Core2/ML/Json/JsonObjects.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -7,7 +8,7 @@ public abstract class JsonValue { internal abstract void Stringify(JsonStringifyConfig config, int indent, StringBuilder target); internal static void MakeIndent(StringBuilder sb, int cnt) => sb.Append(' ', cnt * 4); - + public sealed override string ToString() { var sb = new StringBuilder(); @@ -39,11 +40,78 @@ public string ToStringForNetwork() { } public sealed class JsonObject : JsonValue { - public IEnumerable Values { get; } + private struct JsonObjectKeyComparer : IComparer { + public JsonObject Father; + + public int Compare(string x, string y) { + int xv = Father._keyOrder.ContainsKey(x) ? Father._keyOrder[x] : -1; + int yv = Father._keyOrder.ContainsKey(y) ? Father._keyOrder[y] : -1; + + return xv.CompareTo(yv); + } + } + + private readonly Dictionary _keyOrder; + private readonly SortedDictionary _values; + + public IEnumerable Values { + get { + foreach (var i in _values) yield return new JsonObjectKeyValuePair(new JsonString(i.Key), i.Value); + } + } + + public JsonValue this[JsonString str] { + get => this[str?.Value ?? throw new ArgumentNullException(nameof(str))]; + set => this[str?.Value ?? throw new ArgumentNullException(nameof(str))] = value; + } + + public JsonValue this[string str] { + get { + if (str == null) throw new ArgumentNullException(nameof(str)); + if (!_values.ContainsKey(str)) throw new KeyNotFoundException(); + + return _values[str]; + } + set { + if (str == null) throw new ArgumentNullException(nameof(str)); + if (value == null) throw new ArgumentNullException(nameof(value)); + + if (!_values.ContainsKey(str)) { + _keyOrder[str] = _keyOrder.Count; + } + + _values[str] = value; + } + } + + public void Append(JsonString key, JsonValue value) { + if (key == null) throw new ArgumentNullException(nameof(key)); + if (value == null) throw new ArgumentNullException(nameof(value)); + + if (_values.ContainsKey(key.Value)) throw new ArgumentOutOfRangeException(nameof(key)); + this[key] = value; + } + + public void Remove(JsonString key) { + if (key == null) throw new ArgumentNullException(nameof(key)); + + if (!_values.ContainsKey(key.Value)) throw new KeyNotFoundException(); + _keyOrder[key.Value] = -1; + _values.Remove(key.Value); + } + + public JsonObject() { + _keyOrder = new Dictionary(); + _values = new SortedDictionary(new JsonObjectKeyComparer { Father = this }); + } - // some (and clr) don't like tuples, make them happy public JsonObject(IEnumerable values) { - Values = values; + _keyOrder = new Dictionary(); + _values = new SortedDictionary(new JsonObjectKeyComparer { Father = this }); + + foreach (var i in values) { + Append(i.Key, i.Value); + } } internal override void Stringify(JsonStringifyConfig config, int indent, StringBuilder target) { @@ -51,7 +119,7 @@ internal override void Stringify(JsonStringifyConfig config, int indent, StringB target.Append("{}"); return; } - + bool compact = config.Contains(JsonStringifyConfig.Compact); bool expandObjects = config.Contains(JsonStringifyConfig.ExpandObjects); @@ -61,7 +129,7 @@ internal override void Stringify(JsonStringifyConfig config, int indent, StringB string tail = compact ? "}" : " }"; target.Append(expandObjects ? "{\n" : head); - + bool first = true; foreach (var i in Values) { if (!first) { @@ -88,10 +156,14 @@ internal override void Stringify(JsonStringifyConfig config, int indent, StringB } public sealed class JsonArray : JsonValue { - public IEnumerable Values { get; } + public List Values { get; } + + public JsonArray() { + Values = new List(); + } public JsonArray(IEnumerable values) { - Values = values; + Values = new List(values); } internal override void Stringify(JsonStringifyConfig config, int indent, StringBuilder target) { @@ -162,9 +234,11 @@ internal JsonNumber(string value) { if (int.TryParse(value, out int vint)) { IsFloat = false; ValueInt = vint; - } else { + } else if (double.TryParse(value, out double vfloat)) { IsFloat = true; - ValueFloat = double.Parse(value); + ValueFloat = vfloat; + } else { + throw new ArgumentOutOfRangeException(nameof(value)); } } diff --git a/TestConsole/Program.cs b/TestConsole/Program.cs index 51f4ce9..4cd1db3 100644 --- a/TestConsole/Program.cs +++ b/TestConsole/Program.cs @@ -1,16 +1,11 @@ using System; +using GeminiLab.Core2; using GeminiLab.Core2.ML.Json; namespace TestConsole { class Program { public static void Main(string[] args) { - var str = Console.In.ReadToEnd(); - - var jsonValue = JsonParser.Parse(str); - Console.WriteLine(jsonValue.ToString()); - Console.WriteLine(jsonValue.ToStringMinimized()); - Console.WriteLine(jsonValue.ToStringForNetwork()); - Console.WriteLine(jsonValue.ToStringPrettified()); + Console.WriteLine("6Iuf5Yip5Zu95a6255Sf5q275Lul".DecodeBase64EncodedString()); } } }