using System; using System.Collections.Generic; using System.Linq; using System.Text; public class Program { public static void Main() { AnyBaseConverter converter10 = new("0123456789"); AnyBaseConverter converter62 = new("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); string year = "9999"; string month = "12"; string day = "31"; string hours = "23"; string minutes = "59"; string seconds = "59"; string milliseconds = "999"; string dt0 = $"{year}{month}{day}{hours}{minutes}{seconds}{milliseconds}"; // yyyyMMddHHmmssfff string sn = "99999999999999999"; // 17 digits of serial number string sum = "99999"; // 5 digits of transaction sum string s0 = dt0 + sn + sum; Console.WriteLine($"initial: {s0.Length} {s0}"); string dayOfYear = "365"; // december 31 string timeInSeconds = "86399"; // 23:59:59 string dt1 = $"{year}{dayOfYear}{timeInSeconds}{milliseconds}"; string s1 = dt1 + sn + sum; Console.WriteLine($"base10: {s1.Length} {s1}"); byte[] sBytes = converter10.Decode(s1); string sBytesStr = new StringBuilder() .AppendJoin(", ", sBytes.Select(b => b.ToString())) .ToString(); Console.WriteLine($"base256: {sBytes.Length} [{sBytesStr}]"); string s2 = converter62.Encode(sBytes); Console.WriteLine($"base62: {s2.Length} {s2}"); } } public class AnyBaseConverter { // Adapted code of Base62 encoding from: // https://github .com/ghost1face/base62/blob/master/Base62/Base62Converter.cs public AnyBaseConverter(string characterSet) { if (characterSet == "") throw new ArgumentException("Character set cannot be empty", characterSet); if (characterSet.Length > 256) throw new ArgumentException("Character set length must not exceed 256 characters", characterSet); this.Base = characterSet.Length; this.CharacterSet = characterSet; this.CharacterIds = characterSet.Select((c, i) => (c, i)).ToDictionary(ci => ci.c, ci => ci.i); if (this.CharacterIds.Count != this.CharacterSet.Length) throw new ArgumentException("Characters in character set must not repeat", "characterSet"); } public int Base { get; } public string CharacterSet { get; } private readonly Dictionary CharacterIds; public string Encode(byte[] bytes) { byte[] converted = this.ConvertBase(bytes, 256, this.Base); StringBuilder builder = new StringBuilder(converted.Length); foreach (byte c in converted.AsSpan()) builder.Append(this.CharacterSet[c]); return builder.ToString(); } public byte[] Decode(string value) { byte[] arr = new byte[value.Length]; for (int i = 0; i < arr.Length; i++) { int characterId = this.CharacterIds.GetValueOrDefault(value[i], -1); if (characterId == -1) throw new ArgumentException("Character must be in specified set, found: " + value); arr[i] = (byte)characterId; } byte[] converted = ConvertBase(arr, this.Base, 256); return converted; } private byte[] ConvertBase(byte[] source, int sourceBase, int targetBase) { int count; List result = new(); List quotient = new(); while ((count = source.Length) > 0) { quotient.Clear(); int remainder = 0; for (var i = 0; i < count; i++) { int accumulator = source[i] + remainder * sourceBase; byte digit = (byte)((accumulator - (accumulator % targetBase)) / targetBase); remainder = accumulator % targetBase; if (quotient.Count > 0 || digit != 0) quotient.Add(digit); } result.Add((byte)remainder); source = quotient.ToArray(); } byte[] output = new byte[result.Count]; for (var (i, j) = (0, result.Count - 1); j >= 0; i++, j--) output[i] = result[j]; return output; } }